File Coverage

gz.c
Criterion Covered Total %
statement 100 133 75.1
branch 38 68 55.8
condition n/a
subroutine n/a
pod n/a
total 138 201 68.6


line stmt bran cond sub pod time code
1             /*
2             * gz.c - zlib inflate/deflate wrappers driving a malloc-grown output
3             * buffer. Both functions consume the entire input in one go (one-shot
4             * codec, no streaming). Output buffer doubles when full; that bounds
5             * realloc count to log2(out_size).
6             */
7              
8             #include "gz.h"
9              
10             #include
11             #include
12             #include
13              
14             #define GZ_DEFAULT_CHUNK (128 * 1024)
15             #define GZ_DEFAULT_LEVEL 6
16             #define GZ_DEFAULT_MEM 8
17              
18 74           void gz_options_init(gz_options_t *opts) {
19 74           opts->level = GZ_DEFAULT_LEVEL;
20 74           opts->mode = GZ_MODE_AUTO;
21 74           opts->chunk_size = GZ_DEFAULT_CHUNK;
22 74           opts->strategy = Z_DEFAULT_STRATEGY;
23 74           opts->mem_level = GZ_DEFAULT_MEM;
24 74           }
25              
26 55           static int wbits_for_mode(gz_mode_t mode, int encoding) {
27 55           switch (mode) {
28 19           case GZ_MODE_GZIP: return MAX_WBITS | 16; /* 31 */
29 13           case GZ_MODE_ZLIB: return MAX_WBITS; /* 15 */
30 12           case GZ_MODE_RAW: return -MAX_WBITS; /* -15 */
31 11 50         case GZ_MODE_AUTO: return encoding ? -1 : (MAX_WBITS | 32); /* 47 decode */
32             }
33 0           return -1;
34             }
35              
36 1           static gz_err_t map_z_err(int z) {
37 1           switch (z) {
38 0           case Z_OK: return GZ_OK;
39 0           case Z_STREAM_END: return GZ_OK;
40 0           case Z_NEED_DICT: return GZ_ERR_NEED_DICT;
41 1           case Z_DATA_ERROR: return GZ_ERR_DATA;
42 0           case Z_MEM_ERROR: return GZ_ERR_MEM;
43 0           case Z_BUF_ERROR: return GZ_ERR_BUF;
44 0           case Z_VERSION_ERROR:return GZ_ERR_VERSION;
45 0           default: return GZ_ERR_DATA;
46             }
47             }
48              
49 3           const char *gz_strerror(gz_err_t err) {
50 3           switch (err) {
51 0           case GZ_OK: return "ok";
52 0           case GZ_ERR_INIT: return "zlib init failed";
53 1           case GZ_ERR_DATA: return "corrupt input stream";
54 0           case GZ_ERR_NEED_DICT: return "stream requires preset dictionary (unsupported)";
55 0           case GZ_ERR_MEM: return "out of memory";
56 0           case GZ_ERR_BUF: return "buffer error";
57 0           case GZ_ERR_VERSION: return "zlib version mismatch";
58 1           case GZ_ERR_OPT: return "invalid option";
59 1           case GZ_ERR_TRUNCATED: return "input ended before stream completion";
60             }
61 0           return "unknown error";
62             }
63              
64             /* Grow `*buf` so it has at least `*cap >= used + need` bytes. Returns 0
65             * on success, GZ_ERR_MEM on allocation failure (leaves *buf untouched
66             * so caller can free what it has). */
67 134           static gz_err_t ensure_cap(unsigned char **buf, size_t *cap,
68             size_t used, size_t need) {
69             size_t want;
70             unsigned char *p;
71 134 100         if (used + need <= *cap) return GZ_OK;
72 67 100         want = *cap ? *cap : 4096;
73 354 100         while (want < used + need) {
74 287           size_t doubled = want * 2;
75 287 50         if (doubled <= want) return GZ_ERR_MEM; /* overflow */
76 287           want = doubled;
77             }
78 67           p = (unsigned char *)realloc(*buf, want);
79 67 50         if (!p) return GZ_ERR_MEM;
80 67           *buf = p;
81 67           *cap = want;
82 67           return GZ_OK;
83             }
84              
85 31           gz_err_t gz_inflate(const unsigned char *in, size_t in_len,
86             const gz_options_t *opts,
87             unsigned char **out, size_t *out_cap, size_t *out_len) {
88             z_stream z;
89             int wbits;
90 31           gz_err_t err = GZ_OK;
91             int z_rc;
92             size_t chunk;
93              
94 31           *out = NULL;
95 31           *out_cap = 0;
96 31           *out_len = 0;
97              
98 31 50         chunk = (opts && opts->chunk_size) ? opts->chunk_size : GZ_DEFAULT_CHUNK;
    50          
99              
100 31 50         wbits = wbits_for_mode(opts ? opts->mode : GZ_MODE_AUTO, 0);
101 31 50         if (wbits == -1) return GZ_ERR_OPT;
102              
103 31           memset(&z, 0, sizeof z);
104 31           z.next_in = (Bytef *)in;
105 31           z.avail_in = (uInt)in_len;
106              
107 31           z_rc = inflateInit2(&z, wbits);
108 31 50         if (z_rc != Z_OK) return GZ_ERR_INIT;
109              
110             /* Empty input: decompress to empty output. zlib will return
111             * Z_BUF_ERROR if asked to inflate zero bytes; treat as a successful
112             * empty round-trip when input was also empty. */
113 31 50         if (in_len == 0) {
114 0           inflateEnd(&z);
115 0           *out = (unsigned char *)malloc(1);
116 0 0         if (!*out) return GZ_ERR_MEM;
117 0           *out_cap = 1;
118 0           *out_len = 0;
119 0           return GZ_OK;
120             }
121              
122             for (;;) {
123 70           err = ensure_cap(out, out_cap, *out_len, chunk);
124 70 50         if (err != GZ_OK) goto fail;
125              
126 70           z.next_out = *out + *out_len;
127 70           z.avail_out = (uInt)chunk;
128              
129 70           z_rc = inflate(&z, Z_NO_FLUSH);
130              
131 70           *out_len += chunk - z.avail_out;
132              
133 70 100         if (z_rc == Z_STREAM_END) break;
134 41 50         if (z_rc == Z_BUF_ERROR && z.avail_in == 0) {
    0          
135 0           err = GZ_ERR_TRUNCATED;
136 0           goto fail;
137             }
138 41 100         if (z_rc != Z_OK) {
139 1           err = map_z_err(z_rc);
140 1           goto fail;
141             }
142             /* Z_OK with avail_out == 0 means the chunk was full mid-stream;
143             * loop and grow. avail_out > 0 with Z_OK means zlib produced
144             * less than chunk and wants more input — but we fed it all the
145             * input we have, so an avail_in == 0 case there means the
146             * stream is truncated. */
147 40 100         if (z.avail_in == 0 && z.avail_out > 0) {
    50          
148 1           err = GZ_ERR_TRUNCATED;
149 1           goto fail;
150             }
151             }
152              
153 29           inflateEnd(&z);
154 29           return GZ_OK;
155              
156 2           fail:
157 2           inflateEnd(&z);
158 2           free(*out);
159 2           *out = NULL;
160 2           *out_cap = 0;
161 2           *out_len = 0;
162 2           return err;
163             }
164              
165 25           gz_err_t gz_deflate(const unsigned char *in, size_t in_len,
166             const gz_options_t *opts,
167             unsigned char **out, size_t *out_cap, size_t *out_len) {
168             z_stream z;
169             int wbits, level, mem_level, strategy;
170 25           gz_err_t err = GZ_OK;
171             int z_rc;
172             size_t chunk;
173              
174 25           *out = NULL;
175 25           *out_cap = 0;
176 25           *out_len = 0;
177              
178 25 50         if (!opts) return GZ_ERR_OPT;
179              
180 25 50         chunk = opts->chunk_size ? opts->chunk_size : GZ_DEFAULT_CHUNK;
181 25           level = opts->level;
182 25           mem_level = opts->mem_level;
183 25           strategy = opts->strategy;
184              
185 25 50         if (level < 0 || level > 9) return GZ_ERR_OPT;
    50          
186 25 50         if (mem_level < 1 || mem_level > 9) return GZ_ERR_OPT;
    50          
187 25 100         if (opts->mode == GZ_MODE_AUTO) return GZ_ERR_OPT;
188              
189 24           wbits = wbits_for_mode(opts->mode, 1);
190 24 50         if (wbits == -1) return GZ_ERR_OPT;
191              
192 24           memset(&z, 0, sizeof z);
193 24           z.next_in = (Bytef *)in;
194 24           z.avail_in = (uInt)in_len;
195              
196 24           z_rc = deflateInit2(&z, level, Z_DEFLATED, wbits, mem_level, strategy);
197 24 50         if (z_rc != Z_OK) return GZ_ERR_INIT;
198              
199             for (;;) {
200 64           err = ensure_cap(out, out_cap, *out_len, chunk);
201 64 50         if (err != GZ_OK) goto fail;
202              
203 64           z.next_out = *out + *out_len;
204 64           z.avail_out = (uInt)chunk;
205              
206 64           z_rc = deflate(&z, Z_FINISH);
207              
208 64           *out_len += chunk - z.avail_out;
209              
210 64 100         if (z_rc == Z_STREAM_END) break;
211 40 50         if (z_rc == Z_OK) continue; /* needs more output room */
212 0 0         if (z_rc == Z_BUF_ERROR && z.avail_out == 0) continue;
    0          
213 0           err = map_z_err(z_rc);
214 0           goto fail;
215             }
216              
217 24           deflateEnd(&z);
218 24           return GZ_OK;
219              
220 0           fail:
221 0           deflateEnd(&z);
222 0           free(*out);
223 0           *out = NULL;
224 0           *out_cap = 0;
225 0           *out_len = 0;
226 0           return err;
227             }