File Coverage

blib/lib/Data/BitStream/Code/Taboo.pm
Criterion Covered Total %
statement 81 87 93.1
branch 29 46 63.0
condition 5 9 55.5
subroutine 7 7 100.0
pod 2 2 100.0
total 124 151 82.1


line stmt bran cond sub pod time code
1             package Data::BitStream::Code::Taboo;
2 28     28   26266 use strict;
  28         73  
  28         1324  
3 28     28   162 use warnings;
  28         59  
  28         1470  
4             BEGIN {
5 28     28   76 $Data::BitStream::Code::Taboo::AUTHORITY = 'cpan:DANAJ';
6 28         3130 $Data::BitStream::Code::Taboo::VERSION = '0.08';
7             }
8              
9             our $CODEINFO = { package => __PACKAGE__,
10             name => 'BlockTaboo',
11             universal => 1,
12             params => 1,
13             encodesub => sub {shift->put_blocktaboo(@_)},
14             decodesub => sub {shift->get_blocktaboo(@_)}, };
15              
16 28     28   172 use Moo::Role;
  28         93  
  28         210  
17             requires qw(read write);
18              
19             sub put_blocktaboo {
20 1632     1632 1 32505 my $self = shift;
21 1632 50       5020 $self->error_stream_mode('write') unless $self->writing;
22 1632         2493 my $taboostr = shift;
23 1632 50       4483 $self->error_code('param', 'taboo must be a binary string') if $taboostr =~ tr/01//c;
24 1632         2955 my $bits = length($taboostr);
25 1632 50 33     8645 $self->error_code('param', 'taboo length must be in range 1-16') unless $bits >= 1 && $bits <= 16;
26 1632         4133 my $taboo = oct("0b$taboostr");
27              
28 1632 50       3582 if ($bits == 1) {
29 0 0       0 return ($taboo == 1) ? $self->put_unary(@_) : $self->put_unary1(@_);
30             }
31              
32 1632         3484 my $base = 2**$bits - 1; # The base of the digits we're writing
33              
34 1632         3394 foreach my $val (@_) {
35 4686 100 100     45631 $self->error_code('zeroval') unless defined $val and $val >= 0;
36              
37 4682 100       10743 if ($val == 0) { $self->write($bits, $taboo); next; }
  153         1017  
  153         406  
38              
39             # val code
40             # 0 00
41             # 1 0100 base^0
42             # 2 1000
43             # 3 1100
44             # 4 010100 base^1+base^0
45             # 12 111100
46             # 13 01010100 base^2+base^1+base^0
47             # 39 11111100
48             # 40 0101010100 base^3+base^2+base^1+base^0
49             # 121 010101010100 base^4+base^3+base^2+base^1+base^0
50              
51 4529         5620 my $lbase = 0;
52 4529         5198 my $baseval = 1; # $base**0
53 4529         12925 while ($val >= ($baseval + $base**($lbase+1))) {
54 9443         9948 $lbase++;
55 9443         21123 $baseval += $base**$lbase;
56             }
57 4529         6310 my $v = $val - $baseval;
58              
59             # block-at-a-time way:
60             # foreach my $i (reverse 0 .. $lbase) {
61             # my $factor = $base ** $i;
62             # my $digit = int($v / $factor);
63             # $v -= $digit * $factor;
64             # $digit++ if $digit >= $taboo; # Make room for the taboo chunk
65             # $self->write($bits, $digit);
66             # }
67             # $self->write($bits, $taboo);
68             # combine blocks into 32-bit writes:
69 4529         9892 my @stack = ($taboo);
70 4529         11349 foreach my $i (0 .. $lbase) {
71 13972         20772 my $digit = $v % $base;
72 13972 100       27609 $digit++ if $digit >= $taboo; # Make room for the taboo chunk
73 13972         22064 push @stack, $digit;
74 13972         38339 $v = int($v / $base);
75             }
76 4529         7939 my $cword = 0;
77 4529         4941 my $cbits = 0;
78 4529         10050 while (@stack) {
79 18501         33436 $cword = ($cword << $bits) | pop @stack;
80 18501         22098 $cbits += $bits;
81 18501 50       59380 if (($cbits + $bits) > 32) {
82 0         0 $self->write($cbits, $cword);
83 0         0 $cword = 0;
84 0         0 $cbits = 0;
85             }
86             }
87 4529 50       21482 $self->write($cbits, $cword) if $cbits;
88             }
89 1628         4780 1;
90             }
91              
92             sub get_blocktaboo {
93 1673     1673 1 27830 my $self = shift;
94 1673 50       5848 $self->error_stream_mode('read') if $self->writing;
95 1673         2533 my $taboostr = shift;
96 1673 50       8661 $self->error_code('param', 'taboo must be a binary string') if $taboostr =~ tr/01//c;
97 1673         2945 my $bits = length($taboostr);
98 1673 50 33     8552 $self->error_code('param', 'taboo length must be in range 1-16') unless $bits >= 1 && $bits <= 16;
99 1673         3914 my $taboo = oct("0b$taboostr");
100              
101 1673 50       3596 if ($bits == 1) {
102 0 0       0 return ($taboo == 1) ? $self->get_unary(@_) : $self->get_unary1(@_);
103             }
104 1673         3115 my $base = 2**$bits - 1; # The base of the digits we're writing
105              
106 1673         2432 my $count = shift;
107 1673 100       3754 if (!defined $count) { $count = 1; }
  1631 50       2654  
    0          
108 42         72 elsif ($count < 0) { $count = ~0; } # Get everything
109 0         0 elsif ($count == 0) { return; }
110              
111 1673         1910 my @vals;
112 1673         5317 $self->code_pos_start('Block Taboo');
113 1673         54009 while ($count-- > 0) {
114 4769         20477 $self->code_pos_set;
115 4769         217835 my $tval = $self->read($bits);
116 4769 100       12319 last unless defined $tval;
117              
118 4725         15368 my $val = 0;
119 4725         6029 my $baseval = 0;
120 4725         5984 my $n = 0;
121 4725         19837 while ($tval != $taboo) {
122 14271 100       41051 my $digit = ($tval > $taboo) ? $tval-1 : $tval;
123 14271         46796 $val = $base * $val + $digit;
124 14271         26808 $baseval += $base**$n;
125 14271         14757 $n++;
126 14271 100       31582 $self->error_code('overflow') if ($val+$baseval) > ~0;
127 14259         42208 $tval = $self->read($bits);
128 14258 100       50923 $self->error_off_stream unless defined $tval;
129             }
130 4711         17896 push @vals, $val+$baseval;
131             }
132 1659         4573 $self->code_pos_end;
133 1659 100       58556 wantarray ? @vals : $vals[-1];
134             }
135              
136 28     28   35571 no Moo::Role;
  28         90  
  28         169  
