File Coverage

blib/lib/Google/ProtocolBuffers/Codec.pm
Criterion Covered Total %
statement 228 257 88.7
branch 88 118 74.5
condition 12 12 100.0
subroutine 38 42 90.4
pod 0 21 0.0
total 366 450 81.3


line stmt bran cond sub pod time code
1             package Google::ProtocolBuffers::Codec;
2 13     13   76 use strict;
  13         23  
  13         566  
3 13     13   58 use warnings;
  13         21  
  13         377  
4             ## FATAL substr warnings ("substring outside of string") was intended
5             ## to report about incomplete messages.
6             ## However, substr("abc", 3, 1) returns chr(0) without warning.
7             ## Thats why the code below has to check length of string and
8             ## substring index manually
9 13     13   59 use warnings FATAL => 'substr';
  13         20  
  13         558  
10            
11 13     13   63 use Config qw/%Config/;
  13         35  
  13         519  
12 13     13   17899 use Google::ProtocolBuffers::Constants qw/:all/;
  13         30  
  13         3514  
13 13     13   38676 use Encode ();
  13         202732  
  13         381  
14            
15 13     13   122 use constant BROKEN_MESSAGE => "Mesage is incomplete or invalid";
  13         44  
  13         851  
16 13     13   70 use constant MAX_UINT32 => 0xffff_ffff;
  13         24  
  13         557  
17 13     13   62 use constant MAX_SINT32 => 0x7fff_ffff;
  13         26  
  13         685  
18 13     13   67 use constant MIN_SINT32 =>-0x8000_0000;
  13         24  
  13         1238  
