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 14     14   44 use strict;
  14         15  
  14         280  
3 14     14   42 use warnings;
  14         13  
  14         285  
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 14     14   38 use warnings FATAL => 'substr';
  14         15  
  14         530  
10            
11 14     14   42 use Config qw/%Config/;
  14         11  
  14         364  
12 14     14   4373 use Google::ProtocolBuffers::Constants qw/:all/;
  14         21  
  14         2214  
13 14     14   6699 use Encode ();
  14         103344  
  14         277  
14            
15 14     14   65 use constant BROKEN_MESSAGE => "Mesage is incomplete or invalid";
  14         14  
  14         596  
16 14     14   44 use constant MAX_UINT32 => 0xffff_ffff;
  14         604  
  14         510  
17 14     14   45 use constant MAX_SINT32 => 0x7fff_ffff;
  14         14  
  14         493  
18 14     14   44 use constant MIN_SINT32 =>-0x8000_0000;
  14         13  
  14         956  
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 14     14   823 my $ivsize = $Config{ivsize};
27 14 50       59 if ($ivsize>=8) {
    0          
28 14         4576 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 14     14   442 my $bo = $Config{byteorder};
41 14 50       62 if ($bo =~ '^1234') {
    0          
42             ## little-endian platform
43 14         38 *encode_float = \&encode_float_le;
44 14         43 *decode_float = \&decode_float_le;
45 14         31 *encode_double = \&encode_double_le;
46 14         5573 *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 63775 my $self = shift;
134 218 100       367 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         199 my $buf = '';
142 218         212 foreach my $field (@{ $self->_pb_fields_list }) {
  218         536  
143 12666         15336 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         9608 my $value = $data->{$name};
147 12666 100       13594 if (!defined $value) {
148 12406 100       10365 if ($cardinality==LABEL_REQUIRED) {
149 11 50       13 if (defined $default) {
150 11         11 $value = $default;
151             } else {
152 0         0 die "Required field '$name' is missing in $self";
153             }
154             } else {
155 12395         9430 next;
156             }
157             }
158            
159 271 100 100     630 if (ref $value && ref $value eq 'ARRAY') {
160 47 50       87 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     485 my $is_repeated = ref $value && ref $value eq 'ARRAY';
169            
170 271         199 $field_number <<= 3;
171            
172 14     14   80 no warnings 'numeric';
  14         20  
  14         497  
173 271         346 my $encoder = $primitive_type_encoders[$type];
174 14     14   50 use warnings;
  14         15  
  14         6762  
175            
176 271 100       443 if ($encoder) {
177             ##
178             ## this field is one of the base types
179             ##
180 235 50       322 die $type unless exists $wire_types[$type];
181 235 100       277 if (!$is_repeated) {
182 197         318 encode_varint($buf, $field_number | $wire_types[$type]);
183 197         377 $encoder->($buf, $value);
184             } else {
185 38         29 my $key;
186 38         68 encode_varint($key, $field_number | $wire_types[$type]);
187 38         50 foreach my $v (@$value) {
188 72         60 $buf .= $key;
189 72         111 $encoder->($buf, $v);
190             }
191             }
192             } else {
193             ##
194             ## This field is one of complex types: another message, group or enum
195             ##
196 36         184 my $kind = $type->_pb_complex_type_kind;
197 36 100       69 if ($kind==MESSAGE) {
    100          
    50          
198 17 100       33 if (!$is_repeated) {
199 11         29 encode_varint($buf, $field_number | WIRETYPE_LENGTH_DELIMITED);
200 11         31 my $message = $type->encode($value);
201 11         17 encode_varint($buf, length($message));
202 11         16 $buf .= $message;
203             } else {
204 6         4 my $key;
205 6         15 encode_varint($key, $field_number | WIRETYPE_LENGTH_DELIMITED);
206 6         7 foreach my $v (@$value) {
207 11         9 $buf .= $key;
208 11         21 my $message = $type->encode($v);
209 11         18 encode_varint($buf, length($message));
210 11         15 $buf .= $message;
211             }
212             }
213             }
214             elsif ($kind==ENUM) {
215 12 100       19 if (!$is_repeated) {
216 11         21 encode_varint($buf, $field_number | WIRETYPE_VARINT);
217 11         26 encode_int($buf, $value);
218             } else {
219 1         2 my $key;
220 1         4 encode_varint($key, $field_number | WIRETYPE_VARINT);
221 1         2 foreach my $v (@$value) {
222 2         3 $buf .= $key;
223 2         4 encode_int($buf, $v);
224             }
225             }
226             }
227             elsif ($kind==GROUP) {
228 7 100       14 if (!$is_repeated) {
229 5         10 encode_varint($buf, $field_number | WIRETYPE_START_GROUP);
230 5         21 $buf .= encode($type, $value);
231 5         13 encode_varint($buf, $field_number | WIRETYPE_END_GROUP);
232             } else {
233 2         4 my ($start,$end);
234 2         5 encode_varint($start, $field_number | WIRETYPE_START_GROUP);
235 2         4 encode_varint($end, $field_number | WIRETYPE_END_GROUP);
236 2         5 foreach my $v (@$value) {
237 3         4 $buf .= $start;
238 3         6 $buf .= encode($type, $v);
239 3         6 $buf .= $end;
240             }
241             }
242             } else {
243 0         0 die "Unkown type: $type ($kind)";
244             }
245             }
246             }
247 218         450 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 68898 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         264 my $pos = 0;
270 243 50       727 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         424 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   220 my $class = shift;
291            
292 282         272 my $length = $_[2];
293 282         256 my $end_position = $_[1]+$length;
294            
295 282         403 my $data = bless {}, $class;
296 282         645 my $fields = $class->_pb_fields_by_number;
297            
298             PAIR:
299 282         459 while ($_[1] < $end_position) {
300 431         797 my $v = decode_varint($_[0], $_[1]);
301 430         465 my ($field_number, $wire_type) = ($v>>3, $v&7);
302            
303 430 100       551 if ($wire_type==WIRETYPE_END_GROUP) {
304 10 50       21 if ($class->_pb_complex_type_kind==GROUP) {
305 10         23 return ($data, $field_number);
306             } else {
307 0         0 die "Unexpected end of group in message";
308             }
309             }
310            
311 420 100       684 if (my $field = $fields->{$field_number}) {
312 398         482 my ($cardinality, $type, $name, $field_number_, $default) = @$field;
313 398 50       503 die unless $field_number_== $field_number;
314 398         226 my $value;
315            
316 14     14   67 no warnings 'numeric';
  14         17  
  14         471  
317 398         435 my $decoder = $primitive_type_decoders[$type];
318 14     14   44 use warnings;
  14         16  
  14         11372  
319            
320 398 100       407 if ($decoder) {
321 345 100 100     779 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         4 my $e = $_[1] + $l; ## last position of the field
329            
330 1         3 my @values;
331 1         5 while ($_[1]<$e) {
332 3         9 push @values, $decoder->($_[0], $_[1]);
333             }
334 1 50       3 if ($cardinality==LABEL_REPEATED) {
335 1         2 push @{$data->{$name}}, @values;
  1         4  
336             } else {
337 0         0 $data->{$name} = $values[-1];
338             }
339 1         3 next PAIR;
340            
341             } else {
342             ## regular primitive value, string or byte array
343 344         541 $value = $decoder->($_[0], $_[1]);
344             }
345             } else {
346 53         233 my $kind = $type->_pb_complex_type_kind;
347 53 100       98 if ($kind==MESSAGE) {
    100          
    50          
348 28         50 my $message_length = decode_varint($_[0], $_[1]);
349 28         48 $value = _decode_partial($type, $_[0], $_[1], $message_length);
350             } elsif ($kind==ENUM) {
351 14         26 $value = decode_int($_[0], $_[1]);
352             } elsif ($kind==GROUP) {
353 11         9 my $end_field_number;
354 11         33 ($value, $end_field_number) = _decode_partial($type, $_[0], $_[1], $end_position-$_[1]);
355 10 50       22 die unless $field_number == $end_field_number;
356             } else {
357 0         0 die "Unkown type: $type ($kind)";
358             }
359             }
360 368 100       420 if ($cardinality==LABEL_REPEATED) {
361 90         58 push @{$data->{$name}}, $value;
  90         288  
362             } else {
363 278         786 $data->{$name} = $value;
364             }
365             }
366             else {
367 22         25 _skip_unknown_field($_[0], $_[1], $field_number, $wire_type);
368             }
369             }
370            
371 242 50       474 if ($class->_pb_complex_type_kind==GROUP) {
372 0         0 die "End of group token was not found";
373             } else {
374 242         440 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   23 my ($field_number, $wire_type) = ($_[2], $_[3]);
391            
392 24 100       44 if ($wire_type==WIRETYPE_VARINT) {
    100          
    100          
    100          
    50          
    50          
393 14         17 _skip_varint($_[0], $_[1]);
394             } elsif ($wire_type==WIRETYPE_FIXED64) {
395 3         5 $_[1] += 8;
396             } elsif ($wire_type==WIRETYPE_LENGTH_DELIMITED) {
397 3         4 my $len = decode_varint($_[0], $_[1]);
398 3         4 $_[1] += $len;
399             } elsif ($wire_type==WIRETYPE_START_GROUP) {
400 1         2 my $closing_field_number = _skip_until_end_of_group($_[0], $_[1]);
401 1 50       4 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         5 $_[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         5 my $v = decode_varint($_[0], $_[1]);
418 3         3 my ($field_number, $wire_type) = ($v>>3, $v&7);
419 3 100       6 return $field_number if $wire_type==WIRETYPE_END_GROUP;
420 2         4 _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   9 my $c = 0;
429 14         10 my $l = length($_[0]);
430 14         11 while (1) {
431 16 50       21 die BROKEN_MESSAGE() if $_[1] >= $l; ## if $_[1]+1 > $l
432 16 100       41 last if (ord(substr($_[0], $_[1]++, 1)) & 0x80) == 0;
433 2 50       6 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 1599 my $v = $_[1];
468 485 50       625 die "Varint is negative" if $v < 0;
469 485         1170 my $c = 0;
470 485         633 while ($v > 0x7F) {
471 377         4281 $_[0] .= chr( ($v&0x7F) | 0x80 );
472 377         17743 $v >>= 7;
473 377 50       9298 die "Number is too long" if ++$c >= 10;
474             }
475 485         1126 $_[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 54 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 75 my $v = decode_varint(@_);
499 34 100       62 if ($v & 1) {
500             ## warning: -(($v+1)>>1) may cause overflow
501 8         18 return -(1 + (($v-1)>>1))
502             } else {
503 26         34 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         11 encode_varint($_[0], 1);
513             } else {
514 4         8 encode_varint($_[0], 0);
515             }
516             }
517            
518             sub decode_bool {
519 12 100   12 0 26 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 49 $_[0] .= pack('d', $_[1]);
540             }
541             sub decode_double_le {
542 19 100   19 0 219 die BROKEN_MESSAGE() if $_[1]+8 > length($_[0]);
543 18         36 my $v = unpack('d', substr($_[0], $_[1], 8));
544 18         16 $_[1] += 8;
545 18         23 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 14 50   14 0 88 use Carp; Carp::cluck("Undefined string") unless defined $_[1];
  14     38   14  
  14         5805  
  38         58  
564 38 50       94 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         59 encode_varint($_[0], length($_[1]));
572 38         60 $_[0] .= $_[1];
573             }
574             }
575            
576             sub decode_string {
577 49     49 0 79 my $length = decode_varint(@_);
578 47 100       421 die BROKEN_MESSAGE() if $_[1]+$length > length($_[0]);
579 45         57 my $str = substr($_[0], $_[1], $length);
580 45         37 $_[1] += $length;
581 45         88 return $str;
582             }
583            
584             ##
585             ## type: unsigned 32-bit
586             ##
587             sub encode_fixed32 {
588 12     12 0 39 $_[0] .= pack('V', $_[1]);
589             }
590             sub decode_fixed32 {
591 15 100   15 0 210 die BROKEN_MESSAGE() if $_[1]+4 > length($_[0]);
592 14         38 my $v = unpack('V', substr($_[0], $_[1], 4));
593 14         12 $_[1] += 4;
594 14         15 return $v;
595             }
596            
597             ##
598             ## type: signed 32-bit
599             ##
600             sub encode_sfixed32 {
601 13     13 0 31 $_[0] .= pack('V', $_[1]);
602             }
603             sub decode_sfixed32 {
604 16 100   16 0 267 die BROKEN_MESSAGE() if $_[1]+4 > length($_[0]);
605 15         30 my $v = unpack('V', substr($_[0], $_[1], 4));
606 15         11 $_[1] += 4;
607 15 100       29 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 80 $_[0] .= pack('f', $_[1]);
615             }
616             sub decode_float_le {
617 28 100   28 0 231 die BROKEN_MESSAGE() if $_[1]+4 > length($_[0]);
618 27         48 my $v = unpack('f', substr($_[0], $_[1], 4));
619 27         27 $_[1] += 4;
620 27         29 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