File Coverage

blib/lib/Convert/VLQ.pm
Criterion Covered Total %
statement 55 55 100.0
branch 12 14 85.7
condition 3 3 100.0
subroutine 11 11 100.0
pod 4 7 57.1
total 85 90 94.4


line stmt bran cond sub pod time code
1             # $Id: VLQ.pm 2774 2026-04-06 21:17:59Z fil $
2             package Convert::VLQ;
3              
4 3     3   843360 use 5.010001;
  3         14  
5 3     3   20 use strict;
  3         6  
  3         121  
6 3     3   35 use warnings;
  3         24  
  3         269  
7              
8 3     3   25 use Carp qw( confess );
  3         6  
  3         2995  
9              
10             our $VERSION = '0.01';
11              
12             require Exporter;
13             our @ISA = qw( Exporter );
14             our @EXPORT_OK = qw( int2vlqs vlqs2int encode_vlq decode_vlq );
15              
16 70     70 0 126 sub VLQ_BASE_SHIFT { 5 }
17 70     70 0 93 sub VLQ_BASE_MASK { 0x1f } # binary: 11111
18 51     51 0 102 sub VLQ_CONTINUATION_BIT { 0x20 } # binary: 100000
19              
20             my $B64chrs = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
21             my @B64 = ("A" .. "Z", "a" .. "z", 0 .. 9, "+", "/" );
22             # my @B64 = ( split //, $B64chrs );
23             my %_B64;
24             @_B64{@B64} = (0..length $B64chrs);
25              
26             sub int2vlqs
27             {
28 27     27 1 6160 my( $v ) = @_;
29 27         44 $v = int $v;
30 27 100       67 return( ((-$v) << 1 ) + 1 ) if $v < 0;
31 18         66 return( ($v<<1) + 0 );
32             }
33              
34             sub vlqs2int
35             {
36 27     27 1 6233 my( $v ) = @_;
37 27         35 my $shifted = $v >> 1;
38 27 100       66 return -$shifted if $v & 1;
39 18         45 return $shifted;
40             }
41              
42              
43             sub encode_vlq
44             {
45 22     22 1 10537 my( $v ) = @_;
46 22 100       55 if( ref $v ) {
47 3         9 return join '', map { encode_vlq( $_ ) } @$v;
  11         23  
48             }
49 19         31 my $ret = '';
50 19         34 my $vlq = int2vlqs( $v );
51 19         30 do {
52 35         82 my $digit = $vlq & VLQ_BASE_MASK; # grab 5 bits
53 35         54 $vlq >>= VLQ_BASE_SHIFT; # remove those bits from input
54 35 100       72 $digit |= VLQ_CONTINUATION_BIT if $vlq > 0; # should we continue?
55 35         83 $ret .= $B64[$digit]; # convert to base64
56             } while( $vlq > 0 );
57 19         61 return $ret;
58             }
59              
60              
61             sub decode_vlq
62             {
63 30     30 1 7468 my( $v ) = @_;
64              
65 30 50       59 return unless defined $v;
66              
67 30 100       50 if( !wantarray ) {
68 11         16 my @ret;
69             my $n;
70 11         33 while( length $v ) {
71 19         32 ( $n, $v ) = decode_vlq( $v );
72 19         38 push @ret, $n;
73             }
74 11         31 return \@ret;
75             }
76              
77 19         37 my @v = split //, $v;
78              
79 19         32 my $ret = 0;
80 19         19 my $shift = 0;
81 19         19 my $cont = 1;
82 19   100     59 while( @v and $cont ) {
83 35         43 my $d = shift @v;
84 35 50       114 confess "Invalid base64 digit: ", $d unless exists $_B64{$d};
85 35         44 my $digit = $_B64{ $d };
86 35         46 $cont = ($digit & VLQ_CONTINUATION_BIT); # 6th bit is continuation
87 35         43 $digit &= VLQ_BASE_MASK; # bottom 5 bits are what we want
88 35         43 $ret += $digit << $shift; # shift and add to answer
89 35         45 $shift += VLQ_BASE_SHIFT; # next time we shift even more
90             }
91 19         28 return( vlqs2int( $ret ), join '', @v );
92             }
93              
94             1;
95              
96             __END__