File Coverage

blib/lib/Data/BitStream/Code/StartStop.pm
Criterion Covered Total %
statement 109 118 92.3
branch 39 68 57.3
condition 11 24 45.8
subroutine 13 14 92.8
pod 5 5 100.0
total 177 229 77.2


line stmt bran cond sub pod time code
1             package Data::BitStream::Code::StartStop;
2 28     28   29537 use strict;
  28         73  
  28         1009  
3 28     28   163 use warnings;
  28         67  
  28         1600  
4             BEGIN {
5 28     28   74 $Data::BitStream::Code::StartStop::AUTHORITY = 'cpan:DANAJ';
6 28         6668 $Data::BitStream::Code::StartStop::VERSION = '0.08';
7             }
8              
9             our $CODEINFO = [
10             { package => __PACKAGE__,
11             name => 'StartStop',
12             universal => 1,
13             params => 1,
14             encodesub => sub {shift->put_startstop([split('-',shift)], @_)},
15             decodesub => sub {shift->get_startstop([split('-',shift)], @_)}, },
16             { package => __PACKAGE__,
17             name => 'StartStepStop',
18             universal => 1,
19             params => 1,
20             encodesub => sub {shift->put_startstepstop([split('-',shift)], @_)},
21             decodesub => sub {shift->get_startstepstop([split('-',shift)], @_)}, },
22             ];
23              
24 28     28   180 use Moo::Role;
  28         54  
  28         217  
