File Coverage

gzip-faster-perl.c
Criterion Covered Total %
statement 176 206 85.4
branch 96 124 77.4
condition n/a
subroutine n/a
pod n/a
total 272 330 82.4


line stmt bran cond sub pod time code
1             /* Buffer size for inflate/deflate. */
2              
3             #define CHUNK 0x4000
4              
5             /* These are magic numbers for zlib, please refer to
6             "/usr/include/zlib.h" for the details. zlib does not provide any
7             macros for these. */
8              
9             /* Just use the maximum. */
10             #define windowBits 15
11             #define DEFLATE_ENABLE_ZLIB_GZIP 16
12             #define INFLATE_ENABLE_ZLIB_GZIP 32
13             /* Default as given in zlib.h. */
14             #define DEFAULT_MEMLEVEL 8
15              
16             #define CALL_ZLIB(x) \
17             zlib_status = x; \
18             if (zlib_status < 0) { \
19             deflateEnd (& gf->strm); \
20             croak ("zlib call %s returned error status %d", \
21             #x, zlib_status); \
22             } \
23              
24             typedef struct
25             {
26             /* Input. */
27             SV * in;
28             const char * in_char;
29             STRLEN in_length;
30             /* Compression structure. */
31             z_stream strm;
32             /* Compression level. */
33             int level;
34             /* This holds the stuff. */
35             unsigned char out_buffer[CHUNK];
36             /* windowBits, adjusted for monkey business. */
37             int wb;
38             /* Optional file name for gzip format. This can only take values
39             for user-visible objects. */
40             SV * file_name;
41             /* User-defined modification time. */
42             SV * mod_time;
43             /* Gzip, not deflate or inflate. */
44             unsigned int is_gzip : 1;
45             /* "Raw" inflate or deflate without adler32 check. */
46             unsigned int is_raw : 1;
47             /* Copy Perl flags like UTF8 flag? */
48             unsigned int copy_perl_flags : 1;
49             /* User can see this object? */
50             unsigned int user_object : 1;
51             }
52             gzip_faster_t;
53              
54             /* This is our marker for the extra buffer in the Gzip header. See
55             "http://www.gzip.org/zlib/rfc-gzip.html". */
56              
57             #define GZIP_PERL_ID "GF\1\0"
58             #define GZIP_PERL_ID_LENGTH 4
59              
60             /* Perl flags which go in our extra buffer. */
61              
62             #define GZIP_PERL_LENGTH 1
63             #define EXTRA_LENGTH GZIP_PERL_ID_LENGTH + GZIP_PERL_LENGTH
64             #define GZIP_PERL_UTF8 (1<<0)
65              
66             /* Add more Perl flags here like
67              
68             #define SOMETHING (1<<1)
69              
70             etc. */
71              
72             /* Set up the gzip buffer in "gf->strm" using the scalar in
73             "gf->in". */
74              
75             static void
76 49           gf_set_up (gzip_faster_t * gf)
77             {
78             /* Extract the information from "gf->in". Note that SvPV is a
79             macro, and so the following line of code sets "gf->in_length"
80             rather than retrieving it. */
81 49           gf->in_char = SvPV (gf->in, gf->in_length);
82 49           gf->strm.next_in = (unsigned char *) gf->in_char;
83 49           gf->strm.avail_in = gf->in_length;
84 49           gf->strm.zalloc = Z_NULL;
85 49           gf->strm.zfree = Z_NULL;
86 49           gf->strm.opaque = Z_NULL;
87 49 100         if (! gf->user_object) {
88 26           gf->level = Z_DEFAULT_COMPRESSION;
89             }
90 49           gf->wb = windowBits;
91 49           }
92              
93             /* Check that we are always dealing with a user-defined object, not a
94             module-defined object, before altering the values. */
95              
96             #define UO \
97             if (! gf->user_object) { \
98             croak ("%s:%d: THIS IS NOT A USER OBJECT", \
99             __FILE__, __LINE__); \
100             }
101              
102             /* Delete the file name in the object. */
103              
104             static void
105 21           gf_delete_file_name (gzip_faster_t * gf)
106             {
107 21 50         UO;
108 21 100         if (gf->file_name) {
109             /* Decrement the reference count by one to tell Perl we are no
110             longer keeping a pointer to this SV. */
111 10           SvREFCNT_dec (gf->file_name);
112 10           gf->file_name = 0;
113             }
114 21           }
115              
116             /* Delete the file name in the object. */
117              
118             static void
119 17           gf_delete_mod_time (gzip_faster_t * gf)
120             {
121 17 50         UO;
122 17 100         if (gf->mod_time) {
123             /* Decrement the reference count by one to tell Perl we are no
124             longer keeping a pointer to this SV. */
125 2           SvREFCNT_dec (gf->mod_time);
126 2           gf->mod_time = 0;
127             }
128 17           }
129              
130             static void
131 4           gf_set_file_name (gzip_faster_t * gf, SV * file_name)
132             {
133 4 50         UO;
134 4 50         if (gf->file_name) {
135 0           gf_delete_file_name (gf);
136             }
137             /* Add one to the reference count of the SV to tell Perl we have a
138             pointer to it in our structure, in case it loses all its other
139             references and Perl decides to delete it. */
140 4           SvREFCNT_inc (file_name);
141 4           gf->file_name = file_name;
142 4           }
143              
144             static SV *
145 7           gf_get_file_name (gzip_faster_t * gf)
146             {
147 7 50         UO;
148 7 100         if (gf->file_name) {
149 5           return gf->file_name;
150             }
151 2           return & PL_sv_undef;
152             }
153              
154             static void
155 1           gf_set_mod_time (gzip_faster_t * gf, SV * mod_time)
156             {
157 1 50         UO;
158 1 50         if (gf->mod_time) {
159 0           gf_delete_mod_time (gf);
160             }
161             /* Add one to the reference count of the SV to tell Perl we have a
162             pointer to it in our structure, in case it loses all its other
163             references and Perl decides to delete it. */
164 1           SvREFCNT_inc (mod_time);
165 1           gf->mod_time = mod_time;
166 1           }
167              
168             static SV *
169 5           gf_get_mod_time (gzip_faster_t * gf)
170             {
171 5 50         UO;
172 5 100         if (gf->mod_time) {
173 1           return gf->mod_time;
174             }
175 4           return & PL_sv_undef;
176             }
177              
178             /* Check for an unlikely condition after the end of the
179             inflate/deflate loop. */
180              
181             static void
182 42           check_avail_in (gzip_faster_t * gf)
183             {
184 42 50         if (gf->strm.avail_in != 0) {
185 0           croak ("Zlib did not finish processing the string: %d bytes left",
186             gf->strm.avail_in);
187             }
188 42           }
189              
190             /* Check for an unlikely condition after the end of the
191             inflate/deflate loop. */
192              
193             static void
194 42           check_zlib_status (int zlib_status)
195             {
196 42 50         if (zlib_status != Z_STREAM_END) {
197 0           croak ("Zlib did not come to the end of the string: zlib_status = %d",
198             zlib_status);
199             }
200 42           }
201              
202             /* Compress "gf->in" (an SV *) and return the compressed value. */
203              
204             static SV *
205 23           gzip_faster (gzip_faster_t * gf)
206             {
207             /* The output. */
208             SV * zipped;
209             /* The message from zlib. */
210             int zlib_status;
211              
212 23 50         if (! SvOK (gf->in)) {
213 0           warn ("Empty input");
214 0           return & PL_sv_undef;
215             }
216 23           gf_set_up (gf);
217 23 100         if (gf->in_length == 0) {
218 3           warn ("Attempt to compress empty string");
219 3           return & PL_sv_undef;
220             }
221              
222             /* Set the windowBits parameter "gf->wb" depending on what kind of
223             compression the user wants. */
224 20 100         if (gf->is_gzip) {
225 16 50         if (gf->is_raw) {
226 0           croak ("Raw deflate and gzip are incompatible");
227             }
228             /* From zlib.h: "windowBits can also be greater than 15 for
229             optional gzip decoding. Add 32 to windowBits to enable
230             zlib and gzip decoding with automatic header detection, or
231             add 16 to decode only the gzip format (the zlib format will
232             return a Z_DATA_ERROR)." */
233              
234 16           gf->wb += DEFLATE_ENABLE_ZLIB_GZIP;
235             }
236             else {
237 4 100         if (gf->is_raw) {
238              
239             /* From zlib.h: " windowBits can also be -8..-15 for raw
240             inflate. In this case, -windowBits determines the
241             window size. inflate() will then process raw deflate
242             data, not looking for a zlib or gzip header, not
243             generating a check value, and not looking for any check
244             values for comparison at the end of the stream. " */
245              
246 3           gf->wb = -gf->wb;
247             }
248             }
249             /* The second-to-final parameter is "memLevel". Currently there is
250             no way for the user to set this. */
251 20 50         CALL_ZLIB (deflateInit2 (& gf->strm, gf->level, Z_DEFLATED,
252             gf->wb, DEFAULT_MEMLEVEL,
253             Z_DEFAULT_STRATEGY));
254              
255 20 100         if (gf->user_object) {
256 11 100         if (gf->is_gzip) {
257 10           gz_header header = {0};
258             unsigned char extra[EXTRA_LENGTH];
259             /* Have at least one of the fields in the header been set? */
260             int set_header;
261 10           set_header = 0;
262 10 100         if (gf->copy_perl_flags) {
263             /* Copy the Perl flags of "in" into the compressed
264             output. */
265 2           memcpy (extra, GZIP_PERL_ID, GZIP_PERL_ID_LENGTH);
266 2           extra[GZIP_PERL_ID_LENGTH] = 0;
267 2 100         if (SvUTF8 (gf->in)) {
268 1           extra[GZIP_PERL_ID_LENGTH] |= GZIP_PERL_UTF8;
269             }
270 2           header.extra = extra;
271 2           header.extra_len = EXTRA_LENGTH;
272 2           set_header++;
273             }
274 10 100         if (gf->file_name) {
275             char * fn;
276             /* zlib doesn't use the length of the name, it uses
277             nul-termination of the string to indicate the
278             length of the file name. */
279 4           fn = SvPV_nolen (gf->file_name);
280 4           header.name = (Bytef *) fn;
281 4           set_header++;
282             }
283 10 100         if (gf->mod_time) {
284 1           header.time = (uLong) SvUV (gf->mod_time);
285 1           set_header++;
286             }
287 10 100         if (set_header) {
288 6 50         CALL_ZLIB (deflateSetHeader (& gf->strm, & header));
289             }
290             }
291             else {
292 1 50         if (gf->copy_perl_flags) {
293 0           warn ("wrong format: perl flags not copied: use gzip_format(1)");
294             }
295 1 50         if (gf->file_name || gf->mod_time) {
    50          
296 0           warn ("wrong format: file name/modification time ignored: use gzip_format(1)");
297             }
298             }
299             }
300             /* Mark the return value as uninitialised. */
301 20           zipped = 0;
302              
303             do {
304             unsigned int have;
305 20           gf->strm.avail_out = CHUNK;
306 20           gf->strm.next_out = gf->out_buffer;
307 20           zlib_status = deflate (& gf->strm, Z_FINISH);
308 20           switch (zlib_status) {
309 20           case Z_OK:
310             case Z_STREAM_END:
311             case Z_BUF_ERROR:
312             /* Keep on chugging. */
313 20           break;
314              
315 0           case Z_STREAM_ERROR:
316 0           deflateEnd (& gf->strm);
317             /* This is supposed to never happen, but just in case it
318             does. */
319 0           croak ("Z_STREAM_ERROR from zlib during deflate");
320             break;
321              
322 0           default:
323 0           deflateEnd (& gf->strm);
324 0           croak ("Unknown status %d from deflate", zlib_status);
325             break;
326             }
327             /* The number of bytes we "have". */
328 20           have = CHUNK - gf->strm.avail_out;
329 20 50         if (! zipped) {
330 20           zipped = newSVpv ((const char *) gf->out_buffer, have);
331             }
332             else {
333 0           sv_catpvn (zipped, (const char *) gf->out_buffer, have);
334             }
335             }
336 20 50         while (gf->strm.avail_out == 0);
337             /* Check completed ok */
338 20           check_avail_in (gf);
339 20           check_zlib_status (zlib_status);
340             /* zlib clean up */
341 20           deflateEnd (& gf->strm);
342             /* Delete any user-defined file names so that they don't
343             accidentally get recycled into the next thing to be zipped. */
344 20 100         if (gf->user_object) {
345 11 100         if (gf->file_name) {
346 4           gf_delete_file_name (gf);
347             }
348             }
349             /* Safety measure. We don't expect this to happen, but on the
350             other hand we have set "zipped" to be zero before the
351             do...while loop, so just check it didn't accidentally remain
352             unset here. */
353 20 50         if (! zipped) {
354 0           croak ("%s:%d: failed to set zipped", __FILE__, __LINE__);
355             }
356 20           return zipped;
357             }
358              
359             /* The maximum length of file name which we allow for. */
360              
361             #define GF_FILE_NAME_MAX 0x400
362              
363             /* Decompress "gf->in" (an SV *) and return the decompressed value. */
364              
365             static SV *
366 27           gunzip_faster (gzip_faster_t * gf)
367             {
368             /* The return value. */
369             SV * plain;
370             /* The message from zlib. */
371             int zlib_status;
372             /* The gzip library header. */
373             gz_header header;
374             /* The name of the file, if it exists. */
375             unsigned char name[GF_FILE_NAME_MAX];
376             /* Extra bytes in the header, if they exist. */
377             unsigned char extra[EXTRA_LENGTH];
378              
379 27 100         if (! SvOK (gf->in)) {
380 1           warn ("Empty input");
381 1           return & PL_sv_undef;
382             }
383 26           gf_set_up (gf);
384 26 100         if (gf->in_length == 0) {
385 3           warn ("Attempt to uncompress empty string");
386 3           return & PL_sv_undef;
387             }
388              
389 23 100         if (gf->is_gzip) {
390 19 50         if (gf->is_raw) {
391 0           croak ("Raw deflate and gzip are incompatible");
392             }
393 19           gf->wb += INFLATE_ENABLE_ZLIB_GZIP;
394             }
395             else {
396 4 100         if (gf->is_raw) {
397 3           gf->wb = -gf->wb;
398             }
399             }
400 23 50         CALL_ZLIB (inflateInit2 (& gf->strm, gf->wb));
401 23 100         if (gf->user_object) {
402 12 100         if (gf->is_gzip) {
403 11 100         if (gf->copy_perl_flags) {
404 2           header.extra = extra;
405 2           header.extra_max = EXTRA_LENGTH;
406             }
407             /* If there are stale file names or modification times in
408             our object, delete them. */
409 11 50         if (gf->file_name) {
410 0           gf_delete_file_name (gf);
411             }
412 11 50         if (gf->mod_time) {
413 0           gf_delete_mod_time (gf);
414             }
415             /* Point the header values to our buffer, "name", and give
416             the length of the buffer. */
417 11           header.name = name;
418 11           header.name_max = GF_FILE_NAME_MAX;
419 11           inflateGetHeader (& gf->strm, & header);
420             }
421             }
422              
423             /* Mark the return value as uninitialised. */
424 23           plain = 0;
425              
426             do {
427             unsigned int have;
428 25           gf->strm.avail_out = CHUNK;
429 25           gf->strm.next_out = gf->out_buffer;
430 25           zlib_status = inflate (& gf->strm, Z_FINISH);
431 25           switch (zlib_status) {
432 24           case Z_OK:
433             case Z_STREAM_END:
434             case Z_BUF_ERROR:
435 24           break;
436              
437 1           case Z_DATA_ERROR:
438 1           inflateEnd (& gf->strm);
439 1           croak ("Data input to inflate is not in libz format");
440             break;
441              
442 0           case Z_MEM_ERROR:
443 0           inflateEnd (& gf->strm);
444 0           croak ("Out of memory during inflate");
445              
446 0           case Z_STREAM_ERROR:
447 0           inflateEnd (& gf->strm);
448 0           croak ("Internal error in zlib");
449              
450 0           default:
451 0           inflateEnd (& gf->strm);
452 0           croak ("Unknown status %d from inflate", zlib_status);
453             break;
454             }
455 24           have = CHUNK - gf->strm.avail_out;
456 24 100         if (! plain) {
457             /* If the return value is uninitialised, set up a new
458             one. */
459 22           plain = newSVpv ((const char *) gf->out_buffer, have);
460             }
461             else {
462             /* If the return value was initialised, append the
463             contents of "gf->out_buffer" to it using
464             "sv_catpvn". */
465 2           sv_catpvn (plain, (const char *) gf->out_buffer, have);
466             }
467             }
468 24 100         while (gf->strm.avail_out == 0);
469             /* Check everything is OK. */
470 22           check_avail_in (gf);
471 22           check_zlib_status (zlib_status);
472             /* Clean up. */
473 22           inflateEnd (& gf->strm);
474             /* "header.done" is filled by zlib. */
475 22 100         if (gf->user_object && gf->is_gzip && header.done == 1) {
    100          
    50          
476              
477 11 100         if (gf->copy_perl_flags) {
478             /* Check whether the "extra" segment of the header looks
479             like one of ours. */
480 2 50         if (strncmp ((const char *) header.extra, GZIP_PERL_ID,
481             GZIP_PERL_ID_LENGTH) == 0) {
482             /* Copy the Perl flags from the extra segment. */
483             unsigned is_utf8;
484 2           is_utf8 = header.extra[GZIP_PERL_ID_LENGTH] & GZIP_PERL_UTF8;
485 2 100         if (is_utf8) {
486 1           SvUTF8_on (plain);
487             }
488             }
489             }
490              
491             /* If the header includes a file name, copy it into
492             $gf->file_name. */
493 11 100         if (header.name && header.name_max > 0) {
    50          
494 6           gf->file_name = newSVpv ((const char *) header.name, 0);
495             }
496              
497             /* If the header includes a modification time, copy it into
498             $gf->mod_time. */
499 11 100         if (header.time) {
500             /* I'm not 100% sure of the type conversion
501             here. header.time is uLong in the zlib documentation,
502             and UV is unsigned int, or something? */
503 1           gf->mod_time = newSVuv (header.time);
504             }
505              
506             /* Old file names and modification times have been deleted
507             before the call to inflate, so we don't need to delete them
508             again here. */
509              
510             }
511             /* Safety measure. See similar comment above. */
512 22 50         if (! plain) {
513 0           croak ("%s:%d: failed to set plain", __FILE__, __LINE__);
514             }
515 22           return plain;
516             }
517              
518             /* Set the values of "gf" to the module defaults. */
519              
520             static void
521 17           new_user_object (gzip_faster_t * gf)
522             {
523 17           gf->file_name = 0;
524 17           gf->mod_time = 0;
525 17           gf->is_gzip = 1;
526 17           gf->is_raw = 0;
527 17           gf->user_object = 1;
528 17           gf->level = Z_DEFAULT_COMPRESSION;
529 17           }
530              
531             /* Set the compression level of "gf" to "level", with checks. */
532              
533             static void
534 3           set_compression_level (gzip_faster_t * gf, int level)
535             {
536 3 100         if (level < Z_NO_COMPRESSION) {
537 1           warn ("Cannot set compression level to less than %d",
538             Z_NO_COMPRESSION);
539 1           gf->level = Z_NO_COMPRESSION;
540             }
541 2 100         else if (level > Z_BEST_COMPRESSION) {
542 1           warn ("Cannot set compression level to more than %d",
543             Z_BEST_COMPRESSION);
544 1           gf->level = Z_BEST_COMPRESSION;
545             }
546             else {
547 1           gf->level = level;
548             }
549 3           }