File Coverage

blib/lib/Term/TtyRec/Plus.pm
Criterion Covered Total %
statement 87 90 96.6
branch 37 42 88.1
condition 13 15 86.6
subroutine 19 20 95.0
pod 13 13 100.0
total 169 180 93.8


line stmt bran cond sub pod time code
1             package Term::TtyRec::Plus;
2 9     9   340030 use warnings;
  9         24  
  9         509  
3 9     9   53 use strict;
  9         19  
  9         332  
4 9     9   50 use Carp qw/croak/;
  9         20  
  9         2254  
5 9     9   28455 use IO::Uncompress::Bunzip2 qw($Bunzip2Error);
  9         617736  
  9         15462  
6              
7             our $VERSION = '0.09';
8              
9             sub new {
10 18     18 1 11072 my $class = shift;
11              
12             my $self = {
13             # options
14             infile => "-",
15             filehandle => undef,
16             bzip2 => undef,
17             time_threshold => undef,
18 7520     7520   10773 frame_filter => sub { @_ },
19              
20             # state
21 18         281 frame => 0,
22             prev_timestamp => undef,
23             accum_diff => 0,
24             relative_time => 0,
25              
26             # allow overriding of options *and* state
27             @_,
28             };
29              
30 72         237 $self->{initial_state} = {
31 18         73 map { $_ => $self->{$_} }
32             qw/frame prev_timestamp accum_diff relative_time/
33             };
34              
35 18         63 bless $self, $class;
36              
37 18 100       199 if (defined($self->{filehandle})) {
38 3         8 undef $self->{infile};
39             }
40             else {
41 15 50 33     139 if (!defined($self->{infile}) || $self->{infile} eq '-') {
42 0         0 $self->{filehandle} = *STDIN;
43             }
44             else {
45 15 100       1172 open($self->{filehandle}, '<', $self->{infile})
46             or croak "Unable to open '$self->{infile}' for reading: $!";
47             }
48             }
49              
50             # If the caller tells us explicitly what to do, we honor that.
51             # Otherwise use bzip2 if and only if the filename ends in .bz2.
52 17 100 100     196 $self->{bzip2} = defined($self->{infile}) && $self->{infile} =~ /\.bz2$/
53             unless defined $self->{bzip2};
54              
55 17         46 $self->{bzip2} = not not $self->{bzip2}; # force 0 or 1
56              
57 17 100       58 if ($self->{bzip2}) {
58 3 50       37 my $bz2_handle = IO::Uncompress::Bunzip2->new(
59             $self->{filehandle}
60             ) or die "bunzip2 failed: $Bunzip2Error\n";
61 3         6809 $self->{filehandle} = $bz2_handle;
62             }
63              
64 17 100 100     261 croak "Cannot have a negative time threshold"
65             if defined($self->{time_threshold}) && $self->{time_threshold} < 0;
66              
67 16         55 return $self;
68             }
69              
70             sub next_frame {
71 11203     11203 1 84926 my $self = shift;
72 11203         14347 $self->{frame}++;
73              
74 11203         27901 my $hgot = read $self->{filehandle}, my $hdr, 12;
75              
76             # clean EOF
77 11203 100       21646 return if $hgot == 0;
78              
79 11191 100       21910 croak "Expected 12-byte header, got $hgot in frame $self->{frame}"
80             if $hgot != 12;
81              
82 11190         25837 my @hdr = unpack "VVV", $hdr;
83              
84 11190         25556 my $orig_timestamp = $hdr[0] + $hdr[1] / 1_000_000;
85 11190         17885 my $diffed_timestamp = $orig_timestamp + $self->{accum_diff};
86 11190         11502 my $timestamp = $diffed_timestamp;
87 11190         11670 my $old_timestamp = $timestamp; # old = pre-filter
88 11190         17141 my $prev_timestamp = $self->{prev_timestamp};
89              
90             # apply a threshold, if applicable
91 11190 100 100     45314 if (defined($self->{time_threshold}) &&
      100        
92             defined($prev_timestamp) &&
93             $timestamp - $prev_timestamp > $self->{time_threshold})
94             {
95 1970         2586 $timestamp = $prev_timestamp + $self->{time_threshold};
96 1970         2836 $self->{accum_diff} += $timestamp - $old_timestamp;
97 1970         2468 $old_timestamp = $timestamp;
98             }
99              
100 11190         24308 my $dgot = read $self->{filehandle}, my ($data), $hdr[2];
101              
102 11190 100       21477 croak "Expected $hdr[2]-byte frame, got $dgot in frame $self->{frame}"
103             if $dgot != $hdr[2];
104              
105 11189         27817 $self->{frame_filter}(\$data, \$timestamp, \$self->{prev_timestamp});
106              
107 11189         33981 $self->{prev_timestamp} = $timestamp;
108              
109 11189 100       20511 my $diff = defined($prev_timestamp) ? $timestamp - $prev_timestamp : 0;
110              
111 11189 100       25758 $self->{relative_time} += $diff
112             unless $self->{frame} == 1;
113              
114 11189         16179 $self->{accum_diff} += $timestamp - $old_timestamp;
115              
116             # rebuild header
117 11189         13552 $hdr[0] = int($timestamp);
118 11189         15724 $hdr[1] = int(1_000_000 * ($timestamp - $hdr[0]));
119 11189         12364 $hdr[2] = length($data);
120              
121 11189         21898 my $newhdr = pack "VVV", @hdr;
122              
123             # test if header is kosher
124 11189         24528 my @newhdr = unpack "VVV", $newhdr;
125              
126 11189 100       23070 croak "Unable to create a new header, seconds portion of timestamp in frame $self->{frame}: want to write $hdr[0], can only write $newhdr[0]"
127             if $hdr[0] != $newhdr[0];
128              
129 11188 50       20375 croak "Unable to create a new header, microseconds portion of timestamp in frame $self->{frame}: want to write $hdr[1], can only write $newhdr[1]"
130             if $hdr[1] != $newhdr[1];
131              
132 11188 50       17579 croak "Unable to create a new header, frame length in frame $self->{frame}: want to write $hdr[2], can only write $newhdr[2]"
133             if $hdr[2] != $newhdr[2];
134              
135             return {
136 11188         110086 data => $data,
137             orig_timestamp => $orig_timestamp,
138             diffed_timestamp => $diffed_timestamp,
139             timestamp => $timestamp,
140             prev_timestamp => $prev_timestamp,
141             diff => $diff,
142             orig_header => $hdr,
143             header => $newhdr,
144             frame => $self->{frame},
145             relative_time => $self->{relative_time},
146             };
147             }
148              
149             sub grep {
150 5     5 1 2721 my $self = shift;
151 5         7 my @conditions;
152              
153 5         34 foreach my $arg (@_) {
154 7 100       43 if (ref($arg) eq 'CODE') {
    100          
    100          
155 2         5 push @conditions, $arg;
156             }
157             elsif (ref($arg) eq 'Regexp') {
158 1     24   7 push @conditions, sub { $_[0]{data} =~ $arg };
  24         170  
159             }
160             elsif (ref($arg) eq '') {
161 78     78   533 push @conditions, sub { index($_[0]{data}, $arg) > -1 }
162 3         20 }
163             else {
164 1         117 croak "Each of grep()'s arguments must be a subroutine, regular expression, or string; you passed a " . ref($arg);
165             }
166             }
167              
168             FRAME:
169 4         15 while (my $frame_ref = $self->next_frame()) {
170             CONDITION:
171 364         565 foreach (@conditions) {
172 366 100       739 next FRAME if not $_->($frame_ref);
173             }
174 4         36 return $frame_ref;
175             }
176              
177             # no matching frames!
178 0         0 return;
179             }
180              
181             sub rewind {
182 2     2 1 17 my $self = shift;
183              
184 2         5 while (my ($k, $v) = each %{$self->{initial_state}}) {
  10         36  
185 8         42 $self->{$k} = $v;
186             }
187              
188 2 50       25 seek $self->{filehandle}, 0, 0
189             or croak "Unable to seek on filehandle";
190             }
191              
192             sub infile {
193 2     2 1 2979 $_[0]->{infile};
194             }
195              
196             sub filehandle {
197 2     2 1 13 $_[0]->{filehandle};
198             }
199              
200             sub bzip2 {
201 0     0 1 0 $_[0]->{bzip2};
202             }
203              
204             sub time_threshold {
205 2     2 1 11 $_[0]->{time_threshold};
206             }
207              
208             sub frame_filter {
209 3     3 1 26 $_[0]->{frame_filter};
210             }
211              
212             sub frame {
213 4     4 1 39 $_[0]->{frame};
214             }
215              
216             sub prev_timestamp {
217 14     14 1 30125 $_[0]->{prev_timestamp};
218             }
219              
220             sub relative_time {
221 14     14 1 9508 $_[0]->{relative_time};
222             }
223              
224             sub accum_diff {
225 5     5 1 2225 $_[0]->{accum_diff};
226             }
227              
228             1;
229              
230             __END__