File Coverage

blib/lib/Compress/LZW/Decompressor.pm
Criterion Covered Total %
statement 74 76 97.3
branch 18 20 90.0
condition 6 9 66.6
subroutine 9 9 100.0
pod 2 2 100.0
total 109 116 93.9


line stmt bran cond sub pod time code
1             package Compress::LZW::Decompressor;
2             # ABSTRACT: Scaling LZW decompressor class
3             $Compress::LZW::Decompressor::VERSION = '0.04';
4              
5 4     4   21 use Compress::LZW qw(:const);
  4         4  
  4         627  
6              
7 4     4   1122 use Types::Standard qw( Bool Int );
  4         112645  
  4         36  
8              
9 4     4   3392 use Moo;
  4         20265  
  4         23  
10 4     4   3890 use namespace::clean;
  4         18131  
  4         47  
11              
12              
13             sub decompress {
14 10     10 1 938 my $self = shift;
15 10         18 my ( $data ) = @_;
16              
17 10         31 $self->reset;
18              
19 10         17 $self->{data} = \$data;
20 10         22 $self->{data_pos} = 0;
21              
22 10         30 $self->_read_magic;
23 10         16 $self->{data_pos} = 24;
24              
25 10         22 $self->_str_reset;
26              
27 10         30 my $next_increase = 2 ** $self->{code_size};
28              
29 10         35 my $seen = $self->_read_code;
30 10         25 my $buf = $self->{str_table}{$seen};
31              
32 10         19 while ( defined( my $code = $self->_read_code ) ){
33              
34 387851 100 100     1110326 if ( $self->{block_mode} and $code == $RESET_CODE ){
35             # warn sprintf('reset table (%s, %s) at %s', $seen, $code, $self->{data_pos} - $self->{code_size});
36             #reset table, next code, and code size
37 3         14 $self->_str_reset;
38 3         13 $next_increase = 2 ** $self->{code_size};
39              
40 3         42 $seen = $self->_read_code;
41 3         75 $buf .= $self->{str_table}{$seen};
42            
43 3         99 next;
44             }
45            
46 387848 100       824371 if ( defined ( my $word = $self->{str_table}{ $code } ) ){
    50          
47              
48 387838         325035 $buf .= $word;
49              
50 387838         1021942 $self->{str_table}{ $self->{next_code} } = $self->{str_table}{ $seen } . substr($word,0,1);
51              
52             }
53             elsif ( $code == $self->{next_code} ){
54            
55 10         18 my $word = $self->{str_table}{$seen};
56            
57 10         35 $self->{str_table}{$code} = $word . substr( $word, 0, 1 );
58            
59 10         15 $buf .= $self->{str_table}{$code};
60              
61             }
62             else {
63 0         0 die "($code != ". $self->{next_code} . ") input may be corrupt before bit $self->{data_pos}";
64             }
65              
66 387848         282035 $seen = $code;
67            
68             # if next code expected will require a larger bit size
69 387848 100       564717 if ( $self->{next_code} + 1 >= $next_increase ){
70 48479 100       61329 if ( $self->{code_size} < $self->{max_code_size} ){
71             # warn "decode up to $self->{code_size} bits at bit $self->{data_pos}";
72 39         68 $self->{code_size} += 1;
73 39         68 $next_increase *= 2;
74             }
75             else {
76 48440         47141 $self->{at_max_code} = 1;
77             }
78             }
79              
80 387848 100       570145 if ( $self->{at_max_code} == 0 ){
81 339408         510295 $self->{next_code} += 1;
82             }
83            
84             }
85 10         65658 return $buf;
86             }
87              
88              
89             sub reset {
90 10     10 1 16 my $self = shift;
91            
92 10         42 $self->{data} = undef;
93 10         25 $self->{data_pos} = 0;
94              
95 10         30 $self->_str_reset;
96             }
97              
98             sub _str_reset {
99 23     23   29 my $self = shift;
100            
101 5888         9596 $self->{str_table} = {
102 23         68 map { $_ => chr($_) } 0 .. 255
103             };
104            
105 23         158795 $self->{code_size} = $INIT_CODE_SIZE;
106 23 100       102 $self->{next_code} = $self->{block_mode} ? $BL_INIT_CODE : $NR_INIT_CODE;
107 23         79 $self->{at_max_code} = 0;
108             }
109              
110             sub _read_magic {
111 10     10   17 my $self = shift;
112            
113 10         13 my $magic = substr( ${ $self->{data} }, 0, 3 );
  10         33  
114              
115 10 50 33     77 if ( length($magic) != 3 or substr($magic,0, 2) ne $MAGIC ){
116 0         0 die "Invalid compress(1) header";
117             }
118              
119 10         21 my $bits = ord( substr( $magic, 2, 1 ) );
120              
121 10         22 $self->{max_code_size} = $bits & $MASK_BITS;
122 10         30 $self->{block_mode} = ( $bits & $MASK_BLOCK ) >> 7;
123             }
124              
125             sub _read_code {
126 387874     387874   288559 my $self = shift;
127              
128 387874 100       303473 if ( $self->{data_pos} > length( ${$self->{data}} ) * 8 ){
  387874         634188  
129             # warn "bailing at $self->{data_pos} + $self->{code_size} > " . length( ${$self->{data}} ) *8;
130 5         16 return undef;
131             }
132            
133 387869         285329 my $code = 0;
134 387869         444325 for ( 0 .. ($self->{code_size} - 1) ){
135 5889753         6772603 $code |=
136 5889753         3514590 vec( ${$self->{data}}, $self->{data_pos} + $_ , 1) << $_;
137             }
138            
139 387869         386766 $self->{data_pos} += $self->{code_size};
140            
141 387869 100 66     626369 return undef if $code == 0 and $self->{data_pos} > length( ${$self->{data}} ) * 8;
  5         36  
142 387864         654373 return $code;
143            
144             }
145              
146             1;
147              
148             __END__