1package MaxMind::DB::Reader::Role::Sysreader;
2
3use strict;
4use warnings;
5use namespace::autoclean;
6use autodie;
7
8our $VERSION = '1.000014';
9
10use Carp qw( confess );
11use MaxMind::DB::Types qw( FileHandle Int );
12
13use Moo::Role;
14
15has data_source => (
16    is      => 'ro',
17    isa     => FileHandle,
18    lazy    => 1,
19    builder => '_build_data_source',
20);
21
22has _data_source_size => (
23    is      => 'ro',
24    isa     => Int,
25    lazy    => 1,
26    builder => '_build_data_source_size',
27);
28
29## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
30sub _read {
31    my $self          = shift;
32    my $buffer        = shift;
33    my $offset        = shift;
34    my $wanted_size   = shift;
35    my $seek_from_end = shift;
36
37    my $source = $self->data_source;
38    seek $source, $offset, $seek_from_end ? 2 : 0;
39
40    my $read_offset = 0;
41    while (1) {
42        my $read_size = read(
43            $source,
44            ${$buffer},
45            $wanted_size,
46            $read_offset,
47        );
48
49        confess $! unless defined $read_size;
50
51        # This error message doesn't provide much context, but it should only
52        # be thrown because of a fundamental logic error in the reader code,
53        # _or_ because the writer generated a database with broken pointers
54        # and/or broken data elements.
55        confess 'Attempted to read past the end of a file/memory buffer'
56            if $read_size == 0;
57
58        return if $wanted_size == $read_size;
59
60        $wanted_size -= $read_size;
61        $read_offset += $read_size;
62    }
63
64    return;
65}
66## use critic
67
68sub _build_data_source {
69    my $class = ref shift;
70
71    die
72        "You must provide a data_source parameter to the constructor for $class";
73}
74
75sub _build_data_source_size {
76    my $self = shift;
77
78    my @stat = stat( $self->data_source ) or die $!;
79    return $stat[7];
80}
81
821;
83