| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package MaxMind::DB::Reader::Decoder; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 21 |  |  | 21 |  | 761711 | use strict; | 
|  | 21 |  |  |  |  | 35 |  | 
|  | 21 |  |  |  |  | 553 |  | 
| 4 | 21 |  |  | 21 |  | 81 | use warnings; | 
|  | 21 |  |  |  |  | 30 |  | 
|  | 21 |  |  |  |  | 570 |  | 
| 5 | 21 |  |  | 21 |  | 7076 | use namespace::autoclean; | 
|  | 21 |  |  |  |  | 225627 |  | 
|  | 21 |  |  |  |  | 95 |  | 
| 6 | 21 |  |  | 21 |  | 1147 | use autodie; | 
|  | 21 |  |  |  |  | 32 |  | 
|  | 21 |  |  |  |  | 153 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | our $VERSION = '1.000012'; | 
| 9 |  |  |  |  |  |  |  | 
| 10 | 21 |  |  | 21 |  | 70012 | use Carp qw( confess ); | 
|  | 21 |  |  |  |  | 34 |  | 
|  | 21 |  |  |  |  | 1353 |  | 
| 11 | 21 |  |  | 21 |  | 10825 | use Data::IEEE754 qw( unpack_double_be unpack_float_be ); | 
|  | 21 |  |  |  |  | 22682 |  | 
|  | 21 |  |  |  |  | 1185 |  | 
| 12 | 21 |  |  | 21 |  | 10885 | use Encode (); | 
|  | 21 |  |  |  |  | 168218 |  | 
|  | 21 |  |  |  |  | 508 |  | 
| 13 | 21 |  |  | 21 |  | 3552 | use Math::BigInt qw(); | 
|  | 21 |  |  |  |  | 73658 |  | 
|  | 21 |  |  |  |  | 653 |  | 
| 14 | 21 |  |  | 21 |  | 10255 | use MaxMind::DB::Common 0.040001 qw( %TypeNumToName ); | 
|  | 21 |  |  |  |  | 8363 |  | 
|  | 21 |  |  |  |  | 3121 |  | 
| 15 | 21 |  |  | 21 |  | 10122 | use MaxMind::DB::Reader::Data::Container; | 
|  | 21 |  |  |  |  | 37 |  | 
|  | 21 |  |  |  |  | 556 |  | 
| 16 | 21 |  |  | 21 |  | 7878 | use MaxMind::DB::Reader::Data::EndMarker; | 
|  | 21 |  |  |  |  | 38 |  | 
|  | 21 |  |  |  |  | 587 |  | 
| 17 | 21 |  |  | 21 |  | 6590 | use MaxMind::DB::Types qw( Int ); | 
|  | 21 |  |  |  |  | 185124 |  | 
|  | 21 |  |  |  |  | 1176 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 | 21 |  |  | 21 |  | 8439 | use Moo; | 
|  | 21 |  |  |  |  | 34530 |  | 
|  | 21 |  |  |  |  | 131 |  | 
| 20 | 21 |  |  | 21 |  | 28450 | use MooX::StrictConstructor; | 
|  | 21 |  |  |  |  | 162952 |  | 
|  | 21 |  |  |  |  | 117 |  | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | with 'MaxMind::DB::Role::Debugs', 'MaxMind::DB::Reader::Role::Sysreader'; | 
| 23 |  |  |  |  |  |  |  | 
| 24 | 21 |  |  | 21 |  | 204245 | use constant DEBUG => $ENV{MAXMIND_DB_DECODER_DEBUG}; | 
|  | 21 |  |  |  |  | 45 |  | 
|  | 21 |  |  |  |  | 1696 |  | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | # This is a constant so that outside of testing any references to it can be | 
| 27 |  |  |  |  |  |  | # optimised away by the compiler. | 
| 28 | 21 |  |  | 21 |  | 96 | use constant POINTER_TEST_HACK => $ENV{MAXMIND_DB_POINTER_TEST_HACK}; | 
|  | 21 |  |  |  |  | 29 |  | 
|  | 21 |  |  |  |  | 31795 |  | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | binmode STDERR, ':utf8' | 
| 31 |  |  |  |  |  |  | if DEBUG; | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | has _pointer_base => ( | 
| 34 |  |  |  |  |  |  | is       => 'ro', | 
| 35 |  |  |  |  |  |  | isa      => Int, | 
| 36 |  |  |  |  |  |  | init_arg => 'pointer_base', | 
| 37 |  |  |  |  |  |  | default  => 0, | 
| 38 |  |  |  |  |  |  | ); | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | sub decode { | 
| 41 | 1237 |  |  | 1237 | 0 | 403529 | my $self   = shift; | 
| 42 | 1237 |  |  |  |  | 1166 | my $offset = shift; | 
| 43 |  |  |  |  |  |  |  | 
| 44 | 1237 | 100 |  |  |  | 2274 | confess 'You must provide an offset to decode from when calling ->decode' | 
| 45 |  |  |  |  |  |  | unless defined $offset; | 
| 46 |  |  |  |  |  |  |  | 
| 47 | 1236 | 100 |  |  |  | 3057 | confess | 
| 48 |  |  |  |  |  |  | q{The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)} | 
| 49 |  |  |  |  |  |  | if $offset >= $self->_data_source_size; | 
| 50 |  |  |  |  |  |  |  | 
| 51 | 1235 |  |  |  |  | 38273 | if (DEBUG) { | 
| 52 |  |  |  |  |  |  | $self->_debug_newline(); | 
| 53 |  |  |  |  |  |  | $self->_debug_string( 'Offset', $offset ); | 
| 54 |  |  |  |  |  |  | } | 
| 55 |  |  |  |  |  |  |  | 
| 56 | 1235 |  |  |  |  | 1128 | my $ctrl_byte; | 
| 57 | 1235 |  |  |  |  | 3091 | $self->_read( \$ctrl_byte, $offset, 1 ); | 
| 58 | 1235 |  |  |  |  | 1301 | $offset++; | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 1235 |  |  |  |  | 969 | $self->_debug_binary( 'Control byte', $ctrl_byte ) | 
| 61 |  |  |  |  |  |  | if DEBUG; | 
| 62 |  |  |  |  |  |  |  | 
| 63 | 1235 |  |  |  |  | 2242 | $ctrl_byte = unpack( C => $ctrl_byte ); | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | # The type is encoded in the first 3 bits of the byte. | 
| 66 | 1235 |  |  |  |  | 2610 | my $type = $TypeNumToName{ $ctrl_byte >> 5 }; | 
| 67 |  |  |  |  |  |  |  | 
| 68 | 1235 |  |  |  |  | 875 | $self->_debug_string( 'Type', $type ) | 
| 69 |  |  |  |  |  |  | if DEBUG; | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | # Pointers are a special case, we don't read the next $size bytes, we use | 
| 72 |  |  |  |  |  |  | # the size to determine the length of the pointer and then follow it. | 
| 73 | 1235 | 100 |  |  |  | 2128 | if ( $type eq 'pointer' ) { | 
| 74 | 37 |  |  |  |  | 96 | my ( $pointer, $new_offset ) | 
| 75 |  |  |  |  |  |  | = $self->_decode_pointer( $ctrl_byte, $offset ); | 
| 76 |  |  |  |  |  |  |  | 
| 77 | 37 |  |  |  |  | 59 | return $pointer if POINTER_TEST_HACK; | 
| 78 |  |  |  |  |  |  |  | 
| 79 | 27 |  |  |  |  | 75 | my $value = $self->decode($pointer); | 
| 80 |  |  |  |  |  |  | return wantarray | 
| 81 | 26 | 100 |  |  |  | 938 | ? ( $value, $new_offset ) | 
| 82 |  |  |  |  |  |  | : $value; | 
| 83 |  |  |  |  |  |  | } | 
| 84 |  |  |  |  |  |  |  | 
| 85 | 1198 | 100 |  |  |  | 1908 | if ( $type eq 'extended' ) { | 
| 86 | 108 |  |  |  |  | 112 | my $next_byte; | 
| 87 | 108 |  |  |  |  | 275 | $self->_read( \$next_byte, $offset, 1 ); | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 108 |  |  |  |  | 87 | $self->_debug_binary( 'Next byte', $next_byte ) | 
| 90 |  |  |  |  |  |  | if DEBUG; | 
| 91 |  |  |  |  |  |  |  | 
| 92 | 108 |  |  |  |  | 232 | my $type_num = unpack( C => $next_byte ) + 7; | 
| 93 | 108 | 50 |  |  |  | 216 | confess | 
| 94 |  |  |  |  |  |  | "Something went horribly wrong in the decoder. An extended type resolved to a type number < 8 ($type_num)" | 
| 95 |  |  |  |  |  |  | if $type_num < 8; | 
| 96 |  |  |  |  |  |  |  | 
| 97 | 108 |  |  |  |  | 196 | $type = $TypeNumToName{$type_num}; | 
| 98 | 108 |  |  |  |  | 121 | $offset++; | 
| 99 |  |  |  |  |  |  | } | 
| 100 |  |  |  |  |  |  |  | 
| 101 | 1198 |  |  |  |  | 2594 | ( my $size, $offset ) | 
| 102 |  |  |  |  |  |  | = $self->_size_from_ctrl_byte( $ctrl_byte, $offset ); | 
| 103 |  |  |  |  |  |  |  | 
| 104 | 1198 |  |  |  |  | 1112 | $self->_debug_string( 'Size', $size ) | 
| 105 |  |  |  |  |  |  | if DEBUG; | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | # The map and array types are special cases, since we don't read the next | 
| 108 |  |  |  |  |  |  | # $size bytes. For all other types, we do. | 
| 109 | 1198 | 100 |  |  |  | 2160 | return $self->_decode_map( $size, $offset ) | 
| 110 |  |  |  |  |  |  | if $type eq 'map'; | 
| 111 |  |  |  |  |  |  |  | 
| 112 | 977 | 100 |  |  |  | 1602 | return $self->_decode_array( $size, $offset ) | 
| 113 |  |  |  |  |  |  | if $type eq 'array'; | 
| 114 |  |  |  |  |  |  |  | 
| 115 | 953 | 100 |  |  |  | 1422 | return $self->_decode_boolean( $size, $offset ) | 
| 116 |  |  |  |  |  |  | if $type eq 'boolean'; | 
| 117 |  |  |  |  |  |  |  | 
| 118 | 951 |  |  |  |  | 857 | my $buffer; | 
| 119 | 951 | 100 |  |  |  | 2750 | $self->_read( \$buffer, $offset, $size ) | 
| 120 |  |  |  |  |  |  | if $size; | 
| 121 |  |  |  |  |  |  |  | 
| 122 | 951 |  |  |  |  | 756 | $self->_debug_binary( 'Buffer', $buffer ) | 
| 123 |  |  |  |  |  |  | if DEBUG; | 
| 124 |  |  |  |  |  |  |  | 
| 125 | 951 | 100 |  |  |  | 2746 | my $method = '_decode_' . ( $type =~ /^uint/ ? 'uint' : $type ); | 
| 126 |  |  |  |  |  |  | return wantarray | 
| 127 | 951 | 100 |  |  |  | 3014 | ? ( $self->$method( $buffer, $size ), $offset + $size ) | 
| 128 |  |  |  |  |  |  | : $self->$method( $buffer, $size ); | 
| 129 |  |  |  |  |  |  | } | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | my %pointer_value_offset = ( | 
| 132 |  |  |  |  |  |  | 1 => 0, | 
| 133 |  |  |  |  |  |  | 2 => 2**11, | 
| 134 |  |  |  |  |  |  | 3 => 2**19 + 2**11, | 
| 135 |  |  |  |  |  |  | 4 => 0, | 
| 136 |  |  |  |  |  |  | ); | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | sub _decode_pointer { | 
| 139 | 37 |  |  | 37 |  | 40 | my $self      = shift; | 
| 140 | 37 |  |  |  |  | 36 | my $ctrl_byte = shift; | 
| 141 | 37 |  |  |  |  | 41 | my $offset    = shift; | 
| 142 |  |  |  |  |  |  |  | 
| 143 | 37 |  |  |  |  | 56 | my $pointer_size = ( ( $ctrl_byte >> 3 ) & 0b00000011 ) + 1; | 
| 144 |  |  |  |  |  |  |  | 
| 145 | 37 |  |  |  |  | 31 | $self->_debug_string( 'Pointer size', $pointer_size ) | 
| 146 |  |  |  |  |  |  | if DEBUG; | 
| 147 |  |  |  |  |  |  |  | 
| 148 | 37 |  |  |  |  | 27 | my $buffer; | 
| 149 | 37 |  |  |  |  | 113 | $self->_read( \$buffer, $offset, $pointer_size ); | 
| 150 |  |  |  |  |  |  |  | 
| 151 | 37 |  |  |  |  | 39 | $self->_debug_binary( 'Buffer', $buffer ) | 
| 152 |  |  |  |  |  |  | if DEBUG; | 
| 153 |  |  |  |  |  |  |  | 
| 154 | 37 | 100 |  |  |  | 230 | my $packed | 
| 155 |  |  |  |  |  |  | = $pointer_size == 4 | 
| 156 |  |  |  |  |  |  | ? $buffer | 
| 157 |  |  |  |  |  |  | : ( pack( C => $ctrl_byte & 0b00000111 ) ) . $buffer; | 
| 158 |  |  |  |  |  |  |  | 
| 159 | 37 |  |  |  |  | 93 | $packed = $self->_zero_pad_left( $packed, 4 ); | 
| 160 |  |  |  |  |  |  |  | 
| 161 | 37 |  |  |  |  | 35 | $self->_debug_binary( 'Packed pointer', $packed ) | 
| 162 |  |  |  |  |  |  | if DEBUG; | 
| 163 |  |  |  |  |  |  |  | 
| 164 | 37 |  |  |  |  | 140 | my $pointer = unpack( 'N' => $packed ) + $self->_pointer_base(); | 
| 165 | 37 |  |  |  |  | 63 | $pointer += $pointer_value_offset{$pointer_size}; | 
| 166 |  |  |  |  |  |  |  | 
| 167 | 37 |  |  |  |  | 38 | $self->_debug_string( 'Pointer to', $pointer ) | 
| 168 |  |  |  |  |  |  | if DEBUG; | 
| 169 |  |  |  |  |  |  |  | 
| 170 | 37 |  |  |  |  | 71 | return ( $pointer, $offset + $pointer_size ); | 
| 171 |  |  |  |  |  |  | } | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines) | 
| 174 |  |  |  |  |  |  | sub _decode_utf8_string { | 
| 175 | 727 |  |  | 727 |  | 706 | my $self   = shift; | 
| 176 | 727 |  |  |  |  | 647 | my $buffer = shift; | 
| 177 | 727 |  |  |  |  | 662 | my $size   = shift; | 
| 178 |  |  |  |  |  |  |  | 
| 179 | 727 | 100 |  |  |  | 1124 | return q{} if $size == 0; | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | ## no critic (Subroutines::ProhibitCallsToUnexportedSubs) | 
| 182 | 726 |  |  |  |  | 2608 | return Encode::decode( 'utf-8', $buffer, Encode::FB_CROAK ); | 
| 183 |  |  |  |  |  |  | } | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | sub _decode_double { | 
| 186 | 17 |  |  | 17 |  | 21 | my $self   = shift; | 
| 187 | 17 |  |  |  |  | 19 | my $buffer = shift; | 
| 188 | 17 |  |  |  |  | 16 | my $size   = shift; | 
| 189 |  |  |  |  |  |  |  | 
| 190 | 17 |  |  |  |  | 41 | $self->_verify_size( 8, $size ); | 
| 191 | 16 |  |  |  |  | 41 | return unpack_double_be($buffer); | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | sub _decode_float { | 
| 195 | 18 |  |  | 18 |  | 13 | my $self   = shift; | 
| 196 | 18 |  |  |  |  | 16 | my $buffer = shift; | 
| 197 | 18 |  |  |  |  | 13 | my $size   = shift; | 
| 198 |  |  |  |  |  |  |  | 
| 199 | 18 |  |  |  |  | 26 | $self->_verify_size( 4, $size ); | 
| 200 | 18 |  |  |  |  | 37 | return unpack_float_be($buffer); | 
| 201 |  |  |  |  |  |  | } | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | sub _decode_bytes { | 
| 204 | 11 |  |  | 11 |  | 11 | my $self   = shift; | 
| 205 | 11 |  |  |  |  | 8 | my $buffer = shift; | 
| 206 | 11 |  |  |  |  | 12 | my $size   = shift; | 
| 207 |  |  |  |  |  |  |  | 
| 208 | 11 | 100 |  |  |  | 21 | return q{} if $size == 0; | 
| 209 |  |  |  |  |  |  |  | 
| 210 | 10 |  |  |  |  | 22 | return $buffer; | 
| 211 |  |  |  |  |  |  | } | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | sub _decode_map { | 
| 214 | 221 |  |  | 221 |  | 238 | my $self   = shift; | 
| 215 | 221 |  |  |  |  | 229 | my $size   = shift; | 
| 216 | 221 |  |  |  |  | 232 | my $offset = shift; | 
| 217 |  |  |  |  |  |  |  | 
| 218 | 221 |  |  |  |  | 153 | $self->_debug_string( 'Map size', $size ) | 
| 219 |  |  |  |  |  |  | if DEBUG; | 
| 220 |  |  |  |  |  |  |  | 
| 221 | 221 |  |  |  |  | 225 | my %map; | 
| 222 | 221 |  |  |  |  | 638 | for ( 1 .. $size ) { | 
| 223 | 420 |  |  |  |  | 1087 | ( my $key, $offset ) = $self->decode($offset); | 
| 224 | 420 |  |  |  |  | 17718 | ( my $val, $offset ) = $self->decode($offset); | 
| 225 |  |  |  |  |  |  |  | 
| 226 | 418 |  |  |  |  | 7674 | if (DEBUG) { | 
| 227 |  |  |  |  |  |  | $self->_debug_string( "Key $_",   $key ); | 
| 228 |  |  |  |  |  |  | $self->_debug_string( "Value $_", $val ); | 
| 229 |  |  |  |  |  |  | } | 
| 230 |  |  |  |  |  |  |  | 
| 231 | 418 |  |  |  |  | 1451 | $map{$key} = $val; | 
| 232 |  |  |  |  |  |  | } | 
| 233 |  |  |  |  |  |  |  | 
| 234 | 219 |  |  |  |  | 255 | $self->_debug_structure( 'Decoded map', \%map ) | 
| 235 |  |  |  |  |  |  | if DEBUG; | 
| 236 |  |  |  |  |  |  |  | 
| 237 | 219 | 100 |  |  |  | 2096 | return wantarray ? ( \%map, $offset ) : \%map; | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | sub _decode_int32 { | 
| 241 | 12 |  |  | 12 |  | 15 | my $self   = shift; | 
| 242 | 12 |  |  |  |  | 12 | my $buffer = shift; | 
| 243 | 12 |  |  |  |  | 12 | my $size   = shift; | 
| 244 |  |  |  |  |  |  |  | 
| 245 | 12 | 100 |  |  |  | 29 | return 0 if $size == 0; | 
| 246 |  |  |  |  |  |  |  | 
| 247 | 11 |  |  |  |  | 26 | return unpack( 'N!' => $self->_zero_pad_left( $buffer, 4 ) ); | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | { | 
| 251 |  |  |  |  |  |  | my $max_int_bytes = log( ~0 ) / ( 8 * log(2) ); | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | sub _decode_uint { | 
| 254 | 164 |  |  | 164 |  | 179 | my $self   = shift; | 
| 255 | 164 |  |  |  |  | 146 | my $buffer = shift; | 
| 256 | 164 |  |  |  |  | 146 | my $size   = shift; | 
| 257 |  |  |  |  |  |  |  | 
| 258 | 164 |  |  |  |  | 148 | if (DEBUG) { | 
| 259 |  |  |  |  |  |  | $self->_debug_string( 'UINT size', $size ); | 
| 260 |  |  |  |  |  |  | $self->_debug_binary( 'Buffer', $buffer ); | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  |  | 
| 263 | 164 | 100 |  |  |  | 301 | my $int = $size <= $max_int_bytes ? 0 : Math::BigInt->bzero(); | 
| 264 | 164 | 100 |  |  |  | 491 | return $int if $size == 0; | 
| 265 |  |  |  |  |  |  |  | 
| 266 | 140 |  |  |  |  | 379 | my @unpacked = unpack( 'C*', $buffer ); | 
| 267 | 140 |  |  |  |  | 256 | for my $piece (@unpacked) { | 
| 268 | 369 |  |  |  |  | 25592 | $int = ( $int << 8 ) | $piece; | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  |  | 
| 271 | 140 |  |  |  |  | 2514 | return $int; | 
| 272 |  |  |  |  |  |  | } | 
| 273 |  |  |  |  |  |  | } | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | sub _decode_array { | 
| 276 | 24 |  |  | 24 |  | 33 | my $self   = shift; | 
| 277 | 24 |  |  |  |  | 32 | my $size   = shift; | 
| 278 | 24 |  |  |  |  | 34 | my $offset = shift; | 
| 279 |  |  |  |  |  |  |  | 
| 280 | 24 |  |  |  |  | 26 | $self->_debug_string( 'Array size', $size ) | 
| 281 |  |  |  |  |  |  | if DEBUG; | 
| 282 |  |  |  |  |  |  |  | 
| 283 | 24 |  |  |  |  | 34 | my @array; | 
| 284 | 24 |  |  |  |  | 75 | for ( 1 .. $size ) { | 
| 285 | 43 |  |  |  |  | 82 | ( my $val, $offset ) = $self->decode($offset); | 
| 286 |  |  |  |  |  |  |  | 
| 287 | 43 |  |  |  |  | 1506 | if (DEBUG) { | 
| 288 |  |  |  |  |  |  | $self->_debug_string( "Value $_", $val ); | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  |  | 
| 291 | 43 |  |  |  |  | 133 | push @array, $val; | 
| 292 |  |  |  |  |  |  | } | 
| 293 |  |  |  |  |  |  |  | 
| 294 | 24 |  |  |  |  | 30 | $self->_debug_structure( 'Decoded array', \@array ) | 
| 295 |  |  |  |  |  |  | if DEBUG; | 
| 296 |  |  |  |  |  |  |  | 
| 297 | 24 | 100 |  |  |  | 100 | return wantarray ? ( \@array, $offset ) : \@array; | 
| 298 |  |  |  |  |  |  | } | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | sub _decode_container { | 
| 301 | 1 |  |  | 1 |  | 12 | return MaxMind::DB::Reader::Data::Container->new(); | 
| 302 |  |  |  |  |  |  | } | 
| 303 |  |  |  |  |  |  |  | 
| 304 |  |  |  |  |  |  | sub _decode_end_marker { | 
| 305 | 1 |  |  | 1 |  | 15 | return MaxMind::DB::Reader::Data::EndMarker->new(); | 
| 306 |  |  |  |  |  |  | } | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | sub _decode_boolean { | 
| 309 | 2 |  |  | 2 |  | 2 | my $self   = shift; | 
| 310 | 2 |  |  |  |  | 2 | my $size   = shift; | 
| 311 | 2 |  |  |  |  | 2 | my $offset = shift; | 
| 312 |  |  |  |  |  |  |  | 
| 313 | 2 | 50 |  |  |  | 6 | return wantarray ? ( $size, $offset ) : $size; | 
| 314 |  |  |  |  |  |  | } | 
| 315 |  |  |  |  |  |  | ## use critic | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | sub _verify_size { | 
| 318 | 35 |  |  | 35 |  | 31 | my $self     = shift; | 
| 319 | 35 |  |  |  |  | 36 | my $expected = shift; | 
| 320 | 35 |  |  |  |  | 23 | my $actual   = shift; | 
| 321 |  |  |  |  |  |  |  | 
| 322 | 35 | 100 |  |  |  | 321 | confess | 
| 323 |  |  |  |  |  |  | q{The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)} | 
| 324 |  |  |  |  |  |  | unless $expected == $actual; | 
| 325 |  |  |  |  |  |  | } | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | sub _size_from_ctrl_byte { | 
| 328 | 1198 |  |  | 1198 |  | 1192 | my $self      = shift; | 
| 329 | 1198 |  |  |  |  | 1138 | my $ctrl_byte = shift; | 
| 330 | 1198 |  |  |  |  | 1100 | my $offset    = shift; | 
| 331 |  |  |  |  |  |  |  | 
| 332 | 1198 |  |  |  |  | 1141 | my $size = $ctrl_byte & 0b00011111; | 
| 333 | 1198 | 100 |  |  |  | 2800 | return ( $size, $offset ) | 
| 334 |  |  |  |  |  |  | if $size < 29; | 
| 335 |  |  |  |  |  |  |  | 
| 336 | 14 |  |  |  |  | 25 | my $bytes_to_read = $size - 28; | 
| 337 |  |  |  |  |  |  |  | 
| 338 | 14 |  |  |  |  | 14 | my $buffer; | 
| 339 | 14 |  |  |  |  | 34 | $self->_read( \$buffer, $offset, $bytes_to_read ); | 
| 340 |  |  |  |  |  |  |  | 
| 341 | 14 | 100 |  |  |  | 46 | if ( $size == 29 ) { | 
|  |  | 100 |  |  |  |  |  | 
| 342 | 8 |  |  |  |  | 20 | $size = 29 + unpack( 'C', $buffer ); | 
| 343 |  |  |  |  |  |  | } | 
| 344 |  |  |  |  |  |  | elsif ( $size == 30 ) { | 
| 345 | 4 |  |  |  |  | 11 | $size = 285 + unpack( 'n', $buffer ); | 
| 346 |  |  |  |  |  |  | } | 
| 347 |  |  |  |  |  |  | else { | 
| 348 | 2 |  |  |  |  | 11 | $size = 65821 + unpack( 'N', $self->_zero_pad_left( $buffer, 4 ) ); | 
| 349 |  |  |  |  |  |  | } | 
| 350 |  |  |  |  |  |  |  | 
| 351 | 14 |  |  |  |  | 30 | return ( $size, $offset + $bytes_to_read ); | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  | sub _zero_pad_left { | 
| 355 | 53 |  |  | 53 |  | 5167 | my $self           = shift; | 
| 356 | 53 |  |  |  |  | 59 | my $content        = shift; | 
| 357 | 53 |  |  |  |  | 61 | my $desired_length = shift; | 
| 358 |  |  |  |  |  |  |  | 
| 359 | 53 |  |  |  |  | 215 | return ( "\x00" x ( $desired_length - length($content) ) ) . $content; | 
| 360 |  |  |  |  |  |  | } | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | 1; |