| 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 |  |  |  |  |  |  |  |