File Coverage

blib/lib/IO/Framed/Read.pm
Criterion Covered Total %
statement 52 56 92.8
branch 18 24 75.0
condition 4 6 66.6
subroutine 10 11 90.9
pod 0 5 0.0
total 84 102 82.3


line stmt bran cond sub pod time code
1             package IO::Framed::Read;
2              
3 5     5   136569 use strict;
  5         26  
  5         114  
4 5     5   19 use warnings;
  5         7  
  5         101  
5              
6 5     5   1587 use IO::Framed::X ();
  5         14  
  5         1712  
7              
8             sub new {
9 6     6 0 1383 my ( $class, $in_fh, $initial_buffer ) = @_;
10              
11 6 50       20 if ( !defined $initial_buffer ) {
12 6         12 $initial_buffer = q<>;
13             }
14              
15 6         22 my $self = {
16             _in_fh => $in_fh,
17             _read_buffer => $initial_buffer,
18             _bytes_to_read => 0,
19             };
20              
21 6         22 return bless $self, $class;
22             }
23              
24 0     0 0 0 sub get_read_fh { return $_[0]->{'_in_fh'} }
25              
26             #----------------------------------------------------------------------
27             # IO subclass interface
28              
29             sub allow_empty_read {
30 2     2 0 541 my ($self) = @_;
31 2         4 $self->{'_ALLOW_EMPTY_READ'} = 1;
32 2         6 return $self;
33             }
34              
35             sub READ {
36 3     3   749 require IO::SigGuard;
37 3         430 IO::SigGuard->import('sysread');
38 3         1248 *READ = *IO::SigGuard::sysread;
39 3         14 goto &READ;
40             }
41              
42             #We assume here that whatever read may be incomplete at first
43             #will eventually be repeated so that we can complete it. e.g.:
44             #
45             # - read 4 bytes, receive 1, cache it - return undef
46             # - select()
47             # - read 4 bytes again; since we already have 1 byte, only read 3
48             # … and now we get the remaining 3, so return the buffer.
49             #
50             sub read {
51 10     10 0 1022 my ( $self, $bytes ) = @_;
52              
53 10 50       24 die "I refuse to read zero!" if !$bytes;
54              
55 10 100       30 if ( length $self->{'_read_buffer'} ) {
56 1 50       5 if ( length($self->{'_read_buffer'}) + $self->{'_bytes_to_read'} != $bytes ) {
57 0         0 my $should_be = length($self->{'_read_buffer'}) + $self->{'_bytes_to_read'};
58 0         0 die "Continuation: should want “$should_be” bytes, not $bytes!";
59             }
60             }
61              
62 10 50       25 if ( $bytes > length($self->{'_read_buffer'}) ) {
63 10         15 $bytes -= length($self->{'_read_buffer'});
64              
65 10         29 local $!;
66              
67 10         17 local $self->{'_return'};
68              
69 10         44 $bytes -= $self->_expand_read_buffer( $bytes );
70              
71 8 100       153 return q<> if $self->{'_return'};
72             }
73              
74 7         13 $self->{'_bytes_to_read'} = $bytes;
75              
76 7 100       16 if ($bytes) {
77 2         6 return undef;
78             }
79              
80 5         32 return substr( $self->{'_read_buffer'}, 0, length($self->{'_read_buffer'}), q<> );
81             }
82              
83             sub _expand_read_buffer {
84 14   100 14   88 return $_[0]->can('READ')->( $_[0]->{'_in_fh'}, $_[0]->{'_read_buffer'}, $_[1], length($_[0]->{'_read_buffer'}) ) || do {
85             if ($!) {
86 4     4   1539 if ( !$!{'EAGAIN'} && !$!{'EWOULDBLOCK'}) {
  4         4456  
  4         28  
87             die IO::Framed::X->create( 'ReadError', $! );
88             }
89             }
90             elsif ($_[0]->{'_ALLOW_EMPTY_READ'}) {
91             $_[0]->{'_return'} = 1;
92             0;
93             }
94             else {
95             die IO::Framed::X->create('EmptyRead');
96             }
97             };
98             }
99              
100             sub read_until {
101 5     5 0 50 my ( $self, $seq ) = @_;
102              
103 5 50       16 if ( $self->{'_bytes_to_read'} ) {
104 0         0 die "Don’t call read_until() after an incomplete read()!";
105             }
106              
107 5 50 33     21 die "Missing read-until sequence!" if !defined $seq || !length $seq;
108              
109 5         11 my $at = index( $self->{'_read_buffer'}, $seq );
110              
111 5 100       10 if ($at > -1) {
112 1         6 return substr( $self->{'_read_buffer'}, 0, $at + length($seq), q<> );
113             }
114              
115 4         7 local $self->{'_return'};
116              
117 4         9 $self->_expand_read_buffer( 65536 );
118              
119 3 100       48 return q<> if $self->{'_return'};
120              
121 2         4 $at = index( $self->{'_read_buffer'}, $seq );
122              
123 2 100       6 if ($at > -1) {
124 1         6 return substr( $self->{'_read_buffer'}, 0, $at + length($seq), q<> );
125             }
126              
127 1         5 return undef;
128             }
129              
130             #----------------------------------------------------------------------
131              
132             1;