File Coverage

blib/lib/Data/BitStream/XS.pm
Criterion Covered Total %
statement 141 146 96.5
branch 77 106 72.6
condition 6 15 40.0
subroutine 29 29 100.0
pod 21 21 100.0
total 274 317 86.4


line stmt bran cond sub pod time code
1             package Data::BitStream::XS;
2             # Tested with Perl 5.6.2 through 5.16.0
3             # Tested on 32-bit big-endian and 64-bit little endian
4 48     48   1442038 use strict;
  48         131  
  48         3995  
5 48     48   281 use warnings;
  48         103  
  48         7418  
6 48     48   760 use Carp qw/croak confess/;
  48         103  
  48         7385  
7              
8             BEGIN {
9 48     48   117 $Data::BitStream::XS::AUTHORITY = 'cpan:DANAJ';
10 48         1592 $Data::BitStream::XS::VERSION = '0.08';
11             }
12              
13             # parent is cleaner, and in the Perl 5.10.1 / 5.12.0 core, but not earlier.
14             # use parent qw( Exporter );
15 48     48   278 use base qw( Exporter );
  48         94  
  48         11416  
16             our @EXPORT_OK = qw(
17             code_is_supported code_is_universal
18             prime_count nth_prime is_prime
19             );
20              
21             BEGIN {
22             eval {
23 48         373 require XSLoader;
24 48         82709 XSLoader::load(__PACKAGE__, $Data::BitStream::XS::VERSION);
25 48         12025 prime_init(0);
26 48         277376 1;
27 48 50   48   112 } or do {
28             # We could insert a Pure Perl implementation here.
29 0         0 croak "XS Code not available: $@";
30             }
31             }
32              
33             ################################################################################
34             #
35             # SUPPORT FUNCTIONS
36             #
37             ################################################################################
38              
39             sub maxbits { # Works as a class method or object method
40 50     50 1 32880 my $self = shift;
41 50         193 _maxbits();
42             }
43             sub erase_for_write {
44 120     120 1 202122 my $self = shift;
45 120         746 $self->erase;
46 120 100       951 $self->write_open if !$self->writing;
47             }
48             sub rewind_for_read {
49 405     405 1 53893 my $self = shift;
50 405 100       3056 $self->write_close if $self->writing;
51 405         1733 $self->rewind;
52             }
53              
54             sub to_string {
55 34     34 1 7193 my $self = shift;
56 34         89 $self->rewind_for_read;
57 34         1917 $self->read_string($self->len);
58             }
59              
60             sub from_string {
61 30     30 1 28189 my $self = shift;
62 30         82 $self->erase_for_write;
63 30         1211 $self->put_string($_[0]);
64 30         67 $self->rewind_for_read;
65             }
66              
67             # TODO:
68             sub to_store {
69 1     1 1 113 shift->to_raw(@_);
70             }
71             sub from_store {
72 1     1 1 15674 shift->from_raw(@_);
73             }
74              
75             # Takes a stream and inserts its contents into the current stream.
76             # Non-destructive to both streams.
77             sub put_stream {
78 2     2 1 14 my $self = shift;
79 2         4 my $source = shift;
80 2 50       7 return 0 unless defined $source;
81              
82 2 50       7 if (ref $source eq __PACKAGE__) {
83             # optimized method for us.
84 2         19 $self->_xput_stream($source);
85             } else {
86 0 0       0 return 0 unless $source->can('to_string');
87 0         0 $self->put_string($source->to_string);
88             # WordVec is still slow with this (it needs a fast put_raw)
89             # $self->put_raw($source->to_raw, $source->len);
90             }
91 2         5 1;
92             }
93              
94             ################################################################################
95             #
96             # CODES
97             #
98             ################################################################################
99              
100             sub get_golomb {
101 1219     1219 1 305376 my $self = shift;
102 1219 100       12237 return (ref $_[0] eq 'CODE')
103             ? $self->_xget_golomb_sub(@_)
104             : $self->_xget_golomb_sub(undef, @_);
105             }
106             sub put_golomb {
107 1154     1154 1 58385 my $self = shift;
108 1154 100       18521 return (ref $_[0] eq 'CODE')
109             ? $self->_xput_golomb_sub(@_)
110             : $self->_xput_golomb_sub(undef, @_);
111             }
112              
113             sub get_rice {
114 797     797 1 77851 my $self = shift;
115 797 50       12125 return (ref $_[0] eq 'CODE')
116             ? $self->_xget_rice_sub(@_)
117             : $self->_xget_rice_sub(undef, @_);
118             }
119             sub put_rice {
120 754     754 1 1086 my $self = shift;
121 754 50       5739 return (ref $_[0] eq 'CODE')
122             ? $self->_xput_rice_sub(@_)
123             : $self->_xput_rice_sub(undef, @_);
124             }
125              
126             sub get_arice {
127 642     642 1 1270 my $self = shift;
128 642 50       4495 return (ref $_[0] eq 'CODE')
129             ? $self->_xget_arice_sub(@_)
130             : $self->_xget_arice_sub(undef, @_);
131             }
132             sub put_arice {
133 619     619 1 1186 my $self = shift;
134 619 50       3294 return (ref $_[0] eq 'CODE')
135             ? $self->_xput_arice_sub(@_)
136             : $self->_xput_arice_sub(undef, @_);
137             }
138              
139              
140             # Map Start-Step-Stop codes to Start/Stop codes.
141             # See Data::BitStream::Code::StartStop for more detail
142              
143             sub _map_sss_to_ss {
144 725     725   1353 my($start, $step, $stop, $maxstop) = @_;
145 725 100 66     3599 $stop = $maxstop if (!defined $stop) || ($stop > $maxstop);
146 725 50 33     4068 croak "invalid parameters" unless ($start >= 0) && ($start <= $maxstop);
147 725 50       1812 croak "invalid parameters" unless $step >= 0;
148 725 50       1427 croak "invalid parameters" unless $stop >= $start;
149 725 50       1536 return if $start == $stop; # Binword
150 725 50       2889 return if $step == 0; # Rice
151              
152 725         1909 my @pmap = ($start);
153 725         1238 my $blen = $start;
154 725         1611 while ($blen < $stop) {
155 15193         19592 $blen += $step;
156 15193 100       30096 $blen = $stop if $blen > $stop;
157 15193         44368 push @pmap, $step;
158             }
159 725         12265 @pmap;
160             }
161              
162             sub put_startstepstop {
163 362     362 1 553 my $self = shift;
164 362         741 my $p = shift;
165 362 50 33     2450 croak "invalid parameters" unless (ref $p eq 'ARRAY') && scalar @$p == 3;
166              
167 362         1002 my($start, $step, $stop) = @$p;
168 362 50       893 return $self->put_binword($start, @_) if $start == $stop;
169 362 50       816 return $self->put_rice($start, @_) if $step == 0;
170 362         1274 my @pmap = _map_sss_to_ss($start, $step, $stop, _maxbits());
171 362 50       1511 confess "unexpected death" unless scalar @pmap >= 2;
172 362         7657 $self->put_startstop( [@pmap], @_ );
173             }
174             sub get_startstepstop {
175 363     363 1 527 my $self = shift;
176 363         447 my $p = shift;
177 363 50 33     1986 croak "invalid parameters" unless (ref $p eq 'ARRAY') && scalar @$p == 3;
178              
179 363         793 my($start, $step, $stop) = @$p;
180 363 50       893 return $self->get_binword($start, @_) if $start == $stop;
181 363 50       772 return $self->get_rice($start, @_) if $step == 0;
182 363         1212 my @pmap = _map_sss_to_ss($start, $step, $stop, _maxbits());
183 363 50       1359 confess "unexpected death" unless scalar @pmap >= 2;
184 363         7406 return $self->get_startstop( [@pmap], @_ );
185             }
186              
187             ################################################################################
188             #
189             # TEXT METHODS
190             #
191             ################################################################################
192              
193             # The Data::BitStream class does this all dynamically and gets its info from
194             # all Data::BitStream::Code::* files that have been loaded as roles.
195             # We're going to do it all statically, which isn't nearly as cool.
196              
197             my @_initinfo = (
198             { package => __PACKAGE__,
199             name => 'Unary',
200             universal => 0,
201             params => 0,
202             encodesub => sub {shift->put_unary(@_)},
203             decodesub => sub {shift->get_unary(@_)}, },
204             { package => __PACKAGE__,
205             name => 'Unary1',
206             universal => 0,
207             params => 0,
208             encodesub => sub {shift->put_unary1(@_)},
209             decodesub => sub {shift->get_unary1(@_)}, },
210             { package => __PACKAGE__,
211             name => 'BinWord',
212             universal => 0, # it is universal if and only if param == maxbits
213             params => 1,
214             encodesub => sub {shift->put_binword(@_)},
215             decodesub => sub {shift->get_binword(@_)}, },
216             { package => __PACKAGE__,
217             name => 'Gamma',
218             universal => 1,
219             params => 0,
220             encodesub => sub {shift->put_gamma(@_)},
221             decodesub => sub {shift->get_gamma(@_)}, },
222             { package => __PACKAGE__,
223             name => 'Delta',
224             universal => 1,
225             params => 0,
226             encodesub => sub {shift->put_delta(@_)},
227             decodesub => sub {shift->get_delta(@_)}, },
228             { package => __PACKAGE__,
229             name => 'Omega',
230             universal => 1,
231             params => 0,
232             encodesub => sub {shift->put_omega(@_)},
233             decodesub => sub {shift->get_omega(@_)}, },
234             { package => __PACKAGE__,
235             name => 'EvenRodeh',
236             universal => 1,
237             params => 0,
238             encodesub => sub {shift->put_evenrodeh(@_)},
239             decodesub => sub {shift->get_evenrodeh(@_)}, },
240             { package => __PACKAGE__,
241             name => 'Levenstein',
242             aliases => ['Levenshtein'],
243             universal => 1,
244             params => 0,
245             encodesub => sub {shift->put_levenstein(@_)},
246             decodesub => sub {shift->get_levenstein(@_)}, },
247             { package => __PACKAGE__,
248             name => 'GoldbachG1',
249             universal => 1,
250             params => 0,
251             encodesub => sub {shift->put_goldbach_g1(@_)},
252             decodesub => sub {shift->get_goldbach_g1(@_)}, },
253             { package => __PACKAGE__,
254             name => 'GoldbachG2',
255             universal => 1,
256             params => 0,
257             encodesub => sub {shift->put_goldbach_g2(@_)},
258             decodesub => sub {shift->get_goldbach_g2(@_)}, },
259             { package => __PACKAGE__,
260             name => 'Fibonacci',
261             universal => 1,
262             params => 0,
263             encodesub => sub {shift->put_fib(@_)},
264             decodesub => sub {shift->get_fib(@_)}, },
265             { package => __PACKAGE__,
266             name => 'FibGen',
267             universal => 1,
268             params => 1,
269             encodesub => sub {shift->put_fibgen(@_)},
270             decodesub => sub {shift->get_fibgen(@_)}, },
271             { package => __PACKAGE__,
272             name => 'Comma',
273             universal => 1,
274             params => 1,
275             encodesub => sub {shift->put_comma(@_)},
276             decodesub => sub {shift->get_comma(@_)}, },
277             { package => __PACKAGE__,
278             name => 'BlockTaboo',
279             universal => 1,
280             params => 1,
281             encodesub => sub {shift->put_blocktaboo(@_)},
282             decodesub => sub {shift->get_blocktaboo(@_)}, },
283             { package => __PACKAGE__,
284             name => 'Golomb',
285             universal => 0,
286             params => 1,
287             encodesub => sub {shift->put_golomb(@_)},
288             decodesub => sub {shift->get_golomb(@_)}, },
289             { package => __PACKAGE__,
290             name => 'Rice',
291             universal => 0,
292             params => 1,
293             encodesub => sub {shift->put_rice(@_)},
294             decodesub => sub {shift->get_rice(@_)}, },
295             { package => __PACKAGE__,
296             name => 'ExpGolomb',
297             universal => 1,
298             params => 1,
299             encodesub => sub {shift->put_expgolomb(@_)},
300             decodesub => sub {shift->get_expgolomb(@_)}, },
301             { package => __PACKAGE__,
302             name => 'GammaGolomb',
303             universal => 1,
304             params => 1,
305             encodesub => sub {shift->put_gammagolomb(@_)},
306             decodesub => sub {shift->get_gammagolomb(@_)}, },
307             { package => __PACKAGE__,
308             name => 'Baer',
309             universal => 1,
310             params => 1,
311             encodesub => sub {shift->put_baer(@_)},
312             decodesub => sub {shift->get_baer(@_)}, },
313             { package => __PACKAGE__,
314             name => 'BoldiVigna',
315             universal => 1,
316             params => 1,
317             encodesub => sub {shift->put_boldivigna(@_)},
318             decodesub => sub {shift->get_boldivigna(@_)}, },
319             { package => __PACKAGE__,
320             name => 'ARice',
321             universal => 1,
322             params => 1,
323             encodesub => sub {shift->put_arice(@_)},
324             decodesub => sub {shift->get_arice(@_)}, },
325             { package => __PACKAGE__,
326             name => 'StartStop',
327             universal => 1,
328             params => 1,
329             encodesub => sub {shift->put_startstop([split('-',shift)], @_)},
330             decodesub => sub {shift->get_startstop([split('-',shift)], @_)}, },
331             { package => __PACKAGE__,
332             name => 'StartStepStop',
333             universal => 1,
334             params => 1,
335             encodesub => sub {shift->put_startstepstop([split('-',shift)], @_)},
336             decodesub => sub {shift->get_startstepstop([split('-',shift)], @_)}, },
337             );
338             my %codeinfo;
339              
340             sub add_code {
341 347     347 1 687 my $rinfo = shift;
342 347 50 33     1533 croak "add_code needs a hash ref" unless defined $rinfo && ref $rinfo eq 'HASH';
343 347         533 foreach my $p (qw(package name universal params encodesub decodesub)) {
344 2082 50       5073 croak "invalid registration: missing $p" unless defined $$rinfo{$p};
345             }
346 347         736 my $name = lc $$rinfo{'name'};
347 347 50       7006 if (defined $codeinfo{$name}) {
348 0 0       0 return 1 if $codeinfo{$name}{'package'} eq $$rinfo{'package'};
349 0         0 croak "module $$rinfo{'package'} trying to reuse code name '$name' already in use by $codeinfo{$name}{'package'}";
350             }
351 347         773 $codeinfo{$name} = $rinfo;
352 347         608 1;
353             };
354              
355             my $init_codeinfo_sub = sub {
356             if (scalar @_initinfo > 0) {
357             foreach my $rinfo (@_initinfo) {
358             add_code($rinfo);
359             }
360             @_initinfo = ();
361             }
362             };
363              
364             sub _find_code {
365 175     175   281 my $code = lc shift;
366              
367 175 100       456 $init_codeinfo_sub->() if scalar @_initinfo > 0;
368 175         476 return $codeinfo{$code};
369             };
370              
371             sub code_is_supported {
372 40     40 1 30313 my $code = lc shift;
373 40 100       48 my $param; $param = $1 if $code =~ s/\((.+)\)$//;
  40         223  
374 40         67 return defined _find_code($code);
375             }
376              
377             sub code_is_universal {
378 123     123 1 111924 my $code = lc shift;
379 123 100       136 my $param; $param = $1 if $code =~ s/\((.+)\)$//;
  123         615  
380 123         238 my $inforef = _find_code($code);
381 123 100       276 return unless defined $inforef; # Unknown code.
382 122         351 return $inforef->{'universal'};
383             }
384              
385             # It would be nice to speed these up, but doing so isn't trivial. I've added
386             # a couple shortcuts for Unary and Gamma, but it isn't a generic solution.
387             sub code_put {
388 8715     8715 1 598643 my $self = shift;
389 8715         17240 my $code = lc shift;
390 8715 100       37661 if ($code eq 'unary' ) { return $self->put_unary(@_); }
  360 100       7018  
391 362         1875 elsif ($code eq 'gamma' ) { return $self->put_gamma(@_); }
392 7993 100       11085 my $param; $param = $1 if $code =~ s/\((.+)\)$//;
  7993         58006  
393 7993         14440 my $inforef = $codeinfo{$code};
394 7993 100       19440 $inforef = _find_code($code) unless defined $inforef;
395 7993 100       16197 croak "Unknown code $code" unless defined $inforef;
396 7992         13889 my $sub = $inforef->{'encodesub'};
397 7992 50       16904 croak "No encoding sub for code $code!" unless defined $sub;
398 7992 100       17977 if ($inforef->{'params'}) {
399 5826 100       11363 croak "Code $code needs a parameter" unless defined $param;
400 5825         13011 return $sub->($self, $param, @_);
401             } else {
402 2166 100       5773 croak "Code $code does not have parameters" if defined $param;
403 2165         4460 return $sub->($self, @_);
404             }
405             }
406              
407             sub code_get {
408 9288     9288 1 683321 my $self = shift;
409 9288         23796 my $code = lc shift;
410 9288 100       28223 if ($code eq 'unary' ) { return $self->get_unary(@_); }
  382 100       5183  
411 387         2648 elsif ($code eq 'gamma' ) { return $self->get_gamma(@_); }
412 8519 100       9784 my $param; $param = $1 if $code =~ s/\((.+)\)$//;
  8519         41831  
413 8519         15657 my $inforef = $codeinfo{$code};
414 8519 100       17140 $inforef = _find_code($code) unless defined $inforef;
415 8519 100       33422 croak "Unknown code $code" unless defined $inforef;
416 8518         16346 my $sub = $inforef->{'decodesub'};
417 8518 50       37153 croak "No decoding sub for code $code!" unless defined $sub;
418 8518 100       20049 if ($inforef->{'params'}) {
419 6219 100       11687 croak "Code $code needs a parameter" unless defined $param;
420 6218         13536 return $sub->($self, $param, @_);
421             } else {
422 2299 100       5214 croak "Code $code does not have parameters" if defined $param;
423 2298         4691 return $sub->($self, @_);
424             }
425             }
426              
427              
428             ################################################################################
429             #
430             # CLASS METHODS
431             #
432             ################################################################################
433              
434             1;
435              
436             __END__