25             requires qw(maxbits read skip write put_unary put_binword put_rice);
26              
27             # Start/Stop and Start-Step-Stop codes.
28             #
29             # See: Steven Pigeon, "Start/Stop Codes", Universite de Montreal.
30             #
31             # See: E.R. Fiala, D.H. Greene, "Data Compression with Finite Windows", Comm ACM, Vol 32, No 4, pp 490-505 , April 1989
32             #
33             # See: Peter Fenwick, "Punctured Elias Codes for variable-length coding of the integers", Technical Report 137, Department of Computer Science, University of Auckland, December 1996
34             #
35             # Note that we keep the same unary convention as the rest of BitStream, which
36             # is that unary codes are written with 0's followed by a 1. The original
37             # paper by Fiala and Greene use 1's followed by a 0.
38             #
39             # The S/S parameters come in as an array. Hence:
40             #
41             # $stream->put_startstop( [0,3,2,0], $value );
42             # $stream->put_startstepstop( [3,2,9], $value );
43             #
44             # $stream->get_startstop( [0,3,2,0], $value );
45             # $stream->get_startstepstop( [3,2,9], $value );
46             #
47             # A stop parameter of undef means infinity.
48              
49             sub _verify_p_array {
50 3264     3264   4595 my $p = shift;
51 3264 50 33     25894 return 0 unless defined $p && ref $p eq 'ARRAY' && scalar @$p >= 2;
      33        
52 3264         7023 foreach my $step (@$p) {
53 47269 50 33     213550 return 0 unless (!defined $step) || ($step >= 0);
54             }
55 3264         21206 1;
56             }
57             sub _make_prefix_map {
58 3264     3264   4771 my $p = shift;
59 3264         4187 my $maxbits = shift;
60              
61 3264         3666 my @pmap; # [prefix bits, prefix cmp, min, max, read bits]
62              
63 3264         5623 my $prefix_size = scalar @$p - 1;
64 3264         15115 my $prefix_cmp = 1 << $prefix_size;
65 3264         6533 my $prefix = 0;
66 3264         3958 my $bits = 0;
67 3264         3725 my $minval = -1;
68 3264         6449 my $maxval = 0;
69 3264         6367 foreach my $step (@$p) {
70 47269 50       98897 $bits += (defined $step) ? $step : $maxbits;
71 47269 100       134691 $bits = $maxbits if $bits > $maxbits;
72 47269         68693 $minval += $maxval+1;
73 47269 100       99517 $maxval = ($bits < $maxbits) ? (1<<$bits)-1 : ~0;
74 47269         50703 $prefix++;
75 47269         64180 $prefix_cmp >>= 1;
76 47269         158773 push @pmap, [$prefix, $prefix_cmp, $minval, $minval+$maxval, $bits];
77             }
78             # Patch the last value
79 3264         6673 $pmap[-1]->[0]--;
80             #foreach my $m (@pmap) { ($prefix,$prefix_cmp,$minval,$maxval,$bits)=@$m; print "[$prefix]: $prefix_cmp cmp $bits bits range $minval - $maxval\n"; }
81 3264         884700 return @pmap;
82             }
83              
84             # class method -- returns the maximum storable value for a given ss(...) code
85             sub max_code_for_startstop {
86 0     0 1 0 my $p = shift;
87 0 0       0 return unless _verify_p_array($p);
88 0         0 my @pmap = _make_prefix_map($p, Data::BitStream::Base::maxbits);
89 0         0 return $pmap[-1]->[3];
90             }
91              
92             sub get_startstop {
93 1631     1631 1 17580 my $self = shift;
94 1631         2536 my $p = shift;
95 1631 50       3776 $self->error_code('param', 'malformed step array') unless _verify_p_array($p);
96 1631         5963 my @pmap = _make_prefix_map($p, $self->maxbits);
97 1631         3247 my $count = shift;
98 1631 100       7666 if (!defined $count) { $count = 1; }
  1589 50       3305  
    0          
99 42         72 elsif ($count < 0) { $count = ~0; } # Get everything
100 0         0 elsif ($count == 0) { return; }
101              
102 1631         3827 my $looksize = $pmap[-1]->[0];
103              
104 1631         1904 my @vals;
105 1631         4443 while ($count-- > 0) {
106 4727         14886 my $look = $self->read($looksize, 'readahead');
107 4727 100       11880 last unless defined $look;
108 4683         5544 my $prefix = 0;
109 4683         27076 $prefix++ while ($look < $pmap[$prefix]->[1]);
110 4683         5179 my($prefix_bits,$prefix_cmp,$minval,$maxval,$bits) = @{$pmap[$prefix]};
  4683         10248  
111 4683         14776 $self->skip($prefix_bits);
112 4683         6698 my $val = $minval;
113 4683 50       18100 $val += $self->read($bits) if $bits > 0;
114 4683         15042 push @vals, $val;
115             }
116 1631 100       18458 wantarray ? @vals : $vals[-1];
117             }
118             sub put_startstop {
119 1633     1633 1 24024 my $self = shift;
120 1633         2380 my $p = shift;
121 1633 50       3714 $self->error_code('param', 'malformed step array') unless _verify_p_array($p);
122 1633         6240 my @pmap = _make_prefix_map($p, $self->maxbits);
123 1633         4035 my $global_maxval = $pmap[-1]->[3];
124 1633         3552 foreach my $val (@_) {
125 4687 100 100     21433 $self->error_code('zeroval') unless defined $val and $val >= 0;
126 4683 50       15170 $self->error_code('range', $val, 0,$global_maxval) if $val > $global_maxval;
127 4683         5800 my $prefix = 0;
128 4683         29787 $prefix++ while ($val > $pmap[$prefix]->[3]);
129 4683         5260 my($prefix_bits,$prefix_cmp,$minval,$maxval,$bits) = @{$pmap[$prefix]};
  4683         10135  
130              
131 4683 50       10116 if (($prefix_bits + $bits) <= 32) {
132             # Single write
133 4683 100       12633 my $v = ($prefix_cmp == 0) ? $val-$minval : ($val-$minval) | (1<<$bits);
134 4683         18925 $self->write($prefix_bits + $bits, $v);
135             } else {
136 0 0       0 if ($prefix_cmp == 0) { $self->write($prefix_bits, 0); }
  0         0  
137 0         0 else { $self->put_unary($prefix_bits-1); }
138 0 0       0 $self->write($bits, $val - $minval) if $bits > 0;
139             }
140             }
141             }
142              
143             sub _extract_p {
144 1631     1631   3173 my $self = shift;
145 1631         2426 my $p = shift;
146              
147 1631 50 33     19912 $self->error_code('param', 'p must be an array')
      33        
148             unless (defined $p) && (ref $p eq 'ARRAY') && scalar @$p >= 3;
149 1631         3711 my($start, $step, $stop) = @$p;
150 1631         5135 my $maxstop = $self->maxbits;
151 1631 100 66     26738 $stop = $maxstop if (!defined $stop) || ($stop > $maxstop);
152              
153 1631 50 33     8320 $self->error_code('param', "start must be in range 0-$maxstop") unless ($start >= 0) && ($start <= $maxstop);
154 1631 50       13914 $self->error_code('param', 'step must be >= 0') unless $step >= 0;
155 1631 50       3618 $self->error_code('param', 'stop must be >= start') unless $stop >= $start;
156              
157 1631         7400 ($start, $step, $stop, $maxstop);
158             }
159             sub _map_sss_to_ss {
160 1631     1631   5299 my($start, $step, $stop, $maxstop) = @_;
161              
162 1631         3820 my @pmap = ($start);
163 1631         2468 my $blen = $start;
164 1631         4327 while ($blen < $stop) {
165 34219         47560 $blen += $step;
166 34219 100       68843 $blen = $stop if $blen > $stop;
167 34219         87363 push @pmap, $step;
168             }
169 1631         19937 @pmap;
170             }
171              
172             sub put_startstepstop {
173 816     816 1 17806 my $self = shift;
174 816 50       2950 $self->error_stream_mode('write') unless $self->writing;
175 816         2579 my ($start, $step, $stop, $maxstop) = _extract_p($self, shift);
176              
177 816 50       2460 return $self->put_binword($start, @_) if $start == $stop;
178 816 50       1744 return $self->put_rice($start, @_) if $step == 0;
179              
180 816         1984 my @pmap = _map_sss_to_ss($start, $step, $stop, $maxstop);
181 816 50       2979 $self->error_code('assert', "unknown array condition") if scalar @pmap < 2;
182             #print "Turning sss($start-$step-$stop) into ss(", join("-",@pmap), ")\n";
183              
184 816         7588 $self->put_startstop( [@pmap], @_ );
185             }
186             sub get_startstepstop {
187 815     815 1 15776 my $self = shift;
188 815 50       2574 $self->error_stream_mode('read') if $self->writing;
189 815         2325 my ($start, $step, $stop, $maxstop) = _extract_p($self, shift);
190              
191 815 50       2349 return $self->get_binword($start, @_) if $start == $stop;
192 815 50       1892 return $self->get_rice($start, @_) if $step == 0;
193              
194 815         2844 my @pmap = _map_sss_to_ss($start, $step, $stop, $maxstop);
195 815 50       3007 $self->error_code('assert', "unknown array condition") if scalar @pmap < 2;
196              
197 815         7297 return $self->get_startstop( [@pmap], @_ );
198             }
199 28     28   52793 no Moo::Role;
  28         67  
  28         179  
