File Coverage

blib/lib/Data/BitStream/Code/Omega.pm
Criterion Covered Total %
statement 94 96 97.9
branch 34 42 80.9
condition 3 3 100.0
subroutine 8 8 100.0
pod 2 2 100.0
total 141 151 93.3


line stmt bran cond sub pod time code
1             package Data::BitStream::Code::Omega;
2 28     28   23729 use strict;
  28         52  
  28         991  
3 28     28   154 use warnings;
  28         54  
  28         1335  
4             BEGIN {
5 28     28   60 $Data::BitStream::Code::Omega::AUTHORITY = 'cpan:DANAJ';
6 28         2865 $Data::BitStream::Code::Omega::VERSION = '0.08';
7             }
8              
9             our $CODEINFO = { package => __PACKAGE__,
10             name => 'Omega',
11             universal => 1,
12             params => 0,
13             encodesub => sub {shift->put_omega(@_)},
14             decodesub => sub {shift->get_omega(@_)}, };
15              
16 28     28   151 use Moo::Role;
  28         51  
  28         270  
17             requires 'read', 'write', 'skip';
18              
19             # Elias Omega code.
20             #
21             # Store the number of binary bits in recursive Gamma codes, followed by the
22             # number in binary.
23             #
24             # Very rarely used code. Sometimes called "recursive Elias" or "logstar".
25             #
26             # See: Peter Elias, "Universal codeword sets and representations of the integers", IEEE Trans. Information Theory 21(2):194-203, Mar 1975.
27             #
28             # 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
29              
30 18092     18092   19933 sub _base_of { my $d = shift; my $base = 0; $base++ while ($d >>= 1); $base; }
  18092         24344  
  18092         118893  
  18092         29732  
31              
32             sub put_omega {
33 4311     4311 1 25277 my $self = shift;
34 4311 50       11856 $self->error_stream_mode('write') unless $self->writing;
35 4311         12579 my $maxval = $self->maxval;
36 4311         11599 my $maxbits = $self->maxbits;
37              
38 4311         7455 foreach my $v (@_) {
39 6987         8927 my $val = $v;
40 6987 100 100     30477 $self->error_code('zeroval') unless defined $val and $val >= 0;
41 6985 100       14860 if ($val == $maxval) { # write special code for maxval
42 6 50       11 if ($maxbits > 32) {
43 6         18 $self->write(13, 0x1681); # 1 0 1 10 1 000000 1
44             } else {
45 0         0 $self->write(12, 0x0AC1); # 1 0 1 01 1 00000 1
46             }
47 6         15 next;
48             }
49              
50 6979         8608 $val++;
51              
52             # Simpler code, prepending each group to a list.
53             # my @d = ( [1,0] ); # bits, value
54             # while ($val > 1) {
55             # my $base = _base_of($val);
56             # unshift @d, [$base+1, $val];
57             # $val = $base;
58             # }
59             # foreach my $aref (@d) { $self->write( @{$aref} ); }
60              
61             # This code bundles up groups of 32-bit writes. Almost 2x faster.
62 6979         10996 my @d;
63 6979         8225 my $cbits = 1;
64 6979         7004 my $cword = 0;
65 6979         14119 while ($val > 1) {
66 18092         30673 my $base = _base_of($val) + 1;
67              
68 18092 100       38944 if (($cbits + $base) >= 32) {
69 2964         7920 unshift @d, [$cbits, $cword];
70 2964         3744 $cword = $val;
71 2964         3904 $cbits = $base;
72             } else {
73 15128         23640 $cword |= ($val << $cbits);
74 15128         17138 $cbits += $base;
75             }
76              
77 18092         38112 $val = $base-1;
78             }
79 6979 100       19115 if (scalar @d == 0) {
80 5062         31980 $self->write($cbits, $cword);
81             } else {
82 1917         4292 unshift @d, [$cbits, $cword];
83 1917         3090 foreach my $aref (@d) {
84 4881         5761 $self->write( @{$aref} );
  4881         14660  
85             }
86             }
87             }
88 4309         16129 1;
89             }
90              
91             sub get_omega {
92 4357     4357 1 30272 my $self = shift;
93 4357 50       11277 $self->error_stream_mode('read') if $self->writing;
94 4357         5508 my $count = shift;
95 4357 100       8098 if (!defined $count) { $count = 1; }
  4333 50       6181  
    0          
96 24         80 elsif ($count < 0) { $count = ~0; } # Get everything
97 0         0 elsif ($count == 0) { return; }
98              
99 4357         5396 my @vals;
100 4357         11083 my $maxbits = $self->maxbits;
101 4357         15747 $self->code_pos_start('Omega');
102 4357         127757 while ($count-- > 0) {
103 7057         18531 $self->code_pos_set;
104 7057         203701 my $val = 1;
105 7057         7829 my $first_bit = 1;
106              
107             # Simple code:
108             # while ($first_bit = $self->read(1)) {
109             # if ($val == $maxbits) { $val = 0; last; }
110             # $val = (1 << $val) | $self->read($val);
111             # }
112              
113             # Speedup reading the first couple sets of codes. 30-80% faster overall.
114 7057         19967 my $prefix = $self->read(7, 'readahead');
115 7057 100       14472 last unless defined $prefix;
116 7008         7739 $prefix <<= 1;
117 7008 100       42309 if (($prefix & 0x80) == 0) {
    100          
    100          
118 1004         2986 $self->skip(1);
119 1004         6902 push @vals, 0;
120 1004         3017 next;
121             } elsif (($prefix & 0x20) == 0) {
122 621         1662 $self->skip(3);
123 621         1054 push @vals, 1 + (($prefix & 0x40) != 0);
124 621         1795 next;
125             } elsif ($prefix & 0x40) { # read 4 more bits
126 950         1484 $val = ($prefix >> 2) & 0x0F;
127 950         2443 $self->skip(7);
128 950 100       3154 if (($prefix & 0x02) == 0) {
129 246         358 push @vals, $val-1;
130 246         672 next;
131             }
132             } else { # read 3 more bits
133 4433         5866 $val = ($prefix >> 3) & 0x07;
134 4433         12496 $self->skip(6);
135 4433 100       10200 if (($prefix & 0x04) == 0) {
136 518         757 push @vals, $val-1;
137 518         1431 next;
138             }
139             }
140 4619         5830 do {
141 6763 100       13080 if ($val == $maxbits) {
142 6         20 push @vals, $self->maxval;
143 6         18 next;
144             }
145 6757 100       11498 $self->error_code('overflow') if $val > $maxbits;
146 6755         17836 my $next = $self->read($val);
147 6754 50       13893 $self->error_off_stream unless defined $next;
148 6754         21724 $val = (1 << $val) | $next;
149             } while ($first_bit = $self->read(1));
150 4610 50       10601 $self->error_off_stream unless defined $first_bit;
151 4610         14570 push @vals, $val-1;
152             }
153 4354         11681 $self->code_pos_end;
154 4354 100       139107 wantarray ? @vals : $vals[-1];
155             }
156 28     28   36127 no Moo::Role;
  28         75  
  28         232  
