File Coverage

gzip-libdeflate-perl.c
Criterion Covered Total %
statement 112 148 75.6
branch 45 118 38.1
condition n/a
subroutine n/a
pod n/a
total 157 266 59.0


line stmt bran cond sub pod time code
1             typedef enum {
2             libdeflate_none = 0,
3             libdeflate_deflate = 1,
4             libdeflate_gzip = 2,
5             libdeflate_zlib = 3,
6             }
7             gzip_libdeflate_type_t;
8              
9             typedef struct {
10             /* Type (gzip, zlib, deflate) */
11             gzip_libdeflate_type_t t;
12             /* Compressor's level. */
13             int level;
14             struct libdeflate_compressor * c;
15             struct libdeflate_decompressor * d;
16             /* Debugging flag */
17             unsigned int verbose : 1;
18             unsigned int init_ok : 1;
19             }
20             gzip_libdeflate_t;
21              
22             struct type2name {
23             const char * name;
24             int namelen;
25             int value;
26             }
27             gl_type_name[] = {
28             {"deflate", strlen ("deflate"), libdeflate_deflate},
29             {"gzip", strlen ("gzip"), libdeflate_gzip},
30             {"zlib", strlen ("zlib"), libdeflate_zlib},
31             };
32              
33             #define N_TYPES (sizeof (gl_type_name)/sizeof (struct type2name))
34              
35             #define DEBUG
36             #ifdef DEBUG
37             #define MSG(format, args...) \
38             if (gl->verbose) { \
39             fprintf (stderr, "%s:%d: ", __FILE__, __LINE__); \
40             fprintf (stderr, format, ## args); \
41             fprintf (stderr, "\n"); \
42             }
43             #else
44             #define MSG(format, args...)
45             #endif /* def DEBUG */
46              
47             static SV *
48 3           gl_get_type (gzip_libdeflate_t * gl)
49             {
50             int i;
51 6 50         for (i = 0; i < N_TYPES; i++) {
52             struct type2name * b;
53 6           b = & gl_type_name[i];
54 6 100         if (gl->t == b->value) {
55 3           return newSVpv (b->name, b->namelen);
56             }
57             }
58 0           return &PL_sv_undef;
59             }
60              
61             static void
62 36           gl_set_type (gzip_libdeflate_t * gl, int type)
63             {
64 36 50         if (type < 1 || type > 3) {
    50          
65 0           warn ("Type out of bounds %d", type);
66 0           return;
67             }
68 36 50         MSG ("Setting type to %d", type);
69 36           gl->t = type;
70             }
71              
72             static SV *
73 0           gl_get_level (gzip_libdeflate_t * gl)
74             {
75 0           return newSViv (gl->level);
76             }
77              
78             static void
79 36           gl_set_level (gzip_libdeflate_t * gl, int level)
80             {
81 36 50         if (level < 0 || level > 12) {
    50          
82 0           warn ("Level out of bounds %d", level);
83 0           return;
84             }
85 36 50         MSG ("Setting level to %d", level);
86 36           gl->level = level;
87             }
88              
89             static void
90 149           gl_check (gzip_libdeflate_t * gl)
91             {
92 149 50         if (! gl->init_ok) {
93 0           croak ("%s:%d: BUG: Uninitialised gl", __FILE__, __LINE__);
94             }
95 149           }
96              
97             static void
98 72           gl_set (gzip_libdeflate_t * gl, SV * key_sv, SV * value_sv)
99             {
100             const char * key;
101             STRLEN keyl;
102             const char * value;
103             STRLEN valuel;
104              
105 72           gl_check (gl);
106 72 50         key = SvPV (key_sv, keyl);
107 72 50         MSG ("Handling key %s", key);
108 72 100         if (strcmp (key, "type") == 0) {
109             int i;
110 36 50         if (SvIOK (value_sv)) {
111 0 0         gl_set_type (gl, SvIV (value_sv));
112 0           return;
113             }
114 36 50         value = SvPV (value_sv, valuel);
115 72 50         for (i = 0; i < 3; i++) {
116             struct type2name * b;
117 72           b = & gl_type_name[i];
118 72 100         if (valuel == b->namelen && strcmp (value, b->name) == 0) {
    100          
119 36           gl_set_type (gl, b->value);
120 36           return;
121             }
122             }
123 0           warn ("Failed to handle 'type' argument - use name or integer");
124 0           return;
125             }
126 36 50         if (strcmp (key, "level") == 0) {
127 36 50         if (SvIOK (value_sv)) {
128 36 50         gl_set_level (gl, SvIV (value_sv));
129 36           return;
130             }
131 0           warn ("Failed to handle 'level' argument - require integer");
132 0           return;
133             }
134 0 0         if (strcmp (key, "verbose") == 0) {
135 0 0         gl->verbose = !! SvTRUE (value_sv);
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
136 0           return;
137             }
138 0           warn ("Failed to handle '%s' argument", key);
139 72           return;
140             }
141              
142             static void
143 38           gl_init (gzip_libdeflate_t * gl)
144             {
145 38           gl->t = libdeflate_gzip;
146 38           gl->level = 6;
147 38           gl->init_ok = 1;
148 38           }
149              
150             static SV *
151 77           set_up_out (SV * out, size_t r)
152             {
153 77 50         if (r == 0) {
154 0           warn ("compression failed, not enough room");
155 0           return &PL_sv_undef;
156             }
157 77           SvPOK_on (out);
158 77           SvCUR_set(out, (STRLEN) r);
159 77           return out;
160             }
161              
162             static SV *
163 37           gzip_libdeflate_compress (gzip_libdeflate_t * gl, SV * in_sv)
164             {
165             const char * in;
166             STRLEN in_len;
167             size_t out_nbytes;
168             size_t r;
169             SV * out;
170             char * out_p;
171              
172 37           gl_check (gl);
173 37 50         if (! gl->c) {
174 37           gl->c = libdeflate_alloc_compressor (gl->level);
175 37 50         if (! gl->c) {
176 0           warn ("Could not allocate a compressor");
177 0           return &PL_sv_undef;
178             }
179             }
180              
181 37 50         in = SvPV (in_sv, in_len);
182 37 50         MSG ("Input buffer of length %d\n", in_len);
183 37           switch (gl->t) {
184             case libdeflate_deflate:
185 12           out_nbytes = libdeflate_deflate_compress_bound (gl->c, in_len);
186 12           break;
187             case libdeflate_gzip:
188 13           out_nbytes = libdeflate_gzip_compress_bound (gl->c, in_len);
189 13           break;
190             case libdeflate_zlib:
191 12           out_nbytes = libdeflate_zlib_compress_bound (gl->c, in_len);
192 12           break;
193             case libdeflate_none:
194             default:
195 0           warn ("Type of compression is not specified");
196 0           return &PL_sv_undef;
197             }
198 37           out = newSV (out_nbytes);
199 37           out_p = SvPVX (out);
200 37 50         MSG ("Output buffer of length %d\n", out_nbytes);
201 37           switch (gl->t) {
202             case libdeflate_deflate:
203 12           r = libdeflate_deflate_compress (gl->c, in, (size_t) in_len,
204             out_p, out_nbytes);
205 12           break;
206             case libdeflate_gzip:
207 13 50         MSG ("Compressing with gzip %p", gl->c);
208 13           r = libdeflate_gzip_compress (gl->c, in, (size_t) in_len,
209             out_p, out_nbytes);
210 13           break;
211             case libdeflate_zlib:
212 12           r = libdeflate_zlib_compress (gl->c, in, (size_t) in_len,
213             out_p, out_nbytes);
214 12           break;
215             case libdeflate_none:
216             default:
217 0           warn ("Type of compression is not specified");
218 0           return &PL_sv_undef;
219             }
220 37 50         MSG ("Finished compression, final length %d", r);
221 37           return set_up_out (out, r);
222             }
223              
224             /* https://github.com/ebiggers/libdeflate/blob/master/programs/gzip.c#L177 */
225              
226             static u32
227 4           load_u32_gzip(const u8 *p)
228             {
229             return
230 4           ((u32)p[0] << 0) |
231 8           ((u32)p[1] << 8) |
232 4           ((u32)p[2] << 16) |
233 4           ((u32)p[3] << 24);
234             }
235              
236             static SV *
237 40           gzip_libdeflate_decompress (gzip_libdeflate_t * gl, SV * in_sv, size_t size)
238             {
239             const char * in;
240             STRLEN in_len;
241             size_t r;
242             SV * out;
243             char * out_p;
244              
245 40           gl_check (gl);
246 40 100         if (! gl->d) {
247 38           gl->d = libdeflate_alloc_decompressor ();
248 38 50         if (! gl->d) {
249 0           warn ("Could not allocate a decompressor");
250 0           return &PL_sv_undef;
251             }
252             }
253              
254 40 50         in = SvPV (in_sv, in_len);
255              
256 40           switch (gl->t) {
257             case libdeflate_deflate:
258             case libdeflate_zlib:
259 24 50         if (size == 0) {
260 0           warn ("A non-zero size is required to decompress deflate/zlib inputs");
261 0           return &PL_sv_undef;
262             }
263 24           r = size;
264 24           break;
265             case libdeflate_gzip:
266 16 100         if (size == 0) {
267 4           r = load_u32_gzip((u8*)(&in[in_len - 4]));
268             }
269             else {
270 12           r = size;
271             }
272 16           break;
273             case libdeflate_none:
274             default:
275 0           warn ("Type of compression is not specified");
276 0           return &PL_sv_undef;
277             }
278 40 50         if (r == 0) {
279 0           r = 1;
280             }
281 40           out = newSV (r);
282 40           out_p = SvPVX (out);
283             do {
284             size_t n;
285             size_t o;
286             enum libdeflate_result result;
287             #define ARGXYZ gl->d, in, in_len, out_p, r, & n, & o
288 40           switch (gl->t) {
289             case libdeflate_deflate:
290 12           result = libdeflate_deflate_decompress_ex (ARGXYZ);
291 12           break;
292             case libdeflate_gzip:
293 16           result = libdeflate_gzip_decompress_ex (ARGXYZ);
294 16           break;
295             case libdeflate_zlib:
296 12           result = libdeflate_zlib_decompress_ex (ARGXYZ);
297 12           break;
298             default:
299 0           warn ("Type of compression is not specified");
300 40           return &PL_sv_undef;
301             }
302 40 50         if (result != LIBDEFLATE_SUCCESS) {
303 0           warn ("Decompress failed with error %d", result);
304 0           return &PL_sv_undef;
305             }
306             #undef ARGXYZ
307             }
308             while (0);
309 40           return set_up_out (out, r);
310             }
311              
312             static void
313 38           gzip_libdeflate_free (gzip_libdeflate_t * gl)
314             {
315 38 50         MSG ("Freeing");
316 38 100         if (gl->c) {
317 37           libdeflate_free_compressor (gl->c);
318 37           gl->c = 0;
319             }
320 38 50         if (gl->d) {
321 38           libdeflate_free_decompressor (gl->d);
322 38           gl->d = 0;
323             }
324 38           Safefree (gl);
325 38           }
326              
327             #define GLSET \
328             if (items > 1) { \
329             if ((items - 1) % 2 != 0) { \
330             warn ("odd number of arguments ignored"); \
331             } \
332             else { \
333             int i; \
334             for (i = 1; i < items; i += 2) { \
335             gl_set (gl, ST(i), ST(i+1)); \
336             } \
337             } \
338             }