File Coverage

blib/lib/Data/BitStream/String.pm
Criterion Covered Total %
statement 220 224 98.2
branch 86 122 70.4
condition 15 36 41.6
subroutine 23 23 100.0
pod 14 14 100.0
total 358 419 85.4


line stmt bran cond sub pod time code
1             package Data::BitStream::String;
2 22     22   816136 use strict;
  22         61  
  22         749  
3 22     22   116 use warnings;
  22         42  
  22         865  
4             BEGIN {
5 22     22   556 $Data::BitStream::String::AUTHORITY = 'cpan:DANAJ';
6             }
7             BEGIN {
8 22     22   335 $Data::BitStream::String::VERSION = '0.08';
9             }
10              
11 22     22   25102 use Moo;
  22         500936  
  22         162  
12              
13             with 'Data::BitStream::Base',
14             'Data::BitStream::Code::Gamma', # implemented here
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 '_str' => (is => 'rw', default => sub{''});
35              
36             # Evil, reference to underlying string
37             sub _strref {
38 185662     185662   240514 my $self = shift;
39 185662         1295870 \$self->{_str};
40             }
41             after 'erase' => sub {
42             my $self = shift;
43             $self->_str('');
44             1;
45             };
46             sub read {
47 80401     80401 1 102522 my $self = shift;
48 80401 50       218176 $self->error_stream_mode('read') if $self->writing;
49 80401         110959 my $bits = shift;
50 80401 50 33     494800 $self->error_code('param', 'bits must be in range 1-' . $self->maxbits)
      33        
51             unless defined $bits && $bits > 0 && $bits <= $self->maxbits;
52 80401   66     221440 my $peek = (defined $_[0]) && ($_[0] eq 'readahead');
53              
54 80401         152844 my $pos = $self->pos;
55 80401         113967 my $len = $self->len;
56 80401 100       153125 return if $pos >= $len;
57 80304 50 66     322948 $self->error_off_stream if !$peek && ($pos+$bits) > $len;
58              
59 80304         152311 my $rstr = $self->_strref;
60 80304         145791 my $str = substr($$rstr, $pos, $bits);
61             { # This is for readahead. We should use a write-close method instead.
62 80304         81845 my $strlen = length($str);
  80304         115020  
63 80304 100       183332 $str .= "0" x ($bits-$strlen) if $strlen < $bits;
64             }
65 80304         80053 my $val;
66             # We could do something like:
67             # $val = unpack("N", pack("B32", substr("0" x 32 . $str, -32)));
68             # and combine for more than 32-bit values, but this works better.
69             {
70 22     22   62932 no warnings 'portable';
  22         54  
  22         35017  
  80304         89218  
71 80304         166047 $val = oct "0b$str";
72             }
73 80304 100       286704 $self->_setpos( $pos + $bits ) unless $peek;
74 80304         259254 $val;
75             }
76             sub write {
77 40890     40890 1 52070 my $self = shift;
78 40890 50       103205 $self->error_stream_mode('write') unless $self->writing;
79 40890         49822 my $bits = shift;
80 40890 50 33     162051 $self->error_code('param', 'bits must be > 0') unless defined $bits && $bits > 0;
81 40890         47375 my $val = shift;
82 40890 50 33     166131 $self->error_code('zeroval') unless defined $val and $val >= 0;
83              
84 40890         98646 my $rstr = $self->_strref;
85              
86 40890 100       101267 if ($val == 0) {
    100          
87 5594         11125 $$rstr .= '0' x $bits;
88             } elsif ($val == 1) {
89 1711 100       5592 $$rstr .= '0' x ($bits-1) if $bits > 1;
90 1711         2499 $$rstr .= '1';
91             } else {
92              
93 33585 50       98079 $self->error_code('param', 'bits must be <= ' . $self->maxbits) if $bits > $self->maxbits;
94              
95             # The following is typically fastest with 5.9.2 and later:
96             #
97             # $$rstr .= scalar reverse unpack("b$bits",($bits>32) ? pack("Q>",$val)
98             # : pack("V" ,$val));
99             #
100             # With 5.9.2 and later on a 64-bit machine, this will work quickly:
101             #
102             # $$rstr .= substr(unpack("B64", pack("Q>", $val)), -$bits);
103             #
104             # This is the best compromise that works with 5.8.x, BE/LE, and 32-bit:
105 33585 100       74480 if ($bits > 32) {
106             #$$rstr .= substr(unpack("B64", pack("Q>", $val)), -$bits); # needs v5.9.2
107 2437         16506 $$rstr .= substr(unpack("B32", pack("N", $val>>32)), -($bits-32))
108             . unpack("B32", pack("N", $val));
109             } else {
110             #$$rstr .= substr(unpack("B32", pack("N", $val)), -$bits);
111 31148         138231 $$rstr .= scalar reverse unpack("b$bits", pack("V", $val));
112             }
113             }
114              
115 40890         98517 $self->_setlen( $self->len + $bits);
116 40890         151550 1;
117             }
118              
119             sub put_unary {
120 5861     5861 1 11094 my $self = shift;
121 5861 50       20154 $self->error_stream_mode('write') unless $self->writing;
122              
123 5861         12855 my $rstr = $self->_strref;
124 5861         10086 my $len = $self->len;
125              
126 5861         9353 foreach my $val (@_) {
127 6463 50 33     26945 $self->error_code('zeroval') unless defined $val and $val >= 0;
128 6463         14840 $$rstr .= '0' x ($val) . '1';
129 6463         14409 $len += $val+1;
130             }
131              
132 5861         12876 $self->_setlen( $len );
133 5861         13192 1;
134             }
135             sub get_unary {
136 20356     20356 1 30067 my $self = shift;
137 20356 50       46562 $self->error_stream_mode('read') if $self->writing;
138 20356         22448 my $count = shift;
139 20356 100       37076 if (!defined $count) { $count = 1; }
  20343 100       24663  
    100          
140 9         26 elsif ($count < 0) { $count = ~0; } # Get everything
141 2         7 elsif ($count == 0) { return; }
142              
143 20354         31898 my $pos = $self->pos;
144 20354         28525 my $len = $self->len;
145 20354         36697 my $rstr = $self->_strref;
146              
147 20354         23978 my @vals;
148 20354         45784 while ($count-- > 0) {
149 20982 100       43446 last if $pos >= $len;
150 20914         31069 my $onepos = index( $$rstr, '1', $pos );
151 20914 50       36209 $self->error_off_stream() if $onepos == -1;
152 20914         23481 my $val = $onepos - $pos;
153 20914         27743 $pos = $onepos + 1;
154 20914         54461 push @vals, $val;
155             }
156 20354         38345 $self->_setpos( $pos );
157 20354 100       68275 wantarray ? @vals : $vals[-1];
158             }
159              
160             sub put_unary1 {
161 4823     4823 1 9573 my $self = shift;
162 4823 50       12292 $self->error_stream_mode('write') unless $self->writing;
163              
164 4823         9064 my $rstr = $self->_strref;
165 4823         8721 my $len = $self->len;
166              
167 4823         7527 foreach my $val (@_) {
168 5408 50 33     20676 $self->error_code('zeroval') unless defined $val and $val >= 0;
169 5408         12924 $$rstr .= '1' x ($val) . '0';
170 5408         12180 $len += $val+1;
171             }
172              
173 4823         10170 $self->_setlen( $len );
174 4823         11837 1;
175             }
176             sub get_unary1 {
177 4879     4879 1 9764 my $self = shift;
178 4879 50       12778 $self->error_stream_mode('read') if $self->writing;
179 4879         5762 my $count = shift;
180 4879 100       9221 if (!defined $count) { $count = 1; }
  4871 50       6467  
    0          
181 8         22 elsif ($count < 0) { $count = ~0; } # Get everything
182 0         0 elsif ($count == 0) { return; }
183              
184 4879         11452 my $pos = $self->pos;
185 4879         7120 my $len = $self->len;
186 4879         8559 my $rstr = $self->_strref;
187              
188 4879         6144 my @vals;
189 4879         12001 while ($count-- > 0) {
190 5472 100       11147 last if $pos >= $len;
191 5433         9807 my $onepos = index( $$rstr, '0', $pos );
192 5433 50       10060 $self->error_off_stream() if $onepos == -1;
193 5433         7388 my $val = $onepos - $pos;
194 5433         6113 $pos = $onepos + 1;
195 5433         13593 push @vals, $val;
196             }
197 4879         8746 $self->_setpos( $pos );
198 4879 100       17644 wantarray ? @vals : $vals[-1];
199             }
200              
201             sub put_gamma {
202 12958     12958 1 40578 my $self = shift;
203 12958 50       44459 $self->error_stream_mode('write') unless $self->writing;
204              
205 12958         23449 my $rstr = $self->_strref;
206 12958         22675 my $len = $self->len;
207 12958         33043 my $maxval = $self->maxval();
208              
209 12958         24365 foreach my $val (@_) {
210 19953 50 33     90980 $self->error_code('zeroval') unless defined $val and $val >= 0;
211 19953         29199 my $vstr;
212 19953 100       63988 if ($val == 0) { $vstr = '1'; }
  2299 100       3371  
    100          
    100          
213 1040         1602 elsif ($val == 1) { $vstr = '010'; }
214 940         1373 elsif ($val == 2) { $vstr = '011'; }
215 2         6 elsif ($val == $maxval) { $vstr = '0' x $self->maxbits . '1'; }
216             else {
217 15672         17491 my $base = 0;
218 15672         16551 { my $v = $val+1; $base++ while ($v >>= 1); }
  15672         19268  
  15672         138809  
219 15672         27301 $vstr = '0' x $base . '1';
220 15672 100       26714 if ($base > 32) {
221 626         3172 $vstr .= substr(unpack("B32", pack("N", ($val+1)>>32)), -($base-32))
222             . unpack("B32", pack("N", $val+1));
223             } else {
224 15046         55911 $vstr .= scalar reverse unpack("b$base", pack("V", $val+1));
225             }
226             }
227 19953         39475 $$rstr .= $vstr;
228 19953         46621 $len += length($vstr);
229             }
230              
231 12958         27472 $self->_setlen( $len );
232 12958         40361 1;
233             }
234              
235             sub get_gamma {
236 13173     13173 1 41219 my $self = shift;
237 13173 50       32665 $self->error_stream_mode('read') if $self->writing;
238 13173         15763 my $count = shift;
239 13173 100       31661 if (!defined $count) { $count = 1; }
  11477 100       15449  
    50          
240 11         27 elsif ($count < 0) { $count = ~0; } # Get everything
241 0         0 elsif ($count == 0) { return; }
242              
243 13173         21786 my $pos = $self->pos;
244 13173         17791 my $len = $self->len;
245 13173         24728 my $rstr = $self->_strref;
246 13173         34198 my $maxbits = $self->maxbits;
247              
248 13173         15131 my @vals;
249 13173         28462 while ($count-- > 0) {
250 18826 100       34936 last if $pos >= $len;
251 18730         29789 my $onepos = index( $$rstr, '1', $pos );
252 18730 50       33802 $self->error_off_stream() if $onepos == -1;
253 18730         23022 my $base = $onepos - $pos;
254 18730         19944 $pos = $onepos + 1;
255 18730 100       52433 if ($base == 0) { push @vals, 0; }
  2441 100       6672  
    50          
256 2         14 elsif ($base == $maxbits) { push @vals, $self->maxval(); }
257 0         0 elsif ($base > $maxbits) { $self->error_code('base', $base); }
258             else {
259 16287 50       32354 $self->error_off_stream() if ($pos+$base) > $len;
260 16287         27920 my $vstr = substr($$rstr, $pos, $base);
261 16287         22089 $pos += $base;
262 16287         16831 my $rval;
263 22     22   166 { no warnings 'portable'; $rval = oct "0b$vstr"; }
  22         43  
  22         16848  
  16287         22042  
  16287         26753  
264 16287         56727 push @vals, ((1 << $base) | $rval)-1;
265             }
266             }
267 13173         24255 $self->_setpos( $pos );
268 13173 100       46892 wantarray ? @vals : $vals[-1];
269             }
270              
271             sub put_string {
272 2415     2415 1 3632 my $self = shift;
273 2415 50       6195 $self->error_stream_mode('write') unless $self->writing;
274              
275 2415         3679 my $len = $self->len;
276 2415         4148 my $rstr = $self->_strref;
277              
278 2415         4110 foreach my $str (@_) {
279 2415 50       4408 next unless defined $str;
280 2415 50       5295 $self->error_code('string') if $str =~ tr/01//c;
281 2415         4091 my $bits = length($str);
282 2415 50       4209 next unless $bits > 0;
283              
284 2415         3722 $$rstr .= $str;
285 2415         5439 $len += $bits;
286             }
287 2415         4967 $self->_setlen( $len );
288 2415         12775 1;
289             }
290             sub read_string {
291 2     2 1 10 my $self = shift;
292 2 50       10 $self->error_stream_mode('read') if $self->writing;
293 2         5 my $bits = shift;
294 2 50 33     17 $self->error_code('param', "bits must be >= 0") unless defined $bits && $bits >= 0;
295              
296 2         7 my $len = $self->len;
297 2         6 my $pos = $self->pos;
298 2 50       7 $self->error_code('short') unless $bits <= ($len - $pos);
299 2         6 my $rstr = $self->_strref;
300              
301 2         8 $self->_setpos( $pos + $bits );
302 2         9 substr($$rstr, $pos, $bits);
303             }
304              
305             # Given the custom read_string and put_string, these aren't really necessary.
306             sub to_string {
307 2877     2877 1 12059 my $self = shift;
308 2877         16698 $self->write_close;
309 2877         9540 $self->_str;
310             }
311             sub from_string {
312 2867     2867 1 31057 my $self = shift;
313 2867         3880 my $str = shift;
314 2867 50       7436 $self->error_code('string') if $str =~ tr/01//c;
315 2867   66     6490 my $bits = shift || length($str);
316 2867         7426 $self->write_open;
317              
318 2867         7614 $self->_str( $str );
319 2867         5400 $self->_setlen( $bits );
320              
321 2867         7359 $self->rewind_for_read;
322             }
323              
324             sub to_raw {
325 5     5 1 910 my $self = shift;
326 5         27 $self->write_close;
327 5         147 return pack("B*", $self->_str);
328             }
329             sub put_raw {
330 3     3 1 8 my $self = shift;
331 3 50       19 $self->error_stream_mode('write') unless $self->writing;
332 3         9 my $vec = shift;
333 3   33     15 my $bits = shift || int((length($vec)+7)/8);
334              
335 3         238 my $str = unpack("B$bits", $vec);
336 3         13 my $strlen = length($str);
337 3 50       13 $self->error_code('assert', "string length") if $strlen > $bits;
338 3 50       11 if ($strlen < $bits) {
339 0         0 $str .= "0" x ($bits - $strlen);
340             }
341              
342 3         24 my $rstr = $self->_strref;
343 3         42 $$rstr .= $str;
344 3         19 $self->_setlen( $self->len + $bits );
345 3         9 1;
346             }
347              
348             # Using default from_raw
349             # Using default to_store, from_store
350              
351             # An example. We have a custom put_string so this isn't much faster.
352             #sub put_stream {
353             # my $self = shift;
354             # die "put while reading" unless $self->writing;
355             # my $source = shift;
356             # return 0 unless defined $source && $source->can('to_string');
357             #
358             # if (ref $source eq __PACKAGE__) {
359             # my $rstr = $self->_strref;
360             # my $sstr = $source->_strref;
361             # $$rstr .= $$sstr;
362             # $self->_setlen( $self->len + $source->len );
363             # } else {
364             # $self->put_string($source->to_string);
365             # }
366             # 1;
367             #}
368              
369             __PACKAGE__->meta->make_immutable;
370 22     22   150 no Moo;
  22         78  
  22         189  
