File Coverage

blib/lib/Compress/SelfExtracting.pm
Criterion Covered Total %
statement 368 401 91.7
branch 72 104 69.2
condition 16 17 94.1
subroutine 35 40 87.5
pod 3 4 75.0
total 494 566 87.2


line stmt bran cond sub pod time code
1             package Compress::SelfExtracting;
2 23     23   27109 use Digest::MD5 'md5_hex';
  23         40  
  23         4584  
3             require Exporter;
4              
5 23     23   136 use vars qw/@ISA @EXPORT @EXPORT_OK $VERSION/;
  23         80  
  23         3201  
6              
7             @EXPORT_OK = qw/compress decompress/;
8             @EXPORT = qw/zscript zfile/;
9             @ISA = qw/Exporter/;
10             $VERSION = 0.04;
11              
12             my %O;
13             BEGIN {
14 23     23   2610476 %O = (standalone => 1,
15             type => 'LZW',
16             op => 'eval',
17             uu => 1);
18             };
19              
20             sub compress
21             {
22 44     44 1 105093464 my $data = shift;
23 44         516 my %o = @_;
24 44         333 @O{keys %o} = values %o;
25 44         243 my $cdata = &{"Compress::SelfExtracting::$O{type}::compress"}($data, \%O);
  44         1703  
26 44 100       369 if ($O{uu}) {
27 22         1145 $cdata = pack 'u', $cdata;
28             }
29 44 100       255 if ($O{standalone}) {
30 22         66 my $sa = &{"Compress::SelfExtracting::$O{type}::standalone"}(\%O);
  22         539  
31 22         718 return $sa.$cdata;
32             } else {
33 110         2174 return "use Compress::SelfExtracting::Filter "
34 22         1601 .join(', ', map { "$_ => '$O{$_}'" }
35             grep!/decompress|file|data/,keys %O).";\n"
36             .md5_hex($data)."\n$cdata\n";
37             }
38             }
39              
40             sub decompress
41             {
42 22     22 0 60 my $data = shift;
43 22         94 my %o = @_;
44 22         156 @O{keys %o} = values %o;
45 22 50       217 if ($data =~ /^([0-9a-f]+)\n(.*)/s) {
46 22 100       78 if ($O{uu}) {
47 11         448 $data = unpack 'u', $2;
48             } else {
49 11         162 chomp($data = $2);
50             }
51 22         52 $data = &{"Compress::SelfExtracting::$O{type}::decompress"}($data, \%O);
  22         185  
52 22         952 my $cksum = md5_hex($data);
53 22 50       218 unless ($cksum eq $1) {
54 0         0 open BAD, ">BAD";
55 0         0 print BAD $data;
56 0         0 close BAD;
57 0         0 die "Bad checksum\n";
58             }
59             } else {
60 0         0 die "$0 doesn't look compressed\n";
61             }
62 22         158 $data;
63             }
64              
65             sub zscript
66             {
67 0     0 1 0 local $/ = undef;
68 0         0 my $data = ;
69 0         0 print compress $data, @_;
70             }
71              
72             sub zfile
73             {
74 0     0 1 0 local $/ = undef;
75 0         0 my $data = ;
76 0         0 print compress $data, @_, op => 'print';
77             }
78              
79             ############################################################
80             package Compress::SelfExtracting::LZ77;
81              
82 0     0   0 sub import { }
83              
84             sub compress
85             {
86 8     8   63 my $str = shift;
87 8 50       116 die "Sorry, code too long\n" if length($str) >= 1<<16;
88 8         35 my @rep;
89 8         21 my $la = 0;
90 8         56 while ($la < length $str) {
91 812         1022 my $n = 1;
92 812         839 my ($tmp, $p);
93 812         858 $p = 0;
94 812   100     8015 while ($la + $n < length $str
      100        
95             && $n < 255
96             && ($tmp = index(substr($str, 0, $la),
97             substr($str, $la, $n),
98             $p)) >= 0) {
99 69704         67925 $p = $tmp;
100 69704         703379 $n++;
101             }
102 812         870 --$n;
103 812         1330 my $c = substr($str, $la + $n, 1);
104 812         3628 push @rep, [$p, $n, ord $c];
105 812         2154 $la += $n + 1;
106             }
107 8         35 join('', map { pack 'SCC', @$_ } @rep);
  812         1987  
108             }
109              
110             sub decompress
111             {
112 4     4   11 my $str = shift;
113 4         8 my $ret = '';
114 4         17 while (length $str) {
115 406         570 my ($s, $l, $c) = unpack 'SCC', $str;
116 406         685 $ret .= substr($ret, $s, $l).chr$c;
117 406         810 $str=substr($str,4);
118             }
119 4         65 $ret;
120             }
121              
122             sub standalone
123             {
124 4     4   16 my $O = shift;
125 4         22 my $ret = <<'EOC';
126             BEGIN{open 0;$_=join'',<0>;s/^.*?}\n//s;#UUDEC#s/(...)(.)/
127             ($o,$l)=unpack SC,$1;$r.=substr($r,$o,$l).$2/egs;#OP#$r;exit}
128             EOC
129 4 100       40 if ($O->{uu}) {
130 2         30 $ret =~ s/#UUDEC#/\$_=unpack'u',\$_;/;
131             } else {
132 2         34 $ret =~ s/#UUDEC#//;
133             }
134 4         56 $ret =~ s/#OP#/$O->{op}/;
135 4         24 $ret;
136             }
137              
138             ############################################################
139             package Compress::SelfExtracting::LZSS;
140              
141 0     0   0 sub import { }
142              
143             sub compress
144             {
145 8     8   57 my $str = shift;
146 8 50       74 die "Sorry, code too long\n" if length($str) >= 1<<16;
147 8         22 my @rep;
148 8         21 my $la = 0;
149 8         59 while ($la < length $str) {
150 1108         1274 my $n = 1;
151 1108         1128 my ($tmp, $p);
152 1108         1318 $p = 0;
153 1108   100     10467 while ($la + $n < length $str
      100        
154             && $n < 255
155             && ($tmp = index(substr($str, 0, $la),
156             substr($str, $la, $n),
157             $p)) >= 0) {
158 70272         77286 $p = $tmp;
159 70272         536834 $n++;
160             }
161 1108         1241 --$n;
162 1108 100       2238 if ($n < 2) {
163 648         2107 push @rep, "\0".substr($str, $la, 1);
164 648         2215 ++$la;
165             } else {
166 460         1874 push @rep, pack 'CS', $n, $p;
167 460         1384 $la += $n;
168             }
169             }
170 8         334 join('', @rep);
171             }
172              
173             sub decompress
174             {
175 4     4   10 my $str = shift;
176 4         9 my $ret = '';
177 4         9 my $o = 0;
178 4         19 while ($o < length $str) {
179 554         760 my $n = unpack 'C', substr($str, $o);
180 554 100       794 if ($n == 0) {
181 324         433 $ret .= substr($str, $o + 1, 1);
182 324         543 $o += 2;
183             } else {
184 230         320 my $p = unpack 'S', substr($str, $o + 1);
185 230         386 $ret .= substr($ret, $p, $n);
186 230         391 $o += 3;
187             }
188             }
189 4         71 $ret;
190             }
191              
192             sub standalone
193             {
194 4     4   24 my $ret = <<'END';
195             BEGIN{open 0;$_=join'',<0>;s/^.*?}\n//s;#UUDEC#($r.=($n=ord substr$_,
196             $o++)?substr$r,(unpack S,substr$_,$o++),$n:substr$_,$o,1),$o++
197             while$o
198             END
199 4         11 my $O = shift;
200 4 100       26 if ($O->{uu}) {
201 2         44 $ret =~ s/#UUDEC#/\$_=unpack'u',\$_;/
202             } else {
203 2         24 $ret =~ s/#UUDEC#//;
204             }
205 4         69 $ret =~ s/#OP#/$O->{op}/;
206 4         34 $ret;
207             }
208              
209             ############################################################
210             package Compress::SelfExtracting::LZW;
211              
212             my (%LZ, %UNLZ, %SA);
213              
214             sub import
215             {
216             %LZ = (12 => sub {
217 8     8   28 my $v = '';
218 8         63 for my $i (0..$#_) {
219 6708         11322 vec($v, 3*$i, 4) = $_[$i]/256;
220 6708         12268 vec($v, 3*$i+1, 4) = ($_[$i]/16)%16;
221 6708         19769 vec($v, 3*$i+2, 4) = $_[$i]%16;
222             }
223 8         3599 $v;
224             },
225 8     8   2820 16 => sub { pack 'S*', @_ });
  23     23   243  
226             %UNLZ = (12 => sub {
227 4     4   10 my $code = shift;
228 4         7 my @code;
229 4         9 my $len = length($code);
230 4         13 my $reallen = 2*$len/3;
231 4         148 print STDERR "len = $len, reallen = $reallen\n";
232 4         19 foreach (0..$reallen - 1) {
233 3354         4883 push @code, (vec($code, 3*$_, 4)<<8)
234             | (vec($code, 3*$_+1, 4)<<4)
235             | (vec($code, 3*$_+2, 4));
236             }
237 4         564 @code;
238             },
239 23     4   391 16 => sub { unpack 'S*', shift; });
  4         632  
240             # Now the self-extracting glop:
241 23         55 my $ANY_16 = <<'EOC';
242             BEGIN{open 0;$/=$!;%d=map{($_,chr)}0..($n=255);($s=join'',<0>)
243             =~s/^.*?}\n//s;#OP# join'',map{($C,$P)=@d{$_,$p};$p=$_;if
244             (!defined$P){$d{$p}}elsif(defined$C){$d{++$n}=$P.substr$C,0,
245             1;$C}else{$d{++$n}=$P.substr$P,0,1}}unpack'S*',#UUDEC#;exit}
246             EOC
247 23         169 (my $u16 = $ANY_16) =~ s/#UUDEC#/unpack'u',\$s/;
248 23         108 (my $n16 = $ANY_16) =~ s/#UUDEC#/\$s/;
249 23         49 my $ANY_12 = <<'EOC';
250             BEGIN{open 0;$/=$!;%d=map{($_,chr)}0..($n=255);($s=join'',<0>)
251             =~s/^.*?}\n//s;#UUDEC##OP# join'',map{($C,$P)=@d{$_,$p};$p=$_;if
252             (!defined$P){$C}elsif(defined$C){$d{++$n}=$P.substr$C,0,1;$C}else{
253             $d{++$n}=$P.substr$P,0,1}}map{vec($s,3*$_,4)<<8|vec($s,3*$_+1,4)<<4
254             |vec$s,3*$_+2,4}0..length($s)*2/3-1;exit}
255             EOC
256 23         287 (my $u12 = $ANY_12) =~ s/#UUDEC#/\$s=unpack'u',\$s;/;
257 23         98 (my $n12 = $ANY_12) =~ s/#UUDEC#//;
258 23         154 %SA = ('12u0' => $n12, '12u1' => $u12, '16u0' => $n16, '16u1' => $u16);
259             }
260              
261             sub compress
262             {
263 16     16   43 my ($str, $O) = @_;
264 16         70 my $p = ''; my %d = map{(chr $_, $_)} 0..255;
  16         89  
  4096         23167  
265 16         594 my @o = ();
266 16         45 my $ncw = 256;
267 16         30467 for (split '', $str) {
268 141032 100       245758 if (exists $d{$p.$_}) {
269 127632         158088 $p .= $_;
270             } else {
271 13400         21448 push @o, $d{$p};
272 13400         33628 $d{$p.$_} = $ncw++;
273 13400         17952 $p = $_;
274             }
275             }
276 16         9130 push @o, $d{$p};
277 16 100 66     342 if ($O->{bits} != 16 && $ncw < 1<<12) {
    50          
278 8         19 $O->{bits} = 12;
279 8         475 return $LZ{12}->(@o);
280             } elsif ($ncw < 1<<16) {
281 8         47 $O->{bits} = 16;
282 8         684 return $LZ{16}->(@o);
283             } else {
284 0         0 die "Sorry, code-word overflow";
285             }
286             }
287              
288             sub decompress
289             {
290 8     8   91 my %d = (map{($_, chr $_)} 0..255);
  2048         5365  
291 8         192 my $ncw = 256;
292 8         29 my $ret = '';
293 8         63 my ($str, $O) = @_;
294 8         49 my ($p, @code) = $UNLZ{$O->{bits}}->($str);
295 8         152 $ret .= $d{$p};
296 8         25 for (@code) {
297 6700 100       19191 if (exists $d{$_}) {
298 6624         8468 $ret .= $d{$_};
299 6624         24614 $d{$ncw++} = $d{$p}.substr($d{$_}, 0, 1);
300             } else {
301 76         99 my $dp = $d{$p};
302 76 50       155 warn unless $_ == $ncw++;
303 76         427 $ret .= ($d{$_} = $dp.substr($dp, 0, 1));
304             }
305 6700         11117 $p = $_;
306             }
307 8         2996 $ret;
308             }
309              
310             sub standalone
311             {
312 8     8   29 my $O = shift;
313 8         73 my $ret = $SA{"$O->{bits}u$O->{uu}"};
314 8         158 $ret =~ s/#OP#/$O->{op}/;
315 8         38 $ret;
316             }
317              
318             ############################################################
319             package Compress::SelfExtracting::Huffman;
320              
321             # Compute bit-codes from tree.
322             sub tree2str
323             {
324 756     756   971 my ($str, $x) = @_;
325 756 100       1303 if (!defined $x->[2]) {
326 384         1082 $rep{$x->[1]} = $str;
327             } else {
328 372         956 tree2str($str.'0', $x->[1]);
329 372         4045 tree2str($str.'1', $x->[2]);
330             }
331             }
332              
333             sub compress
334             {
335 12     12   264 my %p = ();
336 12         35 my $s = shift;
337 12         26 my @chars;
338 12 50       50 if (ref $s eq 'ARRAY') {
339 0         0 @chars = @$s;
340             } else {
341 12         31819 @chars = split '', $s;
342             }
343 12         2370 for (@chars) {
344 71012         96608 $p{$_}++;
345             }
346 1462         10681 my @elts = sort { $a->[0] <=> $b->[0] }
  384         887  
347 12         108 map { [ $p{$_}, $_, undef ] } keys %p;
348 12         121 while (@elts > 1) {
349 372         562 my ($x, $y) = splice @elts, 0, 2;
350 372         810 my $z = [ $x->[0] + $y->[0], $x, $y ];
351 372         675 foreach my $i (0..$#elts) {
352 2896 100       5902 if ($elts[$i]->[0] >= $z->[0]) {
353 296         603 splice @elts, $i, 0, $z;
354 296         342 undef $z;
355 296         354 last;
356             }
357             }
358 372 100       1193 push @elts, $z if $z;
359             }
360 12         42 local %rep = (); # gets filled in by tree2str.
361 12         73 tree2str '', pop @elts;
362 12 50       156 if ($::DEBUG) {
363 0         0 foreach (sort keys %rep) {
364 0         0 print STDERR "$_ <- $rep{$_}\n";
365             }
366             }
367 12         28 my $data = '';
368 12         31 for (@chars) {
369 71012         86689 $data .= $rep{$_};
370             }
371 12         60 my $nbits = length($data);
372 12         109 my $tree = pack 'CL', scalar keys %rep, $nbits;
373 12 50       48 print STDERR "len = ", scalar keys %rep, "nbits = $nbits\n" if $::DEBUG;
374 12         76 while (my ($k, $v) = each %rep) {
375 384 50       671 die "Sorry, Huffman code too long ($v)\n" if length $v >= 32;
376 384         1561 $tree .= pack('Cb32', ord($k), '0'x(31 - length $v).'1'.$v);
377             }
378 12         2393 $data = pack 'b*', $data.('0'x((8 - $nbits%8) % 8));
379 12 50       38 print STDERR length($data), " bytes of data\n" if $::DEBUG;
380 12         5383 $tree.$data;
381             }
382              
383             sub decompress
384             {
385 6     6   36 my $str = shift;
386 6         71 my ($len, $nbits) = unpack 'CL', $str;
387 6         54 $str = substr($str, 5);
388 6 50       28 print STDERR "len = $len, nbits = $nbits\n" if $::DEBUG;
389 6         14 my %rep;
390 6         34 for (0..$len - 1) {
391 192         550 my ($c, $x) = unpack 'Cb32', substr($str, 5*$_, 5);
392 192         862 $x =~ s/^0*1//;
393 192 50       429 die "Duplicate: $x -> $c" if exists $rep{$x};
394 192         517 $rep{$x} = chr $c;
395             }
396 6 50       27 if ($::DEBUG) {
397 0         0 foreach (sort keys %rep) {
398 0         0 print STDERR "$_ <- $rep{$_}\n";
399             }
400             }
401 6         65 $str = substr($str, 5*$len);
402 6 50       22 print STDERR length $str, " bytes of data\n" if $::DEBUG;
403 6         743 my $data = unpack "b$nbits", $str;
404 6         14 my $ret = '';
405 6         10 my $n;
406 6         22 while (length $data > 0) {
407 35506         36191 $n = 1;
408 35506         67586 while (!exists($rep{substr($data, 0, $n)})) {
409 91696         80672 $n++;
410 91696 50       240091 die $n if $n > length $data;
411             }
412 35506         46334 $ret .= $rep{substr($data, 0, $n)};
413 35506         356027 $data = substr($data, $n);
414             }
415 6         121 $ret;
416             }
417              
418             sub standalone
419             {
420              
421 4     4   29 my $ret = <<'EOC';
422             BEGIN{open 0;$/=$!;($s=join'',<0>)=~s/^.*?}\n//s;#UUDEC#($l,$L)=
423             unpack'CL',$s;$s=substr$s,5;for(1..$l){($c,$x)=unpack'Cb32',$s;
424             $x=~s/^0*1//;$r{$x}=chr$c;$s=substr$s,5}$_=unpack"b$L",$s;while
425             (length){$n=1;1while!exists$r{substr$_,0,$n++};$r.=$r{substr$_,
426             0,--$n};$_=substr$_,$n}#OP#$r;exit}
427             EOC
428 4         12 my $O = shift;
429 4 100       19 if ($O->{uu}) {
430 2         28 $ret =~ s/#UUDEC#/\$s=unpack'u',\$s;/;
431             } else {
432 2         24 $ret =~ s/#UUDEC#//;
433             }
434 4         77 $ret =~ s/#OP#/$O->{op}/;
435 4         19 $ret;
436             }
437              
438             ############################################################
439             package Compress::SelfExtracting::BWT;
440             # Burrows-Wheeler Transform block-sorting compression (i.e. bzip).
441             #
442             # This implementation is a straightforward translation of this Dr
443             # Dobbs' piece: http://www.ddj.com/documents/s=957/ddj9609f/. Also
444             # see
445             # http://gatekeeper.dec.com/pub/DEC/SRC/research-reports/SRC-124.ps.gz
446             # for the original, which IMO better describes the block-sorting.
447             #
448              
449             import Compress::SelfExtracting::Huffman;
450              
451 0     0   0 sub import { }
452              
453             ##############################
454             # BWT block-sorting
455              
456             sub BLKSIZE() { 16*1024 } # unused, so this sucks for big files.
457             sub QSORT_SIZE() { 5 } # when to use qsort instead of counting sort.
458             sub _counting_sort
459             {
460 16     16   30 my ($p, $o) = @_;
461 16 50       43 if ($::DEBUG) {
462 0         0 ++$calls;
463 0 0       0 if ($o > $maxdepth) {
464 0         0 $maxdepth = $o;
465 0         0 print STDERR "$o\r";
466             }
467             }
468 16         23 my @a;
469 16         39 foreach (@$p) {
470 320         360 push @{$a[ord substr($s, $_+$o, 1)]}, $_;
  320         899  
471             }
472 16         27 my @ret;
473 16         55 foreach (@a) {
474 1848 100       4862 next unless ref $_;
475 152 100       367 if (@$_ == 1) {
    100          
476 72         783 push @ret, $_->[0];
477             } elsif (@$_ < QSORT_SIZE) {
478 68         89 my $tmp = $o+1;
479 68         233 push @ret, sort { substr($s, $a+$tmp).substr($s, 0, $a+$o) cmp
  156         639  
480             substr($s, $b+$tmp).substr($s, 0, $b+$o) }
481             @$_;
482             } else {
483 12         70 push @ret, _counting_sort($_, $o+1);
484             }
485             }
486 16         152 @ret;
487             }
488              
489             sub counting_sort
490             {
491 4     4   28 local $s = shift;
492 4         47 local $^W = 0;
493 4         15 my $l = length $s;
494 4         20 $s .= $s;
495 4         27 local $maxdepth = 0;
496 4         25 local $calls = 0;
497 4         188 my @ret = _counting_sort([0..$l-1], 0);
498 4 50       32 print STDERR "Counting sort max depth $maxdepth, calls = $calls\n"
499             if $::DEBUG;
500 4         89 @ret;
501             }
502              
503             sub BWT
504             {
505 4     4   11 my $str = shift;
506 4         8 my $slow;
507 4 50       25 if (length $str > BLKSIZE) {
508 0         0 $slow = 1;
509 0         0 warn "BWT will be very slow for ", length $str, " bytes\n";
510             }
511 4         15 my $d = 0;
512 4         28 my ($pi, @L);
513 4         36 my @posns = counting_sort($str);
514             # This is quite a bit slower than counting sort.
515             # my @posns = sort { substr($str, $a).substr($str, 0, $a-1) cmp
516             # substr($str, $b).substr($str, 0, $b-1) }
517             # (0 .. length($str) - 1);
518 4         16 my $i;
519 4         31 foreach $i (0..$#posns) {
520 256 100       409 if ($posns[$i] == 0) {
521 4         7 $pi = $i;
522             }
523 256         417 push @L, ord(substr($str, $posns[$i] - 1, 1));
524             }
525 4         23 ($pi, \@L);
526             }
527              
528             sub unBWT
529             {
530 2     2   4 my ($pi, $L) = @_;
531 2         4 my (@P, @C);
532 0         0 my @ret;
533 2 50       7 print STDERR "length = ".@$L."\n" if $::DEBUG;
534 2         4 for (0..$#{$L}) {
  2         7  
535 128         123 my $c = $L->[$_];
536 128   100     292 $P[$_] = $C[$c] || 0;
537 128         169 $C[$c]++;
538             }
539 2         6 my $sum = 0;
540             {
541 23     23   260 no warnings;
  23         44  
  23         34348  
  2         3  
542 2         8 for (@C) {
543 240         233 $sum += $_;
544 240         409 $_ = $sum - $_;
545             }
546             }
547 2         5 for (reverse 0..$#{$L}) {
  2         11  
548 128         113 my $c = $L->[$pi];
549 128         120 $ret[$_] = $c;
550 128         142 $pi = $P[$pi] + $C[$c];
551             }
552 2 50       11 die unless @ret == @$L;
553 2         16 return \@ret;
554             }
555              
556             ##############################
557             # Move-to-front coder
558              
559             sub MTF
560             {
561 4     4   7 my $L = shift;
562 4         4 my @ret;
563 4         78 my @c = 0..255;
564 4         10 foreach (@$L) {
565 256         381 for my $i (0..$#c) {
566 10312 100       16058 if ($c[$i] == $_) {
567 256         364 push @ret, $i;
568 256         279 splice @c, $i, 1;
569 256         275 unshift @c, $_;
570 256         393 last;
571             }
572             }
573             }
574 4         42 \@ret;
575             }
576              
577             sub unMTF
578             {
579 2     2   11 my $L = shift;
580 2         2 my @ret;
581 2         45 my @c = 0..255;
582 2         6 foreach (@$L) {
583 128         129 my $x = $c[$_];
584 128         166 push @ret, $x;
585 128         140 splice @c, $_, 1;
586 128         193 unshift @c, $x;
587             }
588 2         26 \@ret;
589             }
590              
591             ##############################
592             # Run-length coder
593              
594             sub RLE
595             {
596 4     4   6 my @ret;
597 4         10 my $l = shift;
598 4         7 my $c = $l->[0];
599 4         6 my $n = 1;
600 4         10 foreach (@{$l}[1..$#{$l}]) {
  4         19  
  4         18  
601 252 100       302 if ($c != $_) {
602 236         289 push @ret, $c, $n;
603 236         213 $n = 1;
604 236         245 $c = $_;
605             } else {
606 16 50       36 if (++$n > 255) {
607 0         0 push @ret, $c, 255;
608 0         0 $n = 1;
609             }
610             }
611             }
612 4         14 push @ret, $c, $n;
613 4 50       12 if ($::DEBUG) {
614 0         0 my $i = 0;
615 0         0 while ($i < @ret) {
616 0         0 print STDERR "$ret[$i], $ret[$i+1]\n";
617 0         0 $i += 2;
618             }
619             }
620 4         26 \@ret;
621             }
622              
623             sub unRLE
624             {
625 2     2   5 my @l = @{shift @_};
  2         19  
626 2         4 my @ret;
627 2 50       11 die unless @l % 2 == 0;
628 2         4 my ($c, $n);
629 2         7 while (@l) {
630 120         122 $c = shift @l;
631 120         108 $n = shift @l;
632 120 50       188 print STDERR "$c, $n\n" if $::DEBUG;
633 120         374 push @ret, $c for 1..$n;
634             }
635 2         52 \@ret;
636             }
637              
638             ##############################
639             # Main compression routines
640              
641             sub compress
642             {
643 4     4   30 my ($str, $O) = @_;
644 4 50       38 print STDERR "BWT..." if $::DEBUG;
645 4         56 my ($pi, $L) = BWT($str);
646 4 50       12 print STDERR "\nMTF..." if $::DEBUG;
647 4         27 $L = MTF($L);
648 4 50       19 print STDERR "\nRLE..." if $::DEBUG;
649 4         21 $L = RLE($L);
650 4 50       20 print STDERR "\nHuffman..." if $::DEBUG;
651 480         753 $L = Compress::SelfExtracting::Huffman::compress(pack('L', $pi)
652 4         30 .join('', map { chr } @$L),
653             $O);
654 4 50       67 print STDERR "done\n" if $::DEBUG;
655 4         13 return $L;
656             }
657              
658             sub decompress
659             {
660 2     2   4 my $str = shift;
661             # Huffman decode to a string:
662 2         8 $str = Compress::SelfExtracting::Huffman::decompress($str);
663 2         8 my $pi = unpack 'L', $str;
664 2         132 $str = [map {ord} split '', substr($str, 4)];
  240         271  
665 2         27 $str = unRLE($str);
666 2         18 $str = unMTF($str);
667 2         12 $str = unBWT($pi, $str);
668 2         9 join '', map { chr } @$str;
  128         174  
669             }
670              
671             # Oh, yeah.
672             sub standalone
673             {
674 2     2   13 my $ret = <<'EOC';
675             BEGIN{open$^W=0;$/=$!;($s=join'',<0>)=~s/^.*?}\n//s;#UUDEC#($l,$L)=
676             unpack'CL',$s;$s=substr$s,5;for(1..$l){($c,$x)=unpack'Cb32',$s;$x=~
677             s/^0*1//;$r{$x}=chr$c;$s=substr$s,5}$_=unpack"b$L",$s;while(length){
678             $n=1;1while!exists$r{substr$_,0,$n++};$r.=$r{substr$_,0,--$n};$_=
679             substr$_,$n}$P=unpack'L',$r;@l=map{ord}split'',substr$r,4;while(@l){
680             push@R,(shift@l)x shift@l}@c=0..255;for(@R){push@M,$x=$c[$_];splice
681             @c,$_,1;unshift@c,$x}for(0..$#M){$c=$M[$_];$P[$_]=$C[$c]++}for(@C){
682             $s+=$_;$_=$s-$_}for(reverse 0..$#M){$c=$M[$P];$r[$_]=$c;$P=$P[$P]+
683             $C[$c]}#OP# join'',map{chr}@r;exit}
684             EOC
685 2         6 my $O = shift;
686 2 100       8 if ($O->{uu}) {
687 1         19 $ret =~ s/#UUDEC#/\$s=unpack'u',\$s;/;
688             } else {
689 1         14 $ret =~ s/#UUDEC#//;
690             }
691 2         23 $ret =~ s/#OP#/$O->{op}/;
692 2         10 $ret;
693             }
694              
695             package Compress::SelfExtracting;
696             import Compress::SelfExtracting::LZW;
697              
698             1;
699             __END__