File Coverage

blib/lib/Compression/Util.pm
Criterion Covered Total %
statement 2756 2863 96.2
branch 781 1012 77.1
condition 222 347 63.9
subroutine 137 137 100.0
pod 113 113 100.0
total 4009 4472 89.6


line stmt bran cond sub pod time code
1             package Compression::Util;
2              
3 47     47   4092481 use utf8;
  47         9632  
  47         239  
4 47     47   2001 use 5.036;
  47         146  
5 47     47   231 use List::Util qw(min uniq max sum all);
  47         82  
  47         3841  
6 47     47   257 use Carp qw(confess);
  47         142  
  47         5663  
7              
8             require Exporter;
9              
10             our @ISA = qw(Exporter);
11              
12             our $VERSION = '0.16';
13             our $VERBOSE = 0; # verbose mode
14              
15             our $LZ_MIN_LEN = 4; # minimum match length in LZ parsing
16             our $LZ_MAX_LEN = 1 << 15; # maximum match length in LZ parsing
17             our $LZ_MAX_DIST = ~0; # maximum allowed back-reference distance in LZ parsing
18             our $LZ_MAX_CHAIN_LEN = 32; # how many recent positions to remember in LZ parsing
19              
20             # Arithmetic Coding settings
21 47     47   295 use constant BITS => 32;
  47         97  
  47         4670  
22 47     47   234 use constant MAX => oct('0b' . ('1' x BITS));
  47         119  
  47         2486  
23 47     47   214 use constant INITIAL_FREQ => 1;
  47         87  
  47         1172692  
