File Coverage

blib/lib/App/dategrep/Iterator/File.pm
Criterion Covered Total %
statement 79 81 97.5
branch 29 34 85.2
condition 4 8 50.0
subroutine 12 12 100.0
pod 0 4 0.0
total 124 139 89.2


line stmt bran cond sub pod time code
1             package App::dategrep::Iterator::File;
2 9     9   31 use strict;
  9         12  
  9         200  
3 9     9   27 use warnings;
  9         11  
  9         186  
4 9     9   27 use Fcntl ":seek";
  9         7  
  9         1034  
5 9     9   3770 use File::stat;
  9         41385  
  9         33  
6 9     9   459 use Moo;
  9         11  
  9         46  
7 9     9   5528 use FileHandle;
  9         5724  
  9         34  
8             extends 'App::dategrep::Iterator';
9              
10             has filename => ( is => 'ro', required => 1 );
11             has blocksize => ( is => 'lazy' );
12              
13 3     3 0 11 sub can_seek { 1 }
14              
15             sub _build_blocksize {
16 24     24   2271 my $self = shift;
17 24   50     355 return stat( $self->fh )->blksize || 8192;
18             }
19              
20             sub _build_fh {
21 28     28   2728 my $self = shift;
22 28         73 my $filename = $self->filename;
23 28 100       1054 open( my $fh, '<', $filename ) or die "Can't open $filename: $!\n";
24 27         104 return $fh;
25             }
26              
27             sub seek {
28 28     28 0 37 my $self = shift;
29              
30 28         184 my $min = $self->search( $self->start );
31              
32 26 100       58 if ( not defined $min ) {
33 2         8 $self->eof(1);
34 2         34 return;
35             }
36              
37 24         600 my $line = $self->fh->getline;
38 24         962 my ( $date, $error ) = $self->to_epoch($line);
39              
40 24 100       4467 if ( $date >= $self->end ) {
41 1         4 $self->eof(1);
42 1         19 return;
43             }
44              
45 23         121 $self->next_line($line);
46 23         61 $self->next_date($date);
47 23         480 return;
48             }
49              
50             sub byte_offsets {
51 2     2 0 3 my $self = shift;
52 2         5 my $tell_beg = $self->search( $self->start );
53              
54 2 50       6 if ( defined $tell_beg ) {
55 2         7 my $tell_end = $self->search( $self->end, $tell_beg );
56 2         6 return $tell_beg, $tell_end;
57             }
58              
59             # return for empty file
60 0         0 return 0, -1;
61             }
62              
63             sub search {
64 35     35 0 58 my $self = shift;
65 35         50 my ( $key, $min_byte ) = @_;
66 35         478 my $fh = $self->fh;
67              
68 34         148 my $size = stat($fh)->size;
69 34         4732 my $blksize = $self->blocksize;
70 34         2504 my $multiline = $self->multiline;
71              
72             # find the right block
73 34         97 my ( $min, $max, $mid ) = ( 0, int( $size / $blksize ) );
74              
75 34 100       78 if ( defined $min_byte ) {
76 5         9 $min = int( $min_byte / $blksize );
77             }
78              
79 34         92 BLOCK: while ( $max - $min > 1 ) {
80 9         15 $mid = int( ( $max + $min ) / 2 );
81 9 50       35 $fh->seek( $mid * $blksize, SEEK_SET ) or return;
82 9 50       260 $fh->getline if $mid; # probably a partial line
83 9         252 LINE: while (1) {
84 12         211 my $line = $fh->getline();
85 12 100       231 if ( !$line ) {
86             ## This can happen if line size is way bigger than blocksize
87 3         6 last BLOCK;
88             }
89 9         28 my ($epoch) = $self->to_epoch($line);
90 9 100       1285 if ( !$epoch ) {
91 3 50 33     12 next LINE if $multiline || $self->skip_unparsable;
92 0         0 die "No date found in line $line";
93             }
94              
95 6 100       28 $epoch < $key
96             ? $min = int( ( $fh->tell - length($line) ) / $blksize )
97             : $max = $mid;
98              
99 6         31 next BLOCK;
100             }
101             }
102              
103             # find the right line
104 34         35 $min *= $blksize;
105 34 50       173 $fh->seek( $min, SEEK_SET ) or return;
106 34 100       316 $fh->getline if $min; # probably a partial line
107 34         96 for ( ; ; ) {
108 66         192 $min = $fh->tell;
109 66 100       1443 defined( my $line = $fh->getline ) or last;
110 61         1584 my ($epoch) = $self->to_epoch($line);
111 61 100       10373 if ( !$epoch ) {
112 9 100 66     40 next if $multiline || $self->skip_unparsable;
113 1         41 die "No date found in line $line";
114             }
115 52 100       124 if ( $epoch >= $key ) {
116 28         92 $fh->seek( $min, SEEK_SET );
117 28         183 return $min;
118             }
119             }
120 5         144 return;
121             }
122              
123             1;