19            
20             BEGIN {
21             ## Protocol Buffer standard requires support of 64-bit integers.
22             ## If platform doen't support them internally, they will be emulated
23             ## by Math::BigInt number.
24             ## Libraries below contains identically named funtions that are either
25             ## use native 64-bit ints or Math::BigInts
26 13     13   13621 my $ivsize = $Config{ivsize};
27 13 50       79015 if ($ivsize>=8) {
    0          
28 13         8067 require 'Google/ProtocolBuffers/CodecIV64.pm';
29             } elsif ($ivsize==4) {
30 0         0 require 'Google/ProtocolBuffers/CodecIV32.pm';
31             } else {
32 0         0 die "Unsupported size of internal Perl IntegerValue: '$ivsize' bytes.";
33             }
34             }
35            
36             BEGIN {
37             ## Floats and doubles are packed in their native format,
38             ## which is different on big-endian and litte-endian platforms
39             ## Maybe create and load one of two files, like CodecIV* above?
40 13     13   213 my $bo = $Config{byteorder};
41 13 50       2073 if ($bo =~ '^1234') {
    0          
42             ## little-endian platform
43 13         71 *encode_float = \&encode_float_le;
44 13         39 *decode_float = \&decode_float_le;
45 13         71 *encode_double = \&encode_double_le;
46 13         20584 *decode_double = \&decode_double_le;
47             } elsif ($bo =~ '4321$') {
48             ## big-endian
49 0         0 *encode_float = \&encode_float_be;
50 0         0 *decode_float = \&decode_float_be;
51 0         0 *encode_double = \&encode_double_be;
52 0         0 *decode_double = \&decode_double_be;
53             }
54             }
55            
56             my @primitive_type_encoders;
57             $primitive_type_encoders[TYPE_DOUBLE] = \&encode_double;
58             $primitive_type_encoders[TYPE_FLOAT] = \&encode_float;
59             $primitive_type_encoders[TYPE_INT64] = \&encode_int;
60             $primitive_type_encoders[TYPE_UINT64] = \&encode_uint;
61             $primitive_type_encoders[TYPE_INT32] = \&encode_int;
62             $primitive_type_encoders[TYPE_FIXED64] = \&encode_fixed64;
63             $primitive_type_encoders[TYPE_FIXED32] = \&encode_fixed32;
64             $primitive_type_encoders[TYPE_BOOL] = \&encode_bool;
65             $primitive_type_encoders[TYPE_STRING] = \&encode_string;
66             $primitive_type_encoders[TYPE_BYTES] = \&encode_string;
67             $primitive_type_encoders[TYPE_UINT32] = \&encode_uint;
68             $primitive_type_encoders[TYPE_ENUM] = \&encode_int;
69             $primitive_type_encoders[TYPE_SFIXED64] = \&encode_sfixed64;
70             $primitive_type_encoders[TYPE_SFIXED32] = \&encode_sfixed32;
71             $primitive_type_encoders[TYPE_SINT32] = \&encode_sint;
72             $primitive_type_encoders[TYPE_SINT64] = \&encode_sint;
73            
74             my @primitive_type_decoders;
75             $primitive_type_decoders[TYPE_DOUBLE] = \&decode_double;
76             $primitive_type_decoders[TYPE_FLOAT] = \&decode_float;
77             $primitive_type_decoders[TYPE_INT64] = \&decode_int;
78             $primitive_type_decoders[TYPE_UINT64] = \&decode_uint;
79             $primitive_type_decoders[TYPE_INT32] = \&decode_int;
80             $primitive_type_decoders[TYPE_FIXED64] = \&decode_fixed64;
81             $primitive_type_decoders[TYPE_FIXED32] = \&decode_fixed32;
82             $primitive_type_decoders[TYPE_BOOL] = \&decode_bool;
83             $primitive_type_decoders[TYPE_STRING] = \&decode_string;
84             $primitive_type_decoders[TYPE_BYTES] = \&decode_string;
85             $primitive_type_decoders[TYPE_UINT32] = \&decode_uint;
86             $primitive_type_decoders[TYPE_ENUM] = \&decode_int;
87             $primitive_type_decoders[TYPE_SFIXED64] = \&decode_sfixed64;
88             $primitive_type_decoders[TYPE_SFIXED32] = \&decode_sfixed32;
89             $primitive_type_decoders[TYPE_SINT32] = \&decode_sint;
90             $primitive_type_decoders[TYPE_SINT64] = \&decode_sint;
91            
92             my @wire_types;
93             $wire_types[TYPE_DOUBLE] = WIRETYPE_FIXED64;
94             $wire_types[TYPE_FLOAT] = WIRETYPE_FIXED32;
95             $wire_types[TYPE_INT64] = WIRETYPE_VARINT;
96             $wire_types[TYPE_UINT64] = WIRETYPE_VARINT;
97             $wire_types[TYPE_INT32] = WIRETYPE_VARINT;
98             $wire_types[TYPE_FIXED64] = WIRETYPE_FIXED64;
99             $wire_types[TYPE_FIXED32] = WIRETYPE_FIXED32;
100             $wire_types[TYPE_BOOL] = WIRETYPE_VARINT;
101             $wire_types[TYPE_STRING] = WIRETYPE_LENGTH_DELIMITED;
102             ## these types were removed deliberatly from the list,
103             ## since they must be serialized by their own classes
104             ##$wire_types[TYPE_GROUP]
105             ##$wire_types[TYPE_MESSAGE]
106             $wire_types[TYPE_BYTES] = WIRETYPE_LENGTH_DELIMITED;
107             $wire_types[TYPE_UINT32] = WIRETYPE_VARINT;
108             ## we create a special class for each enum, but these classes
109             ## are just namespaces for constants. User can create a message
110             ## field with type=TYPE_ENUM and integer value.
111             $wire_types[TYPE_ENUM] = WIRETYPE_VARINT;
112             $wire_types[TYPE_SFIXED32] = WIRETYPE_FIXED32;
113             $wire_types[TYPE_SFIXED64] = WIRETYPE_FIXED64;
114             $wire_types[TYPE_SINT32] = WIRETYPE_VARINT;
115             $wire_types[TYPE_SINT64] = WIRETYPE_VARINT;
116            
117            
118             ##
119             ## Class or instance method.
120             ## Must not be called directly, only as a method of derived class.
121             ##
122             ## Input: data structure (hash-ref)
123             ## Output: in-memory string with serialized data
124             ##
125             ## Example:
126             ## my $str = My::Message->encode({a => 1});
127             ## or
128             ## my $message = bless {a => 1}, 'My::Message';
129             ## my $str = $message->encode;
130             ##
131             sub encode
132             {
133 218     218 0 68811 my $self = shift;
134 218 100       480 my $data = (ref $self) ? $self : shift();
135            
136             ##unless (ref $data eq 'HASH') {
137             ## my $class = ref $self || $self;
138             ## die "Hashref was expected for $self->encode; found '$data' instead";
139             ##}
140            
141 218         254 my $buf = '';
142 218         236 foreach my $field (@{ $self->_pb_fields_list }) {
  218         722  
143 12666         20369 my ($cardinality, $type, $name, $field_number, $default) = @$field;
144             ## Check mising values and their cardinality (i.e. label): required, optional or repeated.
145             ## For required fields, put a default value into stream, if exists, and raise an error otherwise.
146 12666         14067 my $value = $data->{$name};
147 12666 100       21794 if (!defined $value) {
148 12406 100       15688 if ($cardinality==LABEL_REQUIRED) {
149 11 50       18 if (defined $default) {
150 11         18 $value = $default;
151             } else {
152 0         0 die "Required field '$name' is missing in $self";
153             }
154             } else {
155 12395         15675 next;
156             }
157             }
158            
159 271 100 100     789 if (ref $value && ref $value eq 'ARRAY') {
160 47 50       112 if ($cardinality!=LABEL_REPEATED) {
161             ## Oops, several values were given for a non-repeated field.
162             ## We'll take the last one - the specification states that
163             ## if several (non-repeaded) fields are in a stream,
164             ## the last one must be taken
165 0         0 $value = $value->[-1];
166             }
167             }
168 271   100     674 my $is_repeated = ref $value && ref $value eq 'ARRAY';
169            
170 271         311 $field_number <<= 3;
171            
172 13     13   149 no warnings 'numeric';
  13         27  
  13         729  
173 271         405 my $encoder = $primitive_type_encoders[$type];
174 13     13   86 use warnings;
  13         52  
  13         12718  
175            
176 271 100       447 if ($encoder) {
177             ##
178             ## this field is one of the base types
179             ##
180 235 50       489 die $type unless exists $wire_types[$type];
181 235 100       371 if (!$is_repeated) {
182 197         486 encode_varint($buf, $field_number | $wire_types[$type]);
183 197         584 $encoder->($buf, $value);
184             } else {
185 38         41 my $key;
186 38         98 encode_varint($key, $field_number | $wire_types[$type]);
187 38         59 foreach my $v (@$value) {
188 72         82 $buf .= $key;
189 72         152 $encoder->($buf, $v);
190             }
191             }
192             } else {
193             ##
194             ## This field is one of complex types: another message, group or enum
195             ##
196 36         256 my $kind = $type->_pb_complex_type_kind;
197 36 100       97 if ($kind==MESSAGE) {
    100          
    50          
198 17 100       37 if (!$is_repeated) {
199 11         45 encode_varint($buf, $field_number | WIRETYPE_LENGTH_DELIMITED);
200 11         42 my $message = $type->encode($value);
201 11         30 encode_varint($buf, length($message));
202 11         25 $buf .= $message;
203             } else {
204 6         7 my $key;
205 6         14 encode_varint($key, $field_number | WIRETYPE_LENGTH_DELIMITED);
206 6         10 foreach my $v (@$value) {
207 11         13 $buf .= $key;
208 11         27 my $message = $type->encode($v);
209 11         44 encode_varint($buf, length($message));
210 11         26 $buf .= $message;
211             }
212             }
213             }
214             elsif ($kind==ENUM) {
215 12 100       24 if (!$is_repeated) {
216 11         28 encode_varint($buf, $field_number | WIRETYPE_VARINT);
217 11         34 encode_int($buf, $value);
218             } else {
219 1         1 my $key;
220 1         5 encode_varint($key, $field_number | WIRETYPE_VARINT);
221 1         2 foreach my $v (@$value) {
222 2         4 $buf .= $key;
223 2         7 encode_int($buf, $v);
224             }
225             }
226             }
227             elsif ($kind==GROUP) {
228 7 100       19 if (!$is_repeated) {
229 5         15 encode_varint($buf, $field_number | WIRETYPE_START_GROUP);
230 5         58 $buf .= encode($type, $value);
231 5         13 encode_varint($buf, $field_number | WIRETYPE_END_GROUP);
232             } else {
233 2         3 my ($start,$end);
234 2         7 encode_varint($start, $field_number | WIRETYPE_START_GROUP);
235 2         6 encode_varint($end, $field_number | WIRETYPE_END_GROUP);
236 2         5 foreach my $v (@$value) {
237 3         3 $buf .= $start;
238 3         6 $buf .= encode($type, $v);
239 3         9 $buf .= $end;
240             }
241             }
242             } else {
243 0         0 die "Unkown type: $type ($kind)";
244             }
245             }
246             }
247 218         685 return $buf;
248             }
249            
250             ##
251             ## Class method.
252             ## Must not be called directly, only as a method of derived class
253             ##
254             ## Input: string of serialized data
255             ## Output: data structure (hashref)
256             ## If serialized data contains errors, an exception will be thrown.
257             ##
258             ## Example:
259             ## my $data = My::Message->decode($str);
260             ## ## $data is now a hashref like this: {a => 1}
261             ##
262             sub decode {
263 243     243 0 125478 my $class = shift;
264            
265             ## position must be a modifiable variable (it's passed by reference
266             ## to all decode subroutines, that call each other recursively)
267             ## It's slightly quicker then passing it as an object attribute
268             ## ($self->{pos}) to each method, but readability is poor.
269 243         403 my $pos = 0;
270 243 50       950 if (Encode::is_utf8($_[0])) {
271             ## oops, wide-character string, where did you get it from?
272             ## Should we silently encode it to utf-8 and then process
273             ## the resulted byte-string?
274 0         0 die "Input data string is a wide-character string";
275             }
276 243         697 return _decode_partial($class, $_[0], $pos, length($_[0]));
277             }
278            
279             ##
280             ## Internal method, decodes both Messages and Groups
281             ## Input:
282             ## data string,
283             ## start_position (passed by reference, this must be a variable),
284             ## length of message
285             ## Output:
286             ## for Messages: data structure
287             ## for Groups: (data structure, field number of ending group tag)
288             ##
289             sub _decode_partial {
290 282     282   375 my $class = shift;
291            
292 282         361 my $length = $_[2];
293 282         389 my $end_position = $_[1]+$length;
294            
295 282         828 my $data = bless {}, $class;
296 282         1117 my $fields = $class->_pb_fields_by_number;
297            
298             PAIR:
299 282         691 while ($_[1] < $end_position) {
300 431         1563 my $v = decode_varint($_[0], $_[1]);
301 430         782 my ($field_number, $wire_type) = ($v>>3, $v&7);
302            
303 430 100       848 if ($wire_type==WIRETYPE_END_GROUP) {
304 10 50       35 if ($class->_pb_complex_type_kind==GROUP) {
305 10         35 return ($data, $field_number);
306             } else {
307 0         0 die "Unexpected end of group in message";
308             }
309             }
310            
311 420 100       1183 if (my $field = $fields->{$field_number}) {
312 398         855 my ($cardinality, $type, $name, $field_number_, $default) = @$field;
313 398 50       959 die unless $field_number_== $field_number;
314 398         438 my $value;
315            
316 13     13   96 no warnings 'numeric';
  13         32  
  13         619  
317 398         720 my $decoder = $primitive_type_decoders[$type];
318 13     13   65 use warnings;
  13         29  
  13         20071  
319            
320 398 100       714 if ($decoder) {
321 345 100 100     1218 if ($wire_type==WIRETYPE_LENGTH_DELIMITED && $type!=TYPE_STRING && $type!=TYPE_BYTES) {
      100        
322             ##
323             ## Packed Repeated Fields:
324             ## ; sequence of encoded
325             ##
326             ## order is important - $_[1] changed by decode_varint()
327 1         5 my $l = decode_varint($_[0], $_[1]); ## length of the packed field
328 1         3 my $e = $_[1] + $l; ## last position of the field
329            
330 1         3 my @values;
331 1         5 while ($_[1]<$e) {
332 3         12 push @values, $decoder->($_[0], $_[1]);
333             }
334 1 50       6 if ($cardinality==LABEL_REPEATED) {
335 1         1 push @{$data->{$name}}, @values;
  1         5  
336             } else {
337 0         0 $data->{$name} = $values[-1];
338             }
339 1         5 next PAIR;
340            
341             } else {
342             ## regular primitive value, string or byte array
343 344         939 $value = $decoder->($_[0], $_[1]);
344             }
345             } else {
346 53         378 my $kind = $type->_pb_complex_type_kind;
347 53 100       165 if ($kind==MESSAGE) {
    100          
    50          
348 28         98 my $message_length = decode_varint($_[0], $_[1]);
349 28         233 $value = _decode_partial($type, $_[0], $_[1], $message_length);
350             } elsif ($kind==ENUM) {
351 14         48 $value = decode_int($_[0], $_[1]);
352             } elsif ($kind==GROUP) {
353 11         17 my $end_field_number;
354 11         56 ($value, $end_field_number) = _decode_partial($type, $_[0], $_[1], $end_position-$_[1]);
355 10 50       36 die unless $field_number == $end_field_number;
356             } else {
357 0         0 die "Unkown type: $type ($kind)";
358             }
359             }
360 368 100       806 if ($cardinality==LABEL_REPEATED) {
361 90         119 push @{$data->{$name}}, $value;
  90         502  
362             } else {
363 278         1377 $data->{$name} = $value;
364             }
365             }
366             else {
367 22         49 _skip_unknown_field($_[0], $_[1], $field_number, $wire_type);
368             }
369             }
370            
371 242 50       931 if ($class->_pb_complex_type_kind==GROUP) {
372 0         0 die "End of group token was not found";
373             } else {
374 242         798 return $data;
375             }
376             }
377            
378             ##
379             ## Subroutines for skipping unknown fields
380             ##
381             ## _skip_unknown_field($buffer, $position, $field_number, $wire_type)
382             ## $buffer is immutable
383             ## $position will be advanced
384             ## $field_number is for groups only, and for checks that closing group
385             ## field_number equals to the (given) opening field_number
386             ## $wire_type is to know lenght of field to be skipped
387             ## Returns none
388             ##
389             sub _skip_unknown_field {
390 24     24   34 my ($field_number, $wire_type) = ($_[2], $_[3]);
391            
392 24 100       66 if ($wire_type==WIRETYPE_VARINT) {
    100          
    100          
    100          
    50          
    50          
393 14         28 _skip_varint($_[0], $_[1]);
394             } elsif ($wire_type==WIRETYPE_FIXED64) {
395 3         7 $_[1] += 8;
396             } elsif ($wire_type==WIRETYPE_LENGTH_DELIMITED) {
397 3         12 my $len = decode_varint($_[0], $_[1]);
398 3         9 $_[1] += $len;
399             } elsif ($wire_type==WIRETYPE_START_GROUP) {
400 1         5 my $closing_field_number = _skip_until_end_of_group($_[0], $_[1]);
401 1 50       7 die unless $closing_field_number==$field_number;
402             } elsif ($wire_type==WIRETYPE_END_GROUP) {
403 0         0 die "Unexpected end of group";
404             } elsif ($wire_type==WIRETYPE_FIXED32) {
405 3         9 $_[1] += 4;
406             } else {
407 0         0 die "Unknown wire type $wire_type";
408             }
409             }
410            
411             ##
412             ## _skip_until_end_of_group($buffer, $position);
413             ## Returns field_number of closing group tag
414             ##
415             sub _skip_until_end_of_group {
416 1     1   2 while (1) {
417 3         9 my $v = decode_varint($_[0], $_[1]);
418 3         7 my ($field_number, $wire_type) = ($v>>3, $v&7);
419 3 100       10 return $field_number if $wire_type==WIRETYPE_END_GROUP;
420 2         8 _skip_unknown_field($_[0], $_[1], $field_number, $wire_type);
421             }
422             }
423            
424             ##
425             ## _skip_varint($buffer, $position)
426             ## Returns none
427             sub _skip_varint {
428 14     14   15 my $c = 0;
429 14         16 my $l = length($_[0]);
430 14         15 while (1) {
431 16 50       38 die BROKEN_MESSAGE() if $_[1] >= $l; ## if $_[1]+1 > $l
432 16 100       67 last if (ord(substr($_[0], $_[1]++, 1)) & 0x80) == 0;
433 2 50       9 die "Varint is too long" if ++$c>=9;
434             }
435             }
436            
437             ##
438             ## Implementations of primitive types serialization/deserialization are
439             ## below. Some of subroutines are defined in IV32/IV64 modules.
440             ##
441             ## Signature of all encode_* subs:
442             ## encode_*($buffer, $value);
443             ## Encoded value of $value will be appended to $buffer, which is a string
444             ## passed by reference. No meaningfull value is returned, in case of errors
445             ## an exception it thrown.
446             ##
447             ## Signature of all encode_* subs:
448             ## my $value = decode_*($buffer, $position);
449             ## $buffer is a string passed by reference, no copy is performed and it
450             ## is not modified. $position is a number variable passed by reference
451             ## (index in the string $buffer where to start decoding of a value), it
452             ## is incremented by decode_* subs. In case of errors an exception is
453             ## thrown.
454             ##
455             ## Sorry for poor readability, these subroutines were optimized for speed.
456             ## Most probably, they (and this module entirely) should be written in XS
457             ##
458            
459             ##
460             ## type: varint
461             ##
462             ## Our implementation of varint knows about positive numbers only.
463             ## It's caller's responsibility to convert negative values into
464             ## 64-bit positives
465             ##
466             sub encode_varint {
467 485     485 0 2335 my $v = $_[1];
468 485 50       896 die "Varint is negative" if $v < 0;
469 485         1769 my $c = 0;
470 485         1430 while ($v > 0x7F) {
471 377         6714 $_[0] .= chr( ($v&0x7F) | 0x80 );
472 377         28061 $v >>= 7;
473 377 50       13939 die "Number is too long" if ++$c >= 10;
474             }
475 485         1814 $_[0] .= chr( ($v&0x7F) );
476             }
477             ## sub decode_varint - word-size sensitive
478            
479             ##
480             ## type: unsigend int (32/64)
481             ##
482             ## sub encode_uint - word-size sensitive
483             *encode_uint = \&encode_int;
484            
485             ## decode_varint always returns positive value
486             sub decode_uint {
487 32     32 0 104 return decode_varint(@_);
488             }
489            
490             ##
491             ## type: signed int (32/64)
492             ##
493             ## Signed zigzag-encode integers
494             ## Acutally, zigzag encoded value is just ($v>0) ? $v*2 : (-$v)*2-1;
495             ##
496            
497             sub decode_sint {
498 38     38 0 110 my $v = decode_varint(@_);
499 34 100       97 if ($v & 1) {
500             ## warning: -(($v+1)>>1) may cause overflow
501 8         25 return -(1 + (($v-1)>>1))
502             } else {
503 26         63 return $v>>1;
504             }
505             }
506            
507             ##
508             ## type: boolean
509             ##
510             sub encode_bool {
511 9 100   9 0 20 if ($_[1]) {
512 5         13 encode_varint($_[0], 1);
513             } else {
514 4         10 encode_varint($_[0], 0);
515             }
516             }
517            
518             sub decode_bool {
519 12 100   12 0 42 return (decode_varint(@_)) ? 1 : 0;
520             }
521            
522             ##
523             ## type: unsigned fixed 64-bit int
524             ##
525             ##sub encode_fixed64 - word-size sensitive
526             ##sub decode_fixed64 - word-size sensitive
527            
528             ##
529             ## type: signed fixed 64-bit int
530             ##
531             ##sub encode_sfixed64 - word-size sensitive
532             ##sub decode_sfixed64 - word-size sensitive
533            
534             ##
535             ## type: double
536             ##
537             ## little-endian versions
538             sub encode_double_le {
539 16     16 0 58 $_[0] .= pack('d', $_[1]);
540             }
541             sub decode_double_le {
542 19 100   19 0 344 die BROKEN_MESSAGE() if $_[1]+8 > length($_[0]);
543 18         52 my $v = unpack('d', substr($_[0], $_[1], 8));
544 18         54 $_[1] += 8;
545 18         37 return $v;
546             }
547            
548             ## big-endian versions
549             sub encode_double_be {
550 0     0 0 0 $_[0] .= reverse pack('d', $_[1]);
551             }
552             sub decode_double_be {
553 0 0   0 0 0 die BROKEN_MESSAGE() if $_[1]+8 > length($_[0]);
554 0         0 my $v = unpack('d', reverse substr($_[0], $_[1], 8));
555 0         0 $_[1] += 8;
556 0         0 return $v;
557             }
558            
559             ##
560             ## type: string and bytes
561             ##
562             sub encode_string {
563 13 50   13 0 89 use Carp; Carp::cluck("Undefined string") unless defined $_[1];
  13     38   23  
  13         21519  
  38         80  
564 38 50       137 if (Encode::is_utf8($_[1])) {
565             ## Ops, the string has wide-characters.
566             ## Well, encode them to utf-8 bytes.
567 0         0 my $v = Encode::encode_utf8($_[1]);
568 0         0 encode_varint($_[0], length($v));
569 0         0 $_[0] .= $v;
570             } else {
571 38         85 encode_varint($_[0], length($_[1]));
572 38         106 $_[0] .= $_[1];
573             }
574             }
575            
576             sub decode_string {
577 49     49 0 140 my $length = decode_varint(@_);
578 47 100       661 die BROKEN_MESSAGE() if $_[1]+$length > length($_[0]);
579 45         206 my $str = substr($_[0], $_[1], $length);
580 45         57 $_[1] += $length;
581 45         257 return $str;
582             }
583            
584             ##
585             ## type: unsigned 32-bit
586             ##
587             sub encode_fixed32 {
588 12     12 0 53 $_[0] .= pack('V', $_[1]);
589             }
590             sub decode_fixed32 {
591 15 100   15 0 337 die BROKEN_MESSAGE() if $_[1]+4 > length($_[0]);
592 14         50 my $v = unpack('V', substr($_[0], $_[1], 4));
593 14         21 $_[1] += 4;
594 14         26 return $v;
595             }
596            
597             ##
598             ## type: signed 32-bit
599             ##
600             sub encode_sfixed32 {
601 13     13 0 45 $_[0] .= pack('V', $_[1]);
602             }
603             sub decode_sfixed32 {
604 16 100   16 0 323 die BROKEN_MESSAGE() if $_[1]+4 > length($_[0]);
605 15         42 my $v = unpack('V', substr($_[0], $_[1], 4));
606 15         23 $_[1] += 4;
607 15 100       56 return ($v>MAX_SINT32()) ? ($v-MAX_UINT32())-1 : $v;
608             }
609            
610             ##
611             ## type: float
612             ##
613             sub encode_float_le {
614 25     25 0 89 $_[0] .= pack('f', $_[1]);
615             }
616             sub decode_float_le {
617 28 100   28 0 328 die BROKEN_MESSAGE() if $_[1]+4 > length($_[0]);
618 27         78 my $v = unpack('f', substr($_[0], $_[1], 4));
619 27         38 $_[1] += 4;
620 27         48 return $v;
621             }
622            
623             sub encode_float_be {
624 0     0 0   $_[0] .= reverse pack('f', $_[1]);
625             }
626             sub decode_float_be {
627 0 0   0 0   die BROKEN_MESSAGE() if $_[1]+4 > length($_[0]);
628 0           my $v = unpack('f', reverse substr($_[0], $_[1], 4));
629 0           $_[1] += 4;
630 0           return $v;
631             }
632            
633