File Coverage

blib/lib/Data/BitStream/WordVec.pm
Criterion Covered Total %
statement 246 246 100.0
branch 114 124 91.9
condition 31 39 79.4
subroutine 17 17 100.0
pod 10 10 100.0
total 418 436 95.8


line stmt bran cond sub pod time code
1             package Data::BitStream::WordVec;
2 28     28   27824 use strict;
  28         71  
  28         1213  
3 28     28   170 use warnings;
  28         51  
  28         1515  
4             BEGIN {
5 28     28   658 $Data::BitStream::WordVec::AUTHORITY = 'cpan:DANAJ';
6             }
7             BEGIN {
8 28     28   442 $Data::BitStream::WordVec::VERSION = '0.08';
9             }
10              
11 28     28   148 use Moo;
  28         47  
  28         190  
12              
13             with 'Data::BitStream::Base',
14             'Data::BitStream::Code::Gamma',
15             'Data::BitStream::Code::Delta',
16             'Data::BitStream::Code::Omega',
17             'Data::BitStream::Code::Levenstein',
18             'Data::BitStream::Code::EvenRodeh',
19             'Data::BitStream::Code::Fibonacci',
20             'Data::BitStream::Code::Golomb',
21             'Data::BitStream::Code::Rice',
22             'Data::BitStream::Code::GammaGolomb',
23             'Data::BitStream::Code::ExponentialGolomb',
24             'Data::BitStream::Code::Baer',
25             'Data::BitStream::Code::BoldiVigna',
26             'Data::BitStream::Code::ARice',
27             'Data::BitStream::Code::Additive',
28             'Data::BitStream::Code::Comma',
29             'Data::BitStream::Code::Taboo',
30             'Data::BitStream::Code::BER',
31             'Data::BitStream::Code::Varint',
32             'Data::BitStream::Code::StartStop';
33              
34             has '_vec' => (is => 'rw', default => sub{''});
35              
36             # Access the raw vector.
37             sub _vecref {
38 228132     228132   318173 my $self = shift;
39 228132         1169412 \$self->{_vec};
40             }
41             after 'erase' => sub {
42             my $self = shift;
43             $self->_vec('');
44             1;
45             };
46              
47              
48             sub read {
49 112204     112204 1 153389 my $self = shift;
50 112204 100       316580 $self->error_stream_mode('read') if $self->writing;
51 112202         147275 my $bits = shift;
52 112202 100 66     2099753 $self->error_code('param', 'bits must be in range 1-' . $self->maxbits)
      100        
53             unless defined $bits && $bits > 0 && $bits <= $self->maxbits;
54 112196   66     330911 my $peek = (defined $_[0]) && ($_[0] eq 'readahead');
55              
56 112196         206649 my $pos = $self->pos;
57 112196         164736 my $len = $self->len;
58 112196 100       255285 return if $pos >= $len;
59 112038 100 100     472678 $self->error_off_stream if !$peek && ($pos+$bits) > $len;
60              
61 112031         158904 my $wpos = $pos >> 5; # / 32
62 112031         129734 my $bpos = $pos & 0x1F; # % 32
63 112031         232830 my $rvec = $self->_vecref;
64 112031         160739 my $val = 0;
65              
66 112031 100       231799 if ( $bpos <= (32-$bits) ) { # optimize single word read
67 86644         179324 $val = (vec($$rvec, $wpos, 32) >> (32-$bpos-$bits))
68             & (0xFFFFFFFF >> (32-$bits));
69             } else {
70 25387         29379 my $bits_left = $bits;
71 25387         51212 while ($bits_left > 0) {
72 62794 100       116794 my $epos = (($bpos+$bits_left) > 32) ? 32 : $bpos+$bits_left;
73 62794         69472 my $bits_to_read = $epos - $bpos; # between 0 and 32
74 62794         78522 my $v = vec($$rvec, $wpos, 32);
75 62794         78346 $v >>= (32-$epos);
76 62794         69325 $v &= (0xFFFFFFFF >> (32-$bits_to_read));
77              
78 62794         85735 $val = ($val << $bits_to_read) | $v;
79              
80 62794         76951 $wpos++;
81 62794         73066 $bits_left -= $bits_to_read;
82 62794         161079 $bpos = 0;
83             }
84             }
85              
86 112031 100       317731 $self->_setpos( $pos + $bits ) unless $peek;
87 112031         371205 $val;
88             }
89             sub write {
90 54117     54117 1 78229 my $self = shift;
91 54117 100       152168 $self->error_stream_mode('write') unless $self->writing;
92 54116         64805 my $bits = shift;
93 54116 100 66     235234 $self->error_code('param', 'bits must be > 0') unless defined $bits && $bits > 0;
94 54114         84304 my $val = shift;
95 54114 50 33     240857 $self->error_code('zeroval') unless defined $val and $val >= 0;
96              
97 54114         94466 my $len = $self->len;
98 54114         77444 my $new_len = $len + $bits;
99              
100 54114 100       136715 if ($val == 0) { # optimize writing 0
101 5642         10719 $self->_setlen( $new_len );
102 5642         20700 return 1;
103             }
104              
105 48472 100       109217 if ($val == 1) { $len += $bits-1; $bits = 1; }
  1749         2405  
  1749         2441  
106              
107 48472 100       168187 $self->error_code('param', 'bits must be <= ' . $self->maxbits) if $bits > $self->maxbits;
108              
109 48471         82592 my $wpos = $len >> 5; # / 32
110 48471         65000 my $bpos = $len & 0x1F; # % 32
111 48471         103773 my $rvec = $self->_vecref;
112              
113 48471         69042 my $wlen = 32-$bits;
114 48471 100       89949 if ( $bpos <= $wlen ) { # optimize single word write
115 31070         108537 vec($$rvec, $wpos, 32) |= ($val & (0xFFFFFFFF >> $wlen)) << ($wlen-$bpos);
116             } else {
117 17401         38782 while ($bits > 0) {
118 42236 100       87867 my $epos = (($bpos+$bits) > 32) ? 32 : $bpos+$bits;
119 42236         55624 my $bits_to_write = $epos - $bpos; # between 0 and 32
120              
121             # get rid of parts of val to the right that we aren't writing yet
122 42236         52592 my $val_to_write = $val >> ($bits - $bits_to_write);
123             # get rid of parts of val to the left
124 42236         57008 $val_to_write &= 0xFFFFFFFF >> (32-$bits_to_write);
125              
126 42236         115712 vec($$rvec, $wpos, 32) |= ($val_to_write << (32-$epos));
127              
128 42236         74111 $wpos++;
129 42236         55431 $bits -= $bits_to_write;
130 42236         105000 $bpos = 0;
131             }
132             }
133              
134 48471         107522 $self->_setlen( $new_len );
135 48471         197150 1;
136             }
137              
138             sub put_unary {
139 5874     5874 1 11745 my $self = shift;
140 5874 50       16092 $self->error_stream_mode('write') unless $self->writing;
141              
142 5874         9785 my $len = $self->len;
143 5874         10669 my $rvec = $self->_vecref;
144              
145 5874         10377 foreach my $val (@_) {
146 6476 100 100     25906 $self->error_code('zeroval') unless defined $val and $val >= 0;
147             # We're writing $val 0's, so just skip them
148 6474         7312 $len += $val;
149 6474         8308 my $wpos = $len >> 5; # / 32
150 6474         8132 my $bpos = $len & 0x1F; # % 32
151              
152             # Write a 1 in the correct position
153 6474         18709 vec($$rvec, $wpos, 32) |= (1 << ((32-$bpos) - 1));
154 6474         16737 $len++;
155             }
156              
157 5872         12067 $self->_setlen( $len );
158 5872         14136 1;
159             }
160              
161             sub get_unary {
162 40076     40076 1 59919 my $self = shift;
163 40076 50       107268 $self->error_stream_mode('read') if $self->writing;
164 40076         49568 my $count = shift;
165 40076 100       74552 if (!defined $count) { $count = 1; }
  40063 100       55287  
    100          
166 9         19 elsif ($count < 0) { $count = ~0; } # Get everything
167 2         9 elsif ($count == 0) { return; }
168              
169 40074         73759 my $pos = $self->pos;
170 40074         61260 my $len = $self->len;
171 40074         82403 my $rvec = $self->_vecref;
172              
173 40074         52363 my @vals;
174 40074         91213 while ($count-- > 0) {
175 40702 100       83262 last if $pos >= $len;
176 40523         45345 my $onepos = $pos;
177 40523         50910 my $wpos = $pos >> 5; # / 32
178 40523         49322 my $bpos = $pos & 0x1F; # % 32
179             # Get the current word, shifted left so current position is leftmost.
180 40523         69364 my $v = ( vec($$rvec, $wpos++, 32) << $bpos ) & 0xFFFFFFFF;
181             # Optimize common small values.
182 40523 100       81464 if ($v & 0xF0000000) {
183 22521 100       71707 my $val = ($v & 0x80000000) ? 0 :
    100          
    100          
184             ($v & 0x40000000) ? 1 :
185             ($v & 0x20000000) ? 2 : 3;
186 22521         27440 push @vals, $val;
187 22521         30347 $pos += $val+1;
188 22521         66642 next;
189             }
190 18002 100       36528 if ($v == 0) {
191             # If this word is 0, advance words until we find one that is non-zero.
192 6853         9294 $onepos += (32-$bpos);
193 6853         9531 $v = vec($$rvec, $wpos++, 32);
194 6853 100       12985 if ($v == 0) {
195             # We've seen at least 33 zeros. Start trying to scan quickly.
196 1526         1670 $onepos += 32;
197 1526         1762 my $startwpos = $wpos;
198 1526         2033 my $lastwpos = ($len+31) >> 5;
199              
200             # 100us: //g followed by pos
201             # 34us: unpack("%32W*", substr($$rvec,$wpos*4,32)) == 0
202             # 27us: substr($$rvec,$wpos*4,32) =~ tr/\000/\000/ == 32
203             # 24us: substr($$rvec,$wpos*4,32) eq "\x00 .... \x00"
204             # 12us: tr with 128 then 32
205              
206 1526   100     11783 $wpos += 32 while ( (($wpos+31) < $lastwpos) && (substr($$rvec,$wpos*4,128) =~ tr/\000/\000/ == 128) );
207 1526   100     10152 $wpos += 8 while ( (($wpos+7) < $lastwpos) && (substr($$rvec,$wpos*4,32) =~ tr/\000/\000/ == 32) );
208 1526   100     20050 $wpos++ while ($wpos <= $lastwpos && vec($$rvec, $wpos, 32) == 0);
209 1526         2068 $v = vec($$rvec, $wpos, 32);
210 1526         2820 $onepos += 32*($wpos - $startwpos);
211             }
212             }
213 18002 100       32532 $self->error_off_stream() if $onepos >= $len;
214 17972 50       34878 $self->error_code('assert', "v must be 0") if $v == 0;
215             # This word is non-zero. Find the leftmost set bit.
216 17972 100       33026 if (($v & 0xFFFF0000) == 0) { $onepos += 16; $v <<= 16; }
  1845         2095  
  1845         2028  
217 17972 100       32921 if (($v & 0xFF000000) == 0) { $onepos += 8; $v <<= 8; }
  6584         7580  
  6584         8269  
218 17972 100       33287 if (($v & 0xF0000000) == 0) { $onepos += 4; $v <<= 4; }
  9175         10015  
  9175         10364  
219 17972 100       31776 if (($v & 0xC0000000) == 0) { $onepos += 2; $v <<= 2; }
  5750         12471  
  5750         6268  
220 17972 100       43267 if (($v & 0x80000000) == 0) { $onepos += 1; $v <<= 1; }
  8819         9023  
  8819         9801  
221 17972         31417 push @vals, $onepos - $pos;
222 17972         48069 $pos = $onepos+1;
223             }
224 40044         76472 $self->_setpos( $pos );
225 40044 100       145880 wantarray ? @vals : $vals[-1];
226             }
227              
228             # This is pretty important for speed
229             sub put_gamma {
230 13478     13478 1 43183 my $self = shift;
231 13478 100       44450 $self->error_stream_mode('write') unless $self->writing;
232              
233 13477         22103 my $len = $self->len;
234 13477         26913 my $rvec = $self->_vecref;
235 13477         41801 my $maxval = $self->maxval();
236              
237 13477         25097 foreach my $val (@_) {
238 17483 100 100     79583 $self->error_code('zeroval') unless defined $val and $val >= 0;
239              
240 17481         23343 my $wpos = $len >> 5; # / 32
241 17481         24083 my $bpos = $len & 0x1F; # % 32
242              
243 17481 100       46123 if ($val == 0) { # Quickly set zero
    100          
244 2296         7099 vec($$rvec, $wpos, 32) |= (1 << ((32-$bpos) - 1));
245 2296         3552 $len++;
246 2296         5720 next;
247             } elsif ($val == $maxval) { # Encode ~0 as unary maxbits
248 2         10 $len += $self->maxbits;
249 2         3 $wpos = $len >> 5; # / 32
250 2         4 $bpos = $len & 0x1F; # % 32
251 2         6 vec($$rvec, $wpos, 32) |= (1 << ((32-$bpos) - 1));
252 2         8 $len++;
253 2         5 next;
254             }
255              
256 15183         16784 my $bits;
257 15183 100       26687 if ($val < 511) {
258 11221 100       49942 $bits = ($val < 1) ? 1 :
    100          
    100          
    100          
    100          
    100          
    100          
    50          
259             ($val < 3) ? 3 :
260             ($val < 7) ? 5 :
261             ($val < 15) ? 7 :
262             ($val < 31) ? 9 :
263             ($val < 63) ? 11 :
264             ($val <127) ? 13 :
265             ($val <255) ? 15 : 17;
266             } else {
267 3962         4965 $bits = 2*9+1;
268 3962         5022 my $v = ($val+1) >> 9;
269 3962         34527 $bits += 2 while ($v >>= 1);
270             }
271              
272             # Quickly insert if the code fits inside a single word
273 15183 100       43180 if ( $bpos <= (32-$bits) ) {
274 9643         273072 vec($$rvec, $wpos, 32) |= ( ($val+1) << ((32-$bpos) - $bits));
275 9643         19210 $len += $bits;
276 9643         36108 next;
277             }
278              
279             # Effectively we're doing:
280             #
281             # $self->put_unary($base);
282             # $self->write($base, $val+1);
283             #
284             # which is equivalent to:
285             #
286             # $self->write($base, 0);
287             # $self->write($base+1, $val+1);
288              
289 5540         6995 my $base = $bits >> 1;
290 5540         6213 $len += $base;
291 5540         6601 $base += 1;
292              
293             # write value in binary using $base bits
294             {
295 5540         5491 my $v = $val+1;
  5540         7126  
296 5540         5933 my $bits = $base;
297 5540         7444 $wpos = $len >> 5; # / 32
298 5540         6924 $bpos = $len & 0x1F; # % 32
299              
300 5540         11162 while ($bits > 0) {
301 9110 100       16772 my $epos = (($bpos+$bits) > 32) ? 32 : $bpos+$bits;
302 9110         10742 my $bits_to_write = $epos - $bpos; # between 0 and 32
303              
304             # get rid of parts of val to the right that we aren't writing yet
305 9110         10699 my $val_to_write = $v >> ($bits - $bits_to_write);
306             # get rid of parts of val to the left
307 9110         10835 $val_to_write &= 0xFFFFFFFF >> (32-$bits_to_write);
308              
309 9110         23701 vec($$rvec, $wpos, 32) |= ($val_to_write << (32-$epos));
310              
311 9110         14527 $wpos++;
312 9110         10258 $bits -= $bits_to_write;
313 9110         21340 $bpos = 0;
314             }
315 5540         15364 $len += $base;
316             }
317             }
318 13475         32117 $self->_setlen( $len );
319 13475         43961 1;
320             }
321              
322              
323             # Using default read_string
324              
325             sub put_string {
326 2457     2457 1 4676 my $self = shift;
327 2457 100       6944 $self->error_stream_mode('write') unless $self->writing;
328              
329 2456         4328 my $len = $self->len;
330 2456         4848 my $rvec = $self->_vecref;
331              
332 2456         4482 foreach my $str (@_) {
333 2456 50       5008 next unless defined $str;
334 2456 50       5550 $self->error_code('string') if $str =~ tr/01//c;
335 2456         3540 my $bits = length($str);
336 2456 50       5153 next unless $bits > 0;
337              
338 2456         2788 my $wpos = $len >> 5;
339 2456         2860 my $bpos = $len & 0x1F;
340 2456         2452 my $bits_to_write = $bits;
341             # First get the part that fills the last word.
342 2456 100       4599 my $first_bits = ($bpos == 0) ? 0 : 32-$bpos;
343 2456 100       3928 if ($bpos > 0) {
344 2215         6276 my $newvec = pack("B*", substr($str, 0, $first_bits) );
345 2215         6761 vec($$rvec, $wpos++, 32) |= vec($newvec, 0, 32) >> $bpos;
346 2215         3942 $bits_to_write -= $first_bits;
347             } else {
348             # The fast part below does a string concat, which means we have to
349             # make sure the vector is extended properly. This happens if we have
350             # written zeros with the write() method, which just extends $len.
351 241 100       741 vec($$rvec, $wpos-1, 32) |= 0 if $wpos > 0;
352             }
353             # Now put the rest of the string in place quickly.
354 2456 100       5668 if ($bits_to_write > 0) {
355 2006         5030 $$rvec .= pack("B*", substr($str, $first_bits));
356             }
357              
358 2456         6085 $len += $bits;
359             }
360 2456         5329 $self->_setlen($len);
361 2456         7932 1;
362             }
363              
364             sub to_string {
365 2874     2874 1 12856 my $self = shift;
366 2874         7346 $self->write_close;
367 2874         4843 my $len = $self->len;
368 2874         5863 my $rvec = $self->_vecref;
369 2874         13029 my $str = unpack("B$len", $$rvec);
370             # unpack sometimes drops 0 bits at the end, so we need to check and add them.
371 2874         4402 my $strlen = length($str);
372 2874 50       6036 $self->error_code('assert', "string length") if $strlen > $len;
373 2874 100       5122 if ($strlen < $len) {
374 7         19 $str .= "0" x ($len - $strlen);
375             }
376 2874         9221 $str;
377             }
378             sub from_string {
379 2867     2867 1 35114 my $self = shift;
380 2867         3731 my $str = shift;
381 2867 50       10244 $self->error_code('string') if $str =~ tr/01//c;
382 2867   66     6258 my $bits = shift || length($str);
383 2867         7725 $self->write_open;
384              
385 2867         6263 my $rvec = $self->_vecref;
386 2867         8556 $$rvec = pack("B*", $str);
387 2867         5786 $self->_setlen($bits);
388              
389 2867         11566 $self->rewind_for_read;
390             }
391              
392             # Our internal format is a big-endian vector, so to_raw and from_raw
393             # are easy. We default to_store and from_store.
394              
395             sub to_raw {
396 5     5 1 815 my $self = shift;
397 5         28 $self->write_close;
398 5         27 my $rvec = $self->_vecref;
399 5         25 return $$rvec;
400             }
401              
402             sub from_raw {
403 3     3 1 7715 my $self = $_[0];
404             # data comes in 2nd argument
405 3   33     29 my $bits = $_[2] || 8*length($_[1]);
406              
407 3         18 $self->write_open;
408              
409 3         12 my $rvec = $self->_vecref;
410 3         10 $$rvec = $_[1];
411              
412 3         180 $self->_setlen( $bits );
413 3         14 $self->rewind_for_read;
414             }
415              
416             __PACKAGE__->meta->make_immutable;
417 28     28   87635 no Moo;
  28         97  
  28         195  
