File Coverage

blib/lib/Compress/LZString.pm
Criterion Covered Total %
statement 191 274 69.7
branch 56 100 56.0
condition n/a
subroutine 26 26 100.0
pod 10 11 90.9
total 283 411 68.8


line stmt bran cond sub pod time code
1             package Compress::LZString;
2              
3 1     1   5372 use 5.006;
  1         4  
4 1     1   6 use strict;
  1         2  
  1         20  
5 1     1   4 use warnings;
  1         3  
  1         61  
6              
7             $Compress::LZString::VERSION = '1.44';
8            
9             BEGIN {
10 1     1   7 use Exporter( );
  1         2  
  1         111  
11 1     1   3 our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, @EXPORT_FAIL, %EXPORT_TAGS);
12              
13 1         2 $VERSION = $Compress::LZString::VERSION;
14 1         17 @ISA = qw/Exporter/;
15 1         4 @EXPORT = qw/compress_b64 compress_b64_safe
16             decompress_b64 decompress_b64_safe/;
17 1         2 @EXPORT_OK = qw/compress compressToBase64 compressToEncodedURIComponent
18             decompress decompressFromBase64 decompressFromEncodedURIComponent/;
19 1         2288 %EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]);
20             }
21              
22       1     END { }
23              
24              
25             my $keyStrBase64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
26             my $keyStrUriSafe = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$';
27             my %baseReverseDic;
28              
29 1     1 1 5 sub compress_b64 { return compressToBase64(shift); }
30 1     1 1 5 sub compress_b64_safe { return compressToEncodedURIComponent(shift); }
31 1     1 1 4 sub decompress_b64 { return decompressFromBase64(shift); }
32 1     1 1 5 sub decompress_b64_safe { return decompressFromEncodedURIComponent(shift); }
33              
34             sub compressToBase64 {
35 2     2 1 6 my $input = shift;
36 2 50       7 return unless $input;
37 2     940   15 return _rpad(_compress($input, 6, sub { substr $keyStrBase64,shift,1; }));
  940         1916  
38             }
39              
40             sub compressToEncodedURIComponent {
41 2     2 1 4 my $input = shift;
42 2 50       8 return unless $input;
43 2     940   14 return _compress($input, 6, sub { substr $keyStrUriSafe,shift,1; });
  940         1895  
44             }
45              
46             sub compress {
47 1     1 1 30 my $uncompressed = shift;
48 1 50       5 return unless $uncompressed;
49 1     177   6 return _compress($uncompressed, 16, sub { chr shift; });
  177         415  
50             }
51              
52             sub decompressFromBase64 {
53 2     2 1 4 my $compressed = shift;
54             return _decompress(length $compressed, 32,
55 2     940   17 sub { getBaseValue($keyStrBase64, substr $compressed,shift,1); });
  940         2050  
56             }
57              
58             sub decompressFromEncodedURIComponent {
59 2     2 1 13 (my $compressed = shift) =~ s/ /+/g;
60             return _decompress(length $compressed, 32,
61 2     940   22 sub { getBaseValue($keyStrUriSafe, substr $compressed,shift,1); });
  940         1986  
62             }
63              
64             sub decompress {
65 1     1 1 3 my $compressed = shift;
66 1     177   10 return _decompress(length $compressed, 32768, sub { ord substr $compressed,shift,1; });
  177         523  
67             }
68              
69             sub _compress {
70 5     5   15 my ($uncompressed, $bitsPerChar, $getCharFromInt) = @_;
71 5 50       15 return unless $uncompressed;
72              
73 5         10 my %context_dictionary;
74             my %context_dictionaryToCreate;
75 5         10 my $context_c = "";
76 5         9 my $context_wc = "";
77 5         9 my $context_w = "";
78 5         9 my $context_enlargeIn = 2;
79 5         7 my $context_dictSize = 3;
80 5         8 my $context_numBits = 2;
81 5         10 my @context_data;
82 5         10 my $context_data_val = 0;
83 5         7 my $context_data_position = 0;
84              
85 5         6 my $value = 0;
86              
87 5         319 foreach (split //, $uncompressed)
88             {
89             eval {
90 240         402 $context_dictionary{$_} = $context_dictSize++;
91 240         411 $context_dictionaryToCreate{$_} = 1;
92 2380 100       4133 } unless defined $context_dictionary{$_};
93              
94 2380         3179 $context_c = $_;
95 2380         3192 $context_wc = $context_w . $context_c;
96 2380 100       4151 $context_w = $context_wc, next if defined $context_dictionary{$context_wc};
97              
98 1550 100       2378 if (defined $context_dictionaryToCreate{$context_w})
99             {
100 240 50       432 if (ord substr $context_w,0,1 < 256)
101             {
102 240         397 foreach (1..$context_numBits)
103             {
104 1550         1950 $context_data_val <<= 1;
105 1550 100       2248 if ($context_data_position == $bitsPerChar-1)
106             {
107 220         302 $context_data_position = 0;
108 220         370 push @context_data, &$getCharFromInt($context_data_val);
109 220         352 $context_data_val = 0;
110 1330         1890 } else { $context_data_position++; }
111             }
112 240         370 $value = ord substr $context_w,0,1;
113 240         356 foreach (1..8)
114             {
115 1920         2578 $context_data_val = ($context_data_val<<1) | ($value&1);
116 1920 100       2892 if ($context_data_position == $bitsPerChar-1)
117             {
118 291         395 $context_data_position = 0;
119 291         442 push @context_data, &$getCharFromInt($context_data_val);
120 291         486 $context_data_val = 0;
121 1629         1977 } else { $context_data_position++; };
122 1920         3012 $value >>= 1;
123             }
124             }
125             else
126             {
127 0         0 $value = 1;
128 0         0 foreach (1..$context_numBits)
129             {
130 0         0 $context_data_val = ($context_data_val<<1) | $value;
131 0 0       0 if ($context_data_position == $bitsPerChar-1)
132             {
133 0         0 $context_data_position = 0;
134 0         0 push @context_data, &$getCharFromInt($context_data_val);
135 0         0 $context_data_val = 0;
136 0         0 } else { $context_data_position++; };
137 0         0 $value = 0;
138             }
139 0         0 $value = ord substr $context_w,0,1;
140 0         0 foreach (1..16)
141             {
142 0         0 $context_data_val = ($context_data_val<<1) | ($value&1);
143 0 0       0 if ($context_data_position == $bitsPerChar-1)
144             {
145 0         0 $context_data_position = 0;
146 0         0 push @context_data, &$getCharFromInt($context_data_val);
147 0         0 $context_data_val = 0;
148 0         0 } else { $context_data_position++; };
149 0         0 $value >>= 1;
150             }
151             }
152 240         331 $context_enlargeIn--;
153 240 100       421 if ($context_enlargeIn == 0)
154             {
155 10         15 $context_enlargeIn = 2**$context_numBits;
156 10         16 $context_numBits++;
157             }
158 240         406 delete $context_dictionaryToCreate{$context_w};
159             }
160             else
161             {
162 1310         1748 $value = $context_dictionary{$context_w};
163 1310         2017 foreach (1..$context_numBits)
164             {
165 10530         13741 $context_data_val = ($context_data_val<<1) | ($value&1);
166 10530 100       15587 if ($context_data_position == $bitsPerChar-1)
167             {
168 1528         1970 $context_data_position = 0;
169 1528         2320 push @context_data, &$getCharFromInt($context_data_val);
170 1528         2082 $context_data_val = 0;
171 9002         10703 } else { $context_data_position++; };
172 10530         14033 $value >>= 1;
173             }
174             }
175 1550         1899 $context_enlargeIn--;
176 1550 100       2469 if ($context_enlargeIn == 0)
177             {
178 25         36 $context_enlargeIn = 2**$context_numBits;
179 25         37 $context_numBits++;
180             }
181             # add wc to the dictionary
182 1550         2890 $context_dictionary{$context_wc} = $context_dictSize++;
183 1550         2324 $context_w = $context_c;
184             }
185              
186             # output the code for w.
187 5 50       149 if ($context_w ne "")
188             {
189 5 50       16 if (defined $context_dictionaryToCreate{$context_w})
190             {
191 0 0       0 if (ord substr $context_w,0,1 < 256)
192             {
193 0         0 foreach (1..$context_numBits)
194             {
195 0         0 $context_data_val <<= 1;
196 0 0       0 if ($context_data_position == $bitsPerChar-1)
197             {
198 0         0 $context_data_position = 0;
199 0         0 push @context_data, &$getCharFromInt($context_data_val);
200 0         0 $context_data_val = 0;
201 0         0 } else { $context_data_position++; }
202             }
203 0         0 $value = ord substr $context_w,0,1;
204 0         0 foreach (1..8)
205             {
206 0         0 $context_data_val = ($context_data_val<<1) | ($value&1);
207 0 0       0 if ($context_data_position == $bitsPerChar-1)
208             {
209 0         0 $context_data_position = 0;
210 0         0 push @context_data, &$getCharFromInt($context_data_val);
211 0         0 $context_data_val = 0;
212 0         0 } else { $context_data_position++; };
213 0         0 $value >>= 1;
214             }
215             }
216             else
217             {
218 0         0 $value = 1;
219 0         0 foreach (1..$context_numBits)
220             {
221 0         0 $context_data_val = ($context_data_val<<1) | $value;
222 0 0       0 if ($context_data_position == $bitsPerChar-1)
223             {
224 0         0 $context_data_position = 0;
225 0         0 push @context_data, &$getCharFromInt($context_data_val);
226 0         0 $context_data_val = 0;
227 0         0 } else { $context_data_position++; };
228 0         0 $value = 0;
229             }
230 0         0 $value = ord substr $context_w,0,1;
231 0         0 foreach (1..16)
232             {
233 0         0 $context_data_val = ($context_data_val<<1) | ($value&1);
234 0 0       0 if ($context_data_position == $bitsPerChar-1)
235             {
236 0         0 $context_data_position = 0;
237 0         0 push @context_data, &$getCharFromInt($context_data_val);
238 0         0 $context_data_val = 0;
239 0         0 } else { $context_data_position++; };
240 0         0 $value >>= 1;
241             }
242             }
243 0         0 $context_enlargeIn--;
244 0 0       0 if ($context_enlargeIn == 0)
245             {
246 0         0 $context_enlargeIn = 2**$context_numBits;
247 0         0 $context_numBits++;
248             }
249 0         0 delete $context_dictionaryToCreate{$context_w};
250             }
251             else
252             {
253 5         8 $value = $context_dictionary{$context_w};
254 5         12 foreach (1..$context_numBits)
255             {
256 45         64 $context_data_val = ($context_data_val<<1) | ($value&1);
257 45 100       74 if ($context_data_position == $bitsPerChar-1)
258             {
259 8         14 $context_data_position = 0;
260 8         15 push @context_data, &$getCharFromInt($context_data_val);
261 8         64 $context_data_val = 0;
262 37         43 } else { $context_data_position++; };
263 45         68 $value >>= 1;
264             }
265             }
266 5         6 $context_enlargeIn--;
267 5 50       15 if ($context_enlargeIn == 0)
268             {
269 0         0 $context_enlargeIn = 2**$context_numBits;
270 0         0 $context_numBits++;
271             }
272             }
273              
274             # mark the end of the stream
275 5         10 $value = 2;
276 5         14 foreach (1..$context_numBits)
277             {
278 45         64 $context_data_val = ($context_data_val<<1) | ($value&1);
279 45 100       77 if ($context_data_position == $bitsPerChar-1)
280             {
281 5         7 $context_data_position = 0;
282 5         12 push @context_data, &$getCharFromInt($context_data_val);
283 5         18 $context_data_val = 0;
284 40         50 } else { $context_data_position++; };
285 45         62 $value >>= 1;
286             }
287              
288             # flush the last char
289 5         9 do { $context_data_val <<= 1; } until $context_data_position++ == $bitsPerChar-1;
  22         50  
290 5         11 push @context_data, &$getCharFromInt($context_data_val);
291              
292 5         454 return pack "A*"x@context_data, @context_data;
293             }
294              
295             sub _decompress {
296 5     5   19 my ($length, $resetValue, $getNextValue) = @_;
297              
298 5         10 my %dictionary;
299 5         12 my $enlargeIn = 4;
300 5         7 my $dictSize = 4;
301 5         10 my $numBits = 3;
302 5         8 my $entry = "";
303 5         19 my @result;
304              
305             my %data;
306 5         19 $data{val} = &$getNextValue(0);
307 5         14 $data{position} = $resetValue;
308 5         14 $data{index} = 1;
309 5         20 @dictionary{0..2} = 0..2;
310              
311 5         11 my ($w, $c) = (0, 0);
312 5         14 my ($bits, $maxpower, $power) = (0, 2**2, 1);
313 5         10 do {{
314 10 50       16 $bits |= (($data{val}&$data{position})>0 ? 1:0)*$power;
  10         30  
315 10 50       84 next if $data{position} >>= 1;
316 0         0 $data{position} = $resetValue;
317 0         0 $data{val} = &$getNextValue($data{index}++);
318             }} until ($power<<=1) == $maxpower;
319              
320 5 50       14 if ($bits == 0)
    0          
    0          
321             {
322 5         12 ($bits, $maxpower, $power) = (0, 2**8, 1);
323 5         9 do {{
324 40 100       48 $bits |= (($data{val}&$data{position})>0 ? 1:0)*$power;
  40         81  
325 40 100       92 next if $data{position} >>= 1;
326 4         7 $data{position} = $resetValue;
327 4         13 $data{val} = &$getNextValue($data{index}++);
328             }} until ($power<<=1) == $maxpower;
329 5         12 $c = chr $bits;
330             }
331             elsif ($bits == 1)
332             {
333 0         0 ($bits, $maxpower, $power) = (0, 2**16, 1);
334 0         0 do {{
335 0 0       0 $bits |= (($data{val}&$data{position})>0 ? 1:0)*$power;
  0         0  
336 0 0       0 next if $data{position} >>= 1;
337 0         0 $data{position} = $resetValue;
338 0         0 $data{val} = &$getNextValue($data{index}++);
339             }} until ($power<<=1) == $maxpower;
340 0         0 $c = chr $bits;
341             }
342 0         0 elsif ($bits == 2) { return; }
343              
344             # print(bits)
345 5         17 $dictionary{3} = $w=$c;
346 5         9 push @result, $c;
347              
348 5         9 do {
349 1555 50       2697 return if $data{index} > $length;
350              
351 1555         2743 ($bits, $maxpower, $power) = (0, 2**$numBits, 1);
352 1555         1975 do {{
353 12160 100       14823 $bits |= (($data{val}&$data{position})>0 ? 1:0)*$power;
  12160         20430  
354 12160 100       25325 next if $data{position} >>= 1;
355 1761         2426 $data{position} = $resetValue;
356 1761         3073 $data{val} = &$getNextValue($data{index}++);
357             }} until ($power<<=1) == $maxpower;
358              
359 1555 100       3455 if (($c=$bits) == 0)
    50          
    100          
360             {
361 235         403 ($bits, $maxpower, $power) = (0, 2**8, 1);
362 235         328 do {{
363 1880 100       2291 $bits |= (($data{val}&$data{position})>0 ? 1:0)*$power;
  1880         3244  
364 1880 100       3783 next if $data{position} >>= 1;
365 287         390 $data{position} = $resetValue;
366 287         488 $data{val} = &$getNextValue($data{index}++);
367             }} until ($power<<=1) == $maxpower;
368 235         324 $c = $dictSize; $enlargeIn--;
  235         313  
369 235         579 $dictionary{$dictSize++} = chr $bits;
370             }
371             elsif ($bits == 1)
372             {
373 0         0 ($bits, $maxpower, $power) = (0, 2**16, 1);
374 0         0 do {{
375 0 0       0 $bits |= (($data{val}&$data{position})>0 ? 1:0)*$power;
  0         0  
376 0 0       0 next if $data{position} >>= 1;
377 0         0 $data{position} = $resetValue;
378 0         0 $data{val} = &$getNextValue($data{index}++);
379             }} until ($power<<=1) == $maxpower;
380 0         0 $c = $dictSize; $enlargeIn--;
  0         0  
381 0         0 $dictionary{$dictSize++} = chr $bits;
382             }
383 5         630 elsif ($bits == 2) { return pack "A*"x@result, @result; };
384              
385 1550 100       2920 $enlargeIn = 2**$numBits++ unless $enlargeIn;
386              
387 1550 50       2658 if (defined $dictionary{$c}) { $entry = $dictionary{$c}; }
  1550         2269  
388 0 0       0 else { return unless $c == $dictSize; $entry = $w.substr $w,0,1; }
  0         0  
389 1550         2549 push @result, $entry;
390              
391             # add w+entry[0] to the dictionary
392 1550         3837 $dictionary{$dictSize++} = $w.substr $entry,0,1;
393 1550         2109 $w = $entry; $enlargeIn--;
  1550         2046  
394              
395 1550 100       3153 $enlargeIn = 2**$numBits++ unless $enlargeIn;
396             } while 1;
397             }
398              
399             sub _rpad {
400 2     2   7 my $str = shift;
401 2         12 my $len = 4*int((length($str)-1)/4)+4;
402 2         17 return substr $str."===", 0, $len;
403             }
404              
405             sub getBaseValue {
406 1880     1880 0 4459 my ($alphabet, $character) = @_;
407             eval {
408 2         27 @{$baseReverseDic{$alphabet}}{split//,$alphabet} = (0..length $alphabet);
  2         53  
409 1880 100       3904 } unless defined $baseReverseDic{$alphabet};
410              
411 1880         5740 return $baseReverseDic{$alphabet}{$character};
412             }
413              
414             1;
415             __END__