File Coverage

blib/lib/App/dategrep/Iterator/File.pm
Criterion Covered Total %
statement 70 81 86.4
branch 25 38 65.7
condition 9 14 64.2
subroutine 10 10 100.0
pod 0 3 0.0
total 114 146 78.0


line stmt bran cond sub pod time code
1             package App::dategrep::Iterator::File;
2 8     8   29 use strict;
  8         9  
  8         172  
3 8     8   22 use warnings;
  8         10  
  8         151  
4 8     8   41 use Fcntl ":seek";
  8         11  
  8         933  
5 8     8   31 use Moo;
  8         14  
  8         44  
6 8     8   4462 use FileHandle;
  8         5077  
  8         33  
7             extends 'App::dategrep::Iterator';
8              
9             has 'filename' => ( is => 'ro', required => 1 );
10             has 'blocksize' => ( is => 'lazy' );
11             has 'tell_beg' => ( is => 'rw' );
12             has 'tell_end' => ( is => 'rw' );
13              
14             sub _build_blocksize {
15 23     23   2421 my $self = shift;
16 23   50     423 return ( stat( $self->filename ) )[11] || 8192;
17             }
18              
19             sub _build_fh {
20 24     24   1974 my $self = shift;
21 24         56 my ( $fh, $tell_beg, $tell_end ) = $self->byte_offsets();
22 21         81 $self->tell_beg($tell_beg);
23 21         45 $self->tell_end($tell_end);
24 21         53 $fh->seek( $tell_beg, SEEK_SET );
25 21         424 return $fh;
26             }
27              
28             sub get_entry_unbuffered {
29 83     83 0 66 my $self = shift;
30 83         159 my $line = $self->getline();
31             ## TODO can $tell_end be undefined?
32             return
33 80 100 100     2595 if defined( $self->tell_end ) && ( $self->fh->tell > $self->tell_end );
34 75 100       252 if ( $self->multiline ) {
35 3   66     52 while ( !$self->fh->eof && !$self->next_line_has_date ) {
36 2         9 $line .= $self->getline();
37             }
38             }
39 75         278 return $line;
40             }
41              
42             sub byte_offsets {
43 26     26 0 29 my $self = shift;
44 26         59 my $filename = $self->filename;
45 26 100       1032 open( my $fh, '<', $filename ) or die "Can't open $filename: $!\n";
46 25         534 my $test_line = $fh->getline;
47 25 100       759 if ( defined($test_line) ) {
48 24         107 my ( $epoch, $error ) = $self->to_epoch($test_line);
49 24 100       4488 if ($error) {
50 1         42 die "No date found in first line: $error\n";
51             }
52 23         126 $fh->seek( 0, SEEK_SET );
53              
54 23         227 my $tell_beg =
55             $self->search( $fh, $self->start, format => $self->format, );
56              
57 23 100       45 if ( defined $tell_beg ) {
58 22         118 my $tell_end = $self->search(
59             $fh, $self->end,
60             min_byte => $tell_beg,
61             format => $self->format
62             );
63              
64 21         60 return $fh, $tell_beg, $tell_end;
65             }
66             }
67              
68             # return for empty file
69 2         5 return $fh, 0, -1;
70             }
71              
72             sub search {
73 45     45 0 47 my $self = shift;
74 45         102 my ( $fh, $key, %options ) = @_;
75 45 50       136 my @stat = $fh->stat or return;
76 45         514 my $size = $stat[7];
77 45         789 my $blksize = $self->blocksize;
78              
79 45         141 my $min_byte = $options{min_byte};
80 45         95 my $multiline = $self->multiline;
81              
82             # find the right block
83 45         102 my ( $min, $max, $mid ) = ( 0, int( $size / $blksize ) );
84              
85 45 100       83 if ( defined $min_byte ) {
86 22         33 $min = int( $min_byte / $blksize );
87             }
88              
89 45         97 BLOCK: while ( $max - $min > 1 ) {
90 0         0 $mid = int( ( $max + $min ) / 2 );
91 0 0       0 $fh->seek( $mid * $blksize, SEEK_SET ) or return;
92 0 0       0 $fh->getline if $mid; # probably a partial line
93 0         0 LINE: while ( my $line = $fh->getline() ) {
94 0         0 my ($epoch) = $self->to_epoch($line);
95 0 0       0 if ( !$epoch ) {
96 0 0 0     0 next LINE if $multiline || $self->skip_unparsable;
97              
98 0         0 chomp($line);
99 0         0 die "Unparsable line: $line\n";
100             }
101              
102 0 0       0 $epoch < $key
103             ? $min = int( ( $fh->tell - length($line) ) / $blksize )
104             : $max = $mid;
105              
106 0         0 next BLOCK;
107             }
108             }
109              
110             # find the right line
111 45         41 $min *= $blksize;
112 45 50       98 $fh->seek( $min, SEEK_SET ) or return;
113 45 50       276 $fh->getline if $min; # probably a partial line
114 45         30 for ( ; ; ) {
115 138         370 $min = $fh->tell;
116 138 100       2960 defined( my $line = $fh->getline ) or last;
117 120         2506 my ($epoch) = $self->to_epoch($line);
118 120 100       20196 if ( !$epoch ) {
119 8 100 100     39 next if $multiline || $self->skip_unparsable;
120 1         2 chomp($line);
121 1         42 die "Unparsable line: $line\n";
122             }
123 112 100       210 if ( $epoch >= $key ) {
124 26         83 $fh->seek( $min, SEEK_SET );
125 26         192 return $min;
126             }
127             }
128 18         438 return;
129             }
130              
131             1;