200             1;
201              
202             # ABSTRACT: A Role implementing Start/Stop and Start-Step-Stop codes
203              
204             =pod
205              
206             =head1 NAME
207              
208             Data::BitStream::Code::StartStop - A Role implementing Start/Stop and Start-Step-Stop codes
209              
210             =head1 VERSION
211              
212             version 0.08
213              
214             =head1 DESCRIPTION
215              
216             A role written for L that provides get and set methods for
217             Start/Stop and Start-Step-Stop codes. The role applies to a stream object.
218              
219             Start-Step-Stop codes are described in Fiala and Greene (1989). The Start/Stop
220             codes are described in Steven Pigeon (2001) and are a generalization of the
221             S-S-S codes. This implementation turns the Start-Step-Stop parameters into
222             Start/Stop codes.
223              
224             =head1 EXAMPLES
225              
226             use Data::BitStream;
227             my $stream = Data::BitStream->new;
228             my @array = (4, 2, 0, 3, 7, 72, 0, 1, 13);
229              
230             $stream->put_startstop( [0,3,2,0], @array );
231             $stream->rewind_for_read;
232             my @array2 = $stream->get_startstop( [0,3,2,0], -1);
233              
234             $stream->erase_for_write;
235             $stream->put_startstepstop( [3,2,9], @array );
236             $stream->rewind_for_read;
237             my @array3 = $stream->get_startstepstop( [3,2,9], -1);
238              
239             # @array equals @array2 equals @array3
240              
241             =head1 METHODS
242              
243             =head2 Provided Class Methods
244              
245             =over 4
246              
247             =item B< max_code_for_startstop([@m]) >
248              
249             Given a set of parameters @m, returns the maximum integer that can be encoded
250             with those parameters (the minimum is always 0, like other codes). For
251             example, for two example the C<{0,3,2,0}> parameters from Pigeon's paper:
252              
253             $maxval = Data::BitStream::Code::StartStop::max_code_for_startstop([0,3,2,0]);
254             # $maxval will be 72
255             $maxval = Data::BitStream::Code::StartStop::max_code_for_startstop([3,3,3,0]);
256             # $maxval will be 1095
257              
258             =back
259              
260             =head2 Provided Object Methods
261              
262             =over 4
263              
264             =item B< put_startstop([@m], $value) >
265              
266             =item B< put_startstop([@m], @values) >
267              
268             Insert one or more values as Start/Stop codes. Returns 1.
269              
270             =item B< put_startstepstop([$start, $step, $stop], $value) >
271              
272             =item B< put_startstepstop([$start, $step, $stop], @values) >
273              
274             Insert one or more values as Start-Step-Stop codes. Returns 1.
275              
276             =item B< get_startstop([@m]) >
277              
278             =item B< get_startstop([@m], $count) >
279              
280             Decode one or more Start/Stop codes from the stream. If count is omitted,
281             one value will be read. If count is negative, values will be read until
282             the end of the stream is reached. In scalar context it returns the last
283             code read; in array context it returns an array of all codes read.
284              
285             =item B< get_startstepstop([$start, $step, $stop]) >
286              
287             =item B< get_startstepstop([$start, $step, $stop], $count) >
288              
289             Decode one or more Start-Step-Stop codes from the stream. If count is omitted,
290             one value will be read. If count is negative, values will be read until
291             the end of the stream is reached. In scalar context it returns the last
292             code read; in array context it returns an array of all codes read.
293              
294             =back
295              
296             =head2 Parameters
297              
298             The Start/Stop and Start-Step-Stop parameters are passed as a array reference.
299              
300             For Start-Step-Stop codes, there must be exactly three entries. All three
301             parameters must be greater than or equal to zero. These are turned into
302             Start/Stop codes.
303              
304             There must be a minimum of two Start/Stop parameters. Each parameter must be
305             greater than or equal to zero. A parameter of undef will be treated as equal
306             to the maximum supported bits in an integer.
307              
308             =head2 Required Methods
309              
310             =over 4
311              
312             =item B< maxbits >
313              
314             =item B< read >
315              
316             =item B< write >
317              
318             =item B< skip >
319              
320             =item B< put_unary >
321              
322             =item B< put_binword >
323              
324             =item B< put_rice >
325              
326             These methods are required for the role.
327              
328             =back
329              
330             =head1 SEE ALSO
331              
332             =over 4
333              
334             =item Steven Pigeon, "Start/Stop Codes", in Proceedings of the 2001 Data
335             Compression Conference, 2001.
336              
337             =item E.R. Fiala, D.H. Greene, "Data Compression with Finite Windows", Comm ACM, Vol 32, No 4, pp 490-505 , April 1989
338              
339             =item Peter Fenwick, "Punctured Elias Codes for variable-length coding of the integers", Technical Report 137, Department of Computer Science, University of Auckland, December 1996
340              
341             =back
342              
343             =head1 AUTHORS
344              
345             Dana Jacobsen
346              
347             =head1 COPYRIGHT
348              
349             Copyright 2011-2012 by Dana Jacobsen
350              
351             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
352              
353             =cut