24              
25             our %EXPORT_TAGS = (
26             'all' => [
27             qw(
28              
29             crc32
30             adler32
31              
32             read_bit
33             read_bit_lsb
34              
35             read_bits
36             read_bits_lsb
37              
38             int2bits
39             int2bits_lsb
40              
41             int2bytes
42             int2bytes_lsb
43              
44             bits2int
45             bits2int_lsb
46              
47             bytes2int
48             bytes2int_lsb
49              
50             string2symbols
51             symbols2string
52              
53             read_null_terminated
54              
55             bwt_encode
56             bwt_decode
57              
58             bwt_encode_symbolic
59             bwt_decode_symbolic
60              
61             bwt_sort
62             bwt_sort_symbolic
63              
64             bwt_compress
65             bwt_decompress
66              
67             bwt_compress_symbolic
68             bwt_decompress_symbolic
69              
70             bzip2_compress
71             bzip2_decompress
72              
73             gzip_compress
74             gzip_decompress
75              
76             mrl_compress
77             mrl_decompress
78              
79             mrl_compress_symbolic
80             mrl_decompress_symbolic
81              
82             create_huffman_entry
83             decode_huffman_entry
84              
85             delta_encode
86             delta_decode
87              
88             huffman_encode
89             huffman_decode
90              
91             huffman_from_freq
92             huffman_from_symbols
93             huffman_from_code_lengths
94              
95             mtf_encode
96             mtf_decode
97              
98             encode_alphabet
99             decode_alphabet
100              
101             encode_alphabet_256
102             decode_alphabet_256
103              
104             deltas
105             accumulate
106             frequencies
107              
108             run_length
109              
110             binary_vrl_encode
111             binary_vrl_decode
112              
113             rle4_encode
114             rle4_decode
115              
116             zrle_encode
117             zrle_decode
118              
119             lzss_compress
120             lzss_decompress
121              
122             make_deflate_tables
123             find_deflate_index
124              
125             deflate_encode
126             deflate_decode
127              
128             lzss_encode
129             lzss_encode_fast
130             lzss_encode_fast_symbolic
131             lzss_decode
132              
133             lzss_encode_symbolic
134             lzss_decode_symbolic
135              
136             lzss_compress_symbolic
137             lzss_decompress_symbolic
138              
139             lz77_encode
140             lz77_decode
141              
142             lz77_encode_symbolic
143             lz77_decode_symbolic
144              
145             lz77_compress
146             lz77_decompress
147              
148             lz77_compress_symbolic
149             lz77_decompress_symbolic
150              
151             lzb_compress
152             lzb_decompress
153              
154             lz4_compress
155             lz4_decompress
156              
157             ac_encode
158             ac_decode
159              
160             create_ac_entry
161             decode_ac_entry
162              
163             adaptive_ac_encode
164             adaptive_ac_decode
165              
166             create_adaptive_ac_entry
167             decode_adaptive_ac_entry
168              
169             abc_encode
170             abc_decode
171              
172             fibonacci_encode
173             fibonacci_decode
174              
175             elias_gamma_encode
176             elias_gamma_decode
177              
178             elias_omega_encode
179             elias_omega_decode
180              
181             obh_encode
182             obh_decode
183              
184             lzw_encode
185             lzw_decode
186              
187             lzw_compress
188             lzw_decompress
189              
190             zlib_compress
191             zlib_decompress
192              
193             deflate_create_block_type_0_header
194             deflate_create_block_type_1
195             deflate_create_block_type_2
196              
197             deflate_extract_next_block
198             deflate_extract_block_type_0
199             deflate_extract_block_type_1
200             deflate_extract_block_type_2
201             )
202             ]
203             );
204              
205             our @EXPORT_OK = (@{$EXPORT_TAGS{'all'}}, '$VERBOSE', '$LZ_MAX_CHAIN_LEN', '$LZ_MIN_LEN', '$LZ_MAX_LEN', '$LZ_MAX_DIST');
206             our @EXPORT;
207              
208             ##########################
209             # Misc low-level functions
210             ##########################
211              
212 168638     168638 1 156506 sub read_bit ($fh, $bitstring) {
  168638         158833  
  168638         157100  
  168638         149978  
213              
214 168638 100 50     261602 if (($$bitstring // '') eq '') {
215 22156   33     48407 $$bitstring = unpack('b*', getc($fh) // confess "can't read bit");
216             }
217              
218 168638         275398 chop($$bitstring);
219             }
220              
221 89521     89521 1 85135 sub read_bit_lsb ($fh, $bitstring) {
  89521         83582  
  89521         79181  
  89521         77728  
222              
223 89521 100 50     138466 if (($$bitstring // '') eq '') {
224 14390   33     30341 $$bitstring = unpack('B*', getc($fh) // confess "can't read bit");
225             }
226              
227 89521         133474 chop($$bitstring);
228             }
229              
230 1005     1005 1 1172 sub read_bits ($fh, $bits_len) {
  1005         1165  
  1005         1341  
  1005         1246  
231              
232 1005   33     4271 read($fh, (my $data), $bits_len >> 3) // confess "Read error: $!";
233 1005         3329 $data = unpack('B*', $data);
234              
235 1005         2676 while (length($data) < $bits_len) {
236 517   33     2952 $data .= unpack('B*', getc($fh) // confess "can't read bits");
237             }
238              
239 1005 100       2344 if (length($data) > $bits_len) {
240 517         1467 $data = substr($data, 0, $bits_len);
241             }
242              
243 1005         3320 return $data;
244             }
245              
246 1     1 1 12 sub read_bits_lsb ($fh, $bits_len) {
  1         2  
  1         2  
  1         1  
247              
248 1   33     5 read($fh, (my $data), $bits_len >> 3) // confess "Read error: $!";
249 1         3 $data = unpack('b*', $data);
250              
251 1         5 while (length($data) < $bits_len) {
252 0   0     0 $data .= unpack('b*', getc($fh) // confess "can't read bits");
253             }
254              
255 1 50       3 if (length($data) > $bits_len) {
256 0         0 $data = substr($data, 0, $bits_len);
257             }
258              
259 1         4 return $data;
260             }
261              
262 79     79 1 2597 sub int2bits ($value, $size) {
  79         187  
  79         115  
  79         96  
263 79         304 sprintf("%0*b", $size, $value);
264             }
265              
266 11974     11974 1 18502 sub int2bits_lsb ($value, $size) {
  11974         14338  
  11974         13915  
  11974         12569  
267 11974         43820 scalar reverse sprintf("%0*b", $size, $value);
268             }
269              
270 221     221 1 281 sub int2bytes ($value, $size) {
  221         339  
  221         314  
  221         239  
271 221         1089 pack('B*', sprintf("%0*b", 8 * $size, $value));
272             }
273              
274 145     145 1 857 sub int2bytes_lsb ($value, $size) {
  145         263  
  145         206  
  145         170  
275 145         1228 pack('b*', scalar reverse sprintf("%0*b", 8 * $size, $value));
276             }
277              
278 766     766 1 1682 sub bytes2int($fh, $n) {
  766         985  
  766         927  
  766         973  
279              
280 766 100       1547 if (ref($fh) eq '') {
281 8 50       83 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
282 8         18 return __SUB__->($fh2, $n);
283             }
284              
285 758         971 my $bytes = '';
286 758         4197 $bytes .= getc($fh) for (1 .. $n);
287 758         3901 oct('0b' . unpack('B*', $bytes));
288             }
289              
290 345     345 1 575 sub bytes2int_lsb ($fh, $n) {
  345         450  
  345         465  
  345         374  
291              
292 345 100       846 if (ref($fh) eq '') {
293 8 50       85 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
294 8         20 return __SUB__->($fh2, $n);
295             }
296              
297 337         429 my $bytes = '';
298 337         1629 $bytes .= getc($fh) for (1 .. $n);
299 337         1802 oct('0b' . reverse unpack('b*', $bytes));
300             }
301              
302 276     276 1 290 sub bits2int ($fh, $size, $buffer) {
  276         298  
  276         283  
  276         290  
  276         239  
303              
304 276 100 100     693 if ($size % 8 == 0 and ($$buffer // '') eq '') { # optimization
      100        
305 29         63 return bytes2int($fh, $size >> 3);
306             }
307              
308 247         307 my $bitstring = '0b';
309 247         333 for (1 .. $size) {
310 3627 100 50     6144 $bitstring .= ($$buffer // '') eq '' ? read_bit($fh, $buffer) : chop($$buffer);
311             }
312 247         443 oct($bitstring);
313             }
314              
315 5587     5587 1 5961 sub bits2int_lsb ($fh, $size, $buffer) {
  5587         5304  
  5587         5446  
  5587         5050  
  5587         5048  
316              
317 5587 100 100     8901 if ($size % 8 == 0 and ($$buffer // '') eq '') { # optimization
      100        
318 154         345 return bytes2int_lsb($fh, $size >> 3);
319             }
320              
321 5433         5397 my $bitstring = '';
322 5433         7467 for (1 .. $size) {
323 28807 100 50     50589 $bitstring .= ($$buffer // '') eq '' ? read_bit_lsb($fh, $buffer) : chop($$buffer);
324             }
325 5433         9371 oct('0b' . reverse($bitstring));
326             }
327              
328 130     130 1 8130 sub string2symbols ($string) {
  130         279  
  130         239  
329 130         87312 [unpack('C*', $string)];
330             }
331              
332 76     76 1 2772 sub symbols2string ($symbols) {
  76         106  
  76         92  
333 76         5828 pack('C*', @$symbols);
334             }
335              
336 8     8 1 1849 sub read_null_terminated ($fh) {
  8         11  
  8         10  
337 8         9 my $string = '';
338 8         10 while (1) {
339 66   33     124 my $c = getc($fh) // confess "can't read character";
340 66 100       108 last if $c eq "\0";
341 58         90 $string .= $c;
342             }
343 8         21 return $string;
344             }
345              
346 727     727 1 875 sub frequencies ($symbols) {
  727         813  
  727         830  
347 727         960 my %freq;
348 727         50507 ++$freq{$_} for @$symbols;
349 727         2002 return \%freq;
350             }
351              
352 1010     1010 1 1140 sub deltas ($integers) {
  1010         1141  
  1010         1153  
353              
354 1010         1106 my @deltas;
355 1010         1218 my $prev = 0;
356              
357 1010         1501 foreach my $n (@$integers) {
358 224547         218148 push @deltas, $n - $prev;
359 224547         227191 $prev = $n;
360             }
361              
362 1010         1834 return \@deltas;
363             }
364              
365 459     459 1 575 sub accumulate ($deltas) {
  459         567  
  459         489  
366              
367 459         594 my @acc;
368 459         584 my $prev = 0;
369              
370 459         845 foreach my $d (@$deltas) {
371 7966         7514 $prev += $d;
372 7966         12401 push @acc, $prev;
373             }
374              
375 459         925 return \@acc;
376             }
377              
378             ########################
379             # Fibonacci Coding
380             ########################
381              
382 426     426 1 598 sub fibonacci_encode ($symbols) {
  426         614  
  426         565  
383              
384 426         713 my $bitstring = '';
385              
386 426         970 foreach my $n (scalar(@$symbols), @$symbols) {
387 2436         3197 my ($f1, $f2, $f3) = (0, 1, 1);
388 2436         3483 my ($rn, $s, $k) = ($n + 1, '', 2);
389 2436         3739 for (; $f3 <= $rn ; ++$k) {
390 18567         31274 ($f1, $f2, $f3) = ($f2, $f3, $f2 + $f3);
391             }
392 2436         3457 foreach my $i (1 .. $k - 2) {
393 18567         20278 ($f3, $f2, $f1) = ($f2, $f1, $f2 - $f1);
394 18567 100       20836 if ($f3 <= $rn) {
395 6027         5827 $rn -= $f3;
396 6027         7290 $s .= '1';
397             }
398             else {
399 12540         14438 $s .= '0';
400             }
401             }
402 2436         4125 $bitstring .= reverse($s) . '1';
403             }
404              
405 426         4249 pack('B*', $bitstring);
406             }
407              
408 439     439 1 619 sub fibonacci_decode ($fh) {
  439         536  
  439         491  
409              
410 439 100       1007 if (ref($fh) eq '') {
411 13 50       216 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
412 13         39 return __SUB__->($fh2);
413             }
414              
415 426         685 my @symbols;
416              
417 426         637 my $enc = '';
418 426         589 my $prev_bit = '0';
419              
420 426         522 my $len = 0;
421 426         528 my $buffer = '';
422              
423 426         957 for (my $k = 0 ; $k <= $len ;) {
424 21003         23318 my $bit = read_bit($fh, \$buffer);
425              
426 21003 100 100     37551 if ($bit eq '1' and $prev_bit eq '1') {
427 2436         3509 my ($value, $f1, $f2) = (0, 1, 1);
428 2436         5617 foreach my $bit (split //, $enc) {
429 18567 100       23082 $value += $f2 if $bit;
430 18567         25298 ($f1, $f2) = ($f2, $f1 + $f2);
431             }
432 2436         3985 push @symbols, $value - 1;
433 2436 100       3724 $len = pop @symbols if (++$k == 1);
434 2436         2673 $enc = '';
435 2436         4160 $prev_bit = '0';
436             }
437             else {
438 18567         18574 $enc .= $bit;
439 18567         26596 $prev_bit = $bit;
440             }
441             }
442              
443 426         1121 return \@symbols;
444             }
445              
446             #######################################
447             # Adaptive Binary Concatenation method
448             #######################################
449              
450 31     31 1 86 sub abc_encode ($integers) {
  31         56  
  31         59  
451              
452 31         52 my @counts;
453 31         55 my $count = 0;
454 31         44 my $bits_width = 1;
455 31         66 my $bits_max_symbol = 1 << $bits_width;
456 31         54 my $processed_len = 0;
457              
458 31         88 foreach my $k (@$integers) {
459 9150         11497 while ($k >= $bits_max_symbol) {
460              
461 202 100       336 if ($count > 0) {
462 23         55 push @counts, [$bits_width, $count];
463 23         32 $processed_len += $count;
464             }
465              
466 202         233 $count = 0;
467 202         225 $bits_max_symbol *= 2;
468 202         359 $bits_width += 1;
469             }
470 9150         9935 ++$count;
471             }
472              
473 31         103 push @counts, grep { $_->[1] > 0 } [$bits_width, scalar(@$integers) - $processed_len];
  31         103  
474              
475 31 50       90 $VERBOSE && say STDERR "Bit sizes: ", join(' ', map { $_->[0] } @counts);
  0         0  
476 31 50       80 $VERBOSE && say STDERR "Lengths : ", join(' ', map { $_->[1] } @counts);
  0         0  
477 31 50       66 $VERBOSE && say STDERR '';
478              
479 31         64 my $compressed = fibonacci_encode([(map { $_->[0] } @counts), (map { $_->[1] } @counts)]);
  52         131  
  52         145  
480              
481 31         68 my $bits = '';
482 31         4923 my @ints = @$integers;
483              
484 31         58 foreach my $pair (@counts) {
485 52         95 my ($blen, $len) = @$pair;
486 52         311 foreach my $symbol (splice(@ints, 0, $len)) {
487 9150         11596 $bits .= sprintf("%0*b", $blen, $symbol);
488             }
489             }
490              
491 31         254 $compressed .= pack('B*', $bits);
492 31         191 return $compressed;
493             }
494              
495 44     44 1 53 sub abc_decode ($fh) {
  44         55  
  44         45  
496              
497 44 100       105 if (ref($fh) eq '') {
498 13 50       191 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
499 13         34 return __SUB__->($fh2);
500             }
501              
502 31         69 my $ints = fibonacci_decode($fh);
503 31         55 my $half = scalar(@$ints) >> 1;
504              
505 31         38 my @counts;
506 31         80 foreach my $i (0 .. ($half - 1)) {
507 52         124 push @counts, [$ints->[$i], $ints->[$half + $i]];
508             }
509              
510 31         40 my $bits_len = 0;
511              
512 31         54 foreach my $pair (@counts) {
513 52         101 my ($blen, $len) = @$pair;
514 52         88 $bits_len += $blen * $len;
515             }
516              
517 31         80 my $bits = read_bits($fh, $bits_len);
518              
519 31         104 my @integers;
520 31         73 foreach my $pair (@counts) {
521 52         92 my ($blen, $len) = @$pair;
522 52         4909 foreach my $chunk (unpack(sprintf('(a%d)*', $blen), substr($bits, 0, $blen * $len, ''))) {
523 9150         19558 push @integers, oct('0b' . $chunk);
524             }
525             }
526              
527 31         209 return \@integers;
528             }
529              
530             ###################################
531             # Arithmetic Coding (in fixed bits)
532             ###################################
533              
534 158     158   205 sub _create_cfreq ($freq) {
  158         168  
  158         170  
535              
536 158         194 my @cf;
537 158         205 my $T = 0;
538              
539 158         742 foreach my $i (sort { $a <=> $b } keys %$freq) {
  5827         6206  
540 1650   50     2337 $freq->{$i} // next;
541 1650         19686 $cf[$i] = $T;
542 1650         1729 $T += $freq->{$i};
543 1650         2366 $cf[$i + 1] = $T;
544             }
545              
546 158         559 return (\@cf, $T);
547             }
548              
549 79     79 1 143 sub ac_encode ($symbols) {
  79         154  
  79         129  
550              
551 79 50       235 if (ref($symbols) eq '') {
552 0         0 $symbols = string2symbols($symbols);
553             }
554              
555 79         117 my $enc = '';
556 79   100     584 my $EOF_SYMBOL = (max(@$symbols) // 0) + 1;
557 79         888 my @bytes = (@$symbols, $EOF_SYMBOL);
558              
559 79         204 my $freq = frequencies(\@bytes);
560 79         265 my ($cf, $T) = _create_cfreq($freq);
561              
562 79 50       202 if ($T > MAX) {
563 0         0 confess "Too few bits: $T > ${\MAX}";
  0         0  
564             }
565              
566 79         103 my $low = 0;
567 79         92 my $high = MAX;
568 79         137 my $uf_count = 0;
569              
570 79         157 foreach my $c (@bytes) {
571              
572 4984         5375 my $w = $high - $low + 1;
573              
574 4984         6781 $high = ($low + int(($w * $cf->[$c + 1]) / $T) - 1) & MAX;
575 4984         6258 $low = ($low + int(($w * $cf->[$c]) / $T)) & MAX;
576              
577 4984 50       6575 if ($high > MAX) {
578 0         0 confess "high > MAX: $high > ${\MAX}";
  0         0  
579             }
580              
581 4984 50       6569 if ($low >= $high) { confess "$low >= $high" }
  0         0  
582              
583 4984         4561 while (1) {
584              
585 19443 100 100     31808 if (($high >> (BITS - 1)) == ($low >> (BITS - 1))) {
    100          
586              
587 11544         12319 my $bit = $high >> (BITS - 1);
588 11544         12180 $enc .= $bit;
589              
590 11544 100       14434 if ($uf_count > 0) {
591 1439         1941 $enc .= join('', 1 - $bit) x $uf_count;
592 1439         1503 $uf_count = 0;
593             }
594              
595 11544         11590 $low <<= 1;
596 11544         14058 ($high <<= 1) |= 1;
597             }
598             elsif (((($low >> (BITS - 2)) & 0x1) == 1) && ((($high >> (BITS - 2)) & 0x1) == 0)) {
599 2915         3234 ($high <<= 1) |= (1 << (BITS - 1));
600 2915         2936 $high |= 1;
601 2915         3058 ($low <<= 1) &= ((1 << (BITS - 1)) - 1);
602 2915         3024 ++$uf_count;
603             }
604             else {
605 4984         7257 last;
606             }
607              
608 14459         14515 $low &= MAX;
609 14459         15506 $high &= MAX;
610             }
611             }
612              
613 79         174 $enc .= '0';
614 79         106 $enc .= '1';
615              
616 79         217 while (length($enc) % 8 != 0) {
617 319         530 $enc .= '1';
618             }
619              
620 79         5064 return ($enc, $freq);
621             }
622              
623 92     92 1 121 sub ac_decode ($fh, $freq) {
  92         116  
  92         113  
  92         138  
624              
625 92 100       239 if (ref($fh) eq '') {
626 13 50       163 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
627 13         39 return __SUB__->($fh2, $freq);
628             }
629              
630 79         162 my ($cf, $T) = _create_cfreq($freq);
631              
632 79         129 my @dec;
633 79         110 my $low = 0;
634 79         98 my $high = MAX;
635 79   100     193 my $enc = oct('0b' . join '', map { getc($fh) // 1 } 1 .. BITS);
  2528         5955  
636              
637 79         341 my @table;
638 79         258 foreach my $i (sort { $a <=> $b } keys %$freq) {
  2936         2986  
639 825         1230 foreach my $j ($cf->[$i] .. $cf->[$i + 1] - 1) {
640 4984         6079 $table[$j] = $i;
641             }
642             }
643              
644 79   50     744 my $EOF_SYMBOL = max(keys %$freq) // 0;
645              
646 79         153 while (1) {
647              
648 4984         5228 my $w = $high - $low + 1;
649 4984         10119 my $ss = int((($T * ($enc - $low + 1)) - 1) / $w);
650              
651 4984   50     7684 my $i = $table[$ss] // last;
652 4984 100       6370 last if ($i == $EOF_SYMBOL);
653              
654 4905         5543 push @dec, $i;
655              
656 4905         6276 $high = ($low + int(($w * $cf->[$i + 1]) / $T) - 1) & MAX;
657 4905         5858 $low = ($low + int(($w * $cf->[$i]) / $T)) & MAX;
658              
659 4905 50       6370 if ($high > MAX) {
660 0         0 confess "error";
661             }
662              
663 4905 50       6224 if ($low >= $high) { confess "$low >= $high" }
  0         0  
664              
665 4905         4576 while (1) {
666              
667 19053 100 100     29393 if (($high >> (BITS - 1)) == ($low >> (BITS - 1))) {
    100          
668 11305         11197 ($high <<= 1) |= 1;
669 11305         10580 $low <<= 1;
670 11305   100     22103 ($enc <<= 1) |= (getc($fh) // 1);
671             }
672             elsif (((($low >> (BITS - 2)) & 0x1) == 1) && ((($high >> (BITS - 2)) & 0x1) == 0)) {
673 2843         2948 ($high <<= 1) |= (1 << (BITS - 1));
674 2843         2748 $high |= 1;
675 2843         2815 ($low <<= 1) &= ((1 << (BITS - 1)) - 1);
676 2843   100     7818 $enc = (($enc >> (BITS - 1)) << (BITS - 1)) | (($enc & ((1 << (BITS - 2)) - 1)) << 1) | (getc($fh) // 1);
677             }
678             else {
679 4905         7806 last;
680             }
681              
682 14148         14879 $low &= MAX;
683 14148         13275 $high &= MAX;
684 14148         16148 $enc &= MAX;
685             }
686             }
687              
688 79         4804 return \@dec;
689             }
690              
691             #############################################
692             # Adaptive Arithemtic Coding (in fixed bits)
693             #############################################
694              
695 334     334   394 sub _create_adaptive_cfreq ($freq_value, $alphabet_size) {
  334         366  
  334         391  
  334         336  
696              
697 334         367 my $T = 0;
698 334         472 my (@cf, @freq);
699              
700 334         640 foreach my $i (0 .. $alphabet_size) {
701 2104         2522 $freq[$i] = $freq_value;
702 2104         2156 $cf[$i] = $T;
703 2104         2012 $T += $freq_value;
704 2104         2892 $cf[$i + 1] = $T;
705             }
706              
707 334         910 return (\@freq, \@cf, $T);
708             }
709              
710 9817     9817   9362 sub _increment_freq ($c, $alphabet_size, $freq, $cf) {
  9817         9493  
  9817         9681  
  9817         9087  
  9817         8891  
  9817         8895  
711              
712 9817         9900 ++$freq->[$c];
713 9817         9532 my $T = $cf->[$c];
714              
715 9817         12787 foreach my $i ($c .. $alphabet_size) {
716 192543         186845 $cf->[$i] = $T;
717 192543         181885 $T += $freq->[$i];
718 192543         220466 $cf->[$i + 1] = $T;
719             }
720              
721 9817         13745 return $T;
722             }
723              
724 167     167 1 255 sub adaptive_ac_encode ($symbols) {
  167         250  
  167         196  
725              
726 167 50       432 if (ref($symbols) eq '') {
727 0         0 $symbols = string2symbols($symbols);
728             }
729              
730 167         254 my $enc = '';
731 167         2023 my @alphabet = sort { $a <=> $b } uniq(@$symbols);
  2547         3121  
732 167 100       566 my $EOF_SYMBOL = scalar(@alphabet) ? ($alphabet[-1] + 1) : 1;
733 167         286 push @alphabet, $EOF_SYMBOL;
734              
735 167         251 my $alphabet_size = $#alphabet;
736 167         446 my ($freq, $cf, $T) = _create_adaptive_cfreq(INITIAL_FREQ, $alphabet_size);
737              
738 167         242 my %table;
739 167         736 @table{@alphabet} = (0 .. $alphabet_size);
740              
741 167 50       395 if ($T > MAX) {
742 0         0 confess "Too few bits: $T > ${\MAX}";
  0         0  
743             }
744              
745 167         213 my $low = 0;
746 167         236 my $high = MAX;
747 167         264 my $uf_count = 0;
748              
749 167         313 foreach my $value (@$symbols, $EOF_SYMBOL) {
750              
751 4992         6029 my $c = $table{$value};
752 4992         5379 my $w = $high - $low + 1;
753              
754 4992         6801 $high = ($low + int(($w * $cf->[$c + 1]) / $T) - 1) & MAX;
755 4992         6163 $low = ($low + int(($w * $cf->[$c]) / $T)) & MAX;
756              
757 4992         5616 $T = _increment_freq($c, $alphabet_size, $freq, $cf);
758              
759 4992 50       6590 if ($high > MAX) {
760 0         0 confess "high > MAX: $high > ${\MAX}";
  0         0  
761             }
762              
763 4992 50       6397 if ($low >= $high) { confess "$low >= $high" }
  0         0  
764              
765 4992         4600 while (1) {
766              
767 18582 100 100     28734 if (($high >> (BITS - 1)) == ($low >> (BITS - 1))) {
    100          
768              
769 10741         10347 my $bit = $high >> (BITS - 1);
770 10741         10735 $enc .= $bit;
771              
772 10741 100       13266 if ($uf_count > 0) {
773 1388         1908 $enc .= join('', 1 - $bit) x $uf_count;
774 1388         1470 $uf_count = 0;
775             }
776              
777 10741         10351 $low <<= 1;
778 10741         11614 ($high <<= 1) |= 1;
779             }
780             elsif (((($low >> (BITS - 2)) & 0x1) == 1) && ((($high >> (BITS - 2)) & 0x1) == 0)) {
781 2849         2997 ($high <<= 1) |= (1 << (BITS - 1));
782 2849         2799 $high |= 1;
783 2849         2823 ($low <<= 1) &= ((1 << (BITS - 1)) - 1);
784 2849         2820 ++$uf_count;
785             }
786             else {
787 4992         7284 last;
788             }
789              
790 13590         13093 $low &= MAX;
791 13590         14142 $high &= MAX;
792             }
793             }
794              
795 167         221 $enc .= '0';
796 167         182 $enc .= '1';
797              
798 167         371 while (length($enc) % 8 != 0) {
799 687         1084 $enc .= '1';
800             }
801              
802 167         1012 return ($enc, \@alphabet);
803             }
804              
805 180     180 1 233 sub adaptive_ac_decode ($fh, $alphabet) {
  180         215  
  180         230  
  180         184  
806              
807 180 100       437 if (ref($fh) eq '') {
808 13 50       172 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
809 13         44 return __SUB__->($fh2, $alphabet);
810             }
811              
812 167         222 my @dec;
813 167         195 my $low = 0;
814 167         195 my $high = MAX;
815              
816 167         230 my $alphabet_size = $#{$alphabet};
  167         287  
817 167         371 my ($freq, $cf, $T) = _create_adaptive_cfreq(INITIAL_FREQ, $alphabet_size);
818              
819 167   100     383 my $enc = oct('0b' . join '', map { getc($fh) // 1 } 1 .. BITS);
  5344         12795  
820              
821 167         654 while (1) {
822 4992         5497 my $w = ($high + 1) - $low;
823 4992         10904 my $ss = int((($T * ($enc - $low + 1)) - 1) / $w);
824              
825 4992         5569 my $i = 0;
826 4992         5878 foreach my $j (0 .. $alphabet_size) {
827 45326 100 66     145894 if ($cf->[$j] <= $ss and $ss < $cf->[$j + 1]) {
828 4992         5595 $i = $j;
829 4992         6474 last;
830             }
831             }
832              
833 4992 100       6895 last if ($i == $alphabet_size);
834 4825         6217 push @dec, $alphabet->[$i];
835              
836 4825         6601 $high = ($low + int(($w * $cf->[$i + 1]) / $T) - 1) & MAX;
837 4825         6139 $low = ($low + int(($w * $cf->[$i]) / $T)) & MAX;
838              
839 4825         5715 $T = _increment_freq($i, $alphabet_size, $freq, $cf);
840              
841 4825 50       6755 if ($high > MAX) {
842 0         0 confess "high > MAX: ($high > ${\MAX})";
  0         0  
843             }
844              
845 4825 50       6378 if ($low >= $high) { confess "$low >= $high" }
  0         0  
846              
847 4825         4619 while (1) {
848              
849 17892 100 100     28834 if (($high >> (BITS - 1)) == ($low >> (BITS - 1))) {
    100          
850 10325         10349 ($high <<= 1) |= 1;
851 10325         9628 $low <<= 1;
852 10325   100     21607 ($enc <<= 1) |= (getc($fh) // 1);
853             }
854             elsif (((($low >> (BITS - 2)) & 0x1) == 1) && ((($high >> (BITS - 2)) & 0x1) == 0)) {
855 2742         2943 ($high <<= 1) |= (1 << (BITS - 1));
856 2742         2756 $high |= 1;
857 2742         2898 ($low <<= 1) &= ((1 << (BITS - 1)) - 1);
858 2742   100     8126 $enc = (($enc >> (BITS - 1)) << (BITS - 1)) | (($enc & ((1 << (BITS - 2)) - 1)) << 1) | (getc($fh) // 1);
859             }
860             else {
861 4825         7975 last;
862             }
863              
864 13067         13574 $low &= MAX;
865 13067         12467 $high &= MAX;
866 13067         15407 $enc &= MAX;
867             }
868             }
869              
870 167         1208 return \@dec;
871             }
872              
873             #####################
874             # Generic run-length
875             #####################
876              
877 4116     4116 1 4567 sub run_length ($arr, $max_run = undef) {
  4116         4257  
  4116         4502  
  4116         4309  
878              
879 4116 100       6785 @$arr || return [];
880              
881 3979         7753 my @result = [$arr->[0], 1];
882 3979         5066 my $prev_value = $arr->[0];
883              
884 3979         7352 foreach my $i (1 .. $#$arr) {
885              
886 487408         468439 my $curr_value = $arr->[$i];
887              
888 487408 100 100     900597 if ($curr_value == $prev_value and (defined($max_run) ? $result[-1][1] < $max_run : 1)) {
    100          
889 450628         435577 ++$result[-1][1];
890             }
891             else {
892 36780         55976 push(@result, [$curr_value, 1]);
893             }
894              
895 487408         529653 $prev_value = $curr_value;
896             }
897              
898 3979         7256 return \@result;
899             }
900              
901             ######################################
902             # Binary variable run-length encoding
903             ######################################
904              
905 1     1 1 790 sub binary_vrl_encode ($bitstring) {
  1         2  
  1         1  
906              
907 1         9 my @bits = split(//, $bitstring);
908 1         2 my $encoded = $bits[0];
909              
910 1         2 foreach my $rle (@{run_length(\@bits)}) {
  1         3  
911 23         28 my ($c, $v) = @$rle;
912              
913 23 100       27 if ($v == 1) {
914 13         17 $encoded .= '0';
915             }
916             else {
917 10         13 my $t = sprintf('%b', $v - 1);
918 10         22 $encoded .= join('', '1' x length($t), '0', substr($t, 1));
919             }
920             }
921              
922 1         10 return $encoded;
923             }
924              
925 1     1 1 6 sub binary_vrl_decode ($bitstring) {
  1         2  
  1         1  
926              
927 1         2 my $decoded = '';
928 1         3 my $bit = substr($bitstring, 0, 1, '');
929              
930 1         3 while ($bitstring ne '') {
931              
932 23         23 $decoded .= $bit;
933              
934 23         21 my $bl = 0;
935 23         51 while (substr($bitstring, 0, 1, '') eq '1') {
936 25         34 ++$bl;
937             }
938              
939 23 100       31 if ($bl > 0) {
940 10         13 $decoded .= $bit x oct('0b1' . join('', map { substr($bitstring, 0, 1, '') } 1 .. $bl - 1));
  15         27  
941             }
942              
943 23 100       44 $bit = ($bit eq '1' ? '0' : '1');
944             }
945              
946 1         3 return $decoded;
947             }
948              
949             ############################
950             # Burrows-Wheeler transform
951             ############################
952              
953 31     31 1 44 sub bwt_sort ($s, $LOOKAHEAD_LEN = 128) { # O(n * LOOKAHEAD_LEN) space (fast)
  31         55  
  31         71  
  31         39  
954 31         53 my $len = length($s);
955 31         110 my $double_s = $s . $s; # Pre-compute doubled string
956              
957             # Schwartzian transform with optimized sorting
958             return [
959 63237         104236 map { $_->[1] }
960             sort {
961             ($a->[0] cmp $b->[0])
962 770584 50       1116893 || do {
963 4596         5501 my ($cmp, $s_len) = (0, $LOOKAHEAD_LEN << 2);
964 4596         4203 while (1) {
965 4596 50       7687 ($cmp = substr($double_s, $a->[1], $s_len) cmp substr($double_s, $b->[1], $s_len)) && last;
966 0         0 $s_len <<= 1;
967             }
968 4596         5465 $cmp;
969             }
970             }
971             map {
972 31         1510 my $pos = $_;
  63237         59006  
973 63237         57938 my $end = $pos + $LOOKAHEAD_LEN;
974              
975             # Handle wraparound efficiently
976 63237 100       98451 my $t =
977             ($end <= $len)
978             ? substr($s, $pos, $LOOKAHEAD_LEN)
979             : substr($double_s, $pos, $LOOKAHEAD_LEN);
980              
981 63237         107959 [$t, $pos]
982             } 0 .. $len - 1
983             ];
984             }
985              
986 31     31 1 73 sub bwt_encode ($s, $LOOKAHEAD_LEN = 128) {
  31         81  
  31         56  
  31         49  
987              
988 31 50       104 if (ref($s) ne '') {
989 0         0 return bwt_encode_symbolic($s);
990             }
991              
992 31         111 my $bwt = bwt_sort($s, $LOOKAHEAD_LEN);
993              
994 31         11104 my $ret = '';
995 31         73 my $idx = 0;
996              
997 31         76 my $i = 0;
998 31         112 foreach my $pos (@$bwt) {
999 63237         68254 $ret .= substr($s, $pos - 1, 1);
1000 63237 100       74471 $idx = $i if !$pos;
1001 63237         69819 ++$i;
1002             }
1003              
1004 31         4093 return ($ret, $idx);
1005             }
1006              
1007 43     43 1 74 sub bwt_decode ($bwt, $idx) {
  43         64  
  43         78  
  43         69  
1008 43         1868 my @L = unpack('C*', $bwt);
1009 43         325 my $n = scalar @L;
1010              
1011 43         507 my @freq = (0) x 256;
1012 43         9313 $freq[$_]++ for @L;
1013              
1014 43         511 my @cumul = (0) x 257;
1015 43         2818 $cumul[$_ + 1] = $cumul[$_] + $freq[$_] for 0 .. 255;
1016              
1017 43         52 my @next;
1018 43         674 my @cnt = (0) x 256;
1019 43         108 for my $i (0 .. $n - 1) {
1020 63573         78474 $next[$cumul[$L[$i]] + $cnt[$L[$i]]++] = $i;
1021             }
1022              
1023 43         73 my @dec;
1024 43         103 my $i = $idx;
1025 43         103 for (1 .. $n) {
1026 63573         66160 $i = $next[$i];
1027 63573         78934 push @dec, $L[$i];
1028             }
1029              
1030 43         5330 return pack('C*', @dec);
1031             }
1032              
1033             ##############################################
1034             # Burrows-Wheeler transform (symbolic variant)
1035             ##############################################
1036              
1037 55     55 1 75 sub bwt_sort_symbolic ($s) { # O(n) space (slowish)
  55         71  
  55         63  
1038              
1039 55         612 my @cyclic = @$s;
1040 55         93 my $len = scalar(@cyclic);
1041              
1042 55         94 my $rle = 1;
1043 55         182 foreach my $i (1 .. $len - 1) {
1044 69 100       207 if ($cyclic[$i] != $cyclic[$i - 1]) {
1045 42         77 $rle = 0;
1046 42         92 last;
1047             }
1048             }
1049              
1050 55 100       148 $rle && return [0 .. $len - 1];
1051              
1052             [
1053             sort {
1054 42         404 my ($i, $j) = ($a, $b);
  26259         29277  
1055              
1056 26259         34324 while ($cyclic[$i] == $cyclic[$j]) {
1057 18474 100       22329 $i %= $len if (++$i >= $len);
1058 18474 100       29938 $j %= $len if (++$j >= $len);
1059             }
1060              
1061 26259         30198 $cyclic[$i] <=> $cyclic[$j];
1062             } 0 .. $len - 1
1063             ];
1064             }
1065              
1066 55     55 1 99 sub bwt_encode_symbolic ($symbols) {
  55         82  
  55         86  
1067              
1068 55 50       142 if (ref($symbols) eq '') {
1069 0         0 $symbols = string2symbols($symbols);
1070             }
1071              
1072 55         120 my $bwt = bwt_sort_symbolic($symbols);
1073 55         187 my @ret = map { $symbols->[$_ - 1] } @$bwt;
  3789         4666  
1074              
1075 55         119 my $idx = 0;
1076 55         104 foreach my $i (@$bwt) {
1077 1184 100       1467 $i || last;
1078 1133         1215 ++$idx;
1079             }
1080              
1081 55         204 return (\@ret, $idx);
1082             }
1083              
1084 55     55 1 72 sub bwt_decode_symbolic ($bwt, $idx) { # fast inversion
  55         64  
  55         105  
  55         93  
1085              
1086 55         190 my @head = sort { $a <=> $b } @$bwt;
  23904         22214  
1087              
1088 55         77 my %indices;
1089 55         130 foreach my $i (0 .. $#head) {
1090 3789         3500 push @{$indices{$bwt->[$i]}}, $i;
  3789         6238  
1091             }
1092              
1093 55         106 my @table;
1094 55         88 foreach my $v (@head) {
1095 3789         3489 push @table, shift(@{$indices{$v}});
  3789         5525  
1096             }
1097              
1098 55         76 my @dec;
1099 55         140 my $i = $idx;
1100              
1101 55         125 for (1 .. scalar(@head)) {
1102 3789         5013 push @dec, $head[$i];
1103 3789         4995 $i = $table[$i];
1104             }
1105              
1106 55         534 return \@dec;
1107             }
1108              
1109             #####################
1110             # RLE4 used in Bzip2
1111             #####################
1112              
1113 2212     2212 1 6484 sub rle4_encode ($symbols, $max_run = 255) { # RLE1
  2212         2910  
  2212         3231  
  2212         2480  
1114              
1115 2212 100       4346 if (ref($symbols) eq '') {
1116 12         43 $symbols = string2symbols($symbols);
1117             }
1118              
1119 2212         3168 my $end = $#{$symbols};
  2212         3367  
1120 2212 100       3846 return [] if ($end < 0);
1121              
1122 2130         2923 my $prev = $symbols->[0];
1123 2130         2409 my $run = 1;
1124 2130         3825 my @rle = ($prev);
1125              
1126 2130         4607 for (my $i = 1 ; $i <= $end ; ++$i) {
1127              
1128 118287 100       167466 if ($symbols->[$i] == $prev) {
1129 19556         21175 ++$run;
1130             }
1131             else {
1132 98731         99403 $run = 1;
1133 98731         119853 $prev = $symbols->[$i];
1134             }
1135              
1136 118287         157375 push @rle, $prev;
1137              
1138 118287 100       219567 if ($run >= 4) {
1139              
1140 3268         3347 $run = 0;
1141 3268         3564 $i += 1;
1142              
1143 3268   100     12716 while ($run < $max_run and $i <= $end and $symbols->[$i] == $prev) {
      100        
1144 437123         390919 ++$run;
1145 437123         1092049 ++$i;
1146             }
1147              
1148 3268         4292 push @rle, $run;
1149 3268         3456 $run = 1;
1150              
1151 3268 100       4926 if ($i <= $end) {
1152 3182         3768 $prev = $symbols->[$i];
1153 3182         7588 push @rle, $symbols->[$i];
1154             }
1155             }
1156             }
1157              
1158 2130         8218 return \@rle;
1159             }
1160              
1161 240     240 1 359 sub rle4_decode ($symbols) { # RLE1
  240         296  
  240         337  
1162              
1163 240 50       615 if (ref($symbols) eq '') {
1164 0         0 $symbols = string2symbols($symbols);
1165             }
1166              
1167 240         329 my $end = $#{$symbols};
  240         402  
1168 240 100       493 return [] if ($end < 0);
1169              
1170 226         630 my @dec = $symbols->[0];
1171 226         472 my $prev = $symbols->[0];
1172 226         318 my $run = 1;
1173              
1174 226         592 for (my $i = 1 ; $i <= $end ; ++$i) {
1175              
1176 98654 100       139490 if ($symbols->[$i] == $prev) {
1177 13384         13963 ++$run;
1178             }
1179             else {
1180 85270         84305 $run = 1;
1181 85270         94161 $prev = $symbols->[$i];
1182             }
1183              
1184 98654         119755 push @dec, $prev;
1185              
1186 98654 100       180883 if ($run >= 4) {
1187 1963 50       2903 if (++$i <= $end) {
1188 1963         2245 $run = $symbols->[$i];
1189 1963         4636 push @dec, (($prev) x $run);
1190             }
1191              
1192 1963         3422 $run = 0;
1193             }
1194             }
1195              
1196 226         567 return \@dec;
1197             }
1198              
1199             #######################
1200             # Delta encoding (+RLE)
1201             #######################
1202              
1203 3992     3992   4691 sub _compute_elias_costs ($run_length) {
  3992         4912  
  3992         3985  
1204              
1205             # Check which method results in better compression
1206 3992         4405 my $with_rle = 0;
1207 3992         4227 my $without_rle = 0;
1208              
1209 3992         4080 my $double_with_rle = 0;
1210 3992         4160 my $double_without_rle = 0;
1211              
1212             # Check if there are any negative values or zero values
1213 3992         4229 my $has_negative = 0;
1214 3992         3978 my $has_zero = 0;
1215              
1216 3992         5117 foreach my $pair (@$run_length) {
1217 39378         48539 my ($c, $v) = @$pair;
1218              
1219 39378 100 100     60424 if ($c < 0 and not $has_negative) {
1220 876         1022 $has_negative = 1;
1221             }
1222              
1223 39378 100       47587 if ($c == 0) {
1224 7317         7441 $with_rle += 1;
1225 7317         7578 $double_with_rle += 1;
1226 7317         7149 $without_rle += $v;
1227 7317         7190 $double_without_rle += $v;
1228 7317   100     12472 $has_zero ||= 1;
1229             }
1230             else {
1231              
1232             { # double
1233 32061         44708 my $t = int(log(abs($c) + 1) / log(2) + 1);
1234 32061         39410 my $l = int(log($t) / log(2) + 1);
1235 32061         40017 my $len = 2 * ($l - 1) + ($t - 1) + 3;
1236              
1237 32061         31776 $double_with_rle += $len;
1238 32061         41229 $double_without_rle += $len * $v;
1239             }
1240              
1241             { # single
1242 32061         31141 my $t = int(log(abs($c) + 1) / log(2) + 1);
  32061         30703  
  32061         40672  
1243 32061         36435 my $len = 2 * ($t - 1) + 3;
1244 32061         31516 $with_rle += $len;
1245 32061         41376 $without_rle += $len * $v;
1246             }
1247             }
1248              
1249 39378 100       47065 if ($v == 1) {
1250 32835         32024 $with_rle += 1;
1251 32835         45349 $double_with_rle += 1;
1252             }
1253             else {
1254 6543         8269 my $t = int(log($v) / log(2) + 1);
1255 6543         6960 my $len = 2 * ($t - 1) + 1;
1256 6543         6343 $with_rle += $len;
1257 6543         9326 $double_with_rle += $len;
1258             }
1259             }
1260              
1261             scalar {
1262 3992         17407 has_negative => $has_negative,
1263             has_zero => $has_zero,
1264             methods => {
1265             with_rle => $with_rle,
1266             without_rle => $without_rle,
1267             double_with_rle => $double_with_rle,
1268             double_without_rle => $double_without_rle,
1269             },
1270             };
1271             }
1272              
1273 3992     3992   4288 sub _find_best_encoding_method ($integers) {
  3992         4186  
  3992         3900  
1274 3992         5920 my $rl = run_length($integers);
1275 3992         5931 my $costs = _compute_elias_costs($rl);
1276 3992         4809 my ($best_method) = sort { $costs->{methods}{$a} <=> $costs->{methods}{$b} } sort keys(%{$costs->{methods}});
  19009         27633  
  3992         15821  
1277 3992 50       7428 $VERBOSE && say STDERR "$best_method --> $costs->{methods}{$best_method}";
1278 3992         12522 return ($rl, $best_method, $costs);
1279             }
1280              
1281 998     998 1 1499 sub delta_encode ($integers) {
  998         1107  
  998         1267  
1282              
1283 998         1862 my $deltas = deltas($integers);
1284              
1285 998         2101 my @methods = (
1286             [_find_best_encoding_method($integers), 0, 0],
1287             [_find_best_encoding_method($deltas), 1, 0],
1288             [_find_best_encoding_method(rle4_encode($integers, scalar(@$integers) + 1)), 0, 1],
1289             [_find_best_encoding_method(rle4_encode($deltas, scalar(@$integers) + 1)), 1, 1],
1290             );
1291              
1292 998         3929 my ($best) = sort { $a->[2]{methods}{$a->[1]} <=> $b->[2]{methods}{$b->[1]} } @methods;
  4628         8033  
1293              
1294 998         2163 my ($rl, $method, $stats, $with_deltas, $with_rle4) = @$best;
1295              
1296 998         1334 my $double = 0;
1297 998         1138 my $with_rle = 0;
1298 998         1336 my $has_negative = $stats->{has_negative};
1299              
1300 998 100       2837 if ($method eq 'with_rle') {
    100          
    100          
    50          
1301 244         372 $with_rle = 1;
1302             }
1303             elsif ($method eq 'without_rle') {
1304             ## ok
1305             }
1306             elsif ($method eq 'double_with_rle') {
1307 67         86 $with_rle = 1;
1308 67         98 $double = 1;
1309             }
1310             elsif ($method eq 'double_without_rle') {
1311 294         424 $double = 1;
1312             }
1313             else {
1314 0         0 confess "[BUG] Unknown encoding method: $method";
1315             }
1316              
1317 998         1407 my $code = '';
1318 998         2587 my $bitstring = join('', $double, $with_rle, $has_negative, $with_deltas, $with_rle4);
1319 998   100     1778 my $length = sum(map { $_->[1] } @$rl) // 0;
  8924         13269  
1320              
1321 998         2311 foreach my $pair ([$length, 1], @$rl) {
1322 9922         12540 my ($d, $v) = @$pair;
1323              
1324 9922 100       14550 if ($d == 0) {
    100          
1325 2113         2280 $code = '0';
1326             }
1327             elsif ($double) {
1328 2614         3944 my $t = sprintf('%b', abs($d) + 1);
1329 2614         3312 my $l = sprintf('%b', length($t));
1330 2614 100       6339 $code = ($has_negative ? ('1' . (($d < 0) ? '0' : '1')) : '') . ('1' x (length($l) - 1)) . '0' . substr($l, 1) . substr($t, 1);
    100          
1331             }
1332             else {
1333 5195 100       9204 my $t = sprintf('%b', abs($d) + ($has_negative ? 0 : 1));
1334 5195 100       11617 $code = ($has_negative ? ('1' . (($d < 0) ? '0' : '1')) : '') . ('1' x (length($t) - 1)) . '0' . substr($t, 1);
    100          
1335             }
1336              
1337 9922         10562 $bitstring .= $code;
1338              
1339 9922 100       12921 if (not $with_rle) {
1340 6431 100       8474 if ($v > 1) {
1341 604         915 $bitstring .= $code x ($v - 1);
1342             }
1343 6431         9018 next;
1344             }
1345              
1346 3491 100       4176 if ($v == 1) {
1347 2357         3228 $bitstring .= '0';
1348             }
1349             else {
1350 1134         1489 my $t = sprintf('%b', $v);
1351 1134         2345 $bitstring .= join('', '1' x (length($t) - 1), '0', substr($t, 1));
1352             }
1353             }
1354              
1355 998         58949 pack('B*', $bitstring);
1356             }
1357              
1358 990     990 1 1226 sub delta_decode ($fh) {
  990         1067  
  990         1111  
1359              
1360 990 100       1739 if (ref($fh) eq '') {
1361 13 50       157 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
1362 13         40 return __SUB__->($fh2);
1363             }
1364              
1365 977         1248 my $buffer = '';
1366 977         1810 my $double = read_bit($fh, \$buffer);
1367 977         1747 my $with_rle = read_bit($fh, \$buffer);
1368 977         1533 my $has_negative = read_bit($fh, \$buffer);
1369 977         1660 my $with_deltas = read_bit($fh, \$buffer);
1370 977         1577 my $with_rle4 = read_bit($fh, \$buffer);
1371              
1372 977         1250 my @deltas;
1373 977         1285 my $len = 0;
1374              
1375 977         2048 for (my $k = 0 ; $k <= $len ; ++$k) {
1376              
1377 10766         13112 my $bit = read_bit($fh, \$buffer);
1378              
1379 10766 100       16978 if ($bit eq '0') {
    100          
1380 3115         3672 push @deltas, 0;
1381             }
1382             elsif ($double) {
1383 2611 100       3518 my $bit = $has_negative ? read_bit($fh, \$buffer) : 0;
1384              
1385 2611 100       3491 my $bl = $has_negative ? 0 : 1;
1386 2611         3069 ++$bl while (read_bit($fh, \$buffer) eq '1');
1387              
1388 2611         3706 my $bl2 = oct('0b1' . join('', map { read_bit($fh, \$buffer) } 1 .. $bl));
  4495         5234  
1389 2611         4073 my $int = oct('0b1' . join('', map { read_bit($fh, \$buffer) } 1 .. ($bl2 - 1)));
  10805         11829  
1390              
1391 2611 100       5932 push @deltas, ($has_negative ? ($bit eq '1' ? 1 : -1) : 1) * ($int - 1);
    100          
1392             }
1393             else {
1394 5040 100       7173 my $bit = $has_negative ? read_bit($fh, \$buffer) : 0;
1395 5040 100       6845 my $n = $has_negative ? 0 : 1;
1396 5040         6179 ++$n while (read_bit($fh, \$buffer) eq '1');
1397 5040         7306 my $d = oct('0b1' . join('', map { read_bit($fh, \$buffer) } 1 .. $n));
  8255         9471  
1398 5040 100       10187 push @deltas, $has_negative ? ($bit eq '1' ? $d : -$d) : ($d - 1);
    100          
1399             }
1400              
1401 10766 100       14879 if ($with_rle) {
1402              
1403 3142         3222 my $bl = 0;
1404 3142         3703 while (read_bit($fh, \$buffer) == 1) {
1405 2891         3746 ++$bl;
1406             }
1407              
1408 3142 100       5076 if ($bl > 0) {
1409 1050         1493 my $run = oct('0b1' . join('', map { read_bit($fh, \$buffer) } 1 .. $bl)) - 1;
  2891         3377  
1410 1050         1434 $k += $run;
1411 1050         8422 push @deltas, ($deltas[-1]) x $run;
1412             }
1413             }
1414              
1415 10766 100       20655 if ($k == 0) {
1416 977         2191 $len = pop(@deltas);
1417             }
1418             }
1419              
1420 977         1218 my $decoded = \@deltas;
1421 977 100       1674 $decoded = rle4_decode($decoded) if $with_rle4;
1422 977 100       2136 $decoded = accumulate($decoded) if $with_deltas;
1423 977         3836 return $decoded;
1424             }
1425              
1426             ################################
1427             # Alphabet encoding (from Bzip2)
1428             ################################
1429              
1430 188     188 1 267 sub encode_alphabet_256 ($alphabet) {
  188         316  
  188         211  
1431              
1432 188         239 my %table;
1433 188         1224 @table{@$alphabet} = ();
1434              
1435 188         271 my $populated = 0;
1436 188         218 my @marked;
1437              
1438 188         466 for (my $i = 0 ; $i <= 255 ; $i += 16) {
1439              
1440 3008         2882 my $enc = 0;
1441 3008         3460 foreach my $j (0 .. 15) {
1442 48128 100       69847 if (exists($table{$i + $j})) {
1443 1525         2001 $enc |= 1 << $j;
1444             }
1445             }
1446              
1447 3008         2929 $populated <<= 1;
1448              
1449 3008 100       5255 if ($enc > 0) {
1450 419         470 $populated |= 1;
1451 419         796 push @marked, $enc;
1452             }
1453             }
1454              
1455 188         334 my $bitstring = join('', map { int2bits_lsb($_, 16) } @marked);
  419         841  
1456              
1457 188 50       457 $VERBOSE && say STDERR "Populated : ", sprintf('%016b', $populated);
1458 188 50       344 $VERBOSE && say STDERR "Marked : @marked";
1459 188 50       300 $VERBOSE && say STDERR "Bits len : ", length($bitstring);
1460              
1461 188         255 my $encoded = '';
1462 188         328 $encoded .= int2bytes($populated, 2);
1463 188         495 $encoded .= pack('B*', $bitstring);
1464 188         909 return $encoded;
1465             }
1466              
1467 21     21 1 301 sub decode_alphabet_256 ($fh) {
  21         33  
  21         29  
1468              
1469 21 50       55 if (ref($fh) eq '') {
1470 0 0       0 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
1471 0         0 return __SUB__->($fh2);
1472             }
1473              
1474 21         33 my @alphabet;
1475 21         48 my $l1 = bytes2int($fh, 2);
1476              
1477 21         57 for my $i (0 .. 15) {
1478 336 100       687 if ($l1 & (0x8000 >> $i)) {
1479 95         153 my $l2 = bytes2int($fh, 2);
1480 95         157 for my $j (0 .. 15) {
1481 1520 100       2778 if ($l2 & (0x8000 >> $j)) {
1482 671         1139 push @alphabet, 16 * $i + $j;
1483             }
1484             }
1485             }
1486             }
1487              
1488 21         72 return \@alphabet;
1489             }
1490              
1491 190     190 1 258 sub encode_alphabet ($alphabet) {
  190         246  
  190         265  
1492              
1493 190   100     522 my $max_symbol = $alphabet->[-1] // -1;
1494              
1495 190 100       393 if ($max_symbol <= 255) {
1496              
1497 176         425 my $delta = delta_encode($alphabet);
1498 176         463 my $enc = encode_alphabet_256($alphabet);
1499              
1500 176 100       537 if (length($delta) < length($enc)) {
1501 155         645 return (chr(0) . $delta);
1502             }
1503              
1504 21         136 return (chr(1) . $enc);
1505             }
1506              
1507 14         52 return (chr(0) . delta_encode($alphabet));
1508             }
1509              
1510 190     190 1 226 sub decode_alphabet ($fh) {
  190         216  
  190         221  
1511              
1512 190 50       405 if (ref($fh) eq '') {
1513 0 0       0 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
1514 0         0 return __SUB__->($fh2);
1515             }
1516              
1517 190 100 33     903 if (ord(getc($fh) // confess "error") == 1) {
1518 21         93 return decode_alphabet_256($fh);
1519             }
1520              
1521 169         368 return delta_decode($fh);
1522             }
1523              
1524             ##########################
1525             # Move to front transform
1526             ##########################
1527              
1528 230     230 1 146971 sub mtf_encode ($symbols, $alphabet = undef) {
  230         479  
  230         447  
  230         362  
1529              
1530 230 100       805 if (ref($symbols) eq '') {
1531 12         26 $symbols = string2symbols($symbols);
1532             }
1533              
1534 230 50 66     791 if (defined($alphabet) and ref($alphabet) eq '') {
1535 0         0 $alphabet = string2symbols($alphabet);
1536             }
1537              
1538 230         748 my (@C, @table);
1539              
1540 230         0 my @alphabet;
1541 230         0 my @alphabet_copy;
1542 230         403 my $return_alphabet = 0;
1543              
1544 230 100       633 if (defined($alphabet)) {
1545 1         3 @alphabet = @$alphabet;
1546             }
1547             else {
1548 229         39086 @alphabet = sort { $a <=> $b } uniq(@$symbols);
  9050         10708  
1549 229         7346 $return_alphabet = 1;
1550 229         953 @alphabet_copy = @alphabet;
1551             }
1552              
1553 230         322 my $index;
1554 230         710 my @indices = (0 .. $#alphabet);
1555              
1556 230         597 foreach my $c (@$symbols) {
1557              
1558 113268         123004 foreach my $i (@indices) {
1559 791659 100       1234847 if ($alphabet[$i] == $c) {
1560 113268         111815 $index = $i;
1561 113268         120480 last;
1562             }
1563             }
1564              
1565 113268         116078 push @C, $index;
1566 113268         171306 unshift(@alphabet, splice(@alphabet, $index, 1));
1567             }
1568              
1569 230 100       668 $return_alphabet || return \@C;
1570 229         4244 return (\@C, \@alphabet_copy);
1571             }
1572              
1573 279     279 1 459 sub mtf_decode ($encoded, $alphabet) {
  279         335  
  279         399  
  279         322  
1574              
1575 279 50       837 if (ref($encoded) eq '') {
1576 0         0 $encoded = string2symbols($encoded);
1577             }
1578              
1579 279 50       694 if (ref($alphabet) eq '') {
1580 0         0 $alphabet = string2symbols($alphabet);
1581             }
1582              
1583 279         330 my @S;
1584 279         731 my @alpha = @$alphabet;
1585              
1586 279         565 foreach my $p (@$encoded) {
1587 114895         132884 push @S, $alpha[$p];
1588 114895         159538 unshift(@alpha, splice(@alpha, $p, 1));
1589             }
1590              
1591 279         895 return \@S;
1592             }
1593              
1594             ###########################
1595             # Zero Run-length encoding
1596             ###########################
1597              
1598 215     215 1 478 sub zrle_encode ($symbols) { # RLE2
  215         374  
  215         329  
1599              
1600 215 50       594 if (ref($symbols) eq '') {
1601 0         0 $symbols = string2symbols($symbols);
1602             }
1603              
1604 215         284 my @rle;
1605 215         377 my $end = $#{$symbols};
  215         488  
1606              
1607 215         581 for (my $i = 0 ; $i <= $end ; ++$i) {
1608              
1609 44325         43388 my $run = 0;
1610 44325   100     89575 while ($i <= $end and $symbols->[$i] == 0) {
1611 67969         62508 ++$run;
1612 67969         132395 ++$i;
1613             }
1614              
1615 44325 100       55890 if ($run >= 1) {
1616 8752         11013 my $t = sprintf('%b', $run + 1);
1617 8752         18844 push @rle, split(//, substr($t, 1));
1618             }
1619              
1620 44325 100       57552 if ($i <= $end) {
1621 44221         69574 push @rle, $symbols->[$i] + 1;
1622             }
1623             }
1624              
1625 215         2235 return \@rle;
1626             }
1627              
1628 227     227 1 318 sub zrle_decode ($rle) { # RLE2
  227         275  
  227         362  
1629              
1630 227 50       503 if (ref($rle) eq '') {
1631 0         0 $rle = string2symbols($rle);
1632             }
1633              
1634 227         278 my @dec;
1635 227         275 my $end = $#{$rle};
  227         399  
1636              
1637 227         517 for (my $i = 0 ; $i <= $end ; ++$i) {
1638 44508         52638 my $k = $rle->[$i];
1639              
1640 44508 100 100     93318 if ($k == 0 or $k == 1) {
1641 8795         9232 my $run = 1;
1642 8795   100     17066 while (($i <= $end) and ($k == 0 or $k == 1)) {
      100        
1643 15799         15802 ($run <<= 1) |= $k;
1644 15799         42601 $k = $rle->[++$i];
1645             }
1646 8795         14241 push @dec, (0) x ($run - 1);
1647             }
1648              
1649 44508 100       58130 if ($i <= $end) {
1650 44401         79940 push @dec, $k - 1;
1651             }
1652             }
1653              
1654 227         2847 return \@dec;
1655             }
1656              
1657             ################################################################
1658             # Move-to-front compression (MTF + RLE4 + ZRLE + Huffman coding)
1659             ################################################################
1660              
1661 116     116 1 275113 sub mrl_compress_symbolic ($symbols, $entropy_sub = \&create_huffman_entry) {
  116         219  
  116         231  
  116         172  
1662              
1663 116 100       440 if (ref($symbols) eq '') {
1664 12         62 $symbols = string2symbols($symbols);
1665             }
1666              
1667 116         481 my ($mtf, $alphabet) = mtf_encode($symbols);
1668 116         316 my $rle = zrle_encode($mtf);
1669 116         342 my $rle4 = rle4_encode($rle, scalar(@$rle));
1670              
1671 116         355 encode_alphabet($alphabet) . $entropy_sub->($rle4);
1672             }
1673              
1674             *mrl_compress = \&mrl_compress_symbolic;
1675              
1676 225     225 1 403 sub mrl_decompress_symbolic ($fh, $entropy_sub = \&decode_huffman_entry) {
  225         258  
  225         286  
  225         273  
1677              
1678 225 100       474 if (ref($fh) eq '') {
1679 109 50       1423 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
1680 109         246 return __SUB__->($fh2, $entropy_sub);
1681             }
1682              
1683 116         269 my $alphabet = decode_alphabet($fh);
1684              
1685 116 50       226 $VERBOSE && say STDERR "Alphabet size: ", scalar(@$alphabet);
1686              
1687 116         282 my $rle4 = $entropy_sub->($fh);
1688 116         266 my $rle = rle4_decode($rle4);
1689 116         293 my $mtf = zrle_decode($rle);
1690 116         258 my $symbols = mtf_decode($mtf, $alphabet);
1691              
1692 116         7001 return $symbols;
1693             }
1694              
1695 1     1 1 7 sub mrl_decompress($fh, $entropy_sub = \&decode_huffman_entry) {
  1         5  
  1         5  
  1         2  
1696 1         5 symbols2string(mrl_decompress_symbolic($fh, $entropy_sub));
1697             }
1698              
1699             ############################################################
1700             # BWT-based compression (BWT + MTF + ZRLE + Huffman coding)
1701             ############################################################
1702              
1703 19     19 1 411966 sub bwt_compress ($chunk, $entropy_sub = \&create_huffman_entry) {
  19         44  
  19         53  
  19         37  
1704              
1705 19 50       121 if (ref($chunk) ne '') {
1706 0         0 return bwt_compress_symbolic($chunk, $entropy_sub);
1707             }
1708              
1709 19         78 my $rle1 = rle4_encode(string2symbols($chunk));
1710 19         6770 my ($bwt, $idx) = bwt_encode(pack('C*', @$rle1));
1711              
1712 19 50       73 $VERBOSE && say STDERR "BWT index = $idx";
1713              
1714 19         65 my ($mtf, $alphabet) = mtf_encode(string2symbols($bwt));
1715 19         4677 my $rle = zrle_encode($mtf);
1716              
1717 19         105 pack('N', $idx) . encode_alphabet($alphabet) . $entropy_sub->($rle);
1718             }
1719              
1720 37     37 1 101 sub bwt_decompress ($fh, $entropy_sub = \&decode_huffman_entry) {
  37         69  
  37         75  
  37         48  
1721              
1722 37 100       102 if (ref($fh) eq '') {
1723 18 50       786 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
1724 18         57 return __SUB__->($fh2, $entropy_sub);
1725             }
1726              
1727 19         55 my $idx = bytes2int($fh, 4);
1728 19         69 my $alphabet = decode_alphabet($fh);
1729              
1730 19 50       56 $VERBOSE && say STDERR "BWT index = $idx";
1731 19 50       63 $VERBOSE && say STDERR "Alphabet size: ", scalar(@$alphabet);
1732              
1733 19         48 my $rle = $entropy_sub->($fh);
1734 19         77 my $mtf = zrle_decode($rle);
1735 19         65 my $bwt = mtf_decode($mtf, $alphabet);
1736 19         1143 my $rle4 = bwt_decode(pack('C*', @$bwt), $idx);
1737 19         77 my $data = rle4_decode(string2symbols($rle4));
1738              
1739 19         5286 pack('C*', @$data);
1740             }
1741              
1742             ###########################################
1743             # BWT-based compression (symbolic variant)
1744             ###########################################
1745              
1746 55     55 1 458743 sub bwt_compress_symbolic ($symbols, $entropy_sub = \&create_huffman_entry) {
  55         104  
  55         160  
  55         79  
1747              
1748 55 100       246 if (ref($symbols) eq '') {
1749 1         4 $symbols = string2symbols($symbols);
1750             }
1751              
1752 55         169 my $rle4 = rle4_encode($symbols);
1753 55         204 my ($bwt, $idx) = bwt_encode_symbolic($rle4);
1754              
1755 55         157 my ($mtf, $alphabet) = mtf_encode($bwt);
1756 55         152 my $rle = zrle_encode($mtf);
1757              
1758 55 50       115 $VERBOSE && say STDERR "BWT index = $idx";
1759 55 50 0     117 $VERBOSE && say STDERR "Max symbol: ", max(@$alphabet) // 0;
1760              
1761 55         264 pack('N', $idx) . encode_alphabet($alphabet) . $entropy_sub->($rle);
1762             }
1763              
1764 104     104 1 417 sub bwt_decompress_symbolic ($fh, $entropy_sub = \&decode_huffman_entry) {
  104         124  
  104         144  
  104         116  
1765              
1766 104 100       205 if (ref($fh) eq '') {
1767 49 50       717 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
1768 49         125 return __SUB__->($fh2, $entropy_sub);
1769             }
1770              
1771 55         137 my $idx = bytes2int($fh, 4);
1772 55         181 my $alphabet = decode_alphabet($fh);
1773              
1774 55 50       121 $VERBOSE && say STDERR "BWT index = $idx";
1775 55 50       107 $VERBOSE && say STDERR "Alphabet size: ", scalar(@$alphabet);
1776              
1777 55         129 my $rle = $entropy_sub->($fh);
1778 55         144 my $mtf = zrle_decode($rle);
1779 55         134 my $bwt = mtf_decode($mtf, $alphabet);
1780 55         134 my $rle4 = bwt_decode_symbolic($bwt, $idx);
1781 55         133 my $data = rle4_decode($rle4);
1782              
1783 55         1094 return $data;
1784             }
1785              
1786             ###########################
1787             # Arithmetic Coding entries
1788             ###########################
1789              
1790 66     66 1 100 sub create_ac_entry ($symbols) {
  66         102  
  66         82  
1791              
1792 66 50       178 if (ref($symbols) eq '') {
1793 0         0 $symbols = string2symbols($symbols);
1794             }
1795              
1796 66         200 my ($enc, $freq) = ac_encode($symbols);
1797 66   50     803 my $max_symbol = max(keys %$freq) // 0;
1798              
1799 66         159 my @freqs;
1800 66         166 foreach my $k (0 .. $max_symbol) {
1801 42159   100     73232 push @freqs, $freq->{$k} // 0;
1802             }
1803              
1804 66         132 push @freqs, length($enc) >> 3;
1805              
1806 66         172 delta_encode(\@freqs) . pack("B*", $enc);
1807             }
1808              
1809 67     67 1 104 sub decode_ac_entry ($fh) {
  67         114  
  67         130  
1810              
1811 67 100       171 if (ref($fh) eq '') {
1812 1 50       12 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
1813 1         5 return __SUB__->($fh2);
1814             }
1815              
1816 66         86 my @freqs = @{delta_decode($fh)};
  66         157  
1817 66         490 my $bits_len = pop(@freqs);
1818              
1819 66         113 my %freq;
1820 66         166 foreach my $i (0 .. $#freqs) {
1821 42159 100       53110 if ($freqs[$i]) {
1822 753         1388 $freq{$i} = $freqs[$i];
1823             }
1824             }
1825              
1826 66 50       157 $VERBOSE && say STDERR "Encoded length: $bits_len";
1827 66         202 my $bits = read_bits($fh, $bits_len << 3);
1828              
1829 66 50       189 if ($bits_len > 0) {
1830 66         536 open my $bits_fh, '<:raw', \$bits;
1831 66         250 return ac_decode($bits_fh, \%freq);
1832             }
1833              
1834 0         0 return [];
1835             }
1836              
1837             ####################################
1838             # Adaptive Arithmetic Coding entries
1839             ####################################
1840              
1841 154     154 1 254 sub create_adaptive_ac_entry ($symbols) {
  154         190  
  154         182  
1842              
1843 154 50       398 if (ref($symbols) eq '') {
1844 0         0 $symbols = string2symbols($symbols);
1845             }
1846              
1847 154         431 my ($enc, $alphabet) = adaptive_ac_encode($symbols);
1848 154         716 delta_encode([@$alphabet, length($enc) >> 3]) . pack('B*', $enc);
1849             }
1850              
1851 155     155 1 209 sub decode_adaptive_ac_entry ($fh) {
  155         198  
  155         167  
1852              
1853 155 100       382 if (ref($fh) eq '') {
1854 1 50       12 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
1855 1         20 return __SUB__->($fh2);
1856             }
1857              
1858 154         275 my $alphabet = delta_decode($fh);
1859 154         253 my $enc_len = pop(@$alphabet);
1860              
1861 154 50       351 if ($enc_len > 0) {
1862 154         343 my $bits = read_bits($fh, $enc_len << 3);
1863 154         1115 open my $bits_fh, '<:raw', \$bits;
1864 154         8662 return adaptive_ac_decode($bits_fh, $alphabet);
1865             }
1866              
1867 0         0 return [];
1868             }
1869              
1870             ###########################
1871             # Huffman Coding algorithm
1872             ###########################
1873              
1874 474     474 1 594 sub huffman_encode ($symbols, $dict) {
  474         543  
  474         498  
  474         500  
1875 474         996 join('', @{$dict}{@$symbols});
  474         16910  
1876             }
1877              
1878 402     402   508 sub _build_trie ($rev_dict) {
  402         492  
  402         432  
1879 402         606 my $root = {};
1880 402         1219 for my $code (keys %$rev_dict) {
1881 4006         4066 my $node = $root;
1882 4006         6662 for my $bit (split //, $code) {
1883 23229   100     38612 $node->{$bit} //= {};
1884 23229         27607 $node = $node->{$bit};
1885             }
1886 4006         7679 $node->{sym} = $rev_dict->{$code};
1887             }
1888 402         963 return $root;
1889             }
1890              
1891 402     402 1 635 sub huffman_decode ($bits, $rev_dict) {
  402         7976  
  402         577  
  402         606  
1892 402         721 my $root = _build_trie($rev_dict);
1893 402         584 my @result;
1894 402         457 my $node = $root;
1895 402         1223 foreach my $i (0 .. length($bits) - 1) {
1896 381893         476795 $node = $node->{substr($bits, $i, 1)};
1897 381893 100       532849 if (exists $node->{sym}) {
1898 77240         98373 push @result, $node->{sym};
1899 77240         89888 $node = $root;
1900             }
1901             }
1902 402         6767 return \@result;
1903             }
1904              
1905             # produce encode and decode dictionary from a tree
1906 11134     11134   10641 sub _huffman_walk_tree ($node, $code, $h) {
  11134         11023  
  11134         10960  
  11134         10671  
  11134         10084  
1907              
1908 11134   100     15977 my $c = $node->[0] // return $h;
1909 10911 100       12990 if (ref $c) { __SUB__->($c->[$_], $code . $_, $h) for ('0', '1') }
  5243         10293  
1910 5668         9294 else { $h->{$c} = $code }
1911              
1912 10911         18682 return $h;
1913             }
1914              
1915 1784     1784 1 2181 sub huffman_from_code_lengths ($code_lengths_table) {
  1784         1971  
  1784         1875  
1916              
1917 1784 100       3486 if (ref($code_lengths_table) eq 'ARRAY') {
1918 568 100       935 my %table = map { (($code_lengths_table->[$_] > 0) ? ($_, $code_lengths_table->[$_]) : ()) } 0 .. $#{$code_lengths_table};
  182425         216474  
  568         4962  
1919 568         4230 return __SUB__->(\%table);
1920             }
1921              
1922             # This algorithm is based on the pseudocode in RFC 1951 (Section 3.2.2)
1923             # (Steps are numbered as in the RFC)
1924              
1925 1216         4837 my @code_lengths = map { [$_, $code_lengths_table->{$_}] } sort { $a <=> $b } keys %$code_lengths_table;
  12835         20210  
  54293         54604  
1926              
1927             # Step 1: Count the number of codes for each length
1928 1216   100     2797 my $max_length = max(map { $_->[1] } @code_lengths) // 0;
  12835         15882  
1929 1216         2948 my @length_counts = (0) x ($max_length + 1);
1930              
1931 1216         1564 foreach my $length (map { $_->[1] } @code_lengths) {
  12835         13968  
1932              
1933             # Treat undef or negative lengths as 0 (unused)
1934 12835 50 33     25145 if (defined($length) and $length > 0) {
1935 12835         15022 ++$length_counts[$length];
1936             }
1937             }
1938              
1939             # Step 2: Generate the starting numerical value for each length
1940 1216         2078 my $code = 0;
1941 1216         1606 $length_counts[0] = 0;
1942 1216         1886 my @next_code = (0) x ($max_length + 1);
1943              
1944 1216         2345 foreach my $bits (1 .. $max_length) {
1945 3317         3941 $code = ($code + $length_counts[$bits - 1]) << 1;
1946 3317         4136 $next_code[$bits] = $code;
1947             }
1948              
1949             # Step 3: Assign numerical values to all codes
1950 1216         2131 my %dict;
1951             my %rev_dict;
1952 1216         1732 foreach my $pair (@code_lengths) {
1953 12835         16291 my ($key, $length) = @$pair;
1954              
1955             # Skip zero-length codes (unused symbols)
1956 12835 50 33     25372 if (defined($length) and $length != 0) {
1957              
1958             # Format the integer code as a binary string with $length bits
1959 12835         17875 my $binary_code = sprintf('%0*b', $length, $next_code[$length]);
1960              
1961 12835         18563 $dict{$key} = $binary_code;
1962 12835         20533 $rev_dict{$binary_code} = $key;
1963              
1964             # Increment the code for the next symbol of this length
1965 12835         17610 ++$next_code[$length];
1966             }
1967             }
1968              
1969 1216 100       12741 return (wantarray ? (\%dict, \%rev_dict) : \%dict);
1970             }
1971              
1972 10766     10766   10868 sub _heap_push ($heap, $item) {
  10766         10379  
  10766         9938  
  10766         9798  
1973 10766         11845 push @$heap, $item;
1974 10766         11324 my $i = $#$heap;
1975 10766         15914 while ($i > 0) {
1976 14841         15277 my $p = ($i - 1) >> 1;
1977 14841 100       28742 last if ($heap->[$p][1] <= $heap->[$i][1]);
1978 5614         5540 @{$heap}[$p, $i] = @{$heap}[$i, $p];
  5614         6963  
  5614         6396  
1979 5614         8649 $i = $p;
1980             }
1981             }
1982              
1983 10196     10196   9820 sub _heap_pop ($heap) {
  10196         9799  
  10196         9207  
1984 10196 100       14186 return pop @$heap if (@$heap == 1);
1985 9771         9957 my $top = $heap->[0];
1986 9771         10711 $heap->[0] = pop @$heap;
1987 9771         10004 my $n = scalar @$heap;
1988 9771         9526 my $i = 0;
1989 9771         8988 while (1) {
1990 37244         35684 my $s = $i;
1991 37244         35990 my $l = 2 * $i + 1;
1992 37244         35777 my $r = $l + 1;
1993 37244 100 100     70850 $s = $l if ($l < $n && $heap->[$l][1] < $heap->[$s][1]);
1994 37244 100 100     69483 $s = $r if ($r < $n && $heap->[$r][1] < $heap->[$s][1]);
1995 37244 100       50019 last if $s == $i;
1996 27473         26243 @{$heap}[$i, $s] = @{$heap}[$s, $i];
  27473         33159  
  27473         31602  
1997 27473         35725 $i = $s;
1998             }
1999 9771         13330 return $top;
2000             }
2001              
2002 648     648 1 829 sub huffman_from_freq($freq) {
  648         793  
  648         704  
2003              
2004             # Initialize Heap
2005             # Structure: [ [symbol_or_children], frequency ]
2006 648         697 my @heap;
2007 648         2844 foreach my $k (sort { $a <=> $b } keys %$freq) {
  21403         22197  
2008 5668         9979 _heap_push(\@heap, [$k, $freq->{$k}]);
2009             }
2010              
2011             # Build Huffman Tree
2012 648         1755 while (@heap > 1) {
2013 5098         6589 my $x = _heap_pop(\@heap);
2014 5098         6319 my $y = _heap_pop(\@heap);
2015 5098         11148 _heap_push(\@heap, [[$x, $y], $x->[1] + $y->[1]]);
2016             }
2017              
2018 648 100 100     2356 if (@heap == 1 && !ref $heap[0][0]) {
2019 145         383 @heap = ([[$heap[0]], $heap[0][1]]);
2020             }
2021              
2022             # Generate Codes
2023 648         1628 my $h = _huffman_walk_tree($heap[0], '', {});
2024              
2025 648         960 my %code_lengths;
2026 648         2031 foreach my $i (keys %$freq) {
2027 5668         8282 $code_lengths{$i} = length($h->{$i});
2028             }
2029              
2030 648         1531 huffman_from_code_lengths(\%code_lengths);
2031             }
2032              
2033 594     594 1 915 sub huffman_from_symbols ($symbols) {
  594         720  
  594         734  
2034              
2035 594 50       1266 if (ref($symbols) eq '') {
2036 0         0 $symbols = string2symbols($symbols);
2037             }
2038              
2039 594         1262 huffman_from_freq(frequencies($symbols));
2040             }
2041              
2042             ########################
2043             # Huffman Coding entries
2044             ########################
2045              
2046 474     474 1 726 sub create_huffman_entry ($symbols) {
  474         542  
  474         523  
2047              
2048 474 50       1189 if (ref($symbols) eq '') {
2049 0         0 $symbols = string2symbols($symbols);
2050             }
2051              
2052 474         969 my $dict = huffman_from_symbols($symbols);
2053 474         1577 my $enc = huffman_encode($symbols, $dict);
2054              
2055 474   100     4161 my $max_symbol = max(keys %$dict) // 0;
2056 474 50       1092 $VERBOSE && say STDERR "Max symbol: $max_symbol\n";
2057              
2058 474         546 my @code_lengths;
2059 474         899 foreach my $i (0 .. $max_symbol) {
2060 176503 100       201737 if (exists($dict->{$i})) {
2061 4006         6085 $code_lengths[$i] = length($dict->{$i});
2062             }
2063             else {
2064 172497         193087 $code_lengths[$i] = 0;
2065             }
2066             }
2067              
2068 474         1205 delta_encode(\@code_lengths) . pack("N", length($enc)) . pack("B*", $enc);
2069             }
2070              
2071 475     475 1 616 sub decode_huffman_entry ($fh) {
  475         722  
  475         505  
2072              
2073 475 100       1017 if (ref($fh) eq '') {
2074 1 50       12 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
2075 1         6 return __SUB__->($fh2);
2076             }
2077              
2078 474         952 my $code_lengths = delta_decode($fh);
2079 474         1002 my (undef, $rev_dict) = huffman_from_code_lengths($code_lengths);
2080              
2081 474         1666 my $enc_len = bytes2int($fh, 4);
2082 474 50       1252 $VERBOSE && say STDERR "Encoded length: $enc_len\n";
2083              
2084 474 100       1160 if ($enc_len > 0) {
2085 402         995 return huffman_decode(read_bits($fh, $enc_len), $rev_dict);
2086             }
2087              
2088 72         178 return [];
2089             }
2090              
2091             ###################################################################################
2092             # DEFLATE-like encoding of literals and backreferences produced by the LZSS methods
2093             ###################################################################################
2094              
2095 714     714 1 867 sub make_deflate_tables ($max_dist = $LZ_MAX_DIST, $max_len = $LZ_MAX_LEN) {
  714         901  
  714         862  
  714         786  
2096              
2097             # [distance value, offset bits]
2098 714         1439 my @DISTANCE_SYMBOLS = map { [$_, 0] } (0 .. 4);
  3570         5849  
2099              
2100 714         1788 until ($DISTANCE_SYMBOLS[-1][0] > $max_dist) {
2101 1952         3969 push @DISTANCE_SYMBOLS, [int($DISTANCE_SYMBOLS[-1][0] * (4 / 3)), $DISTANCE_SYMBOLS[-1][1] + 1];
2102 1952         4831 push @DISTANCE_SYMBOLS, [int($DISTANCE_SYMBOLS[-1][0] * (3 / 2)), $DISTANCE_SYMBOLS[-1][1]];
2103             }
2104              
2105             # [length, offset bits]
2106 714         1192 my @LENGTH_SYMBOLS = ((map { [$_, 0] } (1 .. 10)));
  7140         9250  
2107              
2108             {
2109 714         960 my $delta = 1;
  714         999  
2110 714         1371 until ($LENGTH_SYMBOLS[-1][0] > $max_len) {
2111 286         549 push @LENGTH_SYMBOLS, [$LENGTH_SYMBOLS[-1][0] + $delta, $LENGTH_SYMBOLS[-1][1] + 1];
2112 286         322 $delta *= 2;
2113 286         419 push @LENGTH_SYMBOLS, [$LENGTH_SYMBOLS[-1][0] + $delta, $LENGTH_SYMBOLS[-1][1]];
2114 286         405 push @LENGTH_SYMBOLS, [$LENGTH_SYMBOLS[-1][0] + $delta, $LENGTH_SYMBOLS[-1][1]];
2115 286         596 push @LENGTH_SYMBOLS, [$LENGTH_SYMBOLS[-1][0] + $delta, $LENGTH_SYMBOLS[-1][1]];
2116             }
2117 714   100     2512 while (@LENGTH_SYMBOLS and $LENGTH_SYMBOLS[-1][0] >= $max_len) {
2118 6186         13320 pop @LENGTH_SYMBOLS;
2119             }
2120 714         1216 push @LENGTH_SYMBOLS, [$max_len, 0];
2121             }
2122              
2123 714         983 my @LENGTH_INDICES;
2124              
2125 714         1577 foreach my $i (0 .. $#LENGTH_SYMBOLS) {
2126 2812         3015 my ($min, $bits) = @{$LENGTH_SYMBOLS[$i]};
  2812         3926  
2127 2812         4269 foreach my $k ($min .. $min + (1 << $bits) - 1) {
2128 14142         19748 $LENGTH_INDICES[$k] = $i;
2129             }
2130             }
2131              
2132 714         1625 return (\@DISTANCE_SYMBOLS, \@LENGTH_SYMBOLS, \@LENGTH_INDICES);
2133             }
2134              
2135 21702     21702 1 21527 sub find_deflate_index ($value, $table) {
  21702         23485  
  21702         21528  
  21702         21589  
2136 21702         21844 foreach my $i (0 .. $#{$table}) {
  21702         32218  
2137 374659 100       566917 if ($table->[$i][0] > $value) {
2138 21702         35397 return $i - 1;
2139             }
2140             }
2141 0         0 confess "error";
2142             }
2143              
2144 97     97 1 141 sub deflate_encode ($literals, $distances, $lengths, $entropy_sub = \&create_huffman_entry) {
  97         178  
  97         148  
  97         139  
  97         134  
  97         117  
2145              
2146 97   100     1035 my $max_dist = max(@$distances) // 0;
2147 97   100     678 my $max_len = max(@$lengths) // 0;
2148 97   100     257 my $max_symbol = (max(grep { defined($_) } @$literals) // -1) + 1;
  12682         15892  
2149              
2150 97         287 my ($DISTANCE_SYMBOLS, $LENGTH_SYMBOLS, $LENGTH_INDICES) = make_deflate_tables($max_dist, $max_len);
2151              
2152 97         184 my @len_symbols;
2153             my @dist_symbols;
2154 97         194 my $offset_bits = '';
2155              
2156 97         267 foreach my $k (0 .. $#$literals) {
2157              
2158 12682 100       19179 if ($lengths->[$k] == 0) {
2159 8710         14619 push @len_symbols, $literals->[$k];
2160 8710         11232 next;
2161             }
2162              
2163 3972         4629 my $len = $lengths->[$k];
2164 3972         5133 my $dist = $distances->[$k];
2165              
2166             {
2167 3972         4486 my $len_idx = $LENGTH_INDICES->[$len];
2168 3972         4299 my ($min, $bits) = @{$LENGTH_SYMBOLS->[$len_idx]};
  3972         6316  
2169              
2170 3972         7581 push @len_symbols, $len_idx + $max_symbol;
2171              
2172 3972 100       7166 if ($bits > 0) {
2173 1497         2777 $offset_bits .= sprintf('%0*b', $bits, $len - $min);
2174             }
2175             }
2176              
2177             {
2178 3972         4184 my $dist_idx = find_deflate_index($dist, $DISTANCE_SYMBOLS);
  3972         4262  
  3972         5294  
2179 3972         4431 my ($min, $bits) = @{$DISTANCE_SYMBOLS->[$dist_idx]};
  3972         6014  
2180              
2181 3972         5068 push @dist_symbols, $dist_idx;
2182              
2183 3972 100       5978 if ($bits > 0) {
2184 3753         8253 $offset_bits .= sprintf('%0*b', $bits, $dist - $min);
2185             }
2186             }
2187             }
2188              
2189 97         331 fibonacci_encode([$max_symbol, $max_dist, $max_len]) . $entropy_sub->(\@len_symbols) . $entropy_sub->(\@dist_symbols) . pack('B*', $offset_bits);
2190             }
2191              
2192 97     97 1 134 sub deflate_decode ($fh, $entropy_sub = \&decode_huffman_entry) {
  97         117  
  97         127  
  97         112  
2193              
2194 97 50       235 if (ref($fh) eq '') {
2195 0 0       0 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
2196 0         0 return __SUB__->($fh2, $entropy_sub);
2197             }
2198              
2199 97         129 my ($max_symbol, $max_dist, $max_len) = @{fibonacci_decode($fh)};
  97         368  
2200 97         261 my ($DISTANCE_SYMBOLS, $LENGTH_SYMBOLS) = make_deflate_tables($max_dist, $max_len);
2201              
2202 97         344 my $len_symbols = $entropy_sub->($fh);
2203 97         240 my $dist_symbols = $entropy_sub->($fh);
2204              
2205 97         160 my $bits_len = 0;
2206              
2207 97         188 foreach my $i (@$dist_symbols) {
2208 3972         4725 $bits_len += $DISTANCE_SYMBOLS->[$i][1];
2209             }
2210              
2211 97         218 foreach my $i (@$len_symbols) {
2212 12682 100       19345 if ($i >= $max_symbol) {
2213 3972         6115 $bits_len += $LENGTH_SYMBOLS->[$i - $max_symbol][1];
2214             }
2215             }
2216              
2217 97         266 my $bits = read_bits($fh, $bits_len);
2218              
2219 97         280 my @literals;
2220             my @lengths;
2221 97         0 my @distances;
2222              
2223 97         149 my $j = 0;
2224              
2225 97         161 foreach my $i (@$len_symbols) {
2226 12682 100       15771 if ($i >= $max_symbol) {
2227 3972         4813 my $dist = $dist_symbols->[$j++];
2228 3972         4165 push @literals, undef;
2229 3972         10399 push @lengths, $LENGTH_SYMBOLS->[$i - $max_symbol][0] + oct('0b' . substr($bits, 0, $LENGTH_SYMBOLS->[$i - $max_symbol][1], ''));
2230 3972         11644 push @distances, $DISTANCE_SYMBOLS->[$dist][0] + oct('0b' . substr($bits, 0, $DISTANCE_SYMBOLS->[$dist][1], ''));
2231             }
2232             else {
2233 8710         10297 push @literals, $i;
2234 8710         9174 push @lengths, 0;
2235 8710         11564 push @distances, 0;
2236             }
2237             }
2238              
2239 97         1910 return (\@literals, \@distances, \@lengths);
2240             }
2241              
2242             #####################
2243             # Elias gamma coding
2244             #####################
2245              
2246 15     15 1 39 sub elias_gamma_encode ($integers) {
  15         30  
  15         26  
2247              
2248 15         35 my $bitstring = '';
2249 15         55 foreach my $k (scalar(@$integers), @$integers) {
2250 1322         1898 my $t = sprintf('%b', $k + 1);
2251 1322         2176 $bitstring .= ('1' x (length($t) - 1)) . '0' . substr($t, 1);
2252             }
2253              
2254 15         137 pack('B*', $bitstring);
2255             }
2256              
2257 28     28 1 42 sub elias_gamma_decode ($fh) {
  28         43  
  28         37  
2258              
2259 28 100       80 if (ref($fh) eq '') {
2260 13 50       165 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
2261 13         40 return __SUB__->($fh2);
2262             }
2263              
2264 15         26 my @ints;
2265 15         24 my $len = 0;
2266 15         30 my $buffer = '';
2267              
2268 15         104 for (my $k = 0 ; $k <= $len ; ++$k) {
2269              
2270 1322         1537 my $n = 0;
2271 1322         1782 ++$n while (read_bit($fh, \$buffer) eq '1');
2272              
2273 1322         2012 push @ints, oct('0b1' . join('', map { read_bit($fh, \$buffer) } 1 .. $n)) - 1;
  8119         10050  
2274              
2275 1322 100       3578 if ($k == 0) {
2276 15         47 $len = pop(@ints);
2277             }
2278             }
2279              
2280 15         140 return \@ints;
2281             }
2282              
2283             #####################
2284             # Elias omega coding
2285             #####################
2286              
2287 15     15 1 31 sub elias_omega_encode ($integers) {
  15         25  
  15         37  
2288              
2289 15         44 my $bitstring = '';
2290 15         58 foreach my $k (scalar(@$integers), @$integers) {
2291 1341 100       1639 if ($k == 0) {
2292 4         8 $bitstring .= '0';
2293             }
2294             else {
2295 1337         1637 my $t = sprintf('%b', $k + 1);
2296 1337         1351 my $l = length($t);
2297 1337         1558 my $L = sprintf('%b', $l);
2298 1337         2242 $bitstring .= ('1' x (length($L) - 1)) . '0' . substr($L, 1) . substr($t, 1);
2299             }
2300             }
2301              
2302 15         131 pack('B*', $bitstring);
2303             }
2304              
2305 28     28 1 39 sub elias_omega_decode ($fh) {
  28         42  
  28         39  
2306              
2307 28 100       104 if (ref($fh) eq '') {
2308 13 50       194 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
2309 13         42 return __SUB__->($fh2);
2310             }
2311              
2312 15         49 my @ints;
2313 15         27 my $len = 0;
2314 15         27 my $buffer = '';
2315              
2316 15         45 for (my $k = 0 ; $k <= $len ; ++$k) {
2317              
2318 1341         1466 my $bl = 0;
2319 1341         1585 ++$bl while (read_bit($fh, \$buffer) eq '1');
2320              
2321 1341 100       1752 if ($bl > 0) {
2322              
2323 1337         1610 my $bl2 = oct('0b1' . join('', map { read_bit($fh, \$buffer) } 1 .. $bl));
  2804         3149  
2324 1337         1855 my $int = oct('0b1' . join('', map { read_bit($fh, \$buffer) } 1 .. ($bl2 - 1))) - 1;
  8254         9058  
2325              
2326 1337         2101 push @ints, $int;
2327             }
2328             else {
2329 4         7 push @ints, 0;
2330             }
2331              
2332 1341 100       2596 if ($k == 0) {
2333 15         45 $len = pop(@ints);
2334             }
2335             }
2336              
2337 15         131 return \@ints;
2338             }
2339              
2340             ###################
2341             # LZSS SYMBOLIC
2342             ###################
2343              
2344 153     153 1 230 sub lzss_encode_symbolic($symbols, %params) {
  153         296  
  153         239  
  153         199  
2345              
2346 153 50       418 if (ref($symbols) eq '') {
2347 0         0 return lzss_encode($symbols, %params);
2348             }
2349              
2350 153   33     722 my $min_len = $params{min_len} // $LZ_MIN_LEN;
2351 153   33     510 my $max_len = $params{max_len} // $LZ_MAX_LEN;
2352 153   33     461 my $max_dist = $params{max_dist} // $LZ_MAX_DIST;
2353 153   33     512 my $max_chain_len = $params{max_chain_len} // $LZ_MAX_CHAIN_LEN;
2354              
2355 153         305 my $end = $#$symbols;
2356 153         323 my (@literals, @distances, @lengths, %table);
2357              
2358 153         400 for (my $la = 0 ; $la <= $end ;) {
2359 4031         4112 my $best_n = 1;
2360 4031         4069 my $best_p = $la;
2361              
2362 4031         4457 my $upto = $la + $min_len - 1;
2363 4031 100       6024 my $lookahead = join(' ', @{$symbols}[$la .. ($upto > $end ? $end : $upto)]);
  4031         7355  
2364              
2365 4031 100       6801 if (exists $table{$lookahead}) {
2366              
2367 430         543 foreach my $p (@{$table{$lookahead}}) {
  430         803  
2368              
2369 1030 50       1567 last if ($la - $p > $max_dist);
2370              
2371 1030         1130 my $n = $min_len;
2372              
2373 1030   100     58727 ++$n while ($la + $n <= $end and $symbols->[$la + $n - 1] == $symbols->[$p + $n - 1] and $n <= $max_len);
      66        
2374              
2375 1030 100       1618 if ($n > $best_n) {
2376 509         571 $best_n = $n;
2377 509         574 $best_p = $p;
2378 509 50       1058 last if ($n > $max_len);
2379             }
2380             }
2381             }
2382              
2383 4031 100       5218 if ($best_n == 1) {
2384 3601         6359 $table{$lookahead} = [$la];
2385             }
2386             else {
2387 430         1217 my @matched = @{$symbols}[$la .. $la + $best_n - 1];
  430         2433  
2388 430         1330 my @key_arr = @matched[0 .. $min_len - 1];
2389              
2390 430         978 foreach my $i (0 .. scalar(@matched) - $min_len) {
2391              
2392 10588         13799 my $key = join(' ', @key_arr);
2393 10588         10030 unshift @{$table{$key}}, $la + $i;
  10588         13098  
2394 10588 100       10447 pop @{$table{$key}} if (@{$table{$key}} > $max_chain_len);
  7870         8963  
  10588         14855  
2395              
2396 10588         10814 shift(@key_arr);
2397 10588         16197 push @key_arr, $matched[$i + $min_len];
2398             }
2399             }
2400              
2401 4031 100       6154 if ($best_n > $min_len) {
    100          
2402              
2403 420         633 push @lengths, $best_n - 1;
2404 420         548 push @distances, $la - $best_p;
2405 420         536 push @literals, undef;
2406              
2407 420         894 $la += $best_n - 1;
2408             }
2409             elsif ($best_n == 1) {
2410 3601         4128 push @lengths, 0;
2411 3601         3853 push @distances, 0;
2412 3601         7364 push @literals, $symbols->[$la++];
2413             }
2414             else {
2415              
2416 10         29 push @lengths, (0) x $best_n;
2417 10         21 push @distances, (0) x $best_n;
2418 10         19 push @literals, @{$symbols}[$la .. $la + $best_n - 1];
  10         24  
2419              
2420 10         25 $la += $best_n;
2421             }
2422             }
2423              
2424 153         1784 return (\@literals, \@distances, \@lengths);
2425             }
2426              
2427 90     90 1 127 sub lzss_decode_symbolic ($literals, $distances, $lengths) {
  90         116  
  90         112  
  90         113  
  90         99  
2428              
2429 90         125 my @data;
2430 90         140 my $data_len = 0;
2431              
2432 90         218 foreach my $i (0 .. $#$lengths) {
2433              
2434 3215 100       4186 if ($lengths->[$i] == 0) {
2435 2826         3400 push @data, $literals->[$i];
2436 2826         2898 $data_len += 1;
2437 2826         3489 next;
2438             }
2439              
2440 389   33     661 my $length = $lengths->[$i] // confess "bad input";
2441 389   33     661 my $dist = $distances->[$i] // confess "bad input";
2442              
2443 389 100       650 if ($dist >= $length) { # non-overlapping matches
    100          
2444 271         1171 push @data, @data[$data_len - $dist .. $data_len - $dist + $length - 1];
2445             }
2446             elsif ($dist == 1) { # run-length of last character
2447 34         635 push @data, ($data[-1]) x $length;
2448             }
2449             else { # overlapping matches
2450 84         140 foreach my $j (1 .. $length) {
2451 737         1366 push @data, $data[$data_len + $j - $dist - 1];
2452             }
2453             }
2454              
2455 389         829 $data_len += $length;
2456             }
2457              
2458 90         902 return \@data;
2459             }
2460              
2461             ###################
2462             # LZSS Encoding
2463             ###################
2464              
2465 269     269 1 137025 sub lzss_encode ($str, %params) {
  269         640  
  269         505  
  269         366  
2466              
2467 269 100       1169 if (ref($str) ne '') {
2468 140         415 return lzss_encode_symbolic($str, %params);
2469             }
2470              
2471 129   66     721 my $min_len = $params{min_len} // $LZ_MIN_LEN;
2472 129   66     502 my $max_len = $params{max_len} // $LZ_MAX_LEN;
2473 129   66     454 my $max_dist = $params{max_dist} // $LZ_MAX_DIST;
2474 129   33     475 my $max_chain_len = $params{max_chain_len} // $LZ_MAX_CHAIN_LEN;
2475              
2476 129         101065 my @symbols = unpack('C*', $str);
2477 129         17455 my $end = $#symbols;
2478              
2479 129         309 my (@literals, @distances, @lengths, %table);
2480              
2481 129         469 for (my $la = 0 ; $la <= $end ;) {
2482              
2483 25792         26241 my $best_n = 1;
2484 25792         26566 my $best_p = $la;
2485              
2486 25792         37288 my $lookahead = substr($str, $la, $min_len);
2487              
2488 25792 100       47742 if (exists $table{$lookahead}) {
2489 8093         9811 foreach my $p (@{$table{$lookahead}}) {
  8093         14787  
2490              
2491 90156 50       124222 last if ($la - $p > $max_dist);
2492              
2493 90156         92157 my $n = $min_len;
2494              
2495 90156   100     2218622 ++$n while ($la + $n <= $end and $symbols[$la + $n - 1] == $symbols[$p + $n - 1] and $n <= $max_len);
      100        
2496              
2497 90156 100       166424 if ($n > $best_n) {
2498 13150         13308 $best_p = $p;
2499 13150         12578 $best_n = $n;
2500 13150 100       26789 last if ($best_n > $max_len);
2501             }
2502             }
2503             }
2504              
2505 25792 100       36949 if ($best_n == 1) {
2506 17699         36765 $table{$lookahead} = [$la];
2507             }
2508             else {
2509              
2510 8093         16516 my $matched = substr($str, $la, $best_n);
2511              
2512 8093         13480 foreach my $i (0 .. $best_n - $min_len) {
2513 153650         215873 my $key = substr($matched, $i, $min_len);
2514 153650         158136 unshift @{$table{$key}}, $la + $i;
  153650         246115  
2515 153650 100       151265 pop(@{$table{$key}}) if (@{$table{$key}} > $max_chain_len);
  64258         107200  
  153650         262284  
2516             }
2517             }
2518              
2519 25792 100       36534 if ($best_n == 1) {
2520 17699         30306 $table{$lookahead} = [$la];
2521             }
2522              
2523 25792 100       38075 if ($best_n > $min_len) {
    100          
2524              
2525 8085         11056 push @lengths, $best_n - 1;
2526 8085         9817 push @distances, $la - $best_p;
2527 8085         9507 push @literals, undef;
2528              
2529 8085         16793 $la += $best_n - 1;
2530             }
2531             elsif ($best_n == 1) {
2532 17699         22946 push @lengths, 0;
2533 17699         19801 push @distances, 0;
2534 17699         41648 push @literals, $symbols[$la++];
2535             }
2536             else {
2537              
2538 8         24 push @lengths, (0) x $best_n;
2539 8         25 push @distances, (0) x $best_n;
2540 8         35 push @literals, @symbols[$la .. $la + $best_n - 1];
2541              
2542 8         27 $la += $best_n;
2543             }
2544             }
2545              
2546 129         38899 return (\@literals, \@distances, \@lengths);
2547             }
2548              
2549 56     56 1 93 sub lzss_decode ($literals, $distances, $lengths) {
  56         82  
  56         84  
  56         71  
  56         77  
2550              
2551 56         99 my $data = '';
2552 56         82 my $data_len = 0;
2553              
2554 56         149 foreach my $i (0 .. $#$lengths) {
2555              
2556 11141 100       16211 if ($lengths->[$i] == 0) {
2557 7344         10452 $data .= chr($literals->[$i]);
2558 7344         7942 ++$data_len;
2559 7344         9893 next;
2560             }
2561              
2562 3797   33     6075 my $length = $lengths->[$i] // confess "bad input";
2563 3797   33     6516 my $dist = $distances->[$i] // confess "bad input";
2564              
2565 3797 100       5371 if ($dist >= $length) { # non-overlapping matches
    100          
2566 3620   33     7878 $data .= substr($data, $data_len - $dist, $length) // confess "bad input";
2567             }
2568             elsif ($dist == 1) { # run-length of last character
2569 37         145 $data .= substr($data, -1) x $length;
2570             }
2571             else { # overlapping matches
2572 140   33     369 my $pattern = substr($data, $data_len - $dist, $dist) // confess "bad input";
2573 140         337 my $full_reps = int(($length + $dist - 1) / $dist) + 1;
2574 140   33     430 $data .= substr($pattern x $full_reps, 0, $length) // confess "bad input";
2575             }
2576              
2577 3797         6944 $data_len += $length;
2578             }
2579              
2580 56         3677 return $data;
2581             }
2582              
2583             ###################
2584             # LZSSF Compression
2585             ###################
2586              
2587 19     19 1 44 sub lzss_encode_fast_symbolic ($symbols, %params) {
  19         31  
  19         36  
  19         28  
2588              
2589 19 50       61 if (ref($symbols) eq '') {
2590 0         0 return lzss_encode_fast($symbols, %params);
2591             }
2592              
2593 19         33 my $la = 0;
2594 19         41 my $end = $#$symbols;
2595              
2596 19   33     99 my $min_len = $params{min_len} // $LZ_MIN_LEN; # minimum match length
2597 19   33     94 my $max_len = $params{max_len} // $LZ_MAX_LEN; # maximum match length
2598 19   33     70 my $max_dist = $params{max_dist} // $LZ_MAX_DIST; # maximum offset distance
2599              
2600 19         45 my (@literals, @distances, @lengths, %table);
2601              
2602 19         57 while ($la <= $end) {
2603              
2604 2765         2649 my $best_n = 1;
2605 2765         2573 my $best_p = $la;
2606              
2607 2765         2655 my $upto = $la + $min_len - 1;
2608 2765 100       3504 my $lookahead = join(' ', @{$symbols}[$la .. ($upto > $end ? $end : $upto)]);
  2765         4064  
2609              
2610 2765 100 66     4736 if (exists($table{$lookahead}) and $la - $table{$lookahead} <= $max_dist) {
2611              
2612 374         410 my $p = $table{$lookahead};
2613 374         343 my $n = $min_len;
2614              
2615 374   100     6330 ++$n while ($la + $n <= $end and $symbols->[$la + $n - 1] == $symbols->[$p + $n - 1] and $n <= $max_len);
      66        
2616              
2617 374         346 $best_p = $p;
2618 374         381 $best_n = $n;
2619             }
2620              
2621 2765         3697 $table{$lookahead} = $la;
2622              
2623 2765 100       3782 if ($best_n > $min_len) {
    100          
2624              
2625 373         412 push @lengths, $best_n - 1;
2626 373         371 push @distances, $la - $best_p;
2627 373         399 push @literals, undef;
2628              
2629 373         569 $la += $best_n - 1;
2630             }
2631             elsif ($best_n == 1) {
2632 2391         2491 push @lengths, 0;
2633 2391         2315 push @distances, 0;
2634 2391         4188 push @literals, $symbols->[$la++];
2635             }
2636             else {
2637              
2638 1         4 push @lengths, (0) x $best_n;
2639 1         3 push @distances, (0) x $best_n;
2640 1         3 push @literals, @{$symbols}[$la .. $la + $best_n - 1];
  1         4  
2641              
2642 1         4 $la += $best_n;
2643             }
2644             }
2645              
2646 19         399 return (\@literals, \@distances, \@lengths);
2647             }
2648              
2649 104     104 1 258 sub lzss_encode_fast($str, %params) {
  104         323  
  104         310  
  104         173  
2650              
2651 104 100       481 if (ref($str) ne '') {
2652 19         78 return lzss_encode_fast_symbolic($str, %params);
2653             }
2654              
2655 85         119030 my @symbols = unpack('C*', $str);
2656              
2657 85         22387 my $la = 0;
2658 85         187 my $end = $#symbols;
2659              
2660 85   33     534 my $min_len = $params{min_len} // $LZ_MIN_LEN; # minimum match length
2661 85   33     339 my $max_len = $params{max_len} // $LZ_MAX_LEN; # maximum match length
2662 85   33     321 my $max_dist = $params{max_dist} // $LZ_MAX_DIST; # maximum offset distance
2663              
2664 85         184 my (@literals, @distances, @lengths, %table);
2665              
2666 85         255 while ($la <= $end) {
2667              
2668 25645         26647 my $best_n = 1;
2669 25645         27670 my $best_p = $la;
2670              
2671 25645         39910 my $lookahead = substr($str, $la, $min_len);
2672              
2673 25645 100 66     71402 if (exists($table{$lookahead}) and $la - $table{$lookahead} <= $max_dist) {
2674              
2675 10518         13277 my $p = $table{$lookahead};
2676 10518         11210 my $n = $min_len;
2677              
2678 10518   100     425256 ++$n while ($la + $n <= $end and $symbols[$la + $n - 1] == $symbols[$p + $n - 1] and $n <= $max_len);
      100        
2679              
2680 10518         13113 $best_p = $p;
2681 10518         13058 $best_n = $n;
2682             }
2683              
2684 25645         42858 $table{$lookahead} = $la;
2685              
2686 25645 100       36210 if ($best_n > $min_len) {
    100          
2687              
2688 10512         13113 push @lengths, $best_n - 1;
2689 10512         12834 push @distances, $la - $best_p;
2690 10512         12030 push @literals, undef;
2691              
2692 10512         21355 $la += $best_n - 1;
2693             }
2694             elsif ($best_n == 1) {
2695 15127         19697 push @lengths, 0;
2696 15127         17746 push @distances, 0;
2697 15127         36577 push @literals, $symbols[$la++];
2698             }
2699             else {
2700              
2701 6         15 push @lengths, (0) x $best_n;
2702 6         12 push @distances, (0) x $best_n;
2703 6         26 push @literals, @symbols[$la .. $la + $best_n - 1];
2704              
2705 6         20 $la += $best_n;
2706             }
2707             }
2708              
2709 85         27300 return (\@literals, \@distances, \@lengths);
2710             }
2711              
2712             ################################
2713             # LZ77 encoding, inspired by LZ4
2714             ################################
2715              
2716 130     130 1 135855 sub lz77_encode($chunk, $lzss_encoding_sub = \&lzss_encode) {
  130         250  
  130         289  
  130         177  
2717              
2718 130         327 local $LZ_MAX_LEN = ~0; # maximum match length
2719              
2720 130         510 my ($literals, $distances, $lengths) = $lzss_encoding_sub->($chunk);
2721              
2722 130         250 my $literals_end = $#{$literals};
  130         221  
2723 130         243 my (@symbols, @len_symbols, @match_symbols, @dist_symbols);
2724              
2725 130         369 for (my $i = 0 ; $i <= $literals_end ; ++$i) {
2726              
2727 4445         4524 my $j = $i;
2728 4445   100     11118 while ($i <= $literals_end and defined($literals->[$i])) {
2729 11215         25443 ++$i;
2730             }
2731              
2732 4445         4942 my $literals_length = $i - $j;
2733 4445   100     6313 my $match_len = $lengths->[$i] // 0;
2734              
2735 4445 100       7699 push @match_symbols, (($literals_length >= 7 ? 7 : $literals_length) << 5) | ($match_len >= 31 ? 31 : $match_len);
    100          
2736              
2737 4445         4469 $literals_length -= 7;
2738 4445         4379 $match_len -= 31;
2739              
2740 4445         5840 while ($literals_length >= 0) {
2741 470 50       723 push @len_symbols, ($literals_length >= 255 ? 255 : $literals_length);
2742 470         693 $literals_length -= 255;
2743             }
2744              
2745 4445 100       5618 if ($i > $j) {
2746 1715         2413 push @symbols, @{$literals}[$j .. $i - 1];
  1715         8242  
2747             }
2748              
2749 4445         6409 while ($match_len >= 0) {
2750 658 100       952 push @match_symbols, ($match_len >= 255 ? 255 : $match_len);
2751 658         985 $match_len -= 255;
2752             }
2753              
2754 4445   100     10529 push @dist_symbols, $distances->[$i] // 0;
2755             }
2756              
2757 130         3230 return (\@symbols, \@dist_symbols, \@len_symbols, \@match_symbols);
2758             }
2759              
2760             *lz77_encode_symbolic = \&lz77_encode;
2761              
2762 44     44 1 86 sub lz77_decode($symbols, $dist_symbols, $len_symbols, $match_symbols) {
  44         62  
  44         62  
  44         57  
  44         66  
  44         62  
2763              
2764 44         82 my $data = '';
2765 44         81 my $data_len = 0;
2766              
2767 44         1200 my @symbols = @$symbols;
2768 44         175 my @len_symbols = @$len_symbols;
2769 44         916 my @match_symbols = @$match_symbols;
2770 44         2503 my @dist_symbols = @$dist_symbols;
2771              
2772 44         113 while (@symbols) {
2773              
2774 3899   33     5381 my $len_byte = shift(@match_symbols) // confess "bad input";
2775              
2776 3899         4018 my $literals_length = $len_byte >> 5;
2777 3899         3831 my $match_len = $len_byte & 0b11111;
2778              
2779 3899 100       4940 if ($literals_length == 7) {
2780 324         318 while (1) {
2781 324   33     514 my $byte_len = shift(@len_symbols) // confess "bad input";
2782 324         366 $literals_length += $byte_len;
2783 324 50       556 last if $byte_len != 255;
2784             }
2785             }
2786              
2787 3899 100       5031 if ($literals_length > 0) {
2788 1314         2467 $data .= pack("C*", splice(@symbols, 0, $literals_length));
2789 1314         1835 $data_len += $literals_length;
2790             }
2791              
2792 3899 100       4824 if ($match_len == 31) {
2793 608         572 while (1) {
2794 620   33     1004 my $byte_len = shift(@match_symbols) // confess "bad input";
2795 620         653 $match_len += $byte_len;
2796 620 100       935 last if $byte_len != 255;
2797             }
2798             }
2799              
2800 3899   33     6021 my $dist = shift(@dist_symbols) // confess "bad input";
2801              
2802 3899 100       5531 if ($dist >= $match_len) { # non-overlapping matches
    100          
2803 3728   33     7150 $data .= substr($data, $data_len - $dist, $match_len) // confess "bad input";
2804             }
2805             elsif ($dist == 1) { # run-length of last character
2806 33         128 $data .= substr($data, -1) x $match_len;
2807             }
2808             else { # overlapping matches
2809 138         250 foreach my $i (1 .. $match_len) {
2810 2510   33     4619 $data .= substr($data, $data_len + $i - $dist - 1, 1) // confess "bad input";
2811             }
2812             }
2813              
2814 3899         7048 $data_len += $match_len;
2815             }
2816              
2817 44         2489 return $data;
2818             }
2819              
2820 86     86 1 102 sub lz77_decode_symbolic($symbols, $dist_symbols, $len_symbols, $match_symbols) {
  86         154  
  86         98  
  86         125  
  86         112  
  86         95  
2821              
2822 86         99 my @data;
2823 86         148 my $data_len = 0;
2824              
2825 86         541 my @symbols = @$symbols;
2826 86         152 my @len_symbols = @$len_symbols;
2827 86         181 my @match_symbols = @$match_symbols;
2828 86         188 my @dist_symbols = @$dist_symbols;
2829              
2830 86         175 while (@symbols) {
2831              
2832 546   33     845 my $len_byte = shift(@match_symbols) // confess "bad input";
2833              
2834 546         702 my $literals_length = $len_byte >> 5;
2835 546         635 my $match_len = $len_byte & 0b11111;
2836              
2837 546 100       804 if ($literals_length == 7) {
2838 146         176 while (1) {
2839 146   33     306 my $byte_len = shift(@len_symbols) // confess "bad input";
2840 146         186 $literals_length += $byte_len;
2841 146 50       242 last if $byte_len != 255;
2842             }
2843             }
2844              
2845 546 100       908 if ($literals_length > 0) {
2846 401         986 push @data, splice(@symbols, 0, $literals_length);
2847 401         611 $data_len += $literals_length;
2848             }
2849              
2850 546 100       814 if ($match_len == 31) {
2851 29         46 while (1) {
2852 38   33     80 my $byte_len = shift(@match_symbols) // confess "bad input";
2853 38         53 $match_len += $byte_len;
2854 38 100       85 last if $byte_len != 255;
2855             }
2856             }
2857              
2858 546   33     916 my $dist = shift(@dist_symbols) // confess "bad input";
2859              
2860 546 100       966 if ($dist >= $match_len) { # non-overlapping matches
    100          
2861 434         1677 push @data, @data[scalar(@data) - $dist .. scalar(@data) - $dist + $match_len - 1];
2862             }
2863             elsif ($dist == 1) { # run-length of last character
2864 33         588 push @data, ($data[-1]) x $match_len;
2865             }
2866             else { # overlapping matches
2867 79         179 foreach my $j (1 .. $match_len) {
2868 703         1312 push @data, $data[$data_len + $j - $dist - 1];
2869             }
2870             }
2871              
2872 546         1341 $data_len += $match_len;
2873             }
2874              
2875 86         890 return \@data;
2876             }
2877              
2878 93     93 1 1538148 sub lz77_compress($chunk, $entropy_sub = \&create_huffman_entry, $lzss_encoding_sub = \&lzss_encode) {
  93         186  
  93         242  
  93         1441  
  93         181  
2879 93         330 my ($symbols, $dist_symbols, $len_symbols, $match_symbols) = lz77_encode($chunk, $lzss_encoding_sub);
2880 93         350 $entropy_sub->($symbols) . $entropy_sub->($len_symbols) . $entropy_sub->($match_symbols) . obh_encode($dist_symbols, $entropy_sub);
2881             }
2882              
2883             *lz77_compress_symbolic = \&lz77_compress;
2884              
2885 46     46 1 128 sub lz77_decompress($fh, $entropy_sub = \&decode_huffman_entry) {
  46         75  
  46         107  
  46         54  
2886              
2887 46 100       153 if (ref($fh) eq '') {
2888 23 50       443 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
2889 23         136 return __SUB__->($fh2, $entropy_sub);
2890             }
2891              
2892 23         88 my $symbols = $entropy_sub->($fh);
2893 23         93 my $len_symbols = $entropy_sub->($fh);
2894 23         67 my $match_symbols = $entropy_sub->($fh);
2895 23         154 my $dist_symbols = obh_decode($fh, $entropy_sub);
2896              
2897 23         146 lz77_decode($symbols, $dist_symbols, $len_symbols, $match_symbols);
2898             }
2899              
2900 134     134 1 190 sub lz77_decompress_symbolic($fh, $entropy_sub = \&decode_huffman_entry) {
  134         172  
  134         194  
  134         172  
2901              
2902 134 100       316 if (ref($fh) eq '') {
2903 64 50       925 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
2904 64         167 return __SUB__->($fh2, $entropy_sub);
2905             }
2906              
2907 70         175 my $symbols = $entropy_sub->($fh);
2908 70         166 my $len_symbols = $entropy_sub->($fh);
2909 70         167 my $match_symbols = $entropy_sub->($fh);
2910 70         243 my $dist_symbols = obh_decode($fh, $entropy_sub);
2911              
2912 70         282 lz77_decode_symbolic($symbols, $dist_symbols, $len_symbols, $match_symbols);
2913             }
2914              
2915             #########################
2916             # LZSS + DEFLATE encoding
2917             #########################
2918              
2919 97     97 1 1377030 sub lzss_compress($chunk, $entropy_sub = \&create_huffman_entry, $lzss_encoding_sub = \&lzss_encode) {
  97         213  
  97         229  
  97         251  
  97         196  
2920 97         343 my ($literals, $distances, $lengths) = $lzss_encoding_sub->($chunk);
2921 97         388 deflate_encode($literals, $distances, $lengths, $entropy_sub);
2922             }
2923              
2924             *lzss_compress_symbolic = \&lzss_compress;
2925              
2926 68     68 1 135 sub lzss_decompress($fh, $entropy_sub = \&decode_huffman_entry) {
  68         121  
  68         108  
  68         74  
2927              
2928 68 100       195 if (ref($fh) eq '') {
2929 34 50       1057 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
2930 34         103 return __SUB__->($fh2, $entropy_sub);
2931             }
2932              
2933 34         148 my ($literals, $distances, $lengths) = deflate_decode($fh, $entropy_sub);
2934 34         147 lzss_decode($literals, $distances, $lengths);
2935             }
2936              
2937 126     126 1 373 sub lzss_decompress_symbolic($fh, $entropy_sub = \&decode_huffman_entry) {
  126         151  
  126         179  
  126         127  
2938              
2939 126 100       288 if (ref($fh) eq '') {
2940 63 50       817 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
2941 63         166 return __SUB__->($fh2, $entropy_sub);
2942             }
2943              
2944 63         166 my ($literals, $distances, $lengths) = deflate_decode($fh, $entropy_sub);
2945 63         194 lzss_decode_symbolic($literals, $distances, $lengths);
2946             }
2947              
2948             #########################################
2949             # LZB -- LZSS with byte-oriented encoding
2950             #########################################
2951              
2952 28     28 1 277100 sub lzb_compress ($chunk, $lzss_encoding_sub = \&lzss_encode) {
  28         68  
  28         82  
  28         39  
2953              
2954 28         62 my ($literals, $distances, $lengths) = do {
2955 28         60 local $LZ_MAX_DIST = (1 << 16) - 1;
2956 28         48 local $LZ_MAX_LEN = ~0;
2957 28         113 $lzss_encoding_sub->($chunk);
2958             };
2959              
2960 28         73 my $literals_end = $#{$literals};
  28         62  
2961 28         50 my $data = '';
2962              
2963 28         91 for (my $i = 0 ; $i <= $literals_end ; ++$i) {
2964              
2965 3601         3667 my $j = $i;
2966 3601   100     9435 while ($i <= $literals_end and defined($literals->[$i])) {
2967 5515         14220 ++$i;
2968             }
2969              
2970 3601         4083 my $literals_length = $i - $j;
2971 3601   100     5018 my $match_len = $lengths->[$i] // 0;
2972              
2973 3601 100       10772 $data .= chr((($literals_length >= 7 ? 7 : $literals_length) << 5) | ($match_len >= 31 ? 31 : $match_len));
    100          
2974              
2975 3601         4008 $literals_length -= 7;
2976 3601         3622 $match_len -= 31;
2977              
2978 3601         4914 while ($literals_length >= 0) {
2979 235 50       406 $data .= $literals_length >= 255 ? "\xff" : chr($literals_length);
2980 235         394 $literals_length -= 255;
2981             }
2982              
2983 3601 100       4655 if ($i > $j) {
2984 1109         1629 $data .= pack('C*', @{$literals}[$j .. $i - 1]);
  1109         2289  
2985             }
2986              
2987 3601         5354 while ($match_len >= 0) {
2988 611 100       1016 $data .= $match_len >= 255 ? "\xff" : chr($match_len);
2989 611         979 $match_len -= 255;
2990             }
2991              
2992 3601   100     10624 $data .= pack('B*', sprintf('%016b', $distances->[$i] // 0));
2993             }
2994              
2995 28         117 return fibonacci_encode([length $data]) . $data;
2996             }
2997              
2998 56     56 1 81 sub lzb_decompress($fh) {
  56         76  
  56         67  
2999              
3000 56 100       128 if (ref($fh) eq '') {
3001 28 50       390 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
3002 28         78 return __SUB__->($fh2);
3003             }
3004              
3005 28         42 my $data = '';
3006 28         64 my $search_window = '';
3007 28         44 my $search_window_size = 1 << 16;
3008              
3009 28   33     70 my $block_size = fibonacci_decode($fh)->[0] // confess "decompression error";
3010              
3011 28   33     513 read($fh, (my $block), $block_size) // confess "Read error: $!";
3012              
3013 28         91 while ($block ne '') {
3014              
3015 3601         5789 my $len_byte = ord substr($block, 0, 1, '');
3016              
3017 3601         4802 my $literals_length = $len_byte >> 5;
3018 3601         4716 my $match_len = $len_byte & 0b11111;
3019              
3020 3601 100       5460 if ($literals_length == 7) {
3021 235         280 while (1) {
3022 235         371 my $byte_len = ord substr($block, 0, 1, '');
3023 235         329 $literals_length += $byte_len;
3024 235 50       471 last if $byte_len != 255;
3025             }
3026             }
3027              
3028 3601 100       5450 if ($literals_length > 0) {
3029 1109         1924 $search_window .= substr($block, 0, $literals_length, '');
3030             }
3031              
3032 3601 100       5336 if ($match_len == 31) {
3033 601         651 while (1) {
3034 611         930 my $byte_len = ord substr($block, 0, 1, '');
3035 611         780 $match_len += $byte_len;
3036 611 100       1302 last if $byte_len != 255;
3037             }
3038             }
3039              
3040 3601         7401 my $offset = oct('0b' . unpack('B*', substr($block, 0, 2, '')));
3041              
3042 3601 100       5535 if ($offset >= $match_len) { # non-overlapping matches
    100          
3043 3467         6711 $search_window .= substr($search_window, length($search_window) - $offset, $match_len);
3044             }
3045             elsif ($offset == 1) { # run-length of last character
3046 20         95 $search_window .= substr($search_window, -1) x $match_len;
3047             }
3048             else { # overlapping matches
3049 114         221 foreach my $i (1 .. $match_len) {
3050 2393         4479 $search_window .= substr($search_window, length($search_window) - $offset, 1);
3051             }
3052             }
3053              
3054 3601         6346 $data .= substr($search_window, -($match_len + $literals_length));
3055 3601 50       12908 $search_window = substr($search_window, -$search_window_size) if (length($search_window) > 2 * $search_window_size);
3056             }
3057              
3058 28         893 return $data;
3059             }
3060              
3061             ################################################################
3062             # Encode a list of symbols, using offset bits and huffman coding
3063             ################################################################
3064              
3065 254     254 1 452 sub obh_encode ($distances, $entropy_sub = \&create_huffman_entry) {
  254         362  
  254         488  
  254         292  
3066              
3067 254   100     1289 my $max_dist = max(@$distances) // 0;
3068 254         687 my ($DISTANCE_SYMBOLS) = make_deflate_tables($max_dist, 0);
3069              
3070 254         514 my @symbols;
3071 254         359 my $offset_bits = '';
3072              
3073 254         443 foreach my $dist (@$distances) {
3074              
3075 10250         11626 my $i = find_deflate_index($dist, $DISTANCE_SYMBOLS);
3076 10250         10290 my ($min, $bits) = @{$DISTANCE_SYMBOLS->[$i]};
  10250         12640  
3077              
3078 10250         10954 push @symbols, $i;
3079              
3080 10250 100       13422 if ($bits > 0) {
3081 9692         15720 $offset_bits .= sprintf('%0*b', $bits, $dist - $min);
3082             }
3083             }
3084              
3085 254         808 fibonacci_encode([$max_dist]) . $entropy_sub->(\@symbols) . pack('B*', $offset_bits);
3086             }
3087              
3088 306     306 1 404 sub obh_decode ($fh, $entropy_sub = \&decode_huffman_entry) {
  306         426  
  306         456  
  306         375  
3089              
3090 306 100       698 if (ref($fh) eq '') {
3091 52 50       647 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
3092 52         110 return __SUB__->($fh2, $entropy_sub);
3093             }
3094              
3095 254         605 my $max_dist = fibonacci_decode($fh)->[0];
3096 254         617 my ($DISTANCE_SYMBOLS) = make_deflate_tables($max_dist, 0);
3097              
3098 254         742 my $symbols = $entropy_sub->($fh);
3099 254         366 my $bits_len = 0;
3100              
3101 254         435 foreach my $i (@$symbols) {
3102 10250         11962 $bits_len += $DISTANCE_SYMBOLS->[$i][1];
3103             }
3104              
3105 254         471 my $bits = read_bits($fh, $bits_len);
3106              
3107 254         456 my @distances;
3108 254         416 foreach my $i (@$symbols) {
3109 10250         23566 push @distances, $DISTANCE_SYMBOLS->[$i][0] + oct('0b' . substr($bits, 0, $DISTANCE_SYMBOLS->[$i][1], ''));
3110             }
3111              
3112 254         1824 return \@distances;
3113             }
3114              
3115             #################
3116             # LZW Compression
3117             #################
3118              
3119 25     25 1 48 sub lzw_encode ($uncompressed) {
  25         44  
  25         46  
3120              
3121             # Build the dictionary
3122 25         38 my $dict_size = 256;
3123 25         42 my %dictionary;
3124              
3125 25         120 foreach my $i (0 .. $dict_size - 1) {
3126 6400         11148 $dictionary{chr($i)} = $i;
3127             }
3128              
3129 25         52 my $w = '';
3130 25         43 my @result;
3131              
3132 25         14896 foreach my $c (split(//, $uncompressed)) {
3133 36992         57228 my $wc = $w . $c;
3134 36992 100       52807 if (exists $dictionary{$wc}) {
3135 28074         45727 $w = $wc;
3136             }
3137             else {
3138 8918         12735 push @result, $dictionary{$w};
3139              
3140             # Add wc to the dictionary
3141 8918         16176 $dictionary{$wc} = $dict_size++;
3142 8918         14773 $w = $c;
3143             }
3144             }
3145              
3146             # Output the code for w
3147 25 100       7222 if ($w ne '') {
3148 24         55 push @result, $dictionary{$w};
3149             }
3150              
3151 25         2460 return \@result;
3152             }
3153              
3154 25     25 1 40 sub lzw_decode ($compressed) {
  25         40  
  25         41  
3155              
3156 25 100       86 @$compressed || return '';
3157              
3158             # Build the dictionary
3159 24         41 my $dict_size = 256;
3160 24         211 my @dictionary = map { chr($_) } 0 .. $dict_size - 1;
  6144         7997  
3161              
3162 24         203 my $w = $dictionary[$compressed->[0]];
3163 24         44 my $result = $w;
3164              
3165 24         79 foreach my $j (1 .. $#$compressed) {
3166 8918         11320 my $k = $compressed->[$j];
3167              
3168 8918 50       14708 my $entry =
    100          
3169             ($k < $dict_size) ? $dictionary[$k]
3170             : ($k == $dict_size) ? ($w . substr($w, 0, 1))
3171             : confess "Bad compressed k: $k";
3172              
3173 8918         10652 $result .= $entry;
3174              
3175             # Add w+entry[0] to the dictionary
3176 8918         16431 push @dictionary, $w . substr($entry, 0, 1);
3177 8918         9054 ++$dict_size;
3178 8918         14043 $w = $entry;
3179             }
3180              
3181 24         4570 return $result;
3182             }
3183              
3184 25     25 1 702063 sub lzw_compress ($chunk, $enc_method = \&abc_encode) {
  25         79  
  25         84  
  25         44  
3185 25         102 $enc_method->(lzw_encode($chunk));
3186             }
3187              
3188 50     50 1 120 sub lzw_decompress ($fh, $dec_method = \&abc_decode) {
  50         68  
  50         76  
  50         59  
3189              
3190 50 100       136 if (ref($fh) eq '') {
3191 25 50       335 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
3192 25         110 return __SUB__->($fh2, $dec_method);
3193             }
3194              
3195 25         88 lzw_decode($dec_method->($fh));
3196             }
3197              
3198             ###################################
3199             # CRC-32 Pure Perl implementation
3200             ###################################
3201              
3202             sub _create_crc32_table {
3203 4     4   5 my @table;
3204 4         12 for my $i (0 .. 255) {
3205 1024         947 my $k = $i;
3206 1024         1141 for (0 .. 7) {
3207 8192 100       9188 if ($k & 1) {
3208 4096         3784 $k >>= 1;
3209 4096         4716 $k ^= 0xedb88320;
3210             }
3211             else {
3212 4096         4560 $k >>= 1;
3213             }
3214             }
3215 1024         1258 push(@table, $k & 0xffffffff);
3216             }
3217 4         15 return \@table;
3218             }
3219              
3220 119     119 1 2688 sub crc32($str, $crc = 0) {
  119         220  
  119         248  
  119         151  
3221 119         186 state $crc_table = _create_crc32_table();
3222 119         210 $crc &= 0xffffffff;
3223 119         190 $crc ^= 0xffffffff;
3224 119         41769 foreach my $c (unpack("C*", $str)) {
3225 212008         365509 $crc = (($crc >> 8) ^ $crc_table->[($crc & 0xff) ^ $c]);
3226             }
3227 119         18401 return (($crc & 0xffffffff) ^ 0xffffffff);
3228             }
3229              
3230 57     57 1 627 sub adler32($str, $adler = 1) {
  57         118  
  57         113  
  57         70  
3231              
3232             # Reference:
3233             # https://datatracker.ietf.org/doc/html/rfc1950#section-9
3234              
3235 57         88 my $s1 = $adler & 0xffff;
3236 57         102 my $s2 = ($adler >> 16) & 0xffff;
3237              
3238 57         817 foreach my $c (unpack('C*', $str)) {
3239 5186         6478 $s1 = ($s1 + $c) % 65521;
3240 5186         7606 $s2 = ($s2 + $s1) % 65521;
3241             }
3242 57         433 return (($s2 << 16) + $s1);
3243             }
3244              
3245             #############################
3246             # Bzip2 compression
3247             #############################
3248              
3249 12     12   29 sub _bzip2_encode_code_lengths($dict) {
  12         15  
  12         13  
3250 12         17 my @lengths;
3251              
3252 12   50     204 foreach my $symbol (0 .. max(keys %$dict) // 0) {
3253 247 50       315 if (exists($dict->{$symbol})) {
3254 247         344 push @lengths, length($dict->{$symbol});
3255             }
3256             else {
3257 0         0 confess "Incomplete Huffman tree not supported";
3258 0         0 push @lengths, 0;
3259             }
3260             }
3261              
3262 12         39 my $deltas = deltas(\@lengths);
3263              
3264 12 50       31 $VERBOSE && say STDERR "Code lengths: (@lengths)";
3265 12 50       25 $VERBOSE && say STDERR "Code lengths deltas: (@$deltas)";
3266              
3267 12         29 my $bitstring = int2bits(shift(@$deltas), 5) . '0';
3268              
3269 12         29 foreach my $d (@$deltas) {
3270 235 100       381 $bitstring .= (($d > 0) ? ('10' x $d) : ('11' x abs($d))) . '0';
3271             }
3272              
3273 12 50       39 $VERBOSE && say STDERR "Deltas bitstring: $bitstring";
3274              
3275 12         55 return $bitstring;
3276             }
3277              
3278 26     26 1 134621 sub bzip2_compress($fh) {
  26         56  
  26         45  
3279              
3280 26 100       117 if (ref($fh) eq '') {
3281 13 50       214 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
3282 13         57 return __SUB__->($fh2);
3283             }
3284              
3285 13         53 my $level = 9;
3286              
3287             # There is a CRC32 issue on some non-compressible inputs, when using very large chunk sizes
3288             ## my $CHUNK_SIZE = 100_000 * $level;
3289 13         26 my $CHUNK_SIZE = 1 << 17;
3290              
3291 13         53 my $compressed = "BZh" . $level;
3292              
3293 13         28 state $block_header_bitstring = unpack("B48", "1AY&SY");
3294 13         24 state $block_footer_bitstring = unpack("B48", "\27rE8P\x90");
3295              
3296 13         22 my $bitstring = '';
3297 13         28 my $stream_crc32 = 0;
3298              
3299 13         141 while (read($fh, (my $chunk), $CHUNK_SIZE)) {
3300              
3301 12         27 $bitstring .= $block_header_bitstring;
3302              
3303 12         1380 my $crc32 = crc32(pack('b*', unpack('B*', $chunk)));
3304 12 50       614 $VERBOSE && say STDERR "CRC32: $crc32";
3305              
3306 12         54 $crc32 = oct('0b' . int2bits_lsb($crc32, 32));
3307 12 50       34 $VERBOSE && say STDERR "Bzip2-CRC32: $crc32";
3308              
3309 12         50 $stream_crc32 = ($crc32 ^ (0xffffffff & ((0xffffffff & ($stream_crc32 << 1)) | (($stream_crc32 >> 31) & 0x1)))) & 0xffffffff;
3310              
3311 12         38 $bitstring .= int2bits($crc32, 32);
3312 12         40 $bitstring .= '0'; # not randomized
3313              
3314 12         48 my $rle4 = rle4_encode($chunk);
3315 12         41 my ($bwt, $bwt_idx) = bwt_encode(symbols2string($rle4));
3316              
3317 12         35 $bitstring .= int2bits($bwt_idx, 24);
3318              
3319 12         47 my ($mtf, $alphabet) = mtf_encode($bwt);
3320 12 50       40 $VERBOSE && say STDERR "Alphabet: (@$alphabet)";
3321              
3322 12         35 $bitstring .= unpack('B*', encode_alphabet_256($alphabet));
3323              
3324 12         27 my @zrle = reverse @{zrle_encode([reverse @$mtf])};
  12         932  
3325              
3326 12         1395 my $eob = scalar(@$alphabet) + 1; # end-of-block symbol
3327 12 50       32 $VERBOSE && say STDERR "EOB symbol: $eob";
3328 12         20 push @zrle, $eob;
3329              
3330 12         1113 my ($dict) = huffman_from_symbols([@zrle, 0 .. $eob - 1]);
3331 12         1743 my $num_sels = int(sprintf('%.0f', 0.5 + (scalar(@zrle) / 50))); # ceil(|zrle| / 50)
3332 12 50       72 $VERBOSE && say STDERR "Number of selectors: $num_sels";
3333              
3334 12         35 $bitstring .= int2bits(2, 3);
3335 12         34 $bitstring .= int2bits($num_sels, 15);
3336 12         165 $bitstring .= '0' x $num_sels;
3337              
3338 12         56 $bitstring .= _bzip2_encode_code_lengths($dict) x 2;
3339 12         73 $bitstring .= join('', @{$dict}{@zrle});
  12         1675  
3340              
3341 12         5720 $compressed .= pack('B*', substr($bitstring, 0, length($bitstring) - (length($bitstring) % 8), ''));
3342             }
3343              
3344 13         36 $bitstring .= $block_footer_bitstring;
3345 13         32 $bitstring .= int2bits($stream_crc32, 32);
3346 13         257 $compressed .= pack('B*', $bitstring);
3347              
3348 13         132 return $compressed;
3349             }
3350              
3351             #################################
3352             # Bzip2 decompression
3353             #################################
3354              
3355 46     46 1 648 sub bzip2_decompress($fh) {
  46         66  
  46         62  
3356              
3357 46 100       121 if (ref($fh) eq '') {
3358 23 50       265 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
3359 23         115 return __SUB__->($fh2);
3360             }
3361              
3362 23         35 state $MaxHuffmanBits = 20;
3363 23         43 my $decompressed = '';
3364              
3365 23         71 while (!eof($fh)) {
3366              
3367 26         46 my $buffer = '';
3368              
3369 26 50 33     52 (bytes2int($fh, 2) == 0x425a and getc($fh) eq 'h')
3370             or confess "Not a valid Bzip2 archive";
3371              
3372 26         70 my $level = getc($fh);
3373              
3374 26 50       136 if ($level !~ /^[1-9]\z/) {
3375 0         0 confess "Invalid level: $level";
3376             }
3377              
3378 26 50       54 $VERBOSE && say STDERR "Compression level: $level";
3379              
3380 26         45 my $stream_crc32 = 0;
3381              
3382 26         63 while (!eof($fh)) {
3383              
3384 50         152 my $block_magic = pack "B48", join('', map { read_bit($fh, \$buffer) } 1 .. 48);
  2400         2754  
3385              
3386 50 100       233 if ($block_magic eq "1AY&SY") { # BlockHeader
    50          
3387 24 50       64 $VERBOSE && say STDERR "Block header detected";
3388              
3389 24         68 my $crc32 = bits2int($fh, 32, \$buffer);
3390 24 50       61 $VERBOSE && say STDERR "CRC32 = $crc32";
3391              
3392 24         80 $stream_crc32 = ($crc32 ^ (0xffffffff & ((0xffffffff & ($stream_crc32 << 1)) | (($stream_crc32 >> 31) & 0x1)))) & 0xffffffff;
3393              
3394 24         48 my $randomized = read_bit($fh, \$buffer);
3395 24 50       67 $randomized == 0 or confess "randomized not supported";
3396              
3397 24         35 my $bwt_idx = bits2int($fh, 24, \$buffer);
3398 24 50       47 $VERBOSE && say STDERR "BWT index: $bwt_idx";
3399              
3400 24         29 my @alphabet;
3401 24         33 my $l1 = bits2int($fh, 16, \$buffer);
3402 24         46 for my $i (0 .. 15) {
3403 384 100       602 if ($l1 & (0x8000 >> $i)) {
3404 78         89 my $l2 = bits2int($fh, 16, \$buffer);
3405 78         114 for my $j (0 .. 15) {
3406 1248 100       1723 if ($l2 & (0x8000 >> $j)) {
3407 300         393 push @alphabet, 16 * $i + $j;
3408             }
3409             }
3410             }
3411             }
3412              
3413 24 50       47 $VERBOSE && say STDERR "MTF alphabet: (@alphabet)";
3414              
3415 24         33 my $num_trees = bits2int($fh, 3, \$buffer);
3416 24 50       67 $VERBOSE && say STDERR "Number or trees: $num_trees";
3417              
3418 24         38 my $num_sels = bits2int($fh, 15, \$buffer);
3419 24 50       42 $VERBOSE && say STDERR "Number of selectors: $num_sels";
3420              
3421 24         30 my @idxs;
3422 24         53 for (1 .. $num_sels) {
3423 239         248 my $i = 0;
3424 239         259 while (read_bit($fh, \$buffer)) {
3425 2         4 $i += 1;
3426 2 50       5 ($i < $num_trees) or confess "error";
3427             }
3428 239         338 push @idxs, $i;
3429             }
3430              
3431 24         90 my $sels = mtf_decode(\@idxs, [0 .. $num_trees - 1]);
3432 24 50       56 $VERBOSE && say STDERR "Selectors: (@$sels)";
3433              
3434 24         41 my $num_syms = scalar(@alphabet) + 2;
3435              
3436 24         37 my @trees;
3437 24         41 for (1 .. $num_trees) {
3438 48         55 my @clens;
3439 48         65 my $clen = bits2int($fh, 5, \$buffer);
3440 48         65 for (1 .. $num_syms) {
3441 696         650 while (1) {
3442              
3443 1125 50 33     2194 ($clen > 0 and $clen <= $MaxHuffmanBits) or confess "invalid code length: $clen";
3444              
3445 1125 100       1258 if (not read_bit($fh, \$buffer)) {
3446 696         763 last;
3447             }
3448              
3449 429 100       535 $clen -= read_bit($fh, \$buffer) ? 1 : -1;
3450             }
3451              
3452 696         1015 push @clens, $clen;
3453             }
3454 48         65 push @trees, \@clens;
3455 48 50       99 $VERBOSE && say STDERR "Code lengths: (@clens)";
3456             }
3457              
3458 24         35 foreach my $tree (@trees) {
3459 48         111 my $maxLen = max(@$tree);
3460 48         58 my $sum = 1 << $maxLen;
3461 48         62 for my $clen (@$tree) {
3462 696         812 $sum -= (1 << $maxLen) >> $clen;
3463             }
3464 48 50       116 $sum == 0 or confess "incomplete tree not supported: (@$tree)";
3465             }
3466              
3467 24         45 my @huffman_trees = map { (huffman_from_code_lengths($_))[1] } @trees;
  48         108  
3468              
3469 24         41 my $eob = @alphabet + 1;
3470              
3471 24         38 my @zrle;
3472 24         37 my $code = '';
3473              
3474 24         31 my $sel_idx = 0;
3475 24         46 my $tree = $huffman_trees[$sels->[$sel_idx]];
3476 24         33 my $decoded = 50;
3477              
3478 24         76 while (!eof($fh)) {
3479 42311         49089 $code .= read_bit($fh, \$buffer);
3480              
3481 42311 50       56913 if (length($code) > $MaxHuffmanBits) {
3482 0         0 confess "[!] Something went wrong: length of code `$code` is > $MaxHuffmanBits.";
3483             }
3484              
3485 42311 100       80827 if (exists($tree->{$code})) {
3486              
3487 11076         13234 my $sym = $tree->{$code};
3488              
3489 11076 100       14431 if ($sym == $eob) { # end of block marker
3490 24 50       41 $VERBOSE && say STDERR "EOB detected: $sym";
3491 24         57 last;
3492             }
3493              
3494 11052         11797 push @zrle, $sym;
3495 11052         10285 $code = '';
3496              
3497 11052 100       24592 if (--$decoded <= 0) {
3498 215 50       454 if (++$sel_idx <= $#$sels) {
3499 215         373 $tree = $huffman_trees[$sels->[$sel_idx]];
3500             }
3501             else {
3502 0         0 confess "No more selectors"; # should not happen
3503             }
3504 215         489 $decoded = 50;
3505             }
3506             }
3507             }
3508              
3509 24         34 my @mtf = reverse @{zrle_decode([reverse @zrle])};
  24         2100  
3510 24         1357 my $bwt = symbols2string mtf_decode(\@mtf, \@alphabet);
3511              
3512 24         395 my $rle4 = string2symbols bwt_decode($bwt, $bwt_idx);
3513 24         70 my $data = rle4_decode($rle4);
3514 24         49 my $dec = symbols2string($data);
3515              
3516 24         1490 my $new_crc32 = oct('0b' . int2bits_lsb(crc32(pack('b*', unpack('B*', $dec))), 32));
3517              
3518 24 50       72 $VERBOSE && say STDERR "Computed CRC32: $new_crc32";
3519              
3520 24 50       73 if ($crc32 != $new_crc32) {
3521 0         0 confess "CRC32 error: $crc32 (stored) != $new_crc32 (actual)";
3522             }
3523              
3524 24         4842 $decompressed .= $dec;
3525             }
3526             elsif ($block_magic eq "\27rE8P\x90") { # BlockFooter
3527 26 50       47 $VERBOSE && say STDERR "Block footer detected";
3528 26         56 my $stored_stream_crc32 = bits2int($fh, 32, \$buffer);
3529 26 50       63 $VERBOSE && say STDERR "Stream CRC: $stored_stream_crc32";
3530              
3531 26 50       63 if ($stored_stream_crc32 != $stream_crc32) {
3532 0         0 confess "Stream CRC32 error: $stored_stream_crc32 (stored) != $stream_crc32 (actual)";
3533             }
3534              
3535 26         45 $buffer = '';
3536 26         63 last;
3537             }
3538             else {
3539 0         0 confess "Unknown block magic: $block_magic";
3540             }
3541             }
3542              
3543 26 50       112 $VERBOSE && say STDERR "End of container";
3544             }
3545              
3546 23         240 return $decompressed;
3547             }
3548              
3549             ########################################
3550             # GZIP compressor
3551             ########################################
3552              
3553 108     108   128 sub _code_length_encoding ($dict) {
  108         124  
  108         146  
3554              
3555 108         138 my @lengths;
3556              
3557 108   100     1116 foreach my $symbol (0 .. max(keys %$dict) // 0) {
3558 14760 100       17961 if (exists($dict->{$symbol})) {
3559 1057         1498 push @lengths, length($dict->{$symbol});
3560             }
3561             else {
3562 13703         15885 push @lengths, 0;
3563             }
3564             }
3565              
3566 108         194 my $size = scalar(@lengths);
3567 108         302 my $rl = run_length(\@lengths);
3568 108         226 my $offset_bits = '';
3569              
3570 108         138 my @CL_symbols;
3571              
3572 108         172 foreach my $pair (@$rl) {
3573 1230         1576 my ($v, $run) = @$pair;
3574              
3575 1230   100     2111 while ($v == 0 and $run >= 3) {
3576              
3577 251 100       439 if ($run >= 11) {
3578 171         239 push @CL_symbols, 18;
3579 171         194 $run -= 11;
3580 171         348 $offset_bits .= int2bits_lsb(min($run, 127), 7);
3581 171         309 $run -= 127;
3582             }
3583              
3584 251 100 100     774 if ($run >= 3 and $run < 11) {
3585 82         120 push @CL_symbols, 17;
3586 82         115 $run -= 3;
3587 82         175 $offset_bits .= int2bits_lsb(min($run, 7), 3);
3588 82         264 $run -= 7;
3589             }
3590             }
3591              
3592 1230 100       1586 if ($v == 0) {
3593 396 100       636 push(@CL_symbols, (0) x $run) if ($run > 0);
3594 396         571 next;
3595             }
3596              
3597 834         961 push @CL_symbols, $v;
3598 834         808 $run -= 1;
3599              
3600 834         1114 while ($run >= 3) {
3601 16         18 push @CL_symbols, 16;
3602 16         20 $run -= 3;
3603 16         28 $offset_bits .= int2bits_lsb(min($run, 3), 2);
3604 16         31 $run -= 3;
3605             }
3606              
3607 834 100       1526 push(@CL_symbols, ($v) x $run) if ($run > 0);
3608             }
3609              
3610 108         767 return (\@CL_symbols, $size, $offset_bits);
3611             }
3612              
3613 108     108   131 sub _cl_encoded_bitstring ($cl_dict, $cl_symbols, $offset_bits) {
  108         126  
  108         127  
  108         144  
  108         114  
3614              
3615 108         136 my $bitstring = '';
3616 108         183 foreach my $cl_symbol (@$cl_symbols) {
3617 1496         2024 $bitstring .= $cl_dict->{$cl_symbol};
3618 1496 100       2874 if ($cl_symbol == 16) {
    100          
    100          
3619 16         26 $bitstring .= substr($offset_bits, 0, 2, '');
3620             }
3621             elsif ($cl_symbol == 17) {
3622 82         176 $bitstring .= substr($offset_bits, 0, 3, '');
3623             }
3624             elsif ($cl_symbol == 18) {
3625 171         308 $bitstring .= substr($offset_bits, 0, 7, '');
3626             }
3627             }
3628              
3629 108         244 return $bitstring;
3630             }
3631              
3632 54     54   81 sub _create_cl_dictionary (@cl_symbols) {
  54         182  
  54         70  
3633              
3634 54         64 my @keys;
3635 54         127 my $freq = frequencies(\@cl_symbols);
3636              
3637 54         83 while (1) {
3638 54         104 my ($cl_dict) = huffman_from_freq($freq);
3639              
3640             # The CL codes must have at most 7 bits
3641 54 50   358   477 return $cl_dict if all { length($_) <= 7 } values %$cl_dict;
  358         811  
3642              
3643 0 0       0 if (scalar(@keys) == 0) {
3644 0         0 @keys = sort { $freq->{$b} <=> $freq->{$a} } keys %$freq;
  0         0  
3645             }
3646              
3647             # Scale down the frequencies and try again
3648 0         0 foreach my $k (@keys) {
3649 0 0       0 if ($freq->{$k} > 1) {
3650 0         0 $freq->{$k} >>= 1;
3651             }
3652             else {
3653 0         0 last;
3654             }
3655             }
3656             }
3657             }
3658              
3659 54     54 1 95 sub deflate_create_block_type_2 ($literals, $distances, $lengths) {
  54         87  
  54         88  
  54         95  
  54         65  
3660              
3661 54 50       117 local $LZ_MIN_LEN = 4 if ($LZ_MIN_LEN < 4); # minimum match length in LZ parsing
3662 54         79 local $LZ_MAX_LEN = 258; # maximum match length in LZ parsing
3663 54         81 local $LZ_MAX_DIST = (1 << 15) - 1; # maximum allowed back-reference distance in LZ parsing
3664              
3665 54         144 state $deflate_tables = [make_deflate_tables()];
3666 54         115 my ($DISTANCE_SYMBOLS, $LENGTH_SYMBOLS, $LENGTH_INDICES) = @$deflate_tables;
3667              
3668 54         165 my @CL_order = (16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15);
3669              
3670 54         88 my $bitstring = '01';
3671              
3672 54         124 my @len_symbols;
3673             my @dist_symbols;
3674 54         79 my $offset_bits = '';
3675              
3676 54         125 foreach my $k (0 .. $#$literals) {
3677              
3678 10267 100       17529 if ($lengths->[$k] == 0) {
3679 6527         12570 push @len_symbols, $literals->[$k];
3680 6527         8909 next;
3681             }
3682              
3683 3740         4439 my $len = $lengths->[$k];
3684 3740         5023 my $dist = $distances->[$k];
3685              
3686             {
3687 3740         4438 my $len_idx = $LENGTH_INDICES->[$len];
3688 3740         4423 my ($min, $bits) = @{$LENGTH_SYMBOLS->[$len_idx]};
  3740         6011  
3689              
3690 3740         7545 push @len_symbols, [$len_idx + 256 - 1, $bits];
3691 3740 100       7057 $offset_bits .= int2bits_lsb($len - $min, $bits) if ($bits > 0);
3692             }
3693              
3694             {
3695 3740         3990 my $dist_idx = find_deflate_index($dist, $DISTANCE_SYMBOLS);
  3740         4791  
  3740         5295  
3696 3740         4644 my ($min, $bits) = @{$DISTANCE_SYMBOLS->[$dist_idx]};
  3740         5798  
3697              
3698 3740         7233 push @dist_symbols, [$dist_idx - 1, $bits];
3699 3740 100       7821 $offset_bits .= int2bits_lsb($dist - $min, $bits) if ($bits > 0);
3700             }
3701             }
3702              
3703 54         99 push @len_symbols, 256; # end-of-block marker
3704              
3705 54 100       182 my ($dict) = huffman_from_symbols([map { ref($_) eq 'ARRAY' ? $_->[0] : $_ } @len_symbols]);
  10321         22430  
3706 54         3019 my ($dist_dict) = huffman_from_symbols([map { $_->[0] } @dist_symbols]);
  3740         5719  
3707              
3708 54         625 my ($LL_code_lengths, $LL_cl_len, $LL_offset_bits) = _code_length_encoding($dict);
3709 54         137 my ($distance_code_lengths, $distance_cl_len, $distance_offset_bits) = _code_length_encoding($dist_dict);
3710              
3711 54         275 my $cl_dict = _create_cl_dictionary(@$LL_code_lengths, @$distance_code_lengths);
3712              
3713 54         88 my @CL_code_lenghts;
3714 54         115 foreach my $symbol (0 .. 18) {
3715 1026 100       1336 if (exists($cl_dict->{$symbol})) {
3716 358         517 push @CL_code_lenghts, length($cl_dict->{$symbol});
3717             }
3718             else {
3719 668         849 push @CL_code_lenghts, 0;
3720             }
3721             }
3722              
3723             # Put the CL codes in the required order
3724 54         185 @CL_code_lenghts = @CL_code_lenghts[@CL_order];
3725              
3726 54   66     234 while (scalar(@CL_code_lenghts) > 4 and $CL_code_lenghts[-1] == 0) {
3727 84         215 pop @CL_code_lenghts;
3728             }
3729              
3730 54         104 my $CL_code_lengths_bitstring = join('', map { int2bits_lsb($_, 3) } @CL_code_lenghts);
  942         1217  
3731              
3732 54         176 my $LL_code_lengths_bitstring = _cl_encoded_bitstring($cl_dict, $LL_code_lengths, $LL_offset_bits);
3733 54         116 my $distance_code_lengths_bitstring = _cl_encoded_bitstring($cl_dict, $distance_code_lengths, $distance_offset_bits);
3734              
3735             # (5 bits) HLIT = (number of LL code entries present) - 257
3736 54         93 my $HLIT = $LL_cl_len - 257;
3737              
3738             # (5 bits) HDIST = (number of distance code entries present) - 1
3739 54         81 my $HDIST = $distance_cl_len - 1;
3740              
3741             # (4 bits) HCLEN = (number of CL code entries present) - 4
3742 54         85 my $HCLEN = scalar(@CL_code_lenghts) - 4;
3743              
3744 54         96 $bitstring .= int2bits_lsb($HLIT, 5);
3745 54         93 $bitstring .= int2bits_lsb($HDIST, 5);
3746 54         99 $bitstring .= int2bits_lsb($HCLEN, 4);
3747              
3748 54         105 $bitstring .= $CL_code_lengths_bitstring;
3749 54         86 $bitstring .= $LL_code_lengths_bitstring;
3750 54         96 $bitstring .= $distance_code_lengths_bitstring;
3751              
3752 54         104 foreach my $symbol (@len_symbols) {
3753 10321 100       13841 if (ref($symbol) eq 'ARRAY') {
3754              
3755 3740         5407 my ($len, $len_offset) = @$symbol;
3756 3740         4829 $bitstring .= $dict->{$len};
3757 3740 100       5548 $bitstring .= substr($offset_bits, 0, $len_offset, '') if ($len_offset > 0);
3758              
3759 3740         3942 my ($dist, $dist_offset) = @{shift(@dist_symbols)};
  3740         5634  
3760 3740         5303 $bitstring .= $dist_dict->{$dist};
3761 3740 100       8887 $bitstring .= substr($offset_bits, 0, $dist_offset, '') if ($dist_offset > 0);
3762             }
3763             else {
3764 6581         10841 $bitstring .= $dict->{$symbol};
3765             }
3766             }
3767              
3768 54         3159 return $bitstring;
3769             }
3770              
3771 54     54 1 117 sub deflate_create_block_type_1 ($literals, $distances, $lengths) {
  54         74  
  54         65  
  54         53  
  54         65  
3772              
3773 54 50       116 local $LZ_MIN_LEN = 4 if ($LZ_MIN_LEN < 4); # minimum match length in LZ parsing
3774 54         72 local $LZ_MAX_LEN = 258; # maximum match length in LZ parsing
3775 54         62 local $LZ_MAX_DIST = (1 << 15) - 1; # maximum allowed back-reference distance in LZ parsing
3776              
3777 54         102 state $deflate_tables = [make_deflate_tables()];
3778 54         165 my ($DISTANCE_SYMBOLS, $LENGTH_SYMBOLS, $LENGTH_INDICES) = @$deflate_tables;
3779              
3780 54         79 state $dict;
3781 54         74 state $dist_dict;
3782              
3783 54 100       142 if (!defined($dict)) {
3784              
3785 4         57 my @code_lengths = (0) x 288;
3786 4         11 foreach my $i (0 .. 143) {
3787 576         612 $code_lengths[$i] = 8;
3788             }
3789 4         8 foreach my $i (144 .. 255) {
3790 448         480 $code_lengths[$i] = 9;
3791             }
3792 4         9 foreach my $i (256 .. 279) {
3793 96         103 $code_lengths[$i] = 7;
3794             }
3795 4         10 foreach my $i (280 .. 287) {
3796 32         34 $code_lengths[$i] = 8;
3797             }
3798              
3799 4         16 ($dict) = huffman_from_code_lengths(\@code_lengths);
3800 4         163 ($dist_dict) = huffman_from_code_lengths([(5) x 32]);
3801             }
3802              
3803 54         127 my $bitstring = '10';
3804              
3805 54         175 foreach my $k (0 .. $#$literals) {
3806              
3807 10267 100       17278 if ($lengths->[$k] == 0) {
3808 6527         13603 $bitstring .= $dict->{$literals->[$k]};
3809 6527         9143 next;
3810             }
3811              
3812 3740         4401 my $len = $lengths->[$k];
3813 3740         4692 my $dist = $distances->[$k];
3814              
3815             {
3816 3740         4213 my $len_idx = $LENGTH_INDICES->[$len];
3817 3740         4062 my ($min, $bits) = @{$LENGTH_SYMBOLS->[$len_idx]};
  3740         6089  
3818              
3819 3740         7034 $bitstring .= $dict->{$len_idx + 256 - 1};
3820 3740 100       7356 $bitstring .= int2bits_lsb($len - $min, $bits) if ($bits > 0);
3821             }
3822              
3823             {
3824 3740         3889 my $dist_idx = find_deflate_index($dist, $DISTANCE_SYMBOLS);
  3740         4800  
  3740         5209  
3825 3740         4213 my ($min, $bits) = @{$DISTANCE_SYMBOLS->[$dist_idx]};
  3740         5628  
3826              
3827 3740         7010 $bitstring .= $dist_dict->{$dist_idx - 1};
3828 3740 100       8287 $bitstring .= int2bits_lsb($dist - $min, $bits) if ($bits > 0);
3829             }
3830             }
3831              
3832 54         138 $bitstring .= $dict->{256}; # end-of-block symbol
3833              
3834 54         1058 return $bitstring;
3835             }
3836              
3837 2     2 1 2751 sub deflate_create_block_type_0_header($chunk) {
  2         5  
  2         3  
3838              
3839 2         4 my $chunk_len = length($chunk);
3840 2         7 my $len = int2bits_lsb($chunk_len, 16);
3841 2         7 my $nlen = int2bits_lsb((~$chunk_len) & 0xffff, 16);
3842              
3843 2         30 $len . $nlen;
3844             }
3845              
3846 56     56 1 272298 sub gzip_compress ($in_fh, $lzss_encoding_sub = \&lzss_encode) {
  56         88  
  56         116  
  56         83  
3847              
3848 56 100       170 if (ref($in_fh) eq '') {
3849 28 50       381 open(my $fh2, '<:raw', \$in_fh) or confess "error: $!";
3850 28         105 return __SUB__->($fh2, $lzss_encoding_sub);
3851             }
3852              
3853 28         60 my $compressed = '';
3854              
3855 28         166 open my $out_fh, '>:raw', \$compressed;
3856              
3857 28 50       85 local $LZ_MIN_LEN = 4 if ($LZ_MIN_LEN < 4); # minimum match length in LZ parsing
3858 28         57 local $LZ_MAX_LEN = 258; # maximum match length in LZ parsing
3859 28         60 local $LZ_MAX_DIST = (1 << 15) - 1; # maximum allowed back-reference distance in LZ parsing
3860              
3861 28         45 state $MAGIC = pack('C*', 0x1f, 0x8b); # magic MIME type
3862 28         44 state $CM = chr(0x08); # 0x08 = DEFLATE
3863 28         47 state $FLAGS = chr(0x00); # flags
3864 28         50 state $MTIME = pack('C*', (0x00) x 4); # modification time
3865 28         52 state $XFLAGS = chr(0x00); # extra flags
3866 28         37 state $OS = chr(0x03); # 0x03 = Unix
3867              
3868 28         126 print $out_fh $MAGIC, $CM, $FLAGS, $MTIME, $XFLAGS, $OS;
3869              
3870 28         49 my $total_length = 0;
3871 28         48 my $crc32 = 0;
3872              
3873 28         49 my $bitstring = '';
3874              
3875 28 100       82 if (eof($in_fh)) { # empty file
3876 2         3 $bitstring = '1' . '10' . '0000000';
3877             }
3878              
3879 28         44 state $CHUNK_SIZE = (1 << 15) - 1;
3880              
3881 28         169 while (read($in_fh, (my $chunk), $CHUNK_SIZE)) {
3882              
3883 28         97 $crc32 = crc32($chunk, $crc32);
3884 28         95 $total_length += length($chunk);
3885 28 100       140 $bitstring .= eof($in_fh) ? '1' : '0';
3886              
3887 28         114 my ($literals, $distances, $lengths) = $lzss_encoding_sub->($chunk);
3888              
3889 28         142 my $bt1_bitstring = deflate_create_block_type_1($literals, $distances, $lengths);
3890              
3891             # When block type 1 is larger than the input, then we have random uncompressible data: use block type 0
3892 28 50       204 if ((length($bt1_bitstring) >> 3) > length($chunk) + 5) {
3893              
3894 0 0       0 $VERBOSE && say STDERR ":: Using block type: 0";
3895              
3896 0         0 $bitstring .= '00';
3897              
3898 0         0 print $out_fh pack('b*', $bitstring); # pads to a byte
3899 0         0 print $out_fh pack('b*', deflate_create_block_type_0_header($chunk));
3900 0         0 print $out_fh $chunk;
3901              
3902 0         0 $bitstring = '';
3903 0         0 next;
3904             }
3905              
3906 28         124 my $bt2_bitstring = deflate_create_block_type_2($literals, $distances, $lengths);
3907              
3908             # When block type 2 is larger than block type 1, then we may have very small data
3909 28 100       142 if (length($bt2_bitstring) > length($bt1_bitstring)) {
3910 22 50       50 $VERBOSE && say STDERR ":: Using block type: 1";
3911 22         54 $bitstring .= $bt1_bitstring;
3912             }
3913             else {
3914 6 50       23 $VERBOSE && say STDERR ":: Using block type: 2";
3915 6         862 $bitstring .= $bt2_bitstring;
3916             }
3917              
3918 28         3243 print $out_fh pack('b*', substr($bitstring, 0, length($bitstring) - (length($bitstring) % 8), ''));
3919             }
3920              
3921 28 100       99 if ($bitstring ne '') {
3922 26         152 print $out_fh pack('b*', $bitstring);
3923             }
3924              
3925 28         92 print $out_fh int2bytes_lsb($crc32, 4);
3926 28         78 print $out_fh int2bytes_lsb($total_length, 4);
3927              
3928 28         573 return $compressed;
3929             }
3930              
3931             ###################
3932             # GZIP DECOMPRESSOR
3933             ###################
3934              
3935 4     4 1 25 sub deflate_extract_block_type_0 ($in_fh, $buffer, $search_window) {
  4         4  
  4         8  
  4         7  
  4         4  
3936              
3937 4 50       10 local $LZ_MIN_LEN = 4 if ($LZ_MIN_LEN < 4); # minimum match length in LZ parsing
3938 4         7 local $LZ_MAX_LEN = 258; # maximum match length in LZ parsing
3939 4         7 local $LZ_MAX_DIST = 32768; # maximum allowed back-reference distance in LZ parsing
3940              
3941 4         7 $$buffer = '';
3942              
3943 4         11 my $len = bytes2int_lsb($in_fh, 2);
3944 4         8 my $nlen = bytes2int_lsb($in_fh, 2);
3945 4         13 my $expected_nlen = (~$len) & 0xffff;
3946              
3947 4 50       13 if ($expected_nlen != $nlen) {
3948 0         0 confess "[!] The ~length value is not correct: $nlen (actual) != $expected_nlen (expected)";
3949             }
3950             else {
3951 4 50       11 $VERBOSE && print STDERR ":: Chunk length: $len\n";
3952             }
3953              
3954 4   33     19 read($in_fh, (my $chunk), $len) // confess "Read error: $!";
3955 4         10 $$search_window .= $chunk;
3956              
3957 4 50       15 $$search_window = substr($$search_window, -$LZ_MAX_DIST)
3958             if (length($$search_window) > 2 * $LZ_MAX_DIST);
3959              
3960 4         17 return $chunk;
3961             }
3962              
3963 83     83   107 sub _deflate_decode_huffman($in_fh, $buffer, $rev_dict, $dist_rev_dict, $search_window) {
  83         113  
  83         122  
  83         96  
  83         93  
  83         106  
  83         115  
3964              
3965 83         126 state $deflate_tables = [make_deflate_tables()];
3966 83         186 my ($DISTANCE_SYMBOLS, $LENGTH_SYMBOLS, $LENGTH_INDICES) = @$deflate_tables;
3967              
3968 83         131 my $data = '';
3969 83         112 my $code = '';
3970              
3971 83         2520 my $max_ll_code_len = max(map { length($_) } keys %$rev_dict);
  21073         22488  
3972 83         1258 my $max_dist_code_len = max(map { length($_) } keys %$dist_rev_dict);
  2398         2665  
3973              
3974 83         232 while (1) {
3975 64701         76975 $code .= read_bit_lsb($in_fh, $buffer);
3976              
3977 64701 50       92532 if (length($code) > $max_ll_code_len) {
3978 0         0 confess "[!] Something went wrong: length of LL code `$code` is > $max_ll_code_len.";
3979             }
3980              
3981 64701 100       95257 if (exists($rev_dict->{$code})) {
3982              
3983 10617         12141 my $symbol = $rev_dict->{$code};
3984              
3985 10617 100       14234 if ($symbol <= 255) {
    100          
3986 6769         8594 $data .= chr($symbol);
3987 6769         9126 $$search_window .= chr($symbol);
3988             }
3989             elsif ($symbol == 256) { # end-of-block marker
3990 83         116 $code = '';
3991 83         223 last;
3992             }
3993             else { # LZSS decoding
3994 3765         3728 my ($length, $LL_bits) = @{$LENGTH_SYMBOLS->[$symbol - 256 + 1]};
  3765         11306  
3995 3765 100       5892 $length += bits2int_lsb($in_fh, $LL_bits, $buffer) if ($LL_bits > 0);
3996              
3997 3765         4031 my $dist_code = '';
3998              
3999 3765         3542 while (1) {
4000 16809         19779 $dist_code .= read_bit_lsb($in_fh, $buffer);
4001              
4002 16809 50       23562 if (length($dist_code) > $max_dist_code_len) {
4003 0         0 confess "[!] Something went wrong: length of distance code `$dist_code` is > $max_dist_code_len.";
4004             }
4005              
4006 16809 100       24857 if (exists($dist_rev_dict->{$dist_code})) {
4007 3765         4548 last;
4008             }
4009             }
4010              
4011 3765         3790 my ($dist, $dist_bits) = @{$DISTANCE_SYMBOLS->[$dist_rev_dict->{$dist_code} + 1]};
  3765         6055  
4012 3765 100       6323 $dist += bits2int_lsb($in_fh, $dist_bits, $buffer) if ($dist_bits > 0);
4013              
4014 3765 100       7557 if ($dist == 1) {
    100          
4015 35         122 $$search_window .= substr($$search_window, -1) x $length;
4016             }
4017             elsif ($dist >= $length) { # non-overlapping matches
4018 3585         7764 $$search_window .= substr($$search_window, length($$search_window) - $dist, $length);
4019             }
4020             else { # overlapping matches
4021 145         242 foreach my $i (1 .. $length) {
4022 2718         4499 $$search_window .= substr($$search_window, length($$search_window) - $dist, 1);
4023             }
4024             }
4025              
4026 3765         7664 $data .= substr($$search_window, -$length);
4027             }
4028              
4029 10534         14029 $code = '';
4030             }
4031             }
4032              
4033 83 50       189 if ($code ne '') {
4034 0         0 confess "[!] Something went wrong: code `$code` is not empty!";
4035             }
4036              
4037 83 50       188 $$search_window = substr($$search_window, -$LZ_MAX_DIST)
4038             if (length($$search_window) > 2 * $LZ_MAX_DIST);
4039              
4040 83         1237 return $data;
4041             }
4042              
4043 71     71 1 699 sub deflate_extract_block_type_1 ($in_fh, $buffer, $search_window) {
  71         82  
  71         88  
  71         70  
  71         133  
4044              
4045 71 50       121 local $LZ_MIN_LEN = 4 if ($LZ_MIN_LEN < 4); # minimum match length in LZ parsing
4046 71         81 local $LZ_MAX_LEN = 258; # maximum match length in LZ parsing
4047 71         72 local $LZ_MAX_DIST = 32768; # maximum allowed back-reference distance in LZ parsing
4048              
4049 71         78 state $rev_dict;
4050 71         72 state $dist_rev_dict;
4051              
4052 71 100       127 if (!defined($rev_dict)) {
4053              
4054 1         13 my @code_lengths = (0) x 288;
4055 1         2 foreach my $i (0 .. 143) {
4056 144         149 $code_lengths[$i] = 8;
4057             }
4058 1         3 foreach my $i (144 .. 255) {
4059 112         115 $code_lengths[$i] = 9;
4060             }
4061 1         2 foreach my $i (256 .. 279) {
4062 24         26 $code_lengths[$i] = 7;
4063             }
4064 1         2 foreach my $i (280 .. 287) {
4065 8         10 $code_lengths[$i] = 8;
4066             }
4067              
4068 1         3 (undef, $rev_dict) = huffman_from_code_lengths(\@code_lengths);
4069 1         30 (undef, $dist_rev_dict) = huffman_from_code_lengths([(5) x 32]);
4070             }
4071              
4072 71         200 _deflate_decode_huffman($in_fh, $buffer, $rev_dict, $dist_rev_dict, $search_window);
4073             }
4074              
4075 24     24   33 sub _decode_CL_lengths($in_fh, $buffer, $CL_rev_dict, $size) {
  24         30  
  24         27  
  24         27  
  24         30  
  24         24  
4076              
4077 24         32 my @lengths;
4078 24         35 my $code = '';
4079              
4080 24         26 while (1) {
4081 4374         5118 $code .= read_bit_lsb($in_fh, $buffer);
4082              
4083 4374 50       6013 if (length($code) > 7) {
4084 0         0 confess "[!] Something went wrong: length of CL code `$code` is > 7.";
4085             }
4086              
4087 4374 100       6187 if (exists($CL_rev_dict->{$code})) {
4088 1424         1556 my $CL_symbol = $CL_rev_dict->{$code};
4089              
4090 1424 100       1751 if ($CL_symbol <= 15) {
    100          
    100          
    50          
4091 1343         1452 push @lengths, $CL_symbol;
4092             }
4093             elsif ($CL_symbol == 16) {
4094 16         29 push @lengths, ($lengths[-1]) x (3 + bits2int_lsb($in_fh, 2, $buffer));
4095             }
4096             elsif ($CL_symbol == 17) {
4097 42         76 push @lengths, (0) x (3 + bits2int_lsb($in_fh, 3, $buffer));
4098             }
4099             elsif ($CL_symbol == 18) {
4100 23         44 push @lengths, (0) x (11 + bits2int_lsb($in_fh, 7, $buffer));
4101             }
4102             else {
4103 0         0 confess "Unknown CL symbol: $CL_symbol";
4104             }
4105              
4106 1424         1362 $code = '';
4107 1424 100       2316 last if (scalar(@lengths) >= $size);
4108             }
4109             }
4110              
4111 24 50       49 if (scalar(@lengths) != $size) {
4112 0         0 confess "Something went wrong: size $size (expected) != ", scalar(@lengths);
4113             }
4114              
4115 24 50       50 if ($code ne '') {
4116 0         0 confess "Something went wrong: code `$code` is not empty!";
4117             }
4118              
4119 24         476 return @lengths;
4120             }
4121              
4122 12     12 1 728 sub deflate_extract_block_type_2 ($in_fh, $buffer, $search_window) {
  12         17  
  12         19  
  12         12  
  12         20  
4123              
4124 12 50       32 local $LZ_MIN_LEN = 4 if ($LZ_MIN_LEN < 4); # minimum match length in LZ parsing
4125 12         17 local $LZ_MAX_LEN = 258; # maximum match length in LZ parsing
4126 12         22 local $LZ_MAX_DIST = 32768; # maximum allowed back-reference distance in LZ parsing
4127              
4128             # (5 bits) HLIT = (number of LL code entries present) - 257
4129 12         20 my $HLIT = bits2int_lsb($in_fh, 5, $buffer) + 257;
4130              
4131             # (5 bits) HDIST = (number of distance code entries present) - 1
4132 12         22 my $HDIST = bits2int_lsb($in_fh, 5, $buffer) + 1;
4133              
4134             # (4 bits) HCLEN = (number of CL code entries present) - 4
4135 12         27 my $HCLEN = bits2int_lsb($in_fh, 4, $buffer) + 4;
4136              
4137 12 50       47 $VERBOSE && say STDERR ":: Number of LL codes: $HLIT";
4138 12 50       27 $VERBOSE && say STDERR ":: Number of dist codes: $HDIST";
4139 12 50       27 $VERBOSE && say STDERR ":: Number of CL codes: $HCLEN";
4140              
4141 12         48 my @CL_code_lenghts = (0) x 19;
4142 12         38 my @CL_order = (16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15);
4143              
4144 12         43 foreach my $i (0 .. $HCLEN - 1) {
4145 192         214 $CL_code_lenghts[$CL_order[$i]] = bits2int_lsb($in_fh, 3, $buffer);
4146             }
4147              
4148 12 50       27 $VERBOSE && say STDERR ":: CL code lengths: @CL_code_lenghts";
4149              
4150 12         38 my (undef, $CL_rev_dict) = huffman_from_code_lengths(\@CL_code_lenghts);
4151              
4152 12         66 my @LL_CL_lengths = _decode_CL_lengths($in_fh, $buffer, $CL_rev_dict, $HLIT);
4153 12         41 my @dist_CL_lengths = _decode_CL_lengths($in_fh, $buffer, $CL_rev_dict, $HDIST);
4154              
4155 12         37 my (undef, $LL_rev_dict) = huffman_from_code_lengths(\@LL_CL_lengths);
4156 12         128 my (undef, $dist_rev_dict) = huffman_from_code_lengths(\@dist_CL_lengths);
4157              
4158 12         91 _deflate_decode_huffman($in_fh, $buffer, $LL_rev_dict, $dist_rev_dict, $search_window);
4159             }
4160              
4161 84     84 1 191 sub deflate_extract_next_block ($in_fh, $buffer, $search_window) {
  84         113  
  84         92  
  84         86  
  84         87  
4162              
4163 84 50       168 local $LZ_MIN_LEN = 4 if ($LZ_MIN_LEN < 4); # minimum match length in LZ parsing
4164 84         102 local $LZ_MAX_LEN = 258; # maximum match length in LZ parsing
4165 84         106 local $LZ_MAX_DIST = 32768; # maximum allowed back-reference distance in LZ parsing
4166              
4167 84         197 my $block_type = bits2int_lsb($in_fh, 2, $buffer);
4168              
4169 84         129 my $chunk = '';
4170              
4171 84 100       253 if ($block_type == 0) {
    100          
    50          
4172 3 50       14 $VERBOSE && say STDERR "\n:: Extracting block of type 0";
4173 3         37 $chunk = deflate_extract_block_type_0($in_fh, $buffer, $search_window);
4174             }
4175             elsif ($block_type == 1) {
4176 70 50       125 $VERBOSE && say STDERR "\n:: Extracting block of type 1";
4177 70         189 $chunk = deflate_extract_block_type_1($in_fh, $buffer, $search_window);
4178             }
4179             elsif ($block_type == 2) {
4180 11 50       34 $VERBOSE && say STDERR "\n:: Extracting block of type 2";
4181 11         37 $chunk = deflate_extract_block_type_2($in_fh, $buffer, $search_window);
4182             }
4183             else {
4184 0         0 confess "[!] Unknown block of type: $block_type";
4185             }
4186              
4187 84         292 return $chunk;
4188             }
4189              
4190 95     95 1 151 sub gzip_decompress ($in_fh) {
  95         118  
  95         112  
4191              
4192 95 100       221 if (ref($in_fh) eq '') {
4193 46 50       455 open(my $fh2, '<:raw', \$in_fh) or confess "error: $!";
4194 46         117 return __SUB__->($fh2);
4195             }
4196              
4197 49         70 my $decompressed = '';
4198              
4199 49         218 open my $out_fh, '>:raw', \$decompressed;
4200              
4201 49 50       96 local $LZ_MIN_LEN = 4 if ($LZ_MIN_LEN < 4); # minimum match length in LZ parsing
4202 49         81 local $LZ_MAX_LEN = 258; # maximum match length in LZ parsing
4203 49         60 local $LZ_MAX_DIST = (1 << 15) - 1; # maximum allowed back-reference distance in LZ parsing
4204              
4205 49   33     353 my $MAGIC = (getc($in_fh) // confess "error") . (getc($in_fh) // confess "error");
      33        
4206              
4207 49 50       172 if ($MAGIC ne pack('C*', 0x1f, 0x8b)) {
4208 0         0 confess "Not a valid GZIP container!";
4209             }
4210              
4211 49   33     137 my $CM = getc($in_fh) // confess "error"; # 0x08 = DEFLATE
4212 49   33     152 my $FLAGS = ord(getc($in_fh) // confess "error"); # flags
4213 49   33     119 my $MTIME = join('', map { getc($in_fh) // confess "error" } 1 .. 4); # modification time
  196         626  
4214 49   33     254 my $XFLAGS = getc($in_fh) // confess "error"; # extra flags
4215 49   33     146 my $OS = getc($in_fh) // confess "error"; # 0x03 = Unix
4216              
4217 49 50       126 if ($CM ne chr(0x08)) {
4218 0         0 confess "Only DEFLATE compression method is supported (0x08)! Got: 0x", sprintf('%02x', ord($CM));
4219             }
4220              
4221             # Reference:
4222             # https://web.archive.org/web/20240221024029/https://forensics.wiki/gzip/
4223              
4224 49         66 my $has_filename = 0;
4225 49         62 my $has_comment = 0;
4226 49         58 my $has_header_checksum = 0;
4227 49         76 my $has_extra_fields = 0;
4228              
4229 49 100       152 if ($FLAGS & 0x08) {
4230 3         5 $has_filename = 1;
4231             }
4232              
4233 49 100       154 if ($FLAGS & 0x10) {
4234 2         4 $has_comment = 1;
4235             }
4236              
4237 49 50       117 if ($FLAGS & 0x02) {
4238 0         0 $has_header_checksum = 1;
4239             }
4240              
4241 49 50       107 if ($FLAGS & 0x04) {
4242 0         0 $has_extra_fields = 1;
4243             }
4244              
4245 49 50       105 if ($has_extra_fields) {
4246 0         0 my $size = bytes2int_lsb($in_fh, 2);
4247 0   0     0 read($in_fh, (my $extra_field_data), $size) // confess "can't read extra field data: $!";
4248 0 0       0 $VERBOSE && say STDERR ":: Extra field data: $extra_field_data";
4249             }
4250              
4251 49 100       108 if ($has_filename) {
4252 3         8 my $filename = read_null_terminated($in_fh); # filename
4253 3 50       9 $VERBOSE && say STDERR ":: Filename: $filename";
4254             }
4255              
4256 49 100       143 if ($has_comment) {
4257 2         6 my $comment = read_null_terminated($in_fh); # comment
4258 2 50       6 $VERBOSE && say STDERR ":: Comment: $comment";
4259             }
4260              
4261             # TODO: verify the header checksum
4262 49 50       88 if ($has_header_checksum) {
4263 0         0 my $header_checksum = bytes2int_lsb($in_fh, 2);
4264 0 0       0 $VERBOSE && say STDERR ":: Header checksum: $header_checksum";
4265             }
4266              
4267 49         72 my $crc32 = 0;
4268 49         57 my $actual_length = 0;
4269 49         70 my $buffer = '';
4270 49         70 my $search_window = '';
4271              
4272 49         67 while (1) {
4273              
4274 51         129 my $is_last = read_bit_lsb($in_fh, \$buffer);
4275 51         185 my $chunk = deflate_extract_next_block($in_fh, \$buffer, \$search_window);
4276              
4277 51         332 print $out_fh $chunk;
4278 51         159 $crc32 = crc32($chunk, $crc32);
4279 51         108 $actual_length += length($chunk);
4280              
4281 51 100       135 last if $is_last;
4282             }
4283              
4284 49         74 $buffer = ''; # discard any padding bits
4285              
4286 49         108 my $stored_crc32 = bits2int_lsb($in_fh, 32, \$buffer);
4287 49         86 my $actual_crc32 = $crc32;
4288              
4289 49 50       121 if ($stored_crc32 != $actual_crc32) {
4290 0         0 confess "[!] The CRC32 does not match: $actual_crc32 (actual) != $stored_crc32 (stored)";
4291             }
4292             else {
4293 49 50       113 $VERBOSE && print STDERR ":: CRC32 value: $actual_crc32\n";
4294             }
4295              
4296 49         83 my $stored_length = bits2int_lsb($in_fh, 32, \$buffer);
4297              
4298 49 50       143 if ($stored_length != $actual_length) {
4299 0         0 confess "[!] The length does not match: $actual_length (actual) != $stored_length (stored)";
4300             }
4301             else {
4302 49 50       112 $VERBOSE && print STDERR ":: Total length: $actual_length\n";
4303             }
4304              
4305 49 100       132 if (eof($in_fh)) {
4306 46 50       86 $VERBOSE && print STDERR "\n:: Reached the end of the file.\n";
4307             }
4308             else {
4309 3 50       12 $VERBOSE && print STDERR "\n:: There is something else in the container! Trying to recurse!\n\n";
4310 3         14 return ($decompressed . __SUB__->($in_fh));
4311             }
4312              
4313 46         931 return $decompressed;
4314             }
4315              
4316             ###############################
4317             # ZLIB compressor
4318             ###############################
4319              
4320 52     52 1 157251 sub zlib_compress ($in_fh, $lzss_encoding_sub = \&lzss_encode) {
  52         88  
  52         110  
  52         85  
4321              
4322 52 100       159 if (ref($in_fh) eq '') {
4323 26 50       347 open(my $fh2, '<:raw', \$in_fh) or confess "error: $!";
4324 26         87 return __SUB__->($fh2, $lzss_encoding_sub);
4325             }
4326              
4327 26         55 my $compressed = '';
4328              
4329 26         145 open my $out_fh, '>:raw', \$compressed;
4330              
4331 26 50       78 local $LZ_MIN_LEN = 4 if ($LZ_MIN_LEN < 4); # minimum match length in LZ parsing
4332 26         58 local $LZ_MAX_LEN = 258; # maximum match length in LZ parsing
4333 26         61 local $LZ_MAX_DIST = (1 << 15) - 1; # maximum allowed back-reference distance in LZ parsing
4334              
4335 26         44 my $CMF = (7 << 4) | 8;
4336 26         41 my $FLG = 2 << 6;
4337              
4338 26         96 while (($CMF * 256 + $FLG) % 31 != 0) {
4339 728         1275 ++$FLG;
4340             }
4341              
4342 26         51 my $bitstring = '';
4343 26         36 my $adler32 = 1;
4344              
4345 26         100 print $out_fh chr($CMF);
4346 26         47 print $out_fh chr($FLG);
4347              
4348 26 100       79 if (eof($in_fh)) { # empty file
4349 2         3 $bitstring = '1' . '10' . '0000000';
4350             }
4351              
4352 26         48 state $CHUNK_SIZE = (1 << 15) - 1;
4353              
4354 26         127 while (read($in_fh, (my $chunk), $CHUNK_SIZE)) {
4355              
4356 24         72 $adler32 = adler32($chunk, $adler32);
4357 24 50       96 $bitstring .= eof($in_fh) ? '1' : '0';
4358              
4359 24         85 my ($literals, $distances, $lengths) = $lzss_encoding_sub->($chunk);
4360              
4361 24         105 my $bt1_bitstring = deflate_create_block_type_1($literals, $distances, $lengths);
4362              
4363             # When block type 1 is larger than the input, then we have random uncompressible data: use block type 0
4364 24 50       128 if ((length($bt1_bitstring) >> 3) > length($chunk) + 5) {
4365              
4366 0 0       0 $VERBOSE && say STDERR ":: Using block type: 0";
4367              
4368 0         0 $bitstring .= '00';
4369              
4370 0         0 print $out_fh pack('b*', $bitstring); # pads to a byte
4371 0         0 print $out_fh pack('b*', deflate_create_block_type_0_header($chunk));
4372 0         0 print $out_fh $chunk;
4373              
4374 0         0 $bitstring = '';
4375 0         0 next;
4376             }
4377              
4378 24         69 my $bt2_bitstring = deflate_create_block_type_2($literals, $distances, $lengths);
4379              
4380             # When block type 2 is larger than block type 1, then we may have very small data
4381 24 100       120 if (length($bt2_bitstring) > length($bt1_bitstring)) {
4382 23 50       51 $VERBOSE && say STDERR ":: Using block type: 1";
4383 23         63 $bitstring .= $bt1_bitstring;
4384             }
4385             else {
4386 1 50       3 $VERBOSE && say STDERR ":: Using block type: 2";
4387 1         18 $bitstring .= $bt2_bitstring;
4388             }
4389              
4390 24         403 print $out_fh pack('b*', substr($bitstring, 0, length($bitstring) - (length($bitstring) % 8), ''));
4391             }
4392              
4393 26 100       92 if ($bitstring ne '') {
4394 24         63 print $out_fh pack('b*', $bitstring);
4395             }
4396              
4397 26         67 print $out_fh int2bytes($adler32, 4);
4398              
4399 26         243 return $compressed;
4400             }
4401              
4402             ###############################
4403             # ZLIB decompressor
4404             ###############################
4405              
4406 59     59 1 102 sub zlib_decompress($in_fh) {
  59         65  
  59         65  
4407              
4408 59 100       128 if (ref($in_fh) eq '') {
4409 29 50       264 open(my $fh2, '<:raw', \$in_fh) or confess "error: $!";
4410 29         78 return __SUB__->($fh2);
4411             }
4412              
4413 30         54 my $decompressed = '';
4414              
4415 30         163 open my $out_fh, '>:raw', \$decompressed;
4416              
4417 30 50       65 local $LZ_MIN_LEN = 4 if ($LZ_MIN_LEN < 4); # minimum match length in LZ parsing
4418 30         45 local $LZ_MAX_LEN = 258; # maximum match length in LZ parsing
4419 30         57 local $LZ_MAX_DIST = (1 << 15) - 1; # maximum allowed back-reference distance in LZ parsin
4420              
4421 30         48 my $adler32 = 1;
4422              
4423 30         92 my $CMF = ord(getc($in_fh));
4424 30         72 my $FLG = ord(getc($in_fh));
4425              
4426 30 50       141 if (($CMF * 256 + $FLG) % 31 != 0) {
4427 0         0 confess "Invalid header checksum!\n";
4428             }
4429              
4430 30         87 my $CINFO = $CMF >> 4;
4431              
4432 30 50       89 if ($CINFO > 7) {
4433 0         0 confess "Values of CINFO above 7 are not supported!\n";
4434             }
4435              
4436 30         73 my $method = $CMF & 0b1111;
4437              
4438 30 50       82 if ($method != 8) {
4439 0         0 confess "Only method 8 (DEFLATE) is supported!\n";
4440             }
4441              
4442 30         57 my $buffer = '';
4443 30         56 my $search_window = '';
4444              
4445 30         41 while (1) {
4446              
4447 30         75 my $is_last = read_bit_lsb($in_fh, \$buffer);
4448 30         83 my $chunk = deflate_extract_next_block($in_fh, \$buffer, \$search_window);
4449              
4450 30         89 print $out_fh $chunk;
4451 30         70 $adler32 = adler32($chunk, $adler32);
4452              
4453 30 50       106 last if $is_last;
4454             }
4455              
4456 30         75 my $stored_adler32 = bytes2int($in_fh, 4);
4457              
4458 30 50       98 if ($adler32 != $stored_adler32) {
4459 0         0 confess "Adler32 checksum does not match: $adler32 (actual) != $stored_adler32 (stored)\n";
4460             }
4461              
4462 30 100       83 if (eof($in_fh)) {
4463 29 50       71 $VERBOSE && print STDERR "\n:: Reached the end of the file.\n";
4464             }
4465             else {
4466 1 50       4 $VERBOSE && print STDERR "\n:: There is something else in the container! Trying to recurse!\n\n";
4467 1         7 return ($decompressed . __SUB__->($in_fh));
4468             }
4469              
4470 29         342 return $decompressed;
4471             }
4472              
4473             ###############################
4474             # LZ4 compressor
4475             ###############################
4476              
4477 56     56 1 272673 sub lz4_compress($fh, $lzss_encoding_sub = \&lzss_encode) {
  56         115  
  56         104  
  56         80  
4478              
4479 56 100       232 if (ref($fh) eq '') {
4480 28 50       419 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
4481 28         103 return __SUB__->($fh2, $lzss_encoding_sub);
4482             }
4483              
4484 28         71 my $compressed = '';
4485              
4486 28         86 $compressed .= int2bytes_lsb(0x184D2204, 4); # LZ4 magic number
4487              
4488 28         71 my $fd = ''; # frame description
4489 28         51 $fd .= chr(0b01_10_00_00); # flags (FLG)
4490 28         42 $fd .= chr(0b0_111_0000); # block description (BD)
4491              
4492 28         62 $compressed .= $fd;
4493 28         68 $compressed .= chr(115); # header checksum
4494              
4495 28         53 state $CHUNK_SIZE = 1 << 17;
4496              
4497 28         240 while (read($fh, (my $chunk), $CHUNK_SIZE)) {
4498              
4499 26         43 my ($literals, $distances, $lengths) = do {
4500 26 50       67 local $LZ_MIN_LEN = 4 if ($LZ_MIN_LEN < 4);
4501 26         49 local $LZ_MAX_LEN = ~0;
4502 26         39 local $LZ_MAX_DIST = (1 << 16) - 1;
4503 26         121 $lzss_encoding_sub->(substr($chunk, 0, -5));
4504             };
4505              
4506             # The last 5 bytes of each block must be literals
4507             # https://github.com/lz4/lz4/issues/1495
4508 26         209 push @$literals, unpack('C*', substr($chunk, -5));
4509              
4510 26         57 my $literals_end = $#{$literals};
  26         52  
4511              
4512 26         51 my $block = '';
4513              
4514 26         68 for (my $i = 0 ; $i <= $literals_end ; ++$i) {
4515              
4516 3594         3340 my @uncompressed;
4517 3594   100     9116 while ($i <= $literals_end and defined($literals->[$i])) {
4518 5577         7511 push @uncompressed, $literals->[$i];
4519 5577         15057 ++$i;
4520             }
4521              
4522 3594         4624 my $literals_string = pack('C*', @uncompressed);
4523 3594         3686 my $literals_length = scalar(@uncompressed);
4524              
4525 3594 100       4704 my $match_len = $lengths->[$i] ? ($lengths->[$i] - 4) : 0;
4526              
4527 3594 100       5742 $block .= chr((($literals_length >= 15 ? 15 : $literals_length) << 4) | ($match_len >= 15 ? 15 : $match_len));
    100          
4528              
4529 3594         3378 $literals_length -= 15;
4530 3594         3446 $match_len -= 15;
4531              
4532 3594         4744 while ($literals_length >= 0) {
4533 72 50       125 $block .= ($literals_length >= 255 ? "\xff" : chr($literals_length));
4534 72         123 $literals_length -= 255;
4535             }
4536              
4537 3594         5222 $block .= $literals_string;
4538              
4539 3594   100     4959 my $dist = $distances->[$i] // last;
4540 3568         6939 $block .= pack('b*', scalar reverse sprintf('%016b', $dist));
4541              
4542 3568         7540 while ($match_len >= 0) {
4543 995 100       1266 $block .= ($match_len >= 255 ? "\xff" : chr($match_len));
4544 995         2315 $match_len -= 255;
4545             }
4546             }
4547              
4548 26 50       57 if ($block ne '') {
4549 26         103 $compressed .= int2bytes_lsb(length($block), 4);
4550 26         2329 $compressed .= $block;
4551             }
4552             }
4553              
4554 28         76 $compressed .= int2bytes_lsb(0x00000000, 4); # EndMark
4555 28         288 return $compressed;
4556             }
4557              
4558             ###############################
4559             # LZ4 decompressor
4560             ###############################
4561              
4562 94     94 1 143 sub lz4_decompress($fh) {
  94         134  
  94         120  
4563              
4564 94 100       221 if (ref($fh) eq '') {
4565 47 50       457 open(my $fh2, '<:raw', \$fh) or confess "error: $!";
4566 47         152 return __SUB__->($fh2);
4567             }
4568              
4569 47         80 my $decompressed = '';
4570              
4571 47         136 while (!eof($fh)) {
4572              
4573 51 50       131 bytes2int_lsb($fh, 4) == 0x184D2204 or confess "Incorrect LZ4 Frame magic number";
4574              
4575 51         148 my $FLG = ord(getc($fh));
4576 51         104 my $BD = ord(getc($fh));
4577              
4578 51         97 my $version = $FLG & 0b11_00_00_00;
4579 51         83 my $B_indep = $FLG & 0b00_10_00_00;
4580 51         85 my $B_checksum = $FLG & 0b00_01_00_00;
4581 51         96 my $C_size = $FLG & 0b00_00_10_00;
4582 51         96 my $C_checksum = $FLG & 0b00_00_01_00;
4583 51         89 my $DictID = $FLG & 0b00_00_00_01;
4584              
4585 51         85 my $Block_MaxSize = $BD & 0b0_111_0000;
4586              
4587 51 50       105 $VERBOSE && say STDERR "Maximum block size: $Block_MaxSize";
4588              
4589 51 50       107 if ($version != 0b01_00_00_00) {
4590 0         0 confess "Error: Invalid version number";
4591             }
4592              
4593 51 50       122 if ($C_size) {
4594 0         0 my $content_size = bytes2int_lsb($fh, 8);
4595 0 0       0 $VERBOSE && say STDERR "Content size: ", $content_size;
4596             }
4597              
4598 51 50       95 if ($DictID) {
4599 0         0 my $dict_id = bytes2int_lsb($fh, 4);
4600 0 0       0 $VERBOSE && say STDERR "Dictionary ID: ", $dict_id;
4601             }
4602              
4603 51         104 my $header_checksum = ord(getc($fh));
4604              
4605             # TODO: compute and verify the header checksum
4606 51 50       105 $VERBOSE && say STDERR "Header checksum: ", $header_checksum;
4607              
4608 51         80 my $decoded = '';
4609              
4610 51         124 while (!eof($fh)) {
4611              
4612 98         178 my $block_size = bytes2int_lsb($fh, 4);
4613              
4614 98 100       228 if ($block_size == 0x00000000) { # signifies an EndMark
4615 51 50       91 $VERBOSE && say STDERR "Block size == 0";
4616 51         88 last;
4617             }
4618              
4619 47 50       100 $VERBOSE && say STDERR "Block size: $block_size";
4620              
4621 47 100       121 if ($block_size >> 31) {
4622 3 50       28 $VERBOSE && say STDERR "Highest bit set: ", $block_size;
4623 3         6 $block_size &= ((1 << 31) - 1);
4624 3 50       5 $VERBOSE && say STDERR "Block size: ", $block_size;
4625 3         6 my $uncompressed = '';
4626 3         9 read($fh, $uncompressed, $block_size);
4627 3         8 $decoded .= $uncompressed;
4628             }
4629             else {
4630              
4631 44         61 my $compressed = '';
4632 44         325 read($fh, $compressed, $block_size);
4633              
4634 44         116 while ($compressed ne '') {
4635 3636         5914 my $len_byte = ord(substr($compressed, 0, 1, ''));
4636              
4637 3636         5042 my $literals_length = $len_byte >> 4;
4638 3636         5189 my $match_len = $len_byte & 0b1111;
4639              
4640             ## say STDERR "Literal: ", $literals_length;
4641             ## say STDERR "Match len: ", $match_len;
4642              
4643 3636 100       5503 if ($literals_length == 15) {
4644 72         123 while (1) {
4645 72         154 my $byte_len = ord(substr($compressed, 0, 1, ''));
4646 72         140 $literals_length += $byte_len;
4647 72 50       185 last if $byte_len != 255;
4648             }
4649             }
4650              
4651             ## say STDERR "Total literals length: ", $literals_length;
4652              
4653 3636         3937 my $literals = '';
4654              
4655 3636 100       5405 if ($literals_length > 0) {
4656 1140         1753 $literals = substr($compressed, 0, $literals_length, '');
4657             }
4658              
4659 3636 100       5624 if ($compressed eq '') { # end of block
4660 44         72 $decoded .= $literals;
4661 44         133 last;
4662             }
4663              
4664 3592         7921 my $offset = oct('0b' . reverse unpack('b16', substr($compressed, 0, 2, '')));
4665              
4666 3592 50       5883 if ($offset == 0) {
4667 0         0 confess "Corrupted block";
4668             }
4669              
4670             ## say STDERR "Offset: $offset";
4671              
4672 3592 100       5521 if ($match_len == 15) {
4673 983         1044 while (1) {
4674 995         1525 my $byte_len = ord(substr($compressed, 0, 1, ''));
4675 995         1191 $match_len += $byte_len;
4676 995 100       1977 last if $byte_len != 255;
4677             }
4678             }
4679              
4680 3592         4726 $decoded .= $literals;
4681 3592         4312 $match_len += 4;
4682              
4683             ## say STDERR "Total match len: $match_len\n";
4684              
4685 3592 100       5529 if ($offset >= $match_len) { # non-overlapping matches
    100          
4686 3447         13522 $decoded .= substr($decoded, length($decoded) - $offset, $match_len);
4687             }
4688             elsif ($offset == 1) {
4689 25         141 $decoded .= substr($decoded, -1) x $match_len;
4690             }
4691             else { # overlapping matches
4692 120         248 foreach my $i (1 .. $match_len) {
4693 2438         7456 $decoded .= substr($decoded, length($decoded) - $offset, 1);
4694             }
4695             }
4696             }
4697             }
4698              
4699 47 100       111 if ($B_checksum) {
4700 5         10 my $content_checksum = bytes2int_lsb($fh, 4);
4701 5 50       12 $VERBOSE && say STDERR "Block checksum: $content_checksum";
4702             }
4703              
4704 47 50       107 if ($B_indep) { # blocks are independent of each other
    0          
4705 47         135 $decompressed .= $decoded;
4706 47         165 $decoded = '';
4707             }
4708             elsif (length($decoded) > 2**16) { # blocks are dependent
4709 0         0 $decompressed .= substr($decoded, 0, -(2**16), '');
4710             }
4711             }
4712              
4713             # TODO: compute and verify checksum
4714 51 100       115 if ($C_checksum) {
4715 12         18 my $content_checksum = bytes2int_lsb($fh, 4);
4716 12 50       26 $VERBOSE && say STDERR "Content checksum: $content_checksum";
4717             }
4718              
4719 51         271 $decompressed .= $decoded;
4720             }
4721              
4722 47         432 return $decompressed;
4723             }
4724              
4725             1;
4726              
4727             __END__