File Coverage

blib/lib/MAS/TIFF/Compression/LZW.pm
Criterion Covered Total %
statement 177 184 96.2
branch 44 62 70.9
condition 5 7 71.4
subroutine 19 19 100.0
pod 0 2 0.0
total 245 274 89.4


line stmt bran cond sub pod time code
1 1     1   568 use strict;
  1         2  
  1         25  
2 1     1   5 use warnings;
  1         1  
  1         38  
3              
4             package MAS::TIFF::Compression::LZW::State;
5              
6 1     1   4 use strict;
  1         4  
  1         23  
7 1     1   4 use warnings;
  1         1  
  1         28  
8              
9 1     1   5 use Carp qw(confess);
  1         2  
  1         1022  
10              
11             sub new {
12 6     6   11 my $class = shift;
13 6         9 my $bytes = shift;
14            
15 6   100     117 return bless {
16             BYTES => $bytes // '',
17             BYTE_INDEX => 0,
18             BIT_INDEX => 0,
19             BYTE => undef,
20             DICT => { },
21             CODES => [ ],
22             }, $class;
23             }
24              
25 3     3   218 sub bytes { return shift->{BYTES} }
26              
27             sub initialize_table {
28 3     3   5 my $self = shift;
29            
30             # print " Initializing table...\n";
31            
32 3         6 $self->{DICT} = { };
33 3         10 $self->{CODES} = [ ];
34             }
35              
36             sub string_from_code {
37 1007     1007   1096 my $self = shift;
38 1007         991 my $code = shift;
39            
40 1007 100       2997 return chr($code) if ($code < 256);
41            
42 403 50       735 confess "Attempt to get string for CLEAR code (256)!" if $code == 256;
43 403 50       690 confess "Attempt to get string for EOI code (257)!" if $code == 257;
44            
45 403         696 my $string = $self->{CODES}[$code - 258];
46            
47 403 50       806 confess "Attempt to get string for undefined code $code!" unless defined $string;
48            
49             # printf " => %s\n", $code, unpack('H*', $string);
50            
51 403         886 return $string;
52             }
53              
54             sub code_from_string {
55 553     553   606 my $self = shift;
56 553         654 my $string = shift;
57            
58 553 50       1278 confess "Attempt to get code for undefined string!" unless defined $string;
59 553 50       1113 confess "Attempt to get code for empty string!" if $string eq '';
60              
61 553 100       1358 return ord($string) if length($string) == 1;
62            
63 250         486 my $code = $self->{DICT}{$string};
64            
65 250 50       473 confess "Attempt to get code for string '" . unpack('H*', $string) . "' failed!" unless defined $code;
66            
67 250         751 return $code;
68             }
69              
70             sub is_code_in_table {
71 550     550   621 my $self = shift;
72 550         556 my $code = shift;
73            
74 550         515 return (scalar(@{$self->{CODES}}) + 258) > $code;
  550         1882  
75             }
76              
77             sub is_string_in_table {
78 9459     9459   11899 my $self = shift;
79 9459         10241 my $value = shift;
80            
81 9459 100       17845 return 1 if length($value) == 1;
82            
83 9456         27945 return exists $self->{DICT}{$value};
84             }
85              
86             sub add_string_to_table {
87 1100     1100   1225 my $self = shift;
88 1100         1290 my $string = shift;
89              
90 1100 50       1903 if (length($string) == 1) {
91             # Why bother adding length-one strings to the table beyond entries 0..255?
92             # return
93             }
94             else {
95 1100 50       2034 confess "Attempt to add '" . unpack('H*', $string) . "' to table, but it is already there!" if $self->is_string_in_table($string);
96             }
97            
98 1100         1975 my $code = scalar(@{$self->{CODES}}) + 258;
  1100         2174  
99            
100             # printf " Code %d => %s\n", $code, unpack('H*', $string);
101            
102 1100         2447 $self->{CODES}[$code - 258] = $string;
103 1100         5057 $self->{DICT}{$string} = $code;
104             }
105              
106             my @write_masks = (0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff);
107              
108             sub write_code {
109 559     559   713 my $self = shift;
110 559         667 my $code = (shift) + 0;
111            
112 559         750 my $code_count = 258 + scalar(@{$self->{CODES}});
  559         1030  
113            
114 559         676 my $size;
115            
116 559 50       1847 if ($code_count >= 2048) {
    50          
    100          
117 0         0 $size = 12;
118             }
119             elsif ($code_count >= 1024) {
120 0         0 $size = 11;
121             }
122             elsif ($code_count >= 512) {
123 40         43 $size = 10;
124             }
125             else {
126 519         752 $size = 9;
127             }
128            
129             # printf "Writing %d bit code %s\n", $size, $code;
130              
131 559   100     1421 my $byte = $self->{BYTE} // 0;
132 559         872 my $bit_index = $self->{BIT_INDEX};
133              
134 559         593 my $remaining = $size;
135            
136 559         1402 while ($remaining > 0) {
137 1129         1479 my $available = 8 - $bit_index;
138            
139 1129         1095 my $writing;
140             my $extra;
141 1129 100       1956 if ($remaining <= $available) {
142 559         563 $writing = $remaining;
143 559         582 $extra = $available - $writing;
144             }
145             else {
146 570         543 $writing = $available;
147 570         666 $extra = 0;
148             }
149            
150 1129         1963 my $bits = ($write_masks[$writing] & ($code >> ($remaining - $writing))) << $extra;
151            
152 1129         1364 $byte |= $bits;
153 1129         1128 $bit_index += $writing;
154            
155 1129 100       1838 if ($bit_index == 8) {
156 633         1317 $self->{BYTES} .= chr($byte);
157 633         817 $self->{BYTE} = undef;
158 633         780 $self->{BIT_INDEX} = 0;
159 633         653 $byte = 0;
160 633         776 $bit_index = 0;
161             }
162             else {
163 496         933 $self->{BYTE} = $byte;
164 496         612 $self->{BIT_INDEX} = $bit_index;
165             }
166            
167 1129         3492 $remaining -= $writing;
168             }
169             }
170              
171             sub finish_write {
172 3     3   7 my $self = shift;
173            
174 3 50       10 if (defined $self->{BYTE}) {
175 3         7 $self->{BYTES} .= chr($self->{BYTE});
176 3         7 $self->{BIT_INDEX} = 0;
177 3         5 $self->{BYTE} = undef;
178             }
179             }
180              
181             my @read_masks = (0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01);
182              
183             sub get_next_code {
184 559     559   643 my $self = shift;
185            
186 559         538 my $code_count = 258 + scalar(@{$self->{CODES}});
  559         910  
187            
188 559         621 my $size;
189            
190 559 50       1619 if ($code_count >= 2047) { # Really? Not 2048?
    50          
    100          
191 0         0 $size = 12;
192             }
193             elsif ($code_count >= 1023) { # Really? Not 1024?
194 0         0 $size = 11;
195             }
196             elsif ($code_count >= 511) { # Really? Not 512?
197 40         47 $size = 10;
198             }
199             else {
200 519         615 $size = 9;
201             }
202            
203 559         833 my $bytes = $self->{BYTES};
204 559         735 my $bit_index = $self->{BIT_INDEX};
205 559         596 my $byte_index = $self->{BYTE_INDEX};
206              
207 559         613 my $input_length = length($bytes);
208 559         702 my $bytes_remaining = $input_length - $byte_index - 1;
209 559         742 my $bits_remaining = (8 - $bit_index) + (8 * $bytes_remaining);
210            
211 559 50       1104 return undef if ($size > $bits_remaining);
212            
213 559         549 my $result = 0;
214 559         560 my $remaining = $size;
215            
216 559         1075 while ($remaining > 0) {
217 1129         1213 my $available = 8 - $bit_index;
218            
219 1129         1072 my $reading;
220             my $extra;
221 1129 100       1879 if ($remaining <= $available) {
222 559         589 $reading = $remaining;
223 559         637 $extra = $available - $remaining;
224             }
225             else {
226 570         571 $reading = $available;
227 570         680 $extra = 0;
228             }
229            
230             # my $byte = unpack('C', substr($bytes, $byte_index, 1));
231 1129         1430 my $byte = ord(substr($bytes, $byte_index, 1));
232 1129         1261 my $mask = $read_masks[$bit_index];
233            
234 1129         1306 my $new_bits = ($mask & $byte) >> $extra;
235 1129         1262 $result = ($result << $reading) | $new_bits;
236 1129         1100 $remaining -= $reading;
237 1129         1189 $bit_index += $reading;
238 1129 100       2684 if ($bit_index == 8) {
239 633         603 $bit_index = 0;
240 633         690 $byte_index++;
241 633 50 33     2369 if (($byte_index == $input_length) && ($remaining > 0)) {
242 0         0 die "Input of $input_length bytes exhausted before finished reading $size bits. There are $remaining left to read!";
243             }
244             }
245             }
246            
247 559         765 $self->{BIT_INDEX} = $bit_index;
248 559         655 $self->{BYTE_INDEX} = $byte_index;
249              
250             # if ($result == 256) {
251             # printf "There are %d codes. Read a %d bit code: CLEAR (256)\n", $code_count, $size;
252             # }
253             # elsif ($result == 257) {
254             # printf "There are %d codes. Read a %d bit code: EOI (257)\n", $code_count, $size;
255             # }
256             # elsif ($result < 256) {
257             # printf "There are %d codes. Read a %d bit code: 0x%02x\n", $code_count, $size, $result;
258             # }
259             # else {
260             # printf "There are %d codes. Read a %d bit code: %d\n", $code_count, $size, $result;
261             # }
262            
263 559         1039 return $result;
264             }
265              
266             package MAS::TIFF::Compression::LZW;
267              
268             use constant {
269 1         527 CLEAR => 256,
270             EOI => 257,
271 1     1   5 };
  1         2  
