File Coverage

blib/lib/MaxMind/DB/Reader/Role/HasMetadata.pm
Criterion Covered Total %
statement 42 42 100.0
branch 2 2 100.0
condition n/a
subroutine 11 11 100.0
pod n/a
total 55 55 100.0


line stmt bran cond sub pod time code
1             package MaxMind::DB::Reader::Role::HasMetadata;
2              
3 21     21   9855 use strict;
  21         30  
  21         498  
4 21     21   65 use warnings;
  21         94  
  21         404  
5 21     21   69 use namespace::autoclean;
  21         20  
  21         87  
6 21     21   811 use autodie;
  21         25  
  21         84  
7              
8             require bytes;
9              
10             our $VERSION = '1.000013';
11 21     21   68892 use Carp qw( confess );
  21         26  
  21         969  
12 21     21   76 use List::AllUtils qw( min );
  21         19  
  21         1167  
13 21     21   2453 use MaxMind::DB::Reader::Decoder;
  21         30  
  21         444  
14 21     21   9091 use MaxMind::DB::Metadata;
  21         85108  
  21         612  
15 21     21   99 use MaxMind::DB::Types qw( Int Metadata );
  21         21  
  21         978  
16              
17 21     21   940 use Moo::Role;
  21         26  
  21         97  
18              
19             with 'MaxMind::DB::Reader::Role::Sysreader';
20              
21             has metadata => (
22             is => 'ro',
23             isa => Metadata,
24             init_arg => undef,
25             lazy => 1,
26             builder => '_build_metadata',
27             handles => [
28             'binary_format_major_version',
29             'binary_format_minor_version',
30             'build_epoch',
31             'database_type',
32             'description',
33             'ip_version',
34             'languages',
35             'node_count',
36             'record_size',
37             ],
38             );
39              
40             has _data_section_end => (
41             is => 'ro',
42             writer => '_set_data_section_end',
43             isa => Int,
44             init_arg => undef,
45             );
46              
47             my $MetadataStartMarker = "\xab\xcd\xefMaxMind.com";
48              
49             sub _build_metadata {
50 21     21   26 my $self = shift;
51              
52             # We need to make sure that whatever chunk we read will have the metadata
53             # in it. The description metadata key is a hash of descriptions, one per
54             # language. The description could be something verbose like "GeoIP 2.0
55             # City Database, Multilingual - English, Chinese (Taiwan), Chinese
56             # (China), French, German, Portuguese" (but with c. 20 languages). That
57             # comes out to about 250 bytes _per key_. Multiply that by 20 languages,
58             # and the description alon ecould use up about 5k. The other keys in the
59             # metadata are very, very tiny.
60             #
61             # Given all this, reading 20k seems fairly future-proof. We'd have to have
62             # extremely long descriptions or descriptions in 80 languages before this
63             # became too long.
64              
65 21         64 my $size = $self->_data_source_size();
66              
67 21         478 my $last_bytes = min( $size, 20 * 1024 );
68 21         39 my $last_block = q{};
69 21         79 $self->_read( \$last_block, -$last_bytes, $last_bytes, 'seek from end' );
70              
71 21         61 my $start = ( $size - $last_bytes )
72             + rindex( $last_block, $MetadataStartMarker );
73              
74 21 100       166 confess 'Error opening database file "'
75             . $self->file
76             . q{": The MaxMind DB file contains invalid metadata.}
77             if $start < 0;
78              
79             # XXX - this is really gross but I couldn't come up with a better way to
80             # factor this out that doesn't involve either looking for the metadata
81             # marker multiple times _or_ storing the whole metadata raw chunk in
82             # memory so we can calculate this later
83 20         85 $self->_set_data_section_end( $size - ( $last_bytes - $start ) );
84              
85             ## no critic (Subroutines::ProhibitCallsToUnexportedSubs)
86 20         2127 $start += bytes::length($MetadataStartMarker);
87             ## use critic
88              
89 20         1802 my $raw = MaxMind::DB::Reader::Decoder->new(
90             data_source => $self->data_source,
91             pointer_base => $start,
92             )->decode($start);
93              
94 20         396 my $metadata = MaxMind::DB::Metadata->new($raw);
95              
96 20         14634 return $metadata;
97             }
98              
99             1;