line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package BERT::Encoder; |
2
|
6
|
|
|
6
|
|
46
|
use strict; |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
255
|
|
3
|
6
|
|
|
6
|
|
32
|
use warnings; |
|
6
|
|
|
|
|
19
|
|
|
6
|
|
|
|
|
169
|
|
4
|
|
|
|
|
|
|
|
5
|
6
|
|
|
6
|
|
164
|
use 5.008; |
|
6
|
|
|
|
|
17
|
|
|
6
|
|
|
|
|
249
|
|
6
|
|
|
|
|
|
|
|
7
|
6
|
|
|
6
|
|
33
|
use Carp 'croak'; |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
388
|
|
8
|
6
|
|
|
6
|
|
33
|
use BERT::Constants; |
|
6
|
|
|
|
|
10
|
|
|
6
|
|
|
|
|
512
|
|
9
|
6
|
|
|
6
|
|
31
|
use BERT::Types; |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
895
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# stolen from Regexp::Common :-) |
12
|
|
|
|
|
|
|
use constant { |
13
|
6
|
|
|
|
|
9767
|
INT_RE => qr/^(?:(?:[+-]?)(?:[0123456789]+))$/, |
14
|
|
|
|
|
|
|
FLOAT_RE => qr/^(?:(?i)(?:[+-]?)(?:(?=[.]?[0123456789])(?:[0123456789]*)(?:(?:[.])(?:[0123456789]{0,}))?)(?:(?:[E])(?:(?:[+-]?)(?:[0123456789]+))|))$/, |
15
|
6
|
|
|
6
|
|
35
|
}; |
|
6
|
|
|
|
|
10
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub new { |
18
|
35
|
|
|
35
|
1
|
51
|
my $class = shift; |
19
|
35
|
|
|
|
|
129
|
return bless { }, $class; |
20
|
|
|
|
|
|
|
} |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
sub encode { |
23
|
35
|
|
|
35
|
1
|
60
|
my ($self, $value) = @_; |
24
|
35
|
|
|
|
|
94
|
return pack('C', MAGIC_NUMBER) . $self->encode_any($value); |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
sub encode_any { |
28
|
97
|
|
|
97
|
0
|
128
|
my ($self, $value) = @_; |
29
|
|
|
|
|
|
|
|
30
|
97
|
100
|
|
|
|
197
|
return $self->encode_nil unless defined $value; |
31
|
|
|
|
|
|
|
|
32
|
95
|
|
|
|
|
131
|
my $type = ref $value; |
33
|
95
|
100
|
|
|
|
469
|
if ($type eq 'ARRAY') { return $self->encode_array($value) } |
|
13
|
100
|
|
|
|
40
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
34
|
2
|
|
|
|
|
7
|
elsif ($type eq 'HASH') { return $self->encode_dict($value) } |
35
|
1
|
|
|
|
|
5
|
elsif ($type eq 'Regexp') { return $self->encode_regex($value) } |
36
|
29
|
|
|
|
|
68
|
elsif ($type eq 'BERT::Atom') { return $self->encode_atom($value) } |
37
|
14
|
|
|
|
|
76
|
elsif ($type eq 'BERT::Tuple') { return $self->encode_tuple($value) } |
38
|
2
|
|
|
|
|
6
|
elsif ($type eq 'BERT::Boolean') { return $self->encode_boolean($value) } |
39
|
2
|
|
|
|
|
8
|
elsif ($type eq 'BERT::Dict') { return $self->encode_dict($value) } |
40
|
1
|
|
|
|
|
5
|
elsif ($type eq 'BERT::Time') { return $self->encode_time($value) } |
41
|
4
|
|
|
|
|
15
|
elsif ($type eq 'Math::BigInt') { return $self->encode_integer($value) } |
42
|
0
|
|
|
|
|
0
|
elsif ($type) { croak "Can't encode type $type" } |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# I didn't use B::svref_2object on this because by only looking at variables |
45
|
|
|
|
|
|
|
# in Perl can actually modify them |
46
|
27
|
100
|
|
|
|
167
|
if ($value =~ INT_RE) { return $self->encode_integer($value) } |
|
15
|
100
|
|
|
|
41
|
|
47
|
2
|
|
|
|
|
44
|
elsif ($value =~ FLOAT_RE) { return $self->encode_float($value) } |
48
|
10
|
|
|
|
|
33
|
else { return $self->encode_binary($value) } |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub encode_nil { |
52
|
2
|
|
|
2
|
0
|
3
|
my ($self) = @_; |
53
|
2
|
|
|
|
|
7
|
my $perl = BERT::Tuple->new([BERT::Atom->new('bert'), BERT::Atom->new('nil')]); |
54
|
2
|
|
|
|
|
6
|
return $self->encode_any($perl); |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
sub is_erl_string { |
58
|
10
|
|
|
10
|
0
|
15
|
my ($self, $value) = @_; |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# Although it works I'm not sure it's the best way to test whether a |
61
|
|
|
|
|
|
|
# scalar is within the byte range |
62
|
10
|
|
|
|
|
13
|
foreach my $item (@{ $value }) { |
|
10
|
|
|
|
|
21
|
|
63
|
17
|
100
|
|
|
|
66
|
if ($item =~ /^\d+$/) { |
64
|
9
|
50
|
33
|
|
|
49
|
return 0 if 0 > $item or $item > 255; |
65
|
|
|
|
|
|
|
} else { |
66
|
8
|
100
|
|
|
|
38
|
return 0 if length $item != 1; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
} |
69
|
3
|
|
|
|
|
28
|
return 1; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
sub encode_array { |
73
|
13
|
|
|
13
|
0
|
20
|
my ($self, $value) = @_; |
74
|
13
|
|
|
|
|
18
|
my @value = @{ $value }; |
|
13
|
|
|
|
|
31
|
|
75
|
|
|
|
|
|
|
|
76
|
13
|
100
|
|
|
|
49
|
return pack('C', NIL_EXT) unless @value; |
77
|
10
|
100
|
|
|
|
30
|
return $self->encode_bytelist(\@value) if $self->is_erl_string(\@value); |
78
|
|
|
|
|
|
|
|
79
|
7
|
|
|
|
|
29
|
my $array = $self->encode_list(\@value, []); |
80
|
7
|
|
|
|
|
16
|
return pack('CN', LIST_EXT, scalar @{ $array }) . join('', @{ $array }) . pack('C', NIL_EXT); |
|
7
|
|
|
|
|
18
|
|
|
7
|
|
|
|
|
58
|
|
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub encode_list { |
84
|
73
|
|
|
73
|
0
|
111
|
my ($self, $value, $array) = @_; |
85
|
|
|
|
|
|
|
|
86
|
73
|
100
|
|
|
|
69
|
if (@{ $value }) { |
|
73
|
|
|
|
|
148
|
|
87
|
52
|
|
|
|
|
40
|
my $head = shift @{ $value }; |
|
52
|
|
|
|
|
78
|
|
88
|
52
|
|
|
|
|
62
|
return $self->encode_list($value, [@{ $array }, $self->encode_any($head)]); |
|
52
|
|
|
|
|
169
|
|
89
|
|
|
|
|
|
|
} else { |
90
|
21
|
|
|
|
|
197
|
return $array; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub encode_dict { |
95
|
4
|
|
|
4
|
0
|
6
|
my ($self, $value) = @_; |
96
|
|
|
|
|
|
|
|
97
|
4
|
|
|
|
|
4
|
my @array; |
98
|
4
|
100
|
|
|
|
15
|
my @value = ref $value eq 'BERT::Dict' ? @{ $value->value } : %{ $value }; |
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
8
|
|
99
|
4
|
|
|
|
|
19
|
while (my @key_value = splice(@value, 0, 2)) { |
100
|
3
|
|
|
|
|
20
|
push @array, BERT::Tuple->new(\@key_value); |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
4
|
|
|
|
|
28
|
my $perl = BERT::Tuple->new([BERT::Atom->new('bert'), BERT::Atom->new('dict'), \@array]); |
104
|
4
|
|
|
|
|
11
|
return $self->encode_any($perl); |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub encode_regex { |
108
|
1
|
|
|
1
|
0
|
4
|
my ($self, $value) = @_; |
109
|
|
|
|
|
|
|
|
110
|
1
|
|
|
|
|
3
|
for ($value) { s/^\(\?//; s/\)$// } |
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
4
|
|
111
|
1
|
|
|
|
|
6
|
my ($modifiers, $pattern) = split /:/, $value, 2; |
112
|
1
|
|
|
|
|
3
|
my ($on, $off) = split /-/, $modifiers; |
113
|
|
|
|
|
|
|
|
114
|
1
|
|
|
|
|
2
|
my @options; |
115
|
1
|
|
|
|
|
3
|
for ($on) { |
116
|
1
|
50
|
|
|
|
4
|
if (/i/) { push @options, BERT::Atom->new('caseless') } |
|
1
|
0
|
|
|
|
5
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
117
|
0
|
|
|
|
|
0
|
elsif (/s/) { push @options, BERT::Atom->new('dotall') } |
118
|
0
|
|
|
|
|
0
|
elsif (/x/) { push @options, BERT::Atom->new('extended') } |
119
|
0
|
|
|
|
|
0
|
elsif (/m/) { push @options, BERT::Atom->new('multiline') } |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
1
|
|
|
|
|
5
|
my $perl = BERT::Tuple->new([BERT::Atom->new('bert'), BERT::Atom->new('regex'), $pattern, \@options]); |
123
|
1
|
|
|
|
|
4
|
return $self->encode_any($perl); |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
sub encode_atom { |
127
|
29
|
|
|
29
|
0
|
81
|
my ($self, $value) = @_; |
128
|
29
|
|
|
|
|
105
|
return pack('Cna*', ATOM_EXT, length $value, $value); |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
sub encode_tuple { |
132
|
14
|
|
|
14
|
0
|
24
|
my ($self, $value) = @_; |
133
|
|
|
|
|
|
|
|
134
|
14
|
|
|
|
|
14
|
my @array = @{ $value->value }; |
|
14
|
|
|
|
|
46
|
|
135
|
14
|
50
|
|
|
|
61
|
return pack('C*', SMALL_TUPLE_EXT, scalar @array) . join('', @{ $self->encode_list(\@array, []) }) if @array < 256; |
|
14
|
|
|
|
|
40
|
|
136
|
0
|
|
|
|
|
0
|
return pack('CN', LARGE_TUPLE_EXT, scalar @array) . join('', @{ $self->encode_list(\@array, []) }); |
|
0
|
|
|
|
|
0
|
|
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
sub encode_bytelist { |
140
|
3
|
|
|
3
|
0
|
6
|
my ($self, $value) = @_; |
141
|
3
|
|
|
|
|
7
|
return pack('CnC*', STRING_EXT, scalar @{ $value }, @{ $value }); |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
23
|
|
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub encode_boolean { |
145
|
2
|
|
|
2
|
0
|
2
|
my ($self, $value) = @_; |
146
|
|
|
|
|
|
|
|
147
|
2
|
100
|
|
|
|
6
|
my $boolean = $value ? BERT::Atom->new('true') : BERT::Atom->new('false'); |
148
|
2
|
|
|
|
|
8
|
my $perl = BERT::Tuple->new([BERT::Atom->new('bert'), $boolean]); |
149
|
2
|
|
|
|
|
6
|
return $self->encode_any($perl); |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub encode_time { |
153
|
1
|
|
|
1
|
0
|
3
|
my ($self, $value) = @_; |
154
|
|
|
|
|
|
|
|
155
|
6
|
|
|
6
|
|
5815
|
use integer; |
|
6
|
|
|
|
|
67
|
|
|
6
|
|
|
|
|
32
|
|
156
|
1
|
|
|
|
|
4
|
my ($seconds, $microseconds) = $value->value; |
157
|
1
|
|
|
|
|
3
|
my $megaseconds = $seconds / 1_000_000; |
158
|
1
|
|
|
|
|
15
|
$seconds = $seconds % 1_000_000; |
159
|
1
|
|
|
|
|
18
|
my $perl = BERT::Tuple->new([BERT::Atom->new('bert'), BERT::Atom->new('time'), $megaseconds, $seconds, $microseconds]); |
160
|
1
|
|
|
|
|
4
|
return $self->encode_any($perl); |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub encode_integer { |
164
|
19
|
|
|
19
|
0
|
26
|
my ($self, $value) = @_; |
165
|
|
|
|
|
|
|
|
166
|
19
|
100
|
100
|
|
|
148
|
return pack('C2', SMALL_INTEGER_EXT, $value) |
167
|
|
|
|
|
|
|
if 0 <= $value and $value <= 255; |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# I think newer versions of erlang no longer have the 28bit limit, |
170
|
|
|
|
|
|
|
# so maybe I should add an option to extend the limit to max_int |
171
|
11
|
100
|
100
|
|
|
844
|
return pack('CN', INTEGER_EXT, $value) |
172
|
|
|
|
|
|
|
if ERL_MIN <= $value and $value <= ERL_MAX; |
173
|
|
|
|
|
|
|
|
174
|
4
|
100
|
|
|
|
522
|
my $sign = $value < 0 ? 1 : 0; |
175
|
4
|
|
|
|
|
445
|
$value = abs($value); |
176
|
|
|
|
|
|
|
|
177
|
4
|
|
|
|
|
112
|
my @bytes; |
178
|
4
|
|
|
|
|
13
|
while ($value > 0) { |
179
|
274
|
|
|
|
|
101526
|
push @bytes, $value & 0xFF; |
180
|
274
|
|
|
|
|
93958
|
$value >>= 8; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
4
|
100
|
|
|
|
1329
|
return pack('C*', SMALL_BIG_EXT, scalar @bytes, $sign, @bytes) if @bytes < 256; |
184
|
1
|
|
|
|
|
14
|
return pack('CNC*', LARGE_BIG_EXT, scalar @bytes, $sign, @bytes); |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub encode_float { |
188
|
2
|
|
|
2
|
0
|
5
|
my ($self, $value) = @_; |
189
|
2
|
|
|
|
|
33
|
return pack('CZ31', FLOAT_EXT, sprintf('%.20e', $value)); |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub encode_binary { |
193
|
10
|
|
|
10
|
0
|
14
|
my ($self, $value) = @_; |
194
|
10
|
|
|
|
|
88
|
return pack('CNa*', BINARY_EXT, length $value, $value); |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
1; |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
__END__ |