137             1;
138              
139             # ABSTRACT: A Role implementing Taboo codes
140              
141             =pod
142              
143             =head1 NAME
144              
145             Data::BitStream::Code::Taboo - A Role implementing Taboo codes
146              
147             =head1 VERSION
148              
149             version 0.08
150              
151             =head1 DESCRIPTION
152              
153             A role written for L that provides get and set methods for
154             Taboo codes. The role applies to a stream object.
155              
156             Taboo codes are described in Steven Pigeon's 2001 PhD Thesis as well as his
157             paper "Taboo Codes: New Classes of Universal Codes."
158              
159             The block methods implement a slight modification of the taboo codes, wherein
160             zero is encoded as the taboo pattern with no preceding bits. This causes no
161             loss of generality and lowers the bit count for small values.
162              
163             An example using '11' as the taboo pattern (chunk size C):
164              
165             value code binary bits
166             0 t 11 2
167             1 0t 0011 4
168             2 1t 0111 4
169             3 2t 1011 4
170             4 00t 000011 6
171             .. 12 22t 101011 6
172             13 000t 00000011 8
173             .. 64 0220t 0010100011 10
174             .. 10000 000012220t 00000000011010100011 20
175              
176             These codes are a more efficient version of comma codes, as they allow leading
177             zeros.
178              
179             The unconstrained taboo codes are not implemented yet. However, the
180             generalized Fibonacci codes are a special case of taboo codes (using a taboo
181             pattern of all ones and a different bit ordering). The lengths of the codes
182             will be identical in all cases, so it is recommended to use them if possible.
183             What unconstrained taboo codes offer over generalized Fibonacci codes is the
184             ability to have any ending pattern and having the prefix be lexicographically
185             ordered. For most purposes these are not important.
186              
187             =head1 METHODS
188              
189             =head2 Provided Object Methods
190              
191             =over 4
192              
193             =item B< put_blocktaboo($taboo, $value) >
194              
195             =item B< put_blocktaboo($taboo, @values) >
196              
197             Insert one or more values as block taboo codes using the binary string
198             C<$taboo> as the terminator. Returns 1.
199              
200             =item B< get_blocktaboo($taboo) >
201              
202             =item B< get_blocktaboo($taboo, $count) >
203              
204             Decode one or more block taboo codes from the stream. If count is omitted,
205             one value will be read. If count is negative, values will be read until
206             the end of the stream is reached. In scalar context it returns the last
207             code read; in array context it returns an array of all codes read.
208              
209             =back
210              
211             =head2 Parameters
212              
213             The parameter C is a binary string, meaning it is a string comprised
214             exclusively of C<'0'> and C<'1'> characters. The length is the chunk size in
215             bits, and must be between 1 and 16. Using C<'00'> gives the codes from
216             table 2 of Pigeon's paper (where the chunk size C and the taboo pattern
217             is the two-bits C<'00'>).
218              
219             If C is C<'0'> then one-based unary coding is used (e.g. a string of
220             C<1> bits followed by a C<0>).
221             If C is C<'1'> then zero-based unary coding is used (e.g. a string of
222             C<0> bits followed by a C<1>).
223              
224             =head2 Required Methods
225              
226             =over 4
227              
228             =item B< read >
229              
230             =item B< write >
231              
232             These methods are required for the role.
233              
234             =back
235              
236             =head1 SEE ALSO
237              
238             =over 4
239              
240             =item Steven Pigeon, "Taboo Codes: New Classes of Universal Codes", 2001.
241              
242             =item L
243              
244             =back
245              
246             =head1 AUTHORS
247              
248             Dana Jacobsen
249              
250             =head1 COPYRIGHT
251              
252             Copyright 2012 by Dana Jacobsen
253              
254             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
255              
256             =cut