157             1;
158              
159             # ABSTRACT: A Role implementing Elias Omega codes
160              
161             =pod
162              
163             =head1 NAME
164              
165             Data::BitStream::Code::Omega - A Role implementing Elias Omega codes
166              
167             =head1 VERSION
168              
169             version 0.08
170              
171             =head1 DESCRIPTION
172              
173             A role written for L that provides get and set methods for
174             the Elias Omega codes. The role applies to a stream object.
175              
176             =head1 METHODS
177              
178             =head2 Provided Object Methods
179              
180             =over 4
181              
182             =item B< put_omega($value) >
183              
184             =item B< put_omega(@values) >
185              
186             Insert one or more values as Omega codes. Returns 1.
187              
188             =item B< get_omega() >
189              
190             =item B< get_omega($count) >
191              
192             Decode one or more Omega codes from the stream. If count is omitted,
193             one value will be read. If count is negative, values will be read until
194             the end of the stream is reached. In scalar context it returns the last
195             code read; in array context it returns an array of all codes read.
196              
197             =back
198              
199             =head2 Required Methods
200              
201             =over 4
202              
203             =item B< read >
204              
205             =item B< write >
206              
207             =item B< skip >
208              
209             These methods are required for the role.
210              
211             =back
212              
213             =head1 SEE ALSO
214              
215             =over 4
216              
217             =item Peter Elias, "Universal codeword sets and representations of the integers", IEEE Trans. Information Theory 21(2), pp. 194-203, Mar 1975.
218              
219             =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
220              
221             =back
222              
223             =head1 AUTHORS
224              
225             Dana Jacobsen
226              
227             =head1 COPYRIGHT
228              
229             Copyright 2011 by Dana Jacobsen
230              
231             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
232              
233             =cut