File: HasMetadata.pm

package info (click to toggle)
libmaxmind-db-reader-perl 1.000014-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 2,040 kB
  • sloc: perl: 1,668; makefile: 10
file content (99 lines) | stat: -rw-r--r-- 2,924 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package MaxMind::DB::Reader::Role::HasMetadata;

use strict;
use warnings;
use namespace::autoclean;
use autodie;

require bytes;

our $VERSION = '1.000014';
use Carp qw( confess );
use List::AllUtils qw( min );
use MaxMind::DB::Reader::Decoder;
use MaxMind::DB::Metadata;
use MaxMind::DB::Types qw( Int Metadata );

use Moo::Role;

with 'MaxMind::DB::Reader::Role::Sysreader';

has metadata => (
    is       => 'ro',
    isa      => Metadata,
    init_arg => undef,
    lazy     => 1,
    builder  => '_build_metadata',
    handles  => [
        'binary_format_major_version',
        'binary_format_minor_version',
        'build_epoch',
        'database_type',
        'description',
        'ip_version',
        'languages',
        'node_count',
        'record_size',
    ],
);

has _data_section_end => (
    is       => 'ro',
    writer   => '_set_data_section_end',
    isa      => Int,
    init_arg => undef,
);

my $MetadataStartMarker = "\xab\xcd\xefMaxMind.com";

sub _build_metadata {
    my $self = shift;

    # We need to make sure that whatever chunk we read will have the metadata
    # in it. The description metadata key is a hash of descriptions, one per
    # language. The description could be something verbose like "GeoIP 2.0
    # City Database, Multilingual - English, Chinese (Taiwan), Chinese
    # (China), French, German, Portuguese" (but with c. 20 languages). That
    # comes out to about 250 bytes _per key_. Multiply that by 20 languages,
    # and the description alon ecould use up about 5k. The other keys in the
    # metadata are very, very tiny.
    #
    # Given all this, reading 20k seems fairly future-proof. We'd have to have
    # extremely long descriptions or descriptions in 80 languages before this
    # became too long.

    my $size = $self->_data_source_size;

    my $last_bytes = min( $size, 20 * 1024 );
    my $last_block = q{};
    $self->_read( \$last_block, -$last_bytes, $last_bytes, 'seek from end' );

    my $start = ( $size - $last_bytes )
        + rindex( $last_block, $MetadataStartMarker );

    confess 'Error opening database file "'
        . $self->file
        . q{": The MaxMind DB file contains invalid metadata.}
        if $start < 0;

    # XXX - this is really gross but I couldn't come up with a better way to
    # factor this out that doesn't involve either looking for the metadata
    # marker multiple times _or_ storing the whole metadata raw chunk in
    # memory so we can calculate this later
    $self->_set_data_section_end( $size - ( $last_bytes - $start ) );

    ## no critic (Subroutines::ProhibitCallsToUnexportedSubs)
    $start += bytes::length($MetadataStartMarker);
    ## use critic

    my $raw = MaxMind::DB::Reader::Decoder->new(
        data_source  => $self->data_source,
        pointer_base => $start,
    )->decode($start);

    my $metadata = MaxMind::DB::Metadata->new($raw);

    return $metadata;
}

1;