/* minideflate.c -- test deflate/inflate under specific conditions * Copyright (C) 2020 Nathan Moinvaziri * For conditions of distribution and use, see copyright notice in zlib.h */ #include #include #include #include #include #include #include #include "zbuild.h" #ifdef ZLIB_COMPAT # include "zlib.h" #else # include "zlib-ng.h" #endif #if defined(_WIN32) || defined(__CYGWIN__) # include # include # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) #else # define SET_BINARY_MODE(file) #endif #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif #define CHECK_ERR(err, msg) { \ if (err != Z_OK) { \ fprintf(stderr, "%s error: %d\n", msg, err); \ exit(1); \ } \ } /* =========================================================================== * deflate() using specialized parameters */ void deflate_params(FILE *fin, FILE *fout, int32_t read_buf_size, int32_t write_buf_size, int32_t level, int32_t window_bits, int32_t mem_level, int32_t strategy, int32_t flush) { PREFIX3(stream) c_stream; /* compression stream */ uint8_t *read_buf; uint8_t *write_buf; int32_t read; int err; read_buf = (uint8_t *)malloc(read_buf_size); if (read_buf == NULL) { fprintf(stderr, "failed to create read buffer (%d)\n", read_buf_size); return; } write_buf = (uint8_t *)malloc(write_buf_size); if (write_buf == NULL) { fprintf(stderr, "failed to create write buffer (%d)\n", write_buf_size); free(read_buf); return; } c_stream.zalloc = NULL; c_stream.zfree = NULL; c_stream.opaque = (void *)0; c_stream.total_in = 0; c_stream.total_out = 0; err = PREFIX(deflateInit2)(&c_stream, level, Z_DEFLATED, window_bits, mem_level, strategy); CHECK_ERR(err, "deflateInit2"); /* Process input using our read buffer and flush type, * output to stdout only once write buffer is full */ do { read = (int32_t)fread(read_buf, 1, read_buf_size, fin); if (read <= 0) break; c_stream.next_in = (z_const uint8_t *)read_buf; c_stream.next_out = write_buf; c_stream.avail_in = read; do { c_stream.avail_out = write_buf_size; err = PREFIX(deflate)(&c_stream, flush); if (err == Z_STREAM_END) break; CHECK_ERR(err, "deflate"); if (c_stream.next_out == write_buf + write_buf_size) { fwrite(write_buf, 1, write_buf_size, fout); c_stream.next_out = write_buf; } } while (c_stream.next_in < read_buf + read); } while (err == Z_OK); /* Finish the stream if necessary */ if (flush != Z_FINISH) { c_stream.avail_in = 0; do { if (c_stream.next_out == write_buf + write_buf_size) { fwrite(write_buf, 1, write_buf_size, fout); c_stream.next_out = write_buf; } c_stream.avail_out = write_buf_size; err = PREFIX(deflate)(&c_stream, Z_FINISH); if (err == Z_STREAM_END) break; CHECK_ERR(err, "deflate"); } while (err == Z_OK); } /* Output remaining data in write buffer */ if (c_stream.next_out != write_buf) { fwrite(write_buf, 1, c_stream.next_out - write_buf, fout); } err = PREFIX(deflateEnd)(&c_stream); CHECK_ERR(err, "deflateEnd"); free(read_buf); free(write_buf); } /* =========================================================================== * inflate() using specialized parameters */ void inflate_params(FILE *fin, FILE *fout, int32_t read_buf_size, int32_t write_buf_size, int32_t window_bits, int32_t flush) { PREFIX3(stream) d_stream; /* decompression stream */ uint8_t *read_buf; uint8_t *write_buf; int32_t read; int err; read_buf = (uint8_t *)malloc(read_buf_size); if (read_buf == NULL) { fprintf(stderr, "failed to create read buffer (%d)\n", read_buf_size); return; } write_buf = (uint8_t *)malloc(write_buf_size); if (write_buf == NULL) { fprintf(stderr, "failed to create write buffer (%d)\n", write_buf_size); free(read_buf); return; } d_stream.zalloc = NULL; d_stream.zfree = NULL; d_stream.opaque = (void *)0; d_stream.total_in = 0; d_stream.total_out = 0; err = PREFIX(inflateInit2)(&d_stream, window_bits); CHECK_ERR(err, "inflateInit2"); /* Process input using our read buffer and flush type, * output to stdout only once write buffer is full */ do { read = (int32_t)fread(read_buf, 1, read_buf_size, fin); if (read <= 0) break; d_stream.next_in = (z_const uint8_t *)read_buf; d_stream.next_out = write_buf; d_stream.avail_in = read; do { d_stream.avail_out = write_buf_size; err = PREFIX(inflate)(&d_stream, flush); if (err == Z_STREAM_END) break; CHECK_ERR(err, "deflate"); if (d_stream.next_out == write_buf + write_buf_size) { fwrite(write_buf, 1, write_buf_size, fout); d_stream.next_out = write_buf; } } while (d_stream.next_in < read_buf + read); } while (err == Z_OK); /* Finish the stream if necessary */ if (flush != Z_FINISH) { d_stream.avail_in = 0; do { if (d_stream.next_out == write_buf + write_buf_size) { fwrite(write_buf, 1, write_buf_size, fout); d_stream.next_out = write_buf; } d_stream.avail_out = write_buf_size; err = PREFIX(inflate)(&d_stream, Z_FINISH); if (err == Z_STREAM_END) break; CHECK_ERR(err, "inflate"); } while (err == Z_OK); } /* Output remaining data in write buffer */ if (d_stream.next_out != write_buf) { fwrite(write_buf, 1, d_stream.next_out - write_buf, fout); } err = PREFIX(inflateEnd)(&d_stream); CHECK_ERR(err, "inflateEnd"); free(read_buf); free(write_buf); } void show_help(void) { printf("Usage: minideflate [-c] [-f|-h|-R|-F] [-m level] [-r/-t size] [-s flush] [-w bits] [-0 to -9] [input file]\n\n" \ " -c : write to standard output\n" \ " -d : decompress\n" \ " -f : compress with Z_FILTERED\n" \ " -h : compress with Z_HUFFMAN_ONLY\n" \ " -R : compress with Z_RLE\n" \ " -F : compress with Z_FIXED\n" \ " -m : memory level (1 to 8)\n" \ " -w : window bits (8 to 15 for gzip, -8 to -15 for zlib)\n" \ " -s : flush type (0 to 5)\n" \ " -r : read buffer size\n" \ " -t : write buffer size\n" \ " -0 to -9 : compression level\n\n"); } int main(int argc, char **argv) { int32_t i; int32_t mem_level = DEF_MEM_LEVEL; int32_t window_bits = MAX_WBITS; int32_t strategy = Z_DEFAULT_STRATEGY; int32_t level = Z_DEFAULT_COMPRESSION; int32_t read_buf_size = 4096; int32_t write_buf_size = 4096; int32_t flush = Z_NO_FLUSH; uint8_t copyout = 0; uint8_t uncompr = 0; char out_file[320]; FILE *fin = stdin; FILE *fout = stdout; for (i = 1; i < argc; i++) { if ((strcmp(argv[i], "-m") == 0) && (i + 1 < argc)) mem_level = atoi(argv[++i]); else if ((strcmp(argv[i], "-w") == 0) && (i + 1 < argc)) window_bits = atoi(argv[++i]); else if ((strcmp(argv[i], "-r") == 0) && (i + 1 < argc)) read_buf_size = atoi(argv[++i]); else if ((strcmp(argv[i], "-t") == 0) && (i + 1 < argc)) write_buf_size = atoi(argv[++i]); else if ((strcmp(argv[i], "-s") == 0) && (i + 1 < argc)) flush = atoi(argv[++i]); else if (strcmp(argv[i], "-c") == 0) copyout = 1; else if (strcmp(argv[i], "-d") == 0) uncompr = 1; else if (strcmp(argv[i], "-f") == 0) strategy = Z_FILTERED; else if (strcmp(argv[i], "-h") == 0) strategy = Z_HUFFMAN_ONLY; else if (strcmp(argv[i], "-R") == 0) strategy = Z_RLE; else if (argv[i][0] == '-' && argv[i][1] >= '0' && argv[i][1] <= '9' && argv[i][2] == 0) level = argv[i][1] - '0'; else if (strcmp(argv[i], "--help") == 0) { show_help(); return 0; } else if (argv[i][0] == '-') { show_help(); return 64; /* EX_USAGE */ } else break; } SET_BINARY_MODE(stdin); SET_BINARY_MODE(stdout); if (i != argc) { fin = fopen(argv[i], "rb+"); if (fin == NULL) { fprintf(stderr, "Failed to open file: %s\n", argv[i]); exit(1); } if (!copyout) { snprintf(out_file, sizeof(out_file), "%s%s", argv[i], (window_bits < 0) ? ".zz" : ".gz"); fout = fopen(out_file, "wb"); if (fout == NULL) { fprintf(stderr, "Failed to open file: %s\n", out_file); exit(1); } } } if (uncompr) { inflate_params(fin, fout, read_buf_size, write_buf_size, window_bits, flush); } else { deflate_params(fin, fout, read_buf_size, write_buf_size, level, window_bits, mem_level, strategy, flush); } if (fin != stdin) { fclose(fin); } if (fout != stdout) { fclose(fout); } return 0; }