File Coverage

blib/lib/Compress/LZString.pm
Criterion Covered Total %
statement 196 277 70.7
branch 58 100 58.0
condition n/a
subroutine 27 27 100.0
pod 10 11 90.9
total 291 415 70.1


line stmt bran cond sub pod time code
1             package Compress::LZString;
2              
3 1     1   4490 use 5.006002;
  1         3  
4 1     1   4 use strict;
  1         2  
  1         16  
5 1     1   3 use warnings;
  1         2  
  1         29  
6              
7             # old perl supresses malformed utf-8-strict characters like unpaired surrogate
8 1     1   581 no if $] < 5.014, warnings => qw/utf8/;
  1         11  
  1         5  
9              
10             $Compress::LZString::VERSION = '1.4401';
11            
12             BEGIN {
13 1     1   45 use Exporter( );
  1         2  
  1         89  
14 1     1   3 our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, @EXPORT_FAIL, %EXPORT_TAGS);
15              
16 1         2 $VERSION = $Compress::LZString::VERSION;
17 1         15 @ISA = qw/Exporter/;
18 1         4 @EXPORT = qw/compress_b64 compress_b64_safe
19             decompress_b64 decompress_b64_safe/;
20 1         2 @EXPORT_OK = qw/compress compressToBase64 compressToEncodedURIComponent
21             decompress decompressFromBase64 decompressFromEncodedURIComponent/;
22 1         1783 %EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]);
23             }
24              
25       1     END { }
26              
27              
28             my $keyStrBase64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
29             my $keyStrUriSafe = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$';
30             my %baseReverseDic;
31              
32 2     2 1 7 sub compress_b64 { return compressToBase64(shift); }
33 2     2 1 9 sub compress_b64_safe { return compressToEncodedURIComponent(shift); }
34 2     2 1 9 sub decompress_b64 { return decompressFromBase64(shift); }
35 2     2 1 8 sub decompress_b64_safe { return decompressFromEncodedURIComponent(shift); }
36              
37             sub compressToBase64 {
38 2     2 1 6 my $input = shift;
39 2 50       115 return unless $input;
40 2     2835   26 return _rpad(_compress($input, 6, sub { substr $keyStrBase64,shift,1; }));
  2835         4740  
41             }
42              
43             sub compressToEncodedURIComponent {
44 2     2 1 4 my $input = shift;
45 2 50       12 return unless $input;
46 2     2835   24 return _compress($input, 6, sub { substr $keyStrUriSafe,shift,1; });
  2835         4575  
47             }
48              
49             sub compress {
50 2     2 1 12369 my $uncompressed = shift;
51 2 50       36 return unless $uncompressed;
52 2     1064   44 return _compress($uncompressed, 16, sub { chr shift; });
  1064         2104  
53             }
54              
55             sub decompressFromBase64 {
56 2     2 1 4 my $compressed = shift;
57             return _decompress(length $compressed, 32,
58 2     2835   26 sub { getBaseValue($keyStrBase64, substr $compressed,shift,1); });
  2835         4637  
59             }
60              
61             sub decompressFromEncodedURIComponent {
62 2     2 1 20 (my $compressed = shift) =~ s/ /+/g;
63             return _decompress(length $compressed, 32,
64 2     2835   21 sub { getBaseValue($keyStrUriSafe, substr $compressed,shift,1); });
  2835         4872  
65             }
66              
67             sub decompress {
68 2     2 1 7 my $compressed = shift;
69 2     1064   47 return _decompress(length $compressed, 32768, sub { ord substr $compressed,shift,1; });
  1064         2574  
70             }
71              
72             sub _compress {
73 6     6   24 my ($uncompressed, $bitsPerChar, $getCharFromInt) = @_;
74 6 50       21 return unless $uncompressed;
75              
76 6         16 my %context_dictionary;
77             my %context_dictionaryToCreate;
78 6         18 my $context_c = "";
79 6         17 my $context_wc = "";
80 6         11 my $context_w = "";
81 6         9 my $context_enlargeIn = 2;
82 6         11 my $context_dictSize = 3;
83 6         11 my $context_numBits = 2;
84 6         12 my @context_data;
85 6         7 my $context_data_val = 0;
86 6         11 my $context_data_position = 0;
87              
88 6         9 my $value = 0;
89              
90 6         1147 foreach (split //, $uncompressed)
91             {
92             eval {
93 360         648 $context_dictionary{$_} = $context_dictSize++;
94 360         455 $context_dictionaryToCreate{$_} = 1;
95 10098 100       14654 } unless defined $context_dictionary{$_};
96              
97 10098         10365 $context_c = $_;
98 10098         10786 $context_wc = $context_w . $context_c;
99 10098 100       15411 $context_w = $context_wc, next if defined $context_dictionary{$context_wc};
100              
101 5124 100       6368 if (defined $context_dictionaryToCreate{$context_w})
102             {
103 360 50       529 if ((ord substr $context_w,0,1) < 256)
104             {
105 360         541 foreach (1..$context_numBits)
106             {
107 2586         2607 $context_data_val <<= 1;
108 2586 100       2996 if ($context_data_position == $bitsPerChar-1)
109             {
110 328         334 $context_data_position = 0;
111 328         396 push @context_data, &$getCharFromInt($context_data_val);
112 328         407 $context_data_val = 0;
113 2258         2437 } else { $context_data_position++; }
114             }
115 360         416 $value = ord substr $context_w,0,1;
116 360         468 foreach (1..8)
117             {
118 2880         3085 $context_data_val = ($context_data_val<<1) | ($value&1);
119 2880 100       3391 if ($context_data_position == $bitsPerChar-1)
120             {
121 403         405 $context_data_position = 0;
122 403         493 push @context_data, &$getCharFromInt($context_data_val);
123 403         454 $context_data_val = 0;
124 2477         2407 } else { $context_data_position++; };
125 2880         3091 $value >>= 1;
126             }
127             }
128             else
129             {
130 0         0 $value = 1;
131 0         0 foreach (1..$context_numBits)
132             {
133 0         0 $context_data_val = ($context_data_val<<1) | $value;
134 0 0       0 if ($context_data_position == $bitsPerChar-1)
135             {
136 0         0 $context_data_position = 0;
137 0         0 push @context_data, &$getCharFromInt($context_data_val);
138 0         0 $context_data_val = 0;
139 0         0 } else { $context_data_position++; };
140 0         0 $value = 0;
141             }
142 0         0 $value = ord substr $context_w,0,1;
143 0         0 foreach (1..16)
144             {
145 0         0 $context_data_val = ($context_data_val<<1) | ($value&1);
146 0 0       0 if ($context_data_position == $bitsPerChar-1)
147             {
148 0         0 $context_data_position = 0;
149 0         0 push @context_data, &$getCharFromInt($context_data_val);
150 0         0 $context_data_val = 0;
151 0         0 } else { $context_data_position++; };
152 0         0 $value >>= 1;
153             }
154             }
155 360         381 $context_enlargeIn--;
156 360 100       517 if ($context_enlargeIn == 0)
157             {
158 12         14 $context_enlargeIn = 2**$context_numBits;
159 12         14 $context_numBits++;
160             }
161 360         483 delete $context_dictionaryToCreate{$context_w};
162             }
163             else
164             {
165 4764         5227 $value = $context_dictionary{$context_w};
166 4764         5842 foreach (1..$context_numBits)
167             {
168 45432         48292 $context_data_val = ($context_data_val<<1) | ($value&1);
169 45432 100       53628 if ($context_data_position == $bitsPerChar-1)
170             {
171 5983         6001 $context_data_position = 0;
172 5983         7253 push @context_data, &$getCharFromInt($context_data_val);
173 5983         6974 $context_data_val = 0;
174 39449         38206 } else { $context_data_position++; };
175 45432         47968 $value >>= 1;
176             }
177             }
178 5124         4987 $context_enlargeIn--;
179 5124 100       6935 if ($context_enlargeIn == 0)
180             {
181 36         55 $context_enlargeIn = 2**$context_numBits;
182 36         40 $context_numBits++;
183             }
184             # add wc to the dictionary
185 5124         8229 $context_dictionary{$context_wc} = $context_dictSize++;
186 5124         6630 $context_w = $context_c;
187             }
188              
189             # output the code for w.
190 6 50       547 if ($context_w ne "")
191             {
192 6 50       17 if (defined $context_dictionaryToCreate{$context_w})
193             {
194 0 0       0 if ((ord substr $context_w,0,1) < 256)
195             {
196 0         0 foreach (1..$context_numBits)
197             {
198 0         0 $context_data_val <<= 1;
199 0 0       0 if ($context_data_position == $bitsPerChar-1)
200             {
201 0         0 $context_data_position = 0;
202 0         0 push @context_data, &$getCharFromInt($context_data_val);
203 0         0 $context_data_val = 0;
204 0         0 } else { $context_data_position++; }
205             }
206 0         0 $value = ord substr $context_w,0,1;
207 0         0 foreach (1..8)
208             {
209 0         0 $context_data_val = ($context_data_val<<1) | ($value&1);
210 0 0       0 if ($context_data_position == $bitsPerChar-1)
211             {
212 0         0 $context_data_position = 0;
213 0         0 push @context_data, &$getCharFromInt($context_data_val);
214 0         0 $context_data_val = 0;
215 0         0 } else { $context_data_position++; };
216 0         0 $value >>= 1;
217             }
218             }
219             else
220             {
221 0         0 $value = 1;
222 0         0 foreach (1..$context_numBits)
223             {
224 0         0 $context_data_val = ($context_data_val<<1) | $value;
225 0 0       0 if ($context_data_position == $bitsPerChar-1)
226             {
227 0         0 $context_data_position = 0;
228 0         0 push @context_data, &$getCharFromInt($context_data_val);
229 0         0 $context_data_val = 0;
230 0         0 } else { $context_data_position++; };
231 0         0 $value = 0;
232             }
233 0         0 $value = ord substr $context_w,0,1;
234 0         0 foreach (1..16)
235             {
236 0         0 $context_data_val = ($context_data_val<<1) | ($value&1);
237 0 0       0 if ($context_data_position == $bitsPerChar-1)
238             {
239 0         0 $context_data_position = 0;
240 0         0 push @context_data, &$getCharFromInt($context_data_val);
241 0         0 $context_data_val = 0;
242 0         0 } else { $context_data_position++; };
243 0         0 $value >>= 1;
244             }
245             }
246 0         0 $context_enlargeIn--;
247 0 0       0 if ($context_enlargeIn == 0)
248             {
249 0         0 $context_enlargeIn = 2**$context_numBits;
250 0         0 $context_numBits++;
251             }
252 0         0 delete $context_dictionaryToCreate{$context_w};
253             }
254             else
255             {
256 6         10 $value = $context_dictionary{$context_w};
257 6         15 foreach (1..$context_numBits)
258             {
259 60         68 $context_data_val = ($context_data_val<<1) | ($value&1);
260 60 100       80 if ($context_data_position == $bitsPerChar-1)
261             {
262 7         9 $context_data_position = 0;
263 7         14 push @context_data, &$getCharFromInt($context_data_val);
264 7         10 $context_data_val = 0;
265 53         56 } else { $context_data_position++; };
266 60         67 $value >>= 1;
267             }
268             }
269 6         9 $context_enlargeIn--;
270 6 50       17 if ($context_enlargeIn == 0)
271             {
272 0         0 $context_enlargeIn = 2**$context_numBits;
273 0         0 $context_numBits++;
274             }
275             }
276              
277             # mark the end of the stream
278 6         9 $value = 2;
279 6         13 foreach (1..$context_numBits)
280             {
281 60         81 $context_data_val = ($context_data_val<<1) | ($value&1);
282 60 100       78 if ($context_data_position == $bitsPerChar-1)
283             {
284 7         8 $context_data_position = 0;
285 7         11 push @context_data, &$getCharFromInt($context_data_val);
286 7         10 $context_data_val = 0;
287 53         55 } else { $context_data_position++; };
288 60         63 $value >>= 1;
289             }
290              
291             # flush the last char
292 6         10 do { $context_data_val <<= 1; } until $context_data_position++ == $bitsPerChar-1;
  26         54  
293 6         13 push @context_data, &$getCharFromInt($context_data_val);
294              
295 6         1418 return pack "A*"x@context_data, @context_data;
296             }
297              
298             sub _decompress {
299 6     6   24 my ($length, $resetValue, $getNextValue) = @_;
300              
301 6         7 my %dictionary;
302 6         10 my $enlargeIn = 4;
303 6         8 my $dictSize = 4;
304 6         8 my $numBits = 3;
305 6         9 my $entry = "";
306 6         7 my @result;
307              
308             my %data;
309 6         14 $data{val} = &$getNextValue(0);
310 6         19 $data{position} = $resetValue;
311 6         17 $data{index} = 1;
312 6         30 @dictionary{0..2} = 0..2;
313              
314 6         12 my ($w, $c) = (0, 0);
315 6         12 my ($bits, $maxpower, $power) = (0, 2**2, 1);
316 6         10 do {{
317 12 50       15 $bits |= (($data{val}&$data{position})>0 ? 1:0)*$power;
  12         26  
318 12 50       41 next if $data{position} >>= 1;
319 0         0 $data{position} = $resetValue;
320 0         0 $data{val} = &$getNextValue($data{index}++);
321             }} until ($power<<=1) == $maxpower;
322              
323 6 50       13 if ($bits == 0)
    0          
    0          
324             {
325 6         10 ($bits, $maxpower, $power) = (0, 2**8, 1);
326 6         9 do {{
327 48 100       46 $bits |= (($data{val}&$data{position})>0 ? 1:0)*$power;
  48         76  
328 48 100       90 next if $data{position} >>= 1;
329 4         6 $data{position} = $resetValue;
330 4         11 $data{val} = &$getNextValue($data{index}++);
331             }} until ($power<<=1) == $maxpower;
332 6         10 $c = chr $bits;
333             }
334             elsif ($bits == 1)
335             {
336 0         0 ($bits, $maxpower, $power) = (0, 2**16, 1);
337 0         0 do {{
338 0 0       0 $bits |= (($data{val}&$data{position})>0 ? 1:0)*$power;
  0         0  
339 0 0       0 next if $data{position} >>= 1;
340 0         0 $data{position} = $resetValue;
341 0         0 $data{val} = &$getNextValue($data{index}++);
342             }} until ($power<<=1) == $maxpower;
343 0         0 $c = chr $bits;
344             }
345 0         0 elsif ($bits == 2) { return; }
346              
347             # print(bits)
348 6         13 $dictionary{3} = $w=$c;
349 6         11 push @result, $c;
350              
351 6         9 do {
352 5130 50       7693 return if $data{index} > $length;
353              
354 5130         7359 ($bits, $maxpower, $power) = (0, 2**$numBits, 1);
355 5130         5187 do {{
356 48126 100       48433 $bits |= (($data{val}&$data{position})>0 ? 1:0)*$power;
  48126         65520  
357 48126 100       79706 next if $data{position} >>= 1;
358 6325         6845 $data{position} = $resetValue;
359 6325         8707 $data{val} = &$getNextValue($data{index}++);
360             }} until ($power<<=1) == $maxpower;
361              
362 5130 100       9828 if (($c=$bits) == 0)
    50          
    100          
363             {
364 354         481 ($bits, $maxpower, $power) = (0, 2**8, 1);
365 354         384 do {{
366 2832 100       2871 $bits |= (($data{val}&$data{position})>0 ? 1:0)*$power;
  2832         4111  
367 2832 100       4769 next if $data{position} >>= 1;
368 399         431 $data{position} = $resetValue;
369 399         562 $data{val} = &$getNextValue($data{index}++);
370             }} until ($power<<=1) == $maxpower;
371 354         384 $c = $dictSize; $enlargeIn--;
  354         382  
372 354         754 $dictionary{$dictSize++} = chr $bits;
373             }
374             elsif ($bits == 1)
375             {
376 0         0 ($bits, $maxpower, $power) = (0, 2**16, 1);
377 0         0 do {{
378 0 0       0 $bits |= (($data{val}&$data{position})>0 ? 1:0)*$power;
  0         0  
379 0 0       0 next if $data{position} >>= 1;
380 0         0 $data{position} = $resetValue;
381 0         0 $data{val} = &$getNextValue($data{index}++);
382             }} until ($power<<=1) == $maxpower;
383 0         0 $c = $dictSize; $enlargeIn--;
  0         0  
384 0         0 $dictionary{$dictSize++} = chr $bits;
385             }
386 6         2017 elsif ($bits == 2) { return pack "A*"x@result, @result; };
387              
388 5124 100       6936 $enlargeIn = 2**$numBits++ unless $enlargeIn;
389              
390 5124 100       7763 if (defined $dictionary{$c}) { $entry = $dictionary{$c}; }
  5121         6080  
391 3 50       5 else { return unless $c == $dictSize; $entry = $w.substr $w,0,1; }
  3         5  
392 5124         6969 push @result, $entry;
393              
394             # add w+entry[0] to the dictionary
395 5124         10835 $dictionary{$dictSize++} = $w.substr $entry,0,1;
396 5124         5839 $w = $entry; $enlargeIn--;
  5124         5422  
397              
398 5124 100       8384 $enlargeIn = 2**$numBits++ unless $enlargeIn;
399             } while 1;
400             }
401              
402             sub _rpad {
403 2     2   8 my $str = shift;
404 2         11 my $len = 4*int((length($str)-1)/4)+4;
405 2         67 return substr $str."===", 0, $len;
406             }
407              
408             sub getBaseValue {
409 5670     5670 0 11014 my ($alphabet, $character) = @_;
410             eval {
411 2         29 @{$baseReverseDic{$alphabet}}{split//,$alphabet} = (0..length $alphabet);
  2         103  
412 5670 100       9937 } unless defined $baseReverseDic{$alphabet};
413              
414 5670         13262 return $baseReverseDic{$alphabet}{$character};
415             }
416              
417             1;
418             __END__