418             1;
419              
420             # ABSTRACT: A Vector-32 implementation of Data::BitStream
421              
422             =pod
423              
424             =head1 NAME
425              
426             Data::BitStream::WordVec - A Vector-32 implementation of Data::BitStream
427              
428             =head1 SYNOPSIS
429              
430             use Data::BitStream::WordVec;
431             my $stream = Data::BitStream::WordVec->new;
432             $stream->put_gamma($_) for (1 .. 20);
433             $stream->rewind_for_read;
434             my @values = $stream->get_gamma(-1);
435              
436             =head1 DESCRIPTION
437              
438             An implementation of L. See the documentation for that
439             module for many more examples, and L for the API.
440             This document only describes the unique features of this implementation,
441             which is of limited value to people purely using L.
442              
443             This implementation uses a Perl C to store the data. The vector is
444             accessed in 32-bit units, which makes it safe for 32-bit and 64-bit machines
445             as well as reasonably time efficient.
446              
447             This is the default L implementation.
448              
449             =head2 DATA
450              
451             =over 4
452              
453             =item B< _vec >
454              
455             A private scalar holding the data as a vector.
456              
457             =back
458              
459             =head2 CLASS METHODS
460              
461             =over 4
462              
463             =item B< _vecref >
464              
465             Retrieves a reference to the private vector.
466              
467             =item I B< erase >
468              
469             Sets the private vector to the empty string C<''>.
470              
471             =item B< read >
472              
473             =item B< write >
474              
475             =item B< put_unary >
476              
477             =item B< get_unary >
478              
479             =item B< put_gamma >
480              
481             =item B< put_string >
482              
483             =item B< to_string >
484              
485             =item B< from_string >
486              
487             =item B< from_raw >
488              
489             =item B< to_raw >
490              
491             These methods have custom implementations.
492              
493             =back
494              
495             =head2 ROLES
496              
497             The following roles are included.
498              
499             =over 4
500              
501             =item L
502              
503             =item L
504              
505             =item L
506              
507             =item L
508              
509             =item L
510              
511             =item L
512              
513             =item L
514              
515             =item L
516              
517             =item L
518              
519             =item L
520              
521             =item L
522              
523             =item L
524              
525             =item L
526              
527             =item L
528              
529             =item L
530              
531             =item L
532              
533             =item L
534              
535             =item L
536              
537             =back
538              
539             =head1 SEE ALSO
540              
541             =over 4
542              
543             =item L
544              
545             =item L
546              
547             =item L
548              
549             =back
550              
551             =head1 AUTHORS
552              
553             Dana Jacobsen Edana@acm.orgE
554              
555             =head1 COPYRIGHT
556              
557             Copyright 2011-2012 by Dana Jacobsen Edana@acm.orgE
558              
559             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
560              
561             =cut