371             1;
372              
373             # ABSTRACT: A String implementation of Data::BitStream
374              
375             =pod
376              
377             =head1 NAME
378              
379             Data::BitStream::String - A String implementation of Data::BitStream
380              
381             =head1 SYNOPSIS
382              
383             use Data::BitStream::String;
384             my $stream = Data::BitStream::String->new;
385             $stream->put_gamma($_) for (1 .. 20);
386             $stream->rewind_for_read;
387             my @values = $stream->get_gamma(-1);
388              
389             =head1 DESCRIPTION
390              
391             An implementation of L. See the documentation for that
392             module for many more examples, and L for the API.
393             This document only describes the unique features of this implementation,
394             which is of limited value to people purely using L.
395              
396             This implementation is very memory inefficient, as it uses a binary string
397             to hold the data, hence uses one byte internally per bit of data. However
398             it is a useful reference implementation, and since most operations use Perl
399             operations it is quite fast.
400              
401             =head2 DATA
402              
403             =over 4
404              
405             =item B< _str >
406              
407             A private string holding the data in binary string form.
408              
409             =back
410              
411             =head2 CLASS METHODS
412              
413             =over 4
414              
415             =item B< _strref >
416              
417             Retrieves a reference to the private string.
418              
419             =item I B< erase >
420              
421             Sets the private string to the empty string C<''>.
422              
423             =item B< read >
424              
425             =item B< write >
426              
427             =item B< put_unary >
428              
429             =item B< get_unary >
430              
431             =item B< put_unary1 >
432              
433             =item B< get_unary1 >
434              
435             =item B< put_gamma >
436              
437             =item B< get_gamma >
438              
439             =item B< put_string >
440              
441             =item B< read_string >
442              
443             =item B< to_string >
444              
445             =item B< from_string >
446              
447             =item B< to_raw >
448              
449             =item B< put_raw >
450              
451             These methods have custom implementations.
452              
453             =back
454              
455             =head2 ROLES
456              
457             The following roles are included. Note that Gamma has an inline
458             implementation.
459              
460             =over 4
461              
462             =item L
463              
464             =item L
465              
466             =item L
467              
468             =item L
469              
470             =item L
471              
472             =item L
473              
474             =item L
475              
476             =item L
477              
478             =item L
479              
480             =item L
481              
482             =item L
483              
484             =item L
485              
486             =item L
487              
488             =item L
489              
490             =item L
491              
492             =item L
493              
494             =item L
495              
496             =back
497              
498             =head1 SEE ALSO
499              
500             =over 4
501              
502             =item L
503              
504             =item L
505              
506             =item L
507              
508             =back
509              
510             =head1 AUTHORS
511              
512             Dana Jacobsen Edana@acm.orgE
513              
514             =head1 COPYRIGHT
515              
516             Copyright 2011-2012 by Dana Jacobsen Edana@acm.orgE
517              
518             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
519              
520             =cut