1 /* minideflate.c -- test deflate/inflate under specific conditions
2  * Copyright (C) 2020 Nathan Moinvaziri
3  * For conditions of distribution and use, see copyright notice in zlib.h
4  */
5 
6 #include <stdio.h>
7 #include <stddef.h>
8 #include <stdint.h>
9 #include <string.h>
10 #include <assert.h>
11 #include <stdlib.h>
12 #include <inttypes.h>
13 
14 #include "zbuild.h"
15 #ifdef ZLIB_COMPAT
16 #  include "zlib.h"
17 #else
18 #  include "zlib-ng.h"
19 #endif
20 
21 #if defined(_WIN32) || defined(__CYGWIN__)
22 #  include <fcntl.h>
23 #  include <io.h>
24 #  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
25 #else
26 #  define SET_BINARY_MODE(file)
27 #endif
28 
29 #if MAX_MEM_LEVEL >= 8
30 #  define DEF_MEM_LEVEL 8
31 #else
32 #  define DEF_MEM_LEVEL  MAX_MEM_LEVEL
33 #endif
34 
35 #define CHECK_ERR(err, msg) { \
36     if (err != Z_OK) { \
37         fprintf(stderr, "%s error: %d\n", msg, err); \
38         exit(1); \
39     } \
40 }
41 
42 /* ===========================================================================
43  * deflate() using specialized parameters
44  */
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)45 void deflate_params(FILE *fin, FILE *fout, int32_t read_buf_size, int32_t write_buf_size, int32_t level,
46     int32_t window_bits, int32_t mem_level, int32_t strategy, int32_t flush) {
47     PREFIX3(stream) c_stream; /* compression stream */
48     uint8_t *read_buf;
49     uint8_t *write_buf;
50     int32_t read;
51     int err;
52 
53     read_buf = (uint8_t *)malloc(read_buf_size);
54     if (read_buf == NULL) {
55         fprintf(stderr, "failed to create read buffer (%d)\n", read_buf_size);
56         return;
57     }
58     write_buf = (uint8_t *)malloc(write_buf_size);
59     if (write_buf == NULL) {
60         fprintf(stderr, "failed to create write buffer (%d)\n", write_buf_size);
61         free(read_buf);
62         return;
63     }
64 
65     c_stream.zalloc = NULL;
66     c_stream.zfree = NULL;
67     c_stream.opaque = (void *)0;
68     c_stream.total_in = 0;
69     c_stream.total_out = 0;
70 
71     err = PREFIX(deflateInit2)(&c_stream, level, Z_DEFLATED, window_bits, mem_level, strategy);
72     CHECK_ERR(err, "deflateInit2");
73 
74     /* Process input using our read buffer and flush type,
75      * output to stdout only once write buffer is full */
76     do {
77         read = (int32_t)fread(read_buf, 1, read_buf_size, fin);
78         if (read <= 0)
79             break;
80 
81         c_stream.next_in  = (z_const uint8_t *)read_buf;
82         c_stream.next_out = write_buf;
83         c_stream.avail_in = read;
84 
85         do {
86             c_stream.avail_out = write_buf_size;
87             err = PREFIX(deflate)(&c_stream, flush);
88             if (err == Z_STREAM_END) break;
89             CHECK_ERR(err, "deflate");
90 
91             if (c_stream.next_out == write_buf + write_buf_size) {
92                 fwrite(write_buf, 1, write_buf_size, fout);
93                 c_stream.next_out = write_buf;
94             }
95         } while (c_stream.next_in < read_buf + read);
96     } while (err == Z_OK);
97 
98     /* Finish the stream if necessary */
99     if (flush != Z_FINISH) {
100         c_stream.avail_in = 0;
101         do {
102             if (c_stream.next_out == write_buf + write_buf_size) {
103                 fwrite(write_buf, 1, write_buf_size, fout);
104                 c_stream.next_out = write_buf;
105             }
106 
107             c_stream.avail_out = write_buf_size;
108             err = PREFIX(deflate)(&c_stream, Z_FINISH);
109             if (err == Z_STREAM_END) break;
110             CHECK_ERR(err, "deflate");
111         } while (err == Z_OK);
112     }
113 
114     /* Output remaining data in write buffer */
115     if (c_stream.next_out != write_buf) {
116         fwrite(write_buf, 1, c_stream.next_out - write_buf, fout);
117     }
118 
119     err = PREFIX(deflateEnd)(&c_stream);
120     CHECK_ERR(err, "deflateEnd");
121 
122     free(read_buf);
123     free(write_buf);
124 }
125 
126 /* ===========================================================================
127  * inflate() using specialized parameters
128  */
inflate_params(FILE * fin,FILE * fout,int32_t read_buf_size,int32_t write_buf_size,int32_t window_bits,int32_t flush)129 void inflate_params(FILE *fin, FILE *fout, int32_t read_buf_size, int32_t write_buf_size, int32_t window_bits,
130     int32_t flush) {
131     PREFIX3(stream) d_stream; /* decompression stream */
132     uint8_t *read_buf;
133     uint8_t *write_buf;
134     int32_t read;
135     int err;
136 
137 
138     read_buf = (uint8_t *)malloc(read_buf_size);
139     if (read_buf == NULL) {
140         fprintf(stderr, "failed to create read buffer (%d)\n", read_buf_size);
141         return;
142     }
143     write_buf = (uint8_t *)malloc(write_buf_size);
144     if (write_buf == NULL) {
145         fprintf(stderr, "failed to create write buffer (%d)\n", write_buf_size);
146         free(read_buf);
147         return;
148     }
149 
150     d_stream.zalloc = NULL;
151     d_stream.zfree = NULL;
152     d_stream.opaque = (void *)0;
153     d_stream.total_in = 0;
154     d_stream.total_out = 0;
155 
156     err = PREFIX(inflateInit2)(&d_stream, window_bits);
157     CHECK_ERR(err, "inflateInit2");
158 
159     /* Process input using our read buffer and flush type,
160      * output to stdout only once write buffer is full */
161     do {
162         read = (int32_t)fread(read_buf, 1, read_buf_size, fin);
163         if (read <= 0)
164             break;
165 
166         d_stream.next_in  = (z_const uint8_t *)read_buf;
167         d_stream.next_out = write_buf;
168         d_stream.avail_in = read;
169 
170         do {
171             d_stream.avail_out = write_buf_size;
172             err = PREFIX(inflate)(&d_stream, flush);
173             if (err == Z_STREAM_END) break;
174             CHECK_ERR(err, "deflate");
175 
176             if (d_stream.next_out == write_buf + write_buf_size) {
177                 fwrite(write_buf, 1, write_buf_size, fout);
178                 d_stream.next_out = write_buf;
179             }
180         } while (d_stream.next_in < read_buf + read);
181     } while (err == Z_OK);
182 
183     /* Finish the stream if necessary */
184     if (flush != Z_FINISH) {
185         d_stream.avail_in = 0;
186         do {
187             if (d_stream.next_out == write_buf + write_buf_size) {
188                 fwrite(write_buf, 1, write_buf_size, fout);
189                 d_stream.next_out = write_buf;
190             }
191 
192             d_stream.avail_out = write_buf_size;
193             err = PREFIX(inflate)(&d_stream, Z_FINISH);
194             if (err == Z_STREAM_END) break;
195             CHECK_ERR(err, "inflate");
196         } while (err == Z_OK);
197     }
198 
199     /* Output remaining data in write buffer */
200     if (d_stream.next_out != write_buf) {
201         fwrite(write_buf, 1, d_stream.next_out - write_buf, fout);
202     }
203 
204     err = PREFIX(inflateEnd)(&d_stream);
205     CHECK_ERR(err, "inflateEnd");
206 
207     free(read_buf);
208     free(write_buf);
209 }
210 
show_help(void)211 void show_help(void) {
212     printf("Usage: minideflate [-c] [-f|-h|-R|-F] [-m level] [-r/-t size] [-s flush] [-w bits] [-0 to -9] [input file]\n\n" \
213            "  -c : write to standard output\n" \
214            "  -d : decompress\n" \
215            "  -f : compress with Z_FILTERED\n" \
216            "  -h : compress with Z_HUFFMAN_ONLY\n" \
217            "  -R : compress with Z_RLE\n" \
218            "  -F : compress with Z_FIXED\n" \
219            "  -m : memory level (1 to 8)\n" \
220            "  -w : window bits (8 to 15 for gzip, -8 to -15 for zlib)\n" \
221            "  -s : flush type (0 to 5)\n" \
222            "  -r : read buffer size\n" \
223            "  -t : write buffer size\n" \
224            "  -0 to -9 : compression level\n\n");
225 }
226 
main(int argc,char ** argv)227 int main(int argc, char **argv) {
228     int32_t i;
229     int32_t mem_level = DEF_MEM_LEVEL;
230     int32_t window_bits = MAX_WBITS;
231     int32_t strategy = Z_DEFAULT_STRATEGY;
232     int32_t level = Z_DEFAULT_COMPRESSION;
233     int32_t read_buf_size = 4096;
234     int32_t write_buf_size = 4096;
235     int32_t flush = Z_NO_FLUSH;
236     uint8_t copyout = 0;
237     uint8_t uncompr = 0;
238     char out_file[320];
239     FILE *fin = stdin;
240     FILE *fout = stdout;
241 
242     for (i = 1; i < argc; i++) {
243         if ((strcmp(argv[i], "-m") == 0) && (i + 1 < argc))
244             mem_level = atoi(argv[++i]);
245         else if ((strcmp(argv[i], "-w") == 0) && (i + 1 < argc))
246             window_bits = atoi(argv[++i]);
247         else if ((strcmp(argv[i], "-r") == 0) && (i + 1 < argc))
248             read_buf_size = atoi(argv[++i]);
249         else if ((strcmp(argv[i], "-t") == 0) && (i + 1 < argc))
250             write_buf_size = atoi(argv[++i]);
251         else if ((strcmp(argv[i], "-s") == 0) && (i + 1 < argc))
252             flush = atoi(argv[++i]);
253         else if (strcmp(argv[i], "-c") == 0)
254             copyout = 1;
255         else if (strcmp(argv[i], "-d") == 0)
256             uncompr = 1;
257         else if (strcmp(argv[i], "-f") == 0)
258             strategy = Z_FILTERED;
259         else if (strcmp(argv[i], "-h") == 0)
260             strategy = Z_HUFFMAN_ONLY;
261         else if (strcmp(argv[i], "-R") == 0)
262             strategy = Z_RLE;
263         else if (argv[i][0] == '-' && argv[i][1] >= '0' && argv[i][1] <= '9' && argv[i][2] == 0)
264             level = argv[i][1] - '0';
265         else if (strcmp(argv[i], "--help") == 0) {
266             show_help();
267             return 0;
268         } else if (argv[i][0] == '-') {
269             show_help();
270             return 64;   /* EX_USAGE */
271         } else
272             break;
273     }
274 
275     SET_BINARY_MODE(stdin);
276     SET_BINARY_MODE(stdout);
277     if (i != argc) {
278         fin = fopen(argv[i], "rb+");
279         if (fin == NULL) {
280             fprintf(stderr, "Failed to open file: %s\n", argv[i]);
281             exit(1);
282         }
283         if (!copyout) {
284             snprintf(out_file, sizeof(out_file), "%s%s", argv[i], (window_bits < 0) ? ".zz" : ".gz");
285             fout = fopen(out_file, "wb");
286             if (fout == NULL) {
287                 fprintf(stderr, "Failed to open file: %s\n", out_file);
288                 exit(1);
289             }
290         }
291     }
292 
293     if (uncompr) {
294         inflate_params(fin, fout, read_buf_size, write_buf_size, window_bits, flush);
295     } else {
296         deflate_params(fin, fout, read_buf_size, write_buf_size, level, window_bits, mem_level, strategy, flush);
297     }
298 
299     if (fin != stdin) {
300         fclose(fin);
301     }
302     if (fout != stdout) {
303         fclose(fout);
304     }
305 
306     return 0;
307 }
308