File Coverage

blib/lib/Bencode.pm
Criterion Covered Total %
statement 71 71 100.0
branch 68 80 85.0
condition 16 18 88.8
subroutine 10 10 100.0
pod 2 3 66.6
total 167 182 91.7


line stmt bran cond sub pod time code
1 3     3   138773 use 5.006;
  3         28  
2 3     3   13 use strict;
  3         4  
  3         85  
3 3     3   15 use warnings;
  3         4  
  3         195  
4              
5             package Bencode;
6             our $VERSION = '1.502';
7              
8 3     3   2790 use Exporter::Tidy all => [qw( bencode bdecode )];
  3         37  
  3         19  
9              
10             our ( $DEBUG, $do_lenient_decode, $max_depth, $undef_encoding );
11              
12             sub croak {
13 42     42 0 61 my ( @c, $i );
14 42   66     805 1 while ( @c = caller $i++ ) and $c[0] eq __PACKAGE__;
15 42 50       90 @c or @c = caller;
16 42         315 die @_, " at $c[1] line $c[2].\n";
17             }
18              
19             sub _bdecode_string {
20              
21 140 100   140   377 if ( m/ \G ( 0 | [1-9] \d* ) : /xgc ) {
22 57         108 my $len = $1;
23              
24 57 100       138 croak 'unexpected end of string data starting at ', 0+pos
25             if $len > length() - pos();
26              
27 55         84 my $str = substr $_, pos(), $len;
28 55         93 pos() = pos() + $len;
29              
30 55 0       102 warn "STRING (length $len)", $len < 200 ? " [$str]" : () if $DEBUG;
    50          
31              
32 55         135 return $str;
33             }
34             else {
35 83         116 my $pos = pos();
36 83 100       201 if ( m/ \G -? 0? \d+ : /xgc ) {
37 4         11 pos() = $pos;
38 4         47 croak 'malformed string length at ', 0+pos;
39             }
40             }
41              
42 79         347 return;
43             }
44              
45             sub _bdecode_chunk {
46 114 50   114   183 warn 'decoding at ', 0+pos if $DEBUG;
47              
48 114 100       185 local $max_depth = $max_depth - 1 if defined $max_depth;
49              
50 114 100       152 if ( defined( my $str = _bdecode_string() ) ) {
    100          
    100          
    100          
51 30         95 return $str;
52             }
53             elsif ( m/ \G i /xgc ) {
54 23 100       59 croak 'unexpected end of data at ', 0+pos if m/ \G \z /xgc;
55              
56 22 100       89 m/ \G ( 0 | -? [1-9] \d* ) e /xgc
57             or croak 'malformed integer data at ', 0+pos;
58              
59 15 50       31 warn "INTEGER $1" if $DEBUG;
60 15         55 return $1;
61             }
62             elsif ( m/ \G l /xgc ) {
63 24 50       46 warn 'LIST' if $DEBUG;
64              
65 24 100 100     74 croak 'nesting depth exceeded at ', 0+pos
66             if defined $max_depth and $max_depth < 0;
67              
68 20         29 my @list;
69 20         51 until ( m/ \G e /xgc ) {
70 27 50       42 warn 'list not terminated at ',0+pos,', looking for another element' if $DEBUG;
71 27         47 push @list, _bdecode_chunk();
72             }
73 14         33 return \@list;
74             }
75             elsif ( m/ \G d /xgc ) {
76 25 50       46 warn 'DICT' if $DEBUG;
77              
78 25 100 100     86 croak 'nesting depth exceeded at ', 0+pos
79             if defined $max_depth and $max_depth < 0;
80              
81 22         31 my $last_key;
82             my %hash;
83 22         47 until ( m/ \G e /xgc ) {
84 28 50       52 warn 'dict not terminated at ',0+pos,', looking for another pair' if $DEBUG;
85              
86 28 100       64 croak 'unexpected end of data at ', 0+pos
87             if m/ \G \z /xgc;
88              
89 26         38 my $key = _bdecode_string();
90 26 100       50 defined $key or croak 'dict key is not a string at ', 0+pos;
91              
92             croak 'duplicate dict key at ', 0+pos
93 25 100       48 if exists $hash{ $key };
94              
95 24 100 100     82 croak 'dict key not in sort order at ', 0+pos
      100        
96             if not( $do_lenient_decode ) and defined $last_key and $key lt $last_key;
97              
98 23 100       51 croak 'dict key is missing value at ', 0+pos
99             if m/ \G e /xgc;
100              
101 22         32 $last_key = $key;
102 22         32 $hash{ $key } = _bdecode_chunk();
103             }
104 12         29 return \%hash;
105             }
106             else {
107 6 100       74 croak m/ \G \z /xgc ? 'unexpected end of data' : 'garbage', ' at ', 0+pos;
108             }
109             }
110              
111             sub bdecode {
112 65     65 1 37733 local $_ = shift;
113 65         97 local $do_lenient_decode = shift;
114 65         74 local $max_depth = shift;
115 65         103 my $deserialised_data = _bdecode_chunk();
116 32 100       99 croak 'trailing garbage at ', 0+pos if $_ !~ m/ \G \z /xgc;
117 27         120 return $deserialised_data;
118             }
119              
120             sub _bencode;
121             sub _bencode {
122             map
123             +( ( not defined ) ? ( $undef_encoding or croak 'unhandled data type' )
124             : ( not ref ) ? ( m/\A (?: 0 | -? [1-9] \d* ) \z/x ? 'i' . $_ . 'e' : length . ':' . $_ )
125             : ( 'SCALAR' eq ref ) ? ( length $$_ ) . ':' . $$_ # escape hatch -- use this to avoid num/str heuristics
126             : ( 'ARRAY' eq ref ) ? 'l' . ( join '', _bencode @$_ ) . 'e'
127 30 100 66 30   238 : ( 'HASH' eq ref ) ? 'd' . do { my @k = sort keys %$_; join '', map +( length $k[0] ) . ':' . ( shift @k ) . $_, _bencode @$_{ @k } } . 'e'
  4 50       14  
  4 100       10  
    100          
    100          
    100          
128             : croak 'unhandled data type'
129             ), @_
130             }
131              
132             sub bencode {
133 21 100   21 1 7895 my $undef_mode = @_ == 2 ? pop : 'str';
134 21 100       42 $undef_mode = 'str' unless defined $undef_mode;
135 21 100       76 local $undef_encoding
    100          
    100          
136             = 'str' eq $undef_mode ? '0:'
137             : 'num' eq $undef_mode ? 'i0e'
138             : 'die' eq $undef_mode ? undef
139             : croak qq'undef_mode argument must be "str", "num", "die" or undefined, not "$undef_mode"';
140 18 50       34 croak 'need exactly one or two arguments' if @_ != 1;
141 18         24 ( &_bencode )[0];
142             }
143              
144             bdecode( 'i1e' );
145              
146             __END__