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   12721 use strict;
  21         35  
  21         621  
4 21     21   91 use warnings;
  21         121  
  21         573  
5 21     21   94 use namespace::autoclean;
  21         27  
  21         129  
6 21     21   1219 use autodie;
  21         33  
  21         131  
7              
8             require bytes;
9              
10             our $VERSION = '1.000012';
11 21     21   71611 use Carp qw( confess );
  21         41  
  21         1679  
12 21     21   103 use List::AllUtils qw( min );
  21         28  
  21         1272  
13 21     21   2650 use MaxMind::DB::Reader::Decoder;
  21         40  
  21         726  
14 21     21   22727 use MaxMind::DB::Metadata;
  21         101591  
  21         738  
15 21     21   125 use MaxMind::DB::Types qw( Int Metadata );
  21         38  
  21         1472  
16              
17 21     21   98 use Moo::Role;
  21         33  
  21         139  
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 => 'rw',
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   44 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         78 my $size = $self->_data_source_size();
66              
67 21         542 my $last_bytes = min( $size, 20 * 1024 );
68 21         35 my $last_block = q{};
69 21         88 $self->_read( \$last_block, -$last_bytes, $last_bytes, 'seek from end' );
70              
71 21         69 my $start = ( $size - $last_bytes )
72             + rindex( $last_block, $MetadataStartMarker );
73              
74 21 100       252 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         103 $self->_set_data_section_end( $size - ( $last_bytes - $start ) );
84              
85             ## no critic (Subroutines::ProhibitCallsToUnexportedSubs)
86 20         2810 $start += bytes::length($MetadataStartMarker);
87             ## use critic
88              
89 20         2668 my $raw = MaxMind::DB::Reader::Decoder->new(
90             data_source => $self->data_source,
91             pointer_base => $start,
92             )->decode($start);
93              
94 20         494 my $metadata = MaxMind::DB::Metadata->new($raw);
95              
96 20         22671 return $metadata;
97             }
98              
99             1;