File Coverage

blib/lib/Compress/LZW/Decompressor.pm
Criterion Covered Total %
statement 73 81 90.1
branch 19 24 79.1
condition 5 6 83.3
subroutine 14 14 100.0
pod 2 2 100.0
total 113 127 88.9


line stmt bran cond sub pod time code
1             package Compress::LZW::Decompressor;
2             {
3             $Compress::LZW::Decompressor::VERSION = '0.03';
4             }
5             # ABSTRACT: Scaling LZW decompressor class
6              
7              
8 4     4   31 use Compress::LZW qw(:const);
  4         8  
  4         799  
9              
10 4     4   2009 use Types::Standard qw( Bool Int );
  4         284162  
  4         163  
11              
12 4     4   5804 use Moo;
  4         34755  
  4         28  
13 4     4   7214 use namespace::clean;
  4         36873  
  4         62  
14              
15              
16             has lsb_first => (
17             is => 'ro',
18             default => \&Compress::LZW::_detect_lsb_first,
19             isa => Bool,
20             );
21              
22              
23             has init_code_size => (
24             is => 'ro',
25             default => 9,
26             isa => Type::Tiny->new(
27             parent => Int,
28             constraint => sub { $_ >= 9 and $_ < $BITS_MASK },
29             message => sub { "$_ isn't between 9 and $BITS_MASK" },
30             ),
31             );
32              
33             has _block_mode => ( # can code table reset
34             is => 'rw',
35             default => 1,
36             );
37              
38             has _max_code_size => ( # max bits
39             is => 'rw',
40             default => 16,
41             );
42              
43             has _code_size => ( # current bits
44             is => 'rw',
45             clearer => 1,
46             lazy => 1,
47             builder => sub {
48 12     12   1970 $_[0]->init_code_size;
49             },
50             );
51              
52             has _buf => (
53             is => 'ro',
54             clearer => 1,
55             default => sub { \'' },
56             );
57              
58             has _code_table => (
59             is => 'ro',
60             lazy => 1,
61             clearer => 1,
62             builder => sub {
63             return {
64 12     12   1880 map { $_ => chr($_) } 0 .. 255
  3072         9164  
65             }
66             },
67             );
68              
69             has _next_code => (
70             is => 'rw',
71             lazy => 1,
72             clearer => 1,
73             builder => sub {
74 12 100   12   2070 $_[0]->_block_mode ? 257 : 256;
75             },
76             );
77              
78              
79              
80             sub decompress {
81 11     11 1 11860 my $self = shift;
82 11         30 my ( $data ) = @_;
83              
84 11         52 $self->reset;
85              
86 11         1794 my $codes = $self->_code_table;
87              
88 11         363 my $code_reader = $self->_begin_read( $data );
89            
90 11         30 my $init_code = $code_reader->();
91 11         33 my $str = $codes->{ $init_code };
92            
93 11         23 my $seen = $init_code;
94 11         27 while ( defined( my $code = $code_reader->() ) ){
95 118728 100 100     700139 if ( $self->_block_mode and $code == $RESET_CODE ){
96             #reset table, next code, and code size
97 1         5 $self->_reset_code_table;
98            
99             # trigger the builder
100 1         29 $codes = $self->_code_table;
101            
102 1         141 my $reinit_code = $code_reader->();
103 1         5 $str .= $codes->{ $reinit_code };
104 1         2 $seen = $reinit_code;
105            
106 1         4 next;
107             }
108            
109 118727 50       452722 if ( my $word = $codes->{ $code } ){
110            
111 118727         250073 $str .= $word;
112 118727         468651 $self->_new_code( $codes->{ $seen } . substr($word,0,1) );
113             }
114             else {
115            
116 0         0 my $word = $codes->{$seen};
117              
118 0 0       0 unless ( $code == $self->_next_code ){
119 0         0 warn "($code != ". $self->_next_code . ") input may be corrupt";
120             }
121 0         0 $self->_inc_next_code;
122            
123 0         0 $codes->{$code} = $word . substr( $word, 0, 1 );
124            
125 0         0 $str .= $codes->{$code};
126             }
127 118727         1429309 $seen = $code;
128            
129             # if next code expected will require a larger bit size
130 118727 100       3020645 if ( $self->_next_code == (2 ** $self->_code_size) ){
131 21 100       1264 $self->{_code_size}++ if $self->_code_size < $self->_max_code_size;
132             }
133            
134             }
135 11         25499 return $str;
136             }
137              
138              
139             sub reset {
140 11     11 1 25 my $self = shift;
141            
142 11         46 $self->_reset_code_table;
143 11         1866 $self->_clear_buf;
144             }
145              
146             sub _reset_code_table {
147 12     12   24 my $self = shift;
148            
149 12         263 $self->_clear_code_table;
150 12         3201 $self->_clear_next_code;
151 12         1811 $self->_clear_code_size;
152             }
153              
154             sub _inc_next_code {
155 118727     118727   171298 my $self = shift;
156            
157 118727         2961329 $self->_next_code( $self->_next_code + 1 );
158             }
159              
160             sub _new_code {
161 118727     118727   211190 my $self = shift;
162 118727         163954 my ( $data ) = @_;
163            
164 118727         3152784 $self->_code_table->{ $self->_next_code } = $data;
165 118727         4811514 $self->_inc_next_code;
166             }
167              
168              
169             sub _begin_read {
170 11     11   39 my $self = shift;
171 11         31 my ( $data ) = @_;
172            
173             #check header,
174             #return : first code @9 bits,
175             # : iterator
176            
177 11         34 my $head = substr( $data, 0, 2 );
178 11 50       71 if ( $head ne $MAGIC ){
179 0         0 die "Magic bytes not found or corrupt.";
180             }
181            
182 11         30 my $bits = ord(substr( $data, 2, 1 ));
183 11         71 $self->_max_code_size( $bits & $BITS_MASK );
184 11         47 $self->_block_mode( ( $bits & $BLOCK_MASK ) >> 7 );
185            
186 11 50       80 if ( $self->init_code_size > $self->_max_code_size ){
187 0         0 die
188             "Can't decompress stream with init_code_size " .
189             $self->init_code_size .
190             " > the stream's max_code_size ".
191             $self->_max_code_size;
192             }
193              
194 11         22 my $rpos = 8 * 3; #reader position in bits;
195 11         117 my $eof = length( $data ) * 8;
196            
197             my $code_reader = sub {
198            
199 118751     118751   7249154 my $code_size = $self->_code_size;
200            
201 118751 100       918161 return undef if ( $rpos > $eof );
202            
203 118745 100       357986 my $cpos = $self->lsb_first ? $rpos : ($rpos + $code_size - 1);
204            
205 118745         161398 my $code = 0;
206 118745         300686 for ( 0 .. $code_size - 1 ){
207 1770165 100       4743121 $code |=
208             vec( $data, $cpos + ( $self->lsb_first ? $_ : (0 - $_) ), 1) << $_;
209             }
210            
211 118745         185528 $rpos += $code_size;
212            
213 118745 100 66     383476 return undef if $code == 0 and $rpos > $eof;
214 118740         398214 return $code;
215            
216 11         87 };
217              
218 11         38 return $code_reader;
219             }
220              
221             1;
222              
223             __END__