File Coverage

blib/lib/Compress/Zlib/Perl.pm
Criterion Covered Total %
statement 258 277 93.1
branch 78 98 79.5
condition 12 23 52.1
subroutine 31 34 91.1
pod 0 23 0.0
total 379 455 83.3


line stmt bran cond sub pod time code
1             package Compress::Zlib::Perl;
2              
3 1     1   8445 use 5.004;
  1         3  
  1         46  
4              
5             # use if $] > 5.006, 'warnings';
6             # use warnings;
7 1     1   7 use strict;
  1         2  
  1         71  
8              
9             require Exporter;
10              
11 1     1   8 use vars qw($VERSION @ISA @EXPORT);
  1         7  
  1         129  
12             @ISA = qw(Exporter);
13              
14             # Items to export into callers namespace by default. Note: do not export
15             # names by default without a very good reason. Use EXPORT_OK instead.
16             # Do not simply export all your public functions/methods/constants.
17              
18 1     1   8 use constant Z_OK => 0;
  1         2  
  1         237  
19 1     1   7 use constant Z_STREAM_END => 1;
  1         2  
  1         184  
20 1     1   7 use constant MAX_WBITS => 16;
  1         2  
  1         4289  
21              
22             @EXPORT = qw(
23             Z_OK Z_STREAM_END MAX_WBITS crc32
24             );
25              
26             $VERSION = '0.02';
27              
28             {
29             my @crc32;
30              
31             sub _init_crc32 {
32             # I'm not sure why Ton wanted to reverse the order of the bits in this
33             # constant, rather than using the bit-reversed constant
34             # my $p=oct reverse sprintf"%032bb0", 0x04C11DB7;
35             # But the only 5.005 friendly way I can find is this:
36 1     1   12 my $p
37             = unpack "I", pack "b*", scalar reverse unpack "b*", pack "I", 0x04C11DB7;
38 1   66     6 @crc32 = map{for my$s(0..7) {$_ = $_>>1 ^ ($_&1 && $p)} $_} 0..255;
  256         335  
  2048         5324  
  256         503  
39             }
40              
41             # Calculate gzip header 16 bit CRCs
42             sub _crc16 {
43 0     0   0 my $crc16 = shift;
44 0 0       0 _init_crc32() unless @crc32;
45 0         0 foreach my $input (@_) {
46             # I have no way to test this, as nothing that I can find generates
47             # gzip files with the header CRC.
48             # Ton's code is this:
49             $crc16 = $crc16>>8^$crc32[$crc16&0xff^ord(substr $input,$_,1)]
50 0         0 for 0..length($input)-1;
51             # I believe that the following is functionally equivalent, but should
52             # be faster:
53             # while ($input =~ /(.)/gs) {
54             # $crc16 = $crc16 >> 8 ^ $crc32[$crc16 & 0xff ^ ord $1];
55             # }
56 0         0 return $crc16;
57             }
58             }
59              
60             # Public interface starts here:
61              
62             # Calculate 32 bit CRCs
63             sub crc32 {
64 7411 100   7411 0 50155 _init_crc32() unless @crc32;
65 7411         10990 my ($buffer, $crc32) = @_;
66 7411   50     23576 $crc32 ||= 0;
67 7411         8663 $crc32 ^= 0xffffffff;
68 7411         9130 my $pos = -length $buffer;
69 7411         116882 $crc32 = $crc32>>8 ^ $crc32[$crc32&0xff^ord(substr($buffer, $pos++, 1))]
70             while $pos;
71 7411         14895 $crc32 ^ 0xffffffff;
72             }
73             }
74              
75             sub inflateInit {
76 8     8 0 3613 my %args = @_;
77 8 50 33     90 die "Please specify negative window size"
78             unless $args{-WindowBits} && $args{-WindowBits} < 0;
79 8         75 my $self = bless {isize=>0,
80             osize=>0,
81             result=>"",
82             huffman=>"",
83             type0length=>"",
84             state=>\&stateReadFinal
85             };
86 8         43 $self->_reset_bits_have;
87 8 50       53 wantarray ? ($self, Z_OK) : $self;
88             }
89              
90             sub total_in {
91 0     0 0 0 $_[0]->{isize};
92             }
93              
94             sub total_out {
95 0     0 0 0 $_[0]->{osize};
96             }
97              
98             sub inflate {
99 7403     7403 0 65581 $_[0]->{input} = \$_[1];
100 7403         10626 my ($return, $status);
101 7403         11241 $_[0]->{izize} += length $_[1];
102 7403 100       7894 if (&{$_[0]->{state}}) {
  7403         14934  
103             # Finished, so flush everything
104 8         15 $return = length $_[0]->{result};
105 8         16 $status = Z_STREAM_END;
106             } else {
107 7395 50       14023 die length ($_[1]) . " input remaining" if length $_[1];
108 7395         15683 $return = length ($_[0]->{result}) - 0x8000;
109 7395 50       15531 $return = 0 if $return < 0;
110 7395         8899 $status = Z_OK;
111             }
112 7403         12172 $_[0]->{izize} -= length $_[1];
113 7403         9891 $_[0]->{osize} += $return;
114 7403 50       31989 wantarray ? (substr ($_[0]->{result}, 0, $return, ""), $status)
115             : substr ($_[0]->{result}, 0, $return, "");
116             }
117              
118             # Public interface ends here
119              
120             sub _reset_bits_have {
121 16     16   30 my $self = shift;
122 16         61 $self->{val} = $self->{have} = 0;
123             }
124              
125              
126             # get arg bits (little endian)
127             sub _get_bits {
128 6884     6884   8625 my ($self, $want) = @_;
129 6884         7540 my ($bits_val, $bits_have) = @{$self}{qw(val have)};
  6884         12210  
130 6884         14954 while ($want > $bits_have) {
131             # inlined input read
132 3600         3805 my $byte = substr ${$_[0]->{input}}, 0, 1, "";
  3600         7880  
133 3600 100       7141 if (!length $byte) {
134 756         890 @{$self}{qw(val have)} = ($bits_val, $bits_have);
  756         1207  
135 756         1333 return;
136             }
137 2844         4750 $bits_val |= ord($byte) << $bits_have;
138 2844         6205 $bits_have += 8;
139             }
140 6128         10667 my $result = $bits_val & (1 << $want)-1;
141 6128         6373 $bits_val >>= $want;
142 6128         6315 $bits_have -= $want;
143 6128         7062 @{$self}{qw(val have)} = ($bits_val, $bits_have);
  6128         10129  
144 6128         10101 return $result;
145             }
146              
147             # Get one huffman code
148             sub _get_huffman {
149 12709     12709   17463 my ($self, $code) = @_;
150 12709         19659 $code = $self->{$code};
151 12709         14360 my ($bits_val, $bits_have, $str) = @{$self}{qw(val have huffman)};
  12709         25818  
152 12709         14960 do {
153 55909 100       98956 if (--$bits_have < 0) {
154             # inlined input read
155 8537         9007 my $byte = substr ${$_[0]->{input}}, 0, 1, "";
  8537         19160  
156 8537 100       16512 if (!length $byte) {
157             # bits_have is -1, but really should be zero, so fix in save
158 1789         2249 @{$self}{qw(val have huffman)} = ($bits_val, 0, $str);
  1789         4397  
159 1789         3824 return;
160             }
161 6748         7188 $bits_val = ord $byte;
162 6748         8311 $bits_have = 7;
163             }
164 54120         63667 $str .= $bits_val & 1;
165 54120         126857 $bits_val >>= 1;
166             } until exists $code->{$str};
167 10920 50       23698 defined($code->{$str}) || die "Bad code $str";
168 10920         14073 @{$self}{qw(val have huffman)} = ($bits_val, $bits_have, "");
  10920         23769  
169 10920         23794 return $code->{$str};
170             }
171              
172             # construct huffman code
173             sub make_huffman {
174 12     12 0 19 my $counts = shift;
175 12         18 my (%code, @counts);
176 12         41 push @{$counts[$counts->[$_]]}, $_ for 0..$#$counts;
  1288         2134  
177 12         21 my $value = 0;
178 12         17 my $bits = -1;
179 12         22 for (@counts) {
180 116         260 $value *= 2;
181 116 100 66     422 next unless ++$bits && $_;
182             # Ton used sprintf"%0${bits}b", $value;
183 76         1511 $code{reverse unpack "b$bits", pack "V", $value++} = $_ for @$_;
184             }
185             # Close the code to avoid infinite loops (and out of memory)
186 12         37 $code{reverse unpack "b$bits", pack "V", $value++} = undef for
187             $value .. (1 << $bits)-1;
188 12 50       30 @code{0, 1} = () unless %code;
189 12         95 return \%code;
190             }
191              
192             # Inflate state machine.
193             {
194             my ($static_lit_code, $static_dist_code, @lit_base, @dist_base);
195              
196             my @lit_extra = (-1,
197             0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,
198             3,3,3,3,4,4,4,4,5,5,5,5,0,-2,-2);
199             my @dist_extra = (0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,
200             9,9,10,10,11,11,12,12,13,13,-1,-1);
201             my @alpha_map = (16, 17, 18, 0, 8, 7, 9, 6, 10,
202             5, 11, 4, 12, 3, 13, 2, 14, 1, 15);
203             sub prepare_tables {
204 1     1 0 2 my $length = 3;
205 1         5 for (@lit_extra) {
206 32         35 push @lit_base, $length;
207 32 100       86 $length += 1 << $_ if $_ >= 0;
208             }
209             # Exceptional case
210 1         4 splice(@lit_base, -3, 3, 258);
211              
212 1         2 my $dist = 1;
213 1         3 for (@dist_extra) {
214 32         41 push @dist_base, $dist;
215 32 100       64 $dist += 1 << $_ if $_ >= 0;
216             }
217 1         4 splice(@dist_base, -2, 2);
218             }
219              
220             sub stateReadFinal {
221 8     8 0 35 my $bit = _get_bits($_[0], 1);
222 8 50       23 if (!defined $bit) {
223             # STALL
224 0         0 return;
225             }
226 8         25 $_[0]->{final} = $bit;
227 8         18 goto &{$_[0]->{state} = \&stateReadType};
  8         41  
228             }
229             sub stateReadType {
230 8     8 0 23 my $type = _get_bits($_[0], 2);
231 8 50       23 if (!defined $type) {
232             # STALL
233 0         0 return;
234             }
235 8         21 $_[0]->{type} = $type;
236 8 100       25 if ($type) {
237 4 100       18 prepare_tables() unless @lit_base;
238 4 50       18 if ($type == 1) {
    50          
239 0   0     0 $_[0]->{lit} = $static_lit_code ||=
240             make_huffman([(8)x144,(9)x112, (7)x24, (8)x8]);
241 0   0     0 $_[0]->{dist} = $static_dist_code ||=
242             make_huffman([(5)x32]);
243             # This is the main inflation loop.
244 0         0 goto &{$_[0]->{state} = \&stateReadLit};
  0         0  
245             } elsif ($type == 2) {
246 4         7 goto &{$_[0]->{state} = \&stateReadHLit};
  4         17  
247             } else {
248 0         0 die "deflate subtype $type not supported\n";
249             }
250             }
251 4         10 goto &{$_[0]->{state} = \&stateReadUncompressedLen};
  4         19  
252             }
253              
254             sub stateReadUncompressedLen {
255             # Not compressed;
256 8     8 0 22 $_[0]->_reset_bits_have;
257             # inlined input read
258 8         30 $_[0]->{type0length}
259 8         15 .= substr ${$_[0]->{input}}, 0, 4 - length $_[0]->{type0length}, "";
260 8 100       32 if (length $_[0]->{type0length} < 4) {
261             # STALL
262 4         11 return;
263             }
264 4         32 my ($len, $nlen) = unpack("vv", $_[0]->{type0length});
265 4         11 $_[0]->{type0length} = "";
266 4 50       20 $len == (~$nlen & 0xffff) ||
267             die "$len is not the 1-complement of $nlen";
268 4         13 $_[0]->{type0left} = $len;
269 4         9 goto &{$_[0]->{state} = \&stateReadUncompressed};
  4         16  
270             }
271              
272             sub stateReadUncompressed {
273             # inlined input read
274 4850     4850 0 5426 my $got = substr ${$_[0]->{input}}, 0, $_[0]->{type0left}, "";
  4850         10611  
275 4850         7817 $_[0]->{result} .= $got;
276 4850 100       11637 if ($_[0]->{type0left} -= length $got) {
277             # Still need more.
278             # STALL
279 4846         9369 return;
280             }
281 4 50       14 if ($_[0]->{final}) {
282             # Finished.
283 4         13 return 1;
284             }
285             # Begin the next block
286 0         0 goto &{$_[0]->{state} = \&stateReadFinal};
  0         0  
287             }
288              
289             sub stateReadHLit {
290 4     4 0 12 my $hlit = _get_bits($_[0], 5);
291 4 50       15 if (!defined $hlit) {
292             # STALL
293 0         0 return;
294             }
295 4         73 $_[0]->{hlit} = $hlit + 257;
296 4         7 goto &{$_[0]->{state} = \&stateReadHDist};
  4         17  
297             }
298             sub stateReadHDist {
299 5     5 0 74 my $hdist = _get_bits($_[0], 5);
300 5 100       15 if (!defined $hdist) {
301             # STALL
302 1         4 return;
303             }
304 4         12 $_[0]->{hdist} = $hdist + 1;
305 4         6 goto &{$_[0]->{state} = \&stateReadHCLen};
  4         16  
306             }
307             sub stateReadHCLen {
308 5     5 0 16 my $hclen = _get_bits($_[0], 4);
309 5 100       17 if (!defined $hclen) {
310             # STALL
311 1         4 return;
312             }
313 4         23 $_[0]->{alphaleft} = $_[0]->{hclen} = $hclen + 4;
314             # Determine the code length huffman code
315 4         24 $_[0]->{alpha_raw} = [(0) x @alpha_map];
316              
317 4         8 goto &{$_[0]->{state} = \&stateReadAlphaCode};
  4         14  
318             }
319             sub stateReadAlphaCode {
320 9     9 0 19 my $alpha_code = $_[0]->{alpha_raw};
321 9         26 while ($_[0]->{alphaleft}) {
322 61         104 my $code = _get_bits($_[0], 3);
323 61 100       120 if (!defined $code) {
324             # STALL
325 5         12 return;
326             }
327             # my $where = $_[0]->{hclen} - $_[0]->{alphaleft};
328 56         188 $alpha_code->[$alpha_map[$_[0]->{hclen} - $_[0]->{alphaleft}--]] = $code;
329             }
330 4         13 $_[0]->{alpha} = make_huffman($alpha_code);
331 4         11 delete $_[0]->{alpha_raw};
332              
333             # Get lit/length and distance tables
334 4         12 $_[0]->{code_len} = [];
335 4         7 goto &{$_[0]->{state} = \&stateBuildAlphaCode};
  4         18  
336             }
337              
338             sub stateBuildAlphaCode {
339 89     89 0 147 my $code_len = $_[0]->{code_len};
340 89         261 while (@$code_len < $_[0]->{hlit}+$_[0]->{hdist}) {
341 565         1011 my $alpha = _get_huffman($_[0], 'alpha');
342 565 100       1047 if (!defined $alpha) {
343             # STALL
344 57         118 return;
345             }
346 508 100       913 if ($alpha < 16) {
    100          
    100          
347 480         1693 push @$code_len, $alpha;
348             } elsif ($alpha == 16) {
349 4         8 goto &{$_[0]->{state} = \&stateReadAlphaCode16};
  4         19  
350             } elsif ($alpha == 17) {
351 16         17 goto &{$_[0]->{state} = \&stateReadAlphaCode17};
  16         58  
352             } else {
353 8         16 goto &{$_[0]->{state} = \&stateReadAlphaCodeOther};
  8         32  
354             }
355             }
356 4 50       14 @$code_len == $_[0]->{hlit}+$_[0]->{hdist} || die "too many codes";
357 4         134 my @lit_len = splice(@$code_len, 0, $_[0]->{hlit});
358 4         38 $_[0]->{lit} = make_huffman(\@lit_len);
359 4         16 $_[0]->{dist} = make_huffman($code_len);
360 4         12 delete $_[0]->{code_len};
361 4         7 goto &{$_[0]->{state} = \&stateReadLit};
  4         44  
362             }
363              
364             sub stateReadAlphaCode16 {
365 5     5 0 14 my $code_len = $_[0]->{code_len};
366 5         14 my $bits = _get_bits($_[0], 2);
367 5 100       12 if (!defined $bits) {
368             # STALL
369 1         4 return;
370             }
371 4         14 push @$code_len, ($code_len->[-1]) x (3+$bits);
372 4         7 goto &{$_[0]->{state} = \&stateBuildAlphaCode};
  4         13  
373             }
374              
375             sub stateReadAlphaCode17 {
376 19     19 0 37 my $code_len = $_[0]->{code_len};
377 19         37 my $bits = _get_bits($_[0], 3);
378 19 100       44 if (!defined $bits) {
379             # STALL
380 3         8 return;
381             }
382 16         50 push @$code_len, (0) x (3+$bits);
383 16         20 goto &{$_[0]->{state} = \&stateBuildAlphaCode};
  16         51  
384             }
385              
386             sub stateReadAlphaCodeOther {
387 10     10 0 23 my $code_len = $_[0]->{code_len};
388 10         21 my $bits = _get_bits($_[0], 7);
389 10 100       25 if (!defined $bits) {
390             # STALL
391 2         7 return;
392             }
393 8         81 push @$code_len, (0) x (11+$bits);
394 8         551 goto &{$_[0]->{state} = \&stateBuildAlphaCode};
  8         27  
395             }
396              
397             sub stateReadLit {
398 4318     4318 0 4852 while (1) {
399 8710         15573 my $lit = _get_huffman($_[0], 'lit');
400 8710 100       17339 if (!defined $lit) {
401             # STALL
402 1306         2551 return;
403             }
404 7404 100       14345 if ($lit >= 256) {
405 3012 100       6293 if ($lit_extra[$lit -= 256] < 0) {
406 4 50       15 die "Invalid literal code" if $lit;
407              
408 4 50       14 if ($_[0]->{final}) {
409             # Finished.
410 4         15 return 1;
411             }
412             # Begin the next block
413 0         0 goto &{$_[0]->{state} = \&stateReadFinal};
  0         0  
414             }
415 3008         4538 $_[0]->{litcode} = $lit;
416             # BREAK
417 3008         3151 goto &{$_[0]->{state} = \&stateGetLength};
  3008         7776  
418             }
419              
420 4392         8753 $_[0]->{result} .= chr $lit;
421             # Back to the main inflation loop
422             # goto &stateReadLit;
423             # ie loop
424             }
425             }
426              
427             sub stateGetLength {
428 3030     3030 0 4297 my $lit = $_[0]->{litcode};
429 3030         5310 my $bits = _get_bits($_[0], $lit_extra[$lit]);
430 3030 100       5599 if (!defined $bits) {
431             # STALL
432 22         41 return;
433             }
434 3008   100     8593 $_[0]->{length} = $lit_base[$lit] + ($lit_extra[$lit] && $bits);
435 3008         3981 goto &{$_[0]->{state} = \&stateGetDCode};
  3008         7045  
436             }
437              
438             sub stateGetDCode {
439 3434     3434 0 6008 my $d = _get_huffman($_[0], 'dist');
440 3434 100       6243 if (!defined $d) {
441             # STALL
442 426         768 return;
443             }
444 3008         4504 $_[0]->{dcode} = $d;
445 3008         3097 goto &{$_[0]->{state} = \&stateGetDistDecompress};
  3008         7754  
446             }
447              
448             sub stateGetDistDecompress {
449 3729     3729 0 5431 my $d = $_[0]->{dcode};
450 3729 50       6819 die "Invalid distance code" if $d >= 30;
451 3729         6774 my $bits = _get_bits($_[0], $dist_extra[$d]);
452 3729 100       6943 if (!defined $bits) {
453             # STALL
454 721         1319 return;
455             }
456 3008   100     11247 my $dist = $dist_base[$d] + ($dist_extra[$d] && $bits);
457              
458             # Go for it
459 3008         4958 my $length = $_[0]->{length};
460 3008 100       5184 if ($dist >= $length) {
461 3004         6538 my $section = substr ($_[0]->{result}, -$dist, $length);
462 3004         5226 $_[0]->{result} .= $section;
463             } else {
464 4         11 my $remaining = $length;
465 4         15 while ($remaining) {
466 12 100       50 my $take
467             = $dist >= $remaining ? $remaining : $dist;
468 12         32 $_[0]->{result} .= substr($_[0]->{result}, -$dist, $take);
469 12         26 $remaining -= $take;
470             }
471             }
472             # Back to the main inflation loop
473 3008         3353 goto &{$_[0]->{state} = \&stateReadLit};
  3008         8363  
474             }
475             }
476              
477             1;
478             __END__