272              
273             sub encode {
274 3     3 0 108723 my $bytes = shift;
275 3         8 my $output = '';
276            
277 3         17 my $state = MAS::TIFF::Compression::LZW::State->new();
278              
279 3         14 $state->write_code(CLEAR);
280 3         4 my $omega = '';
281 3         4 my $l = length($bytes);
282 3         11 for (my $i = 0; $i < $l; ++$i) {
283 8359         10477 my $k = substr($bytes, $i, 1);
284 8359         10052 my $key = $omega . $k;
285 8359 100       15154 if ($state->is_string_in_table($key)) {
286 7809         19996 $omega = $key;
287             }
288             else {
289 550         1172 my $code = $state->code_from_string($omega);
290 550         1093 $state->write_code($code);
291 550         1409 my $new_code = $state->add_string_to_table($key);
292 550         800 $omega = $k;
293            
294 550 50       2348 if ($new_code == 4096) {
295 0         0 $state->write_code(CLEAR);
296 0         0 $state->initialize_table;
297             }
298             }
299             }
300 3         7 my $code = $state->code_from_string($omega);
301 3         8 $state->write_code($code);
302 3         9 $state->write_code(EOI);
303            
304 3         8 $state->finish_write;
305            
306 3         11 return $state->bytes;
307             }
308              
309             sub decode {
310 3     3 0 40 my $bytes = shift;
311 3         6 my $output = '';
312            
313 3         34 my $state = MAS::TIFF::Compression::LZW::State->new($bytes);
314              
315 3         7 my $code;
316             my $old_code;
317            
318 3         4 while (1) {
319 556         1092 $code = $state->get_next_code;
320 556 50       1127 die "Unexpected end of input while reading!" unless defined $code;
321 556 100       1048 last if $code == EOI;
322            
323 553 100       892 if ($code == CLEAR) {
324 3         9 $state->initialize_table;
325 3         7 $code = $state->get_next_code;
326 3 50       8 last if ($code == EOI);
327 3         9 $output .= $state->string_from_code($code);
328 3         6 $old_code = $code;
329             }
330             else {
331 550 100       1002 if ($state->is_code_in_table($code)) {
332 454         861 my $string = $state->string_from_code($code);
333 454         644 $output .= $string;
334 454         936 my $new_string = $state->string_from_code($old_code) . substr($string, 0, 1);
335 454         926 $state->add_string_to_table($new_string);
336 454         944 $old_code = $code;
337             }
338             else {
339 96         171 my $old_string = $state->string_from_code($old_code);
340 96         208 my $out_string = $old_string . substr($old_string, 0, 1); # Why append first char of string from old_code?
341 96         141 $output .= $out_string;
342 96         162 $state->add_string_to_table($out_string);
343 96         181 $old_code = $code;
344             }
345             }
346             }
347            
348             # print "Got EOI.\n";
349            
350 3         231 return $output;
351             }
352              
353              
354             1;