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