File Coverage

gzip-faster-perl.c
Criterion Covered Total %
statement 174 198 87.8
branch 98 134 73.1
condition n/a
subroutine n/a
pod n/a
total 272 332 81.9


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. */
7              
8             #define windowBits 15
9             #define DEFLATE_ENABLE_ZLIB_GZIP 16
10             #define INFLATE_ENABLE_ZLIB_GZIP 32
11              
12             #define CALL_ZLIB(x) \
13             zlib_status = x; \
14             if (zlib_status < 0) { \
15             deflateEnd (& gf->strm); \
16             croak ("zlib call %s returned error status %d", \
17             #x, zlib_status); \
18             } \
19              
20             typedef struct
21             {
22             /* Input. */
23             SV * in;
24             const char * in_char;
25             STRLEN in_length;
26             /* Compression structure. */
27             z_stream strm;
28             /* Compression level. */
29             int level;
30             /* This holds the stuff. */
31             unsigned char out_buffer[CHUNK];
32             /* windowBits, adjusted for monkey business. */
33             int wb;
34             /* Optional file name for gzip format. This can only take values
35             for user-visible objects. */
36             SV * file_name;
37             /* User-defined modification time. */
38             SV * mod_time;
39             /* Gzip, not deflate or inflate. */
40             unsigned int is_gzip : 1;
41             /* "Raw" inflate or deflate without adler32 check. */
42             unsigned int is_raw : 1;
43             /* Copy Perl flags like UTF8 flag? */
44             unsigned int copy_perl_flags : 1;
45             /* User can see this object? */
46             unsigned int user_object : 1;
47             }
48             gzip_faster_t;
49              
50             /* See "http://www.gzip.org/zlib/rfc-gzip.html". */
51              
52             #define GZIP_PERL_ID "GF\1\0"
53             #define GZIP_PERL_ID_LENGTH 4
54              
55             /* Perl stuff. */
56              
57             #define GZIP_PERL_LENGTH 1
58             #define EXTRA_LENGTH GZIP_PERL_ID_LENGTH + GZIP_PERL_LENGTH
59             #define GZIP_PERL_UTF8 (1<<0)
60             /* Add more Perl flags here like
61              
62             #define SOMETHING (1<<1)
63              
64             etc. */
65              
66             static void
67 43           gf_set_up (gzip_faster_t * gf)
68             {
69             /* Extract the information from "gf->in". */
70 43 50         gf->in_char = SvPV (gf->in, gf->in_length);
71 43           gf->strm.next_in = (unsigned char *) gf->in_char;
72 43           gf->strm.avail_in = gf->in_length;
73 43           gf->strm.zalloc = Z_NULL;
74 43           gf->strm.zfree = Z_NULL;
75 43           gf->strm.opaque = Z_NULL;
76 43 100         if (! gf->user_object) {
77 23           gf->level = Z_DEFAULT_COMPRESSION;
78             }
79 43           gf->wb = windowBits;
80 43           }
81              
82             /* Check that we are always dealing with a user-defined object, not a
83             module-defined object, before altering the values. */
84              
85             #define UO \
86             if (! gf->user_object) { \
87             croak ("THIS IS NOT A USER OBJECT"); \
88             }
89              
90              
91             /* Delete the file name in the object. */
92              
93             static void
94 21           gf_delete_file_name (gzip_faster_t * gf)
95             {
96 21 50         UO;
97 21 100         if (gf->file_name) {
98 10           SvREFCNT_dec (gf->file_name);
99 10           gf->file_name = 0;
100             }
101 21           }
102              
103             /* Delete the file name in the object. */
104              
105             static void
106 22           gf_delete_mod_time (gzip_faster_t * gf)
107             {
108 22 50         UO;
109 22 100         if (gf->mod_time) {
110 2           SvREFCNT_dec (gf->mod_time);
111 2           gf->mod_time = 0;
112             }
113 22           }
114              
115             static void
116 4           gf_set_file_name (gzip_faster_t * gf, SV * file_name)
117             {
118 4 50         UO;
119 4 50         if (gf->file_name) {
120 0           gf_delete_file_name (gf);
121             }
122 4           SvREFCNT_inc (file_name);
123 4           gf->file_name = file_name;
124 4           }
125              
126             static SV *
127 5           gf_get_file_name (gzip_faster_t * gf)
128             {
129 5 50         UO;
130 5 50         if (gf->file_name) {
131 5           return gf->file_name;
132             }
133 0           return & PL_sv_undef;
134             }
135              
136             static void
137 1           gf_set_mod_time (gzip_faster_t * gf, SV * mod_time)
138             {
139 1 50         UO;
140 1 50         if (gf->mod_time) {
141 0           gf_delete_mod_time (gf);
142             }
143 1           SvREFCNT_inc (mod_time);
144 1           gf->mod_time = mod_time;
145 1           }
146              
147             static SV *
148 3           gf_get_mod_time (gzip_faster_t * gf)
149             {
150 3 50         UO;
151 3 100         if (gf->mod_time) {
152 1           return gf->mod_time;
153             }
154 2           return & PL_sv_undef;
155             }
156              
157             static void
158 36           check_avail_in (gzip_faster_t * gf)
159             {
160 36 50         if (gf->strm.avail_in != 0) {
161 0           croak ("Zlib did not finish processing the string: %d bytes left",
162             gf->strm.avail_in);
163             }
164 36           }
165              
166             static void
167 36           check_zlib_status (int zlib_status)
168             {
169 36 50         if (zlib_status != Z_STREAM_END) {
170 0           croak ("Zlib did not come to the end of the string: zlib_status = %d",
171             zlib_status);
172             }
173 36           }
174              
175             static SV *
176 20           gzip_faster (gzip_faster_t * gf)
177             {
178             /* The output. */
179             SV * zipped;
180             /* The message from zlib. */
181             int zlib_status;
182              
183 20 50         if (! SvOK (gf->in)) {
    0          
    0          
184 0           warn ("Empty input");
185 0           return & PL_sv_undef;
186             }
187 20           gf_set_up (gf);
188 20 100         if (gf->in_length == 0) {
189 3           warn ("Attempt to compress empty string");
190 3           return & PL_sv_undef;
191             }
192              
193 17 100         if (gf->is_gzip) {
194 13 50         if (gf->is_raw) {
195 0           croak ("Raw deflate and gzip are incompatible");
196             }
197 13           gf->wb += DEFLATE_ENABLE_ZLIB_GZIP;
198             }
199             else {
200 4 100         if (gf->is_raw) {
201 3           gf->wb = -gf->wb;
202             }
203             }
204 17 50         CALL_ZLIB (deflateInit2 (& gf->strm, gf->level, Z_DEFLATED,
205             gf->wb, 8,
206             Z_DEFAULT_STRATEGY));
207              
208 17 100         if (gf->user_object) {
209 10 100         if (gf->is_gzip) {
210 9           gz_header header = {0};
211             unsigned char extra[EXTRA_LENGTH];
212             /* Have at least one of the fields in the header been set? */
213             int set_header;
214 9           set_header = 0;
215 9 100         if (gf->copy_perl_flags) {
216 2           memcpy (extra, GZIP_PERL_ID, GZIP_PERL_ID_LENGTH);
217 2           extra[GZIP_PERL_ID_LENGTH] = 0;
218 2 100         if (SvUTF8 (gf->in)) {
219 1           extra[GZIP_PERL_ID_LENGTH] |= GZIP_PERL_UTF8;
220             }
221 2           header.extra = extra;
222 2           header.extra_len = EXTRA_LENGTH;
223 2           set_header++;
224             }
225 9 100         if (gf->file_name) {
226             char * fn;
227 4 50         fn = SvPV_nolen (gf->file_name);
228 4           header.name = (Bytef *) fn;
229 4           set_header++;
230             }
231 9 100         if (gf->mod_time) {
232 1 50         header.time = (uLong) SvUV (gf->mod_time);
233 1           set_header++;
234             }
235 9 100         if (set_header) {
236 9 50         CALL_ZLIB (deflateSetHeader (& gf->strm, & header));
237             }
238             }
239             else {
240 1 50         if (gf->copy_perl_flags) {
241 0           warn ("wrong format: perl flags not copied: use gzip_format(1)");
242             }
243 1 50         if (gf->file_name || gf->mod_time) {
    50          
244 0           warn ("wrong format: file name/modification time ignored: use gzip_format(1)");
245             }
246             }
247             }
248 17           zipped = 0;
249              
250             do {
251             unsigned int have;
252 17           gf->strm.avail_out = CHUNK;
253 17           gf->strm.next_out = gf->out_buffer;
254 17           zlib_status = deflate (& gf->strm, Z_FINISH);
255 17           switch (zlib_status) {
256             case Z_OK:
257             case Z_STREAM_END:
258             case Z_BUF_ERROR:
259             /* Keep on chugging. */
260 17           break;
261              
262             case Z_STREAM_ERROR:
263 0           deflateEnd (& gf->strm);
264             /* This is supposed to never happen, but just in case it
265             does. */
266 0           croak ("Z_STREAM_ERROR from zlib during deflate");
267              
268             default:
269 0           deflateEnd (& gf->strm);
270 0           croak ("Unknown status %d from deflate", zlib_status);
271             break;
272             }
273             /* The number of bytes we "have". */
274 17           have = CHUNK - gf->strm.avail_out;
275 17 50         if (! zipped) {
276 17           zipped = newSVpv ((const char *) gf->out_buffer, have);
277             }
278             else {
279 0           sv_catpvn (zipped, (const char *) gf->out_buffer, have);
280             }
281             }
282 17 50         while (gf->strm.avail_out == 0);
283 17           check_avail_in (gf);
284 17           check_zlib_status (zlib_status);
285 17           deflateEnd (& gf->strm);
286 17 100         if (gf->user_object) {
287 10 100         if (gf->file_name) {
288 4           gf_delete_file_name (gf);
289             }
290             }
291 17           return zipped;
292             }
293              
294             #define GF_FILE_NAME_MAX 0x400
295              
296             static SV *
297 24           gunzip_faster (gzip_faster_t * gf)
298             {
299             /* The return value. */
300             SV * plain;
301             /* The message from zlib. */
302             int zlib_status;
303             /* The gzip library header. */
304             gz_header header;
305             /* The name of the file, if it exists. */
306             unsigned char name[GF_FILE_NAME_MAX];
307             /* Extra bytes in the header, if they exist. */
308             unsigned char extra[EXTRA_LENGTH];
309              
310 24 100         if (! SvOK (gf->in)) {
    50          
    50          
311 1           warn ("Empty input");
312 1           return & PL_sv_undef;
313             }
314 23           gf_set_up (gf);
315 23 100         if (gf->in_length == 0) {
316 3           warn ("Attempt to uncompress empty string");
317 3           return & PL_sv_undef;
318             }
319              
320 20 100         if (gf->is_gzip) {
321 16 50         if (gf->is_raw) {
322 0           croak ("Raw deflate and gzip are incompatible");
323             }
324 16           gf->wb += INFLATE_ENABLE_ZLIB_GZIP;
325             }
326             else {
327 4 100         if (gf->is_raw) {
328 3           gf->wb = -gf->wb;
329             }
330             }
331 20 50         CALL_ZLIB (inflateInit2 (& gf->strm, gf->wb));
332 20 100         if (gf->user_object) {
333 10 100         if (gf->is_gzip) {
334 9 100         if (gf->copy_perl_flags) {
335 2           header.extra = extra;
336 2           header.extra_max = EXTRA_LENGTH;
337             }
338             /* If there are stale file names or modification times in
339             our object, delete them. */
340 9 50         if (gf->file_name) {
341 0           gf_delete_file_name (gf);
342             }
343 9 50         if (gf->mod_time) {
344 0           gf_delete_mod_time (gf);
345             }
346             /* Point the header values to our buffer, "name", and give
347             the length of the buffer. */
348 9           header.name = name;
349 9           header.name_max = GF_FILE_NAME_MAX;
350 9           inflateGetHeader (& gf->strm, & header);
351             }
352             }
353              
354             /* Mark the return value as uninitialised. */
355 20           plain = 0;
356              
357             do {
358             unsigned int have;
359 22           gf->strm.avail_out = CHUNK;
360 22           gf->strm.next_out = gf->out_buffer;
361 22           zlib_status = inflate (& gf->strm, Z_FINISH);
362 22           switch (zlib_status) {
363             case Z_OK:
364             case Z_STREAM_END:
365             case Z_BUF_ERROR:
366 21           break;
367              
368             case Z_DATA_ERROR:
369 1           inflateEnd (& gf->strm);
370 1           croak ("Data input to inflate is not in libz format");
371             break;
372              
373             case Z_MEM_ERROR:
374 0           inflateEnd (& gf->strm);
375 0           croak ("Out of memory during inflate");
376              
377             case Z_STREAM_ERROR:
378 0           inflateEnd (& gf->strm);
379 0           croak ("Internal error in zlib");
380              
381             default:
382 0           inflateEnd (& gf->strm);
383 0           croak ("Unknown status %d from inflate", zlib_status);
384             break;
385             }
386 21           have = CHUNK - gf->strm.avail_out;
387 21 100         if (! plain) {
388             /* If the return value is uninitialised, set up a new
389             one. */
390 19           plain = newSVpv ((const char *) gf->out_buffer, have);
391             }
392             else {
393             /* If the return value was initialised, append the
394             contents of "gf->out_buffer" to it using
395             "sv_catpvn". */
396 2           sv_catpvn (plain, (const char *) gf->out_buffer, have);
397             }
398             }
399 21 100         while (gf->strm.avail_out == 0);
400 19           check_avail_in (gf);
401 19           check_zlib_status (zlib_status);
402 19           inflateEnd (& gf->strm);
403 19 100         if (gf->user_object && gf->is_gzip && header.done == 1) {
    100          
    50          
404 9 100         if (gf->copy_perl_flags) {
405 2 50         if (strncmp ((const char *) header.extra, GZIP_PERL_ID,
406             GZIP_PERL_ID_LENGTH) == 0) {
407             unsigned is_utf8;
408 2           is_utf8 = header.extra[GZIP_PERL_ID_LENGTH] & GZIP_PERL_UTF8;
409 2 100         if (is_utf8) {
410 1           SvUTF8_on (plain);
411             }
412             }
413             }
414 9 100         if (header.name && header.name_max > 0) {
    50          
415 6           gf->file_name = newSVpv ((const char *) header.name, 0);
416 6           SvREFCNT_inc (gf->file_name);
417             }
418             else {
419 3           gf_delete_file_name (gf);
420             }
421             /* If the header includes a modification time, copy it into
422             $gf->mod_time. If the header doesn't include a modification
423             time, make sure the modification time of $gf is completely
424             clear, so the user doesn't get old junk data. */
425 9 100         if (header.time) {
426             /* I'm not 100% sure of the type conversion
427             here. header.time is uLong in the zlib documentation,
428             and UV is unsigned int, or something? */
429 1           gf->mod_time = newSVuv (header.time);
430 1           SvREFCNT_inc (gf->mod_time);
431             }
432             else {
433 8           gf_delete_mod_time (gf);
434             }
435             }
436 23           return plain;
437             }
438              
439             static void
440 14           new_user_object (gzip_faster_t * gf)
441             {
442 14           gf->file_name = 0;
443 14           gf->mod_time = 0;
444 14           gf->is_gzip = 1;
445 14           gf->is_raw = 0;
446 14           gf->user_object = 1;
447 14           gf->level = Z_DEFAULT_COMPRESSION;
448 14           }
449              
450             static void
451 3           set_compression_level (gzip_faster_t * gf, int level)
452             {
453 3 100         if (level < Z_NO_COMPRESSION) {
454 1           warn ("Cannot set compression level to less than %d",
455             Z_NO_COMPRESSION);
456 1           gf->level = Z_NO_COMPRESSION;
457             }
458 2 100         else if (level > Z_BEST_COMPRESSION) {
459 1           warn ("Cannot set compression level to more than %d",
460             Z_BEST_COMPRESSION);
461 1           gf->level = Z_BEST_COMPRESSION;
462             }
463             else {
464 1           gf->level = level;
465             }
466 3           }