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
gf_set_up(gzip_faster_t * gf)67 gf_set_up (gzip_faster_t * gf)
68 {
69     /* Extract the information from "gf->in". */
70     gf->in_char = SvPV (gf->in, gf->in_length);
71     gf->strm.next_in = (unsigned char *) gf->in_char;
72     gf->strm.avail_in = gf->in_length;
73     gf->strm.zalloc = Z_NULL;
74     gf->strm.zfree = Z_NULL;
75     gf->strm.opaque = Z_NULL;
76     if (! gf->user_object) {
77 	gf->level = Z_DEFAULT_COMPRESSION;
78     }
79     gf->wb = windowBits;
80 }
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
gf_delete_file_name(gzip_faster_t * gf)94 gf_delete_file_name (gzip_faster_t * gf)
95 {
96     UO;
97     if (gf->file_name) {
98 	SvREFCNT_dec (gf->file_name);
99 	gf->file_name = 0;
100     }
101 }
102 
103 /* Delete the file name in the object. */
104 
105 static void
gf_delete_mod_time(gzip_faster_t * gf)106 gf_delete_mod_time (gzip_faster_t * gf)
107 {
108     UO;
109     if (gf->mod_time) {
110 	SvREFCNT_dec (gf->mod_time);
111 	gf->mod_time = 0;
112     }
113 }
114 
115 static void
gf_set_file_name(gzip_faster_t * gf,SV * file_name)116 gf_set_file_name (gzip_faster_t * gf, SV * file_name)
117 {
118     UO;
119     if (gf->file_name) {
120 	gf_delete_file_name (gf);
121     }
122     SvREFCNT_inc (file_name);
123     gf->file_name = file_name;
124 }
125 
126 static SV *
gf_get_file_name(gzip_faster_t * gf)127 gf_get_file_name (gzip_faster_t * gf)
128 {
129     UO;
130     if (gf->file_name) {
131 	return gf->file_name;
132     }
133     return & PL_sv_undef;
134 }
135 
136 static void
gf_set_mod_time(gzip_faster_t * gf,SV * mod_time)137 gf_set_mod_time (gzip_faster_t * gf, SV * mod_time)
138 {
139     UO;
140     if (gf->mod_time) {
141 	gf_delete_mod_time (gf);
142     }
143     SvREFCNT_inc (mod_time);
144     gf->mod_time = mod_time;
145 }
146 
147 static SV *
gf_get_mod_time(gzip_faster_t * gf)148 gf_get_mod_time (gzip_faster_t * gf)
149 {
150     UO;
151     if (gf->mod_time) {
152 	return gf->mod_time;
153     }
154     return & PL_sv_undef;
155 }
156 
157 static void
check_avail_in(gzip_faster_t * gf)158 check_avail_in (gzip_faster_t * gf)
159 {
160     if (gf->strm.avail_in != 0) {
161 	croak ("Zlib did not finish processing the string: %d bytes left",
162 	       gf->strm.avail_in);
163     }
164 }
165 
166 static void
check_zlib_status(int zlib_status)167 check_zlib_status (int zlib_status)
168 {
169     if (zlib_status != Z_STREAM_END) {
170 	croak ("Zlib did not come to the end of the string: zlib_status = %d",
171 	       zlib_status);
172     }
173 }
174 
175 static SV *
gzip_faster(gzip_faster_t * gf)176 gzip_faster (gzip_faster_t * gf)
177 {
178     /* The output. */
179     SV * zipped;
180     /* The message from zlib. */
181     int zlib_status;
182 
183     if (! SvOK (gf->in)) {
184 	warn ("Empty input");
185 	return & PL_sv_undef;
186     }
187     gf_set_up (gf);
188     if (gf->in_length == 0) {
189 	warn ("Attempt to compress empty string");
190 	return & PL_sv_undef;
191     }
192 
193     if (gf->is_gzip) {
194 	if (gf->is_raw) {
195 	    croak ("Raw deflate and gzip are incompatible");
196 	}
197 	gf->wb += DEFLATE_ENABLE_ZLIB_GZIP;
198     }
199     else {
200 	if (gf->is_raw) {
201 	    gf->wb = -gf->wb;
202 	}
203     }
204     CALL_ZLIB (deflateInit2 (& gf->strm, gf->level, Z_DEFLATED,
205 			     gf->wb, 8,
206 			     Z_DEFAULT_STRATEGY));
207 
208     if (gf->user_object) {
209 	if (gf->is_gzip) {
210 	    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 	    set_header = 0;
215 	    if (gf->copy_perl_flags) {
216 		memcpy (extra, GZIP_PERL_ID, GZIP_PERL_ID_LENGTH);
217 		extra[GZIP_PERL_ID_LENGTH] = 0;
218 		if (SvUTF8 (gf->in)) {
219 		    extra[GZIP_PERL_ID_LENGTH] |= GZIP_PERL_UTF8;
220 		}
221 		header.extra = extra;
222 		header.extra_len = EXTRA_LENGTH;
223 		set_header++;
224 	    }
225 	    if (gf->file_name) {
226 		char * fn;
227 		fn = SvPV_nolen (gf->file_name);
228 		header.name = (Bytef *) fn;
229 		set_header++;
230 	    }
231 	    if (gf->mod_time) {
232 		header.time = (uLong) SvUV (gf->mod_time);
233 		set_header++;
234 	    }
235 	    if (set_header) {
236 		CALL_ZLIB (deflateSetHeader (& gf->strm, & header));
237 	    }
238 	}
239 	else {
240 	    if (gf->copy_perl_flags) {
241 		warn ("wrong format: perl flags not copied: use gzip_format(1)");
242 	    }
243 	    if (gf->file_name || gf->mod_time) {
244 		warn ("wrong format: file name/modification time ignored: use gzip_format(1)");
245 	    }
246 	}
247     }
248     zipped = 0;
249 
250     do {
251 	unsigned int have;
252 	gf->strm.avail_out = CHUNK;
253 	gf->strm.next_out = gf->out_buffer;
254 	zlib_status = deflate (& gf->strm, Z_FINISH);
255 	switch (zlib_status) {
256 	case Z_OK:
257 	case Z_STREAM_END:
258 	case Z_BUF_ERROR:
259 	    /* Keep on chugging. */
260 	    break;
261 
262 	case Z_STREAM_ERROR:
263 	    deflateEnd (& gf->strm);
264 	    /* This is supposed to never happen, but just in case it
265 	       does. */
266 	    croak ("Z_STREAM_ERROR from zlib during deflate");
267 
268 	default:
269 	    deflateEnd (& gf->strm);
270 	    croak ("Unknown status %d from deflate", zlib_status);
271 	    break;
272 	}
273 	/* The number of bytes we "have". */
274 	have = CHUNK - gf->strm.avail_out;
275 	if (! zipped) {
276 	    zipped = newSVpv ((const char *) gf->out_buffer, have);
277 	}
278 	else {
279 	    sv_catpvn (zipped, (const char *) gf->out_buffer, have);
280 	}
281     }
282     while (gf->strm.avail_out == 0);
283     check_avail_in (gf);
284     check_zlib_status (zlib_status);
285     deflateEnd (& gf->strm);
286     if (gf->user_object) {
287 	if (gf->file_name) {
288 	    gf_delete_file_name (gf);
289 	}
290     }
291     return zipped;
292 }
293 
294 #define GF_FILE_NAME_MAX 0x400
295 
296 static SV *
gunzip_faster(gzip_faster_t * gf)297 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     if (! SvOK (gf->in)) {
311 	warn ("Empty input");
312 	return & PL_sv_undef;
313     }
314     gf_set_up (gf);
315     if (gf->in_length == 0) {
316 	warn ("Attempt to uncompress empty string");
317 	return & PL_sv_undef;
318     }
319 
320     if (gf->is_gzip) {
321 	if (gf->is_raw) {
322 	    croak ("Raw deflate and gzip are incompatible");
323 	}
324 	gf->wb += INFLATE_ENABLE_ZLIB_GZIP;
325     }
326     else {
327 	if (gf->is_raw) {
328 	    gf->wb = -gf->wb;
329 	}
330     }
331     CALL_ZLIB (inflateInit2 (& gf->strm, gf->wb));
332     if (gf->user_object) {
333 	if (gf->is_gzip) {
334 	    if (gf->copy_perl_flags) {
335 		header.extra = extra;
336 		header.extra_max = EXTRA_LENGTH;
337 	    }
338 	    /* If there are stale file names or modification times in
339 	       our object, delete them. */
340 	    if (gf->file_name) {
341 		gf_delete_file_name (gf);
342 	    }
343 	    if (gf->mod_time) {
344 		gf_delete_mod_time (gf);
345 	    }
346 	    /* Point the header values to our buffer, "name", and give
347 	       the length of the buffer. */
348 	    header.name = name;
349 	    header.name_max = GF_FILE_NAME_MAX;
350 	    inflateGetHeader (& gf->strm, & header);
351 	}
352     }
353 
354     /* Mark the return value as uninitialised. */
355     plain = 0;
356 
357     do {
358 	unsigned int have;
359 	gf->strm.avail_out = CHUNK;
360 	gf->strm.next_out = gf->out_buffer;
361 	zlib_status = inflate (& gf->strm, Z_FINISH);
362 	switch (zlib_status) {
363 	case Z_OK:
364 	case Z_STREAM_END:
365 	case Z_BUF_ERROR:
366 	    break;
367 
368 	case Z_DATA_ERROR:
369 	    inflateEnd (& gf->strm);
370 	    croak ("Data input to inflate is not in libz format");
371 	    break;
372 
373 	case Z_MEM_ERROR:
374 	    inflateEnd (& gf->strm);
375 	    croak ("Out of memory during inflate");
376 
377 	case Z_STREAM_ERROR:
378 	    inflateEnd (& gf->strm);
379 	    croak ("Internal error in zlib");
380 
381 	default:
382 	    inflateEnd (& gf->strm);
383 	    croak ("Unknown status %d from inflate", zlib_status);
384 	    break;
385 	}
386 	have = CHUNK - gf->strm.avail_out;
387 	if (! plain) {
388 	    /* If the return value is uninitialised, set up a new
389 	       one. */
390 	    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 	    sv_catpvn (plain, (const char *) gf->out_buffer, have);
397 	}
398     }
399     while (gf->strm.avail_out == 0);
400     check_avail_in (gf);
401     check_zlib_status (zlib_status);
402     inflateEnd (& gf->strm);
403     if (gf->user_object && gf->is_gzip && header.done == 1) {
404 	if (gf->copy_perl_flags) {
405 	    if (strncmp ((const char *) header.extra, GZIP_PERL_ID,
406 			 GZIP_PERL_ID_LENGTH) == 0) {
407 		unsigned is_utf8;
408 		is_utf8 = header.extra[GZIP_PERL_ID_LENGTH] & GZIP_PERL_UTF8;
409 		if (is_utf8) {
410 		    SvUTF8_on (plain);
411 		}
412 	    }
413 	}
414 	if (header.name && header.name_max > 0) {
415 	    gf->file_name = newSVpv ((const char *) header.name, 0);
416 	    SvREFCNT_inc (gf->file_name);
417 	}
418 	else {
419 	    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 	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 	    gf->mod_time = newSVuv (header.time);
430 	    SvREFCNT_inc (gf->mod_time);
431 	}
432 	else {
433 	    gf_delete_mod_time (gf);
434 	}
435     }
436     return plain;
437 }
438 
439 static void
new_user_object(gzip_faster_t * gf)440 new_user_object (gzip_faster_t * gf)
441 {
442     gf->file_name = 0;
443     gf->mod_time = 0;
444     gf->is_gzip = 1;
445     gf->is_raw = 0;
446     gf->user_object = 1;
447     gf->level = Z_DEFAULT_COMPRESSION;
448 }
449 
450 static void
set_compression_level(gzip_faster_t * gf,int level)451 set_compression_level (gzip_faster_t * gf, int level)
452 {
453     if (level < Z_NO_COMPRESSION) {
454 	warn ("Cannot set compression level to less than %d",
455 	      Z_NO_COMPRESSION);
456 	gf->level = Z_NO_COMPRESSION;
457     }
458     else if (level > Z_BEST_COMPRESSION) {
459 	warn ("Cannot set compression level to more than %d",
460 	      Z_BEST_COMPRESSION);
461 	gf->level = Z_BEST_COMPRESSION;
462     }
463     else {
464 	gf->level = level;
465     }
466 }
467