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