Absolute hinky bare-bones implementation of multiformats in Perl
at main 130 lines 4.1 kB view raw
1package 2 Multiformats::Varint { 3 use strict; 4 use warnings; 5 use feature 'signatures'; 6 7 use Exporter 'import'; 8 our @EXPORT_OK = qw/varint_encode varint_decode varint_decode_raw varint_decode_stream/; 9 10 sub decode($self, $value) { 11 return varint_decode($value); 12 } 13 14 sub decode_raw($self, $value) { 15 return varint_decode_raw($value); 16 } 17 18 sub encode($self, $value) { 19 return varint_encode($value); 20 } 21 22 sub decode_stream($self, $stream) { 23 return varint_decode_stream($stream); 24 } 25 26 sub new($pkg) { 27 return bless({}, $pkg); 28 } 29 30 # varint_encode, varint_decode_raw and varint_decode lifted from python multiformats https://github.com/hashberg-io/multiformats 31 sub varint_encode($value) { 32 die 'Multiformats::Varint::varint_encode: cannot encode negative values' unless $value >= 0; 33 my @out = (); 34 while(1) { 35 my $next_byte = $value & 0b01111111; 36 $value >>= 7; 37 if($value > 0) { 38 push(@out, $next_byte | 0b10000000); 39 } else { 40 push(@out, $next_byte); 41 last; 42 } 43 } 44 die 'Multiformats::Varint::varint_encode: encoded varint > 9 bytes' unless scalar(@out) <= 9; 45 return wantarray 46 ? (pack('C*', @out), scalar(@out)) 47 : pack('C*', @out) 48 ; 49 } 50 51 sub varint_decode($value) { 52 my ($x, $read) = varint_decode_raw($value); 53 die 'Multiformats::Varint::varint_decode: not all bytes used by encoding' if($read < length($value)); 54 return $x; 55 } 56 57 sub varint_decode_stream($stream) { 58 my $expect_next = 1; 59 my $num_bytes_read = 0; 60 my $x = 0; 61 62 while($expect_next) { 63 die 'Multiformats::Varint::varint_decode_stream: no next byte to read' if $stream->eof; 64 my $raw_byte; 65 my $bread = $stream->read($raw_byte, 1); 66 my $next_byte = unpack('C', $raw_byte); 67 $x += ($next_byte & 0b01111111) << (7 * $num_bytes_read); 68 $expect_next = ($next_byte >> 7 == 0b1) ? 1 : undef; 69 $num_bytes_read += $bread; 70 } 71 72 return wantarray 73 ? ($x, $num_bytes_read) 74 : $x; 75 } 76 77 sub varint_decode_raw($value) { 78 my $expect_next = 1; 79 my $num_bytes_read = 0; 80 my $x = 0; 81 82 my @buf = unpack('C*', $value); # value is untouched, we'll need to lop the appropriate of bytes off 83 # via the num_bytes_read later 84 85 while($expect_next) { 86 die 'Multiformats::Varint::varint_decode_raw: no next byte to read' if $num_bytes_read >= scalar(@buf); 87 my $next_byte = $buf[$num_bytes_read]; 88 $x += ($next_byte & 0b01111111) << (7 * $num_bytes_read); 89 $expect_next = ($next_byte >> 7 == 0b1) ? 1 : undef; 90 $num_bytes_read++; 91 } 92 93 return wantarray 94 ? ($x, $num_bytes_read) 95 : $x; 96 } 97} 98 99=pod 100 101=head1 NAME 102 103Multiformats::Varint - Varint decoding and encoding 104 105=head1 SYNOPSIS 106 107 use Multiformats::Varint qw/varint_encode varint_decode/; 108 109 my $encoded = varint_encode(300); # \xAC\x02 110 my $decoded = varint_decode("\xAC\x02"); # 300 111 112=head1 FUNCTIONS 113 114=head2 varint_encode(...) 115 116Encodes the given unsigned integer number to an unsigned Varint; returns a byte string. Will die if the varint is larger than the spec allows (>9 bytes). 117 118=head2 varint_decode(...) 119 120Decodes the given byte string to an unsigned integer. Will die if there are more bytes passed than required to decode a Varint. 121 122=head2 varint_decode_raw(...) 123 124Like varint_decode, but will not die when there are bytes left in the input. 125 126When called in scalar context will return the decoded unsigned integer, when called in list context will return a list containing the unsigned integer, and the number of bytes used from the input. Does not alter the input value, so you will have to use C<substr> or some other mechanism to strip the used bytes out of the input value. 127 128=cut 129 1301;