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   165441 use strict;
  5         25  
  5         139  
4 5     5   24 use warnings;
  5         11  
  5         112  
5              
6 5     5   1988 use IO::Framed::X ();
  5         13  
  5         2116  
7              
8             sub new {
9 6     6 0 1673 my ( $class, $in_fh, $initial_buffer ) = @_;
10              
11 6 50       25 if ( !defined $initial_buffer ) {
12 6         12 $initial_buffer = q<>;
13             }
14              
15 6         24 my $self = {
16             _in_fh => $in_fh,
17             _read_buffer => $initial_buffer,
18             _bytes_to_read => 0,
19             };
20              
21 6         24 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 573 my ($self) = @_;
31 2         6 $self->{'_ALLOW_EMPTY_READ'} = 1;
32 2         6 return $self;
33             }
34              
35             sub READ {
36 3     3   944 require IO::SigGuard;
37 3         549 IO::SigGuard->import('sysread');
38 3         1542 *READ = *IO::SigGuard::sysread;
39 3         15 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 1209 my ( $self, $bytes ) = @_;
52              
53 10 50       27 die "I refuse to read zero!" if !$bytes;
54              
55 10 100       32 if ( length $self->{'_read_buffer'} ) {
56 1 50       4 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       28 if ( $bytes > length($self->{'_read_buffer'}) ) {
63 10         21 $bytes -= length($self->{'_read_buffer'});
64              
65 10         29 local $!;
66              
67 10         23 local $self->{'_return'};
68              
69 10         74 $bytes -= $self->_expand_read_buffer( $bytes );
70              
71 8 100       161 return q<> if $self->{'_return'};
72             }
73              
74 7         15 $self->{'_bytes_to_read'} = $bytes;
75              
76 7 100       18 if ($bytes) {
77 2         8 return undef;
78             }
79              
80 5         30 return substr( $self->{'_read_buffer'}, 0, length($self->{'_read_buffer'}), q<> );
81             }
82              
83             sub _expand_read_buffer {
84 14   100 14   98 return $_[0]->can('READ')->( $_[0]->{'_in_fh'}, $_[0]->{'_read_buffer'}, $_[1], length($_[0]->{'_read_buffer'}) ) || do {
85             if ($!) {
86 4     4   1823 if ( !$!{'EAGAIN'} && !$!{'EWOULDBLOCK'}) {
  4         5375  
  4         35  
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 58 my ( $self, $seq ) = @_;
102              
103 5 50       18 if ( $self->{'_bytes_to_read'} ) {
104 0         0 die "Don’t call read_until() after an incomplete read()!";
105             }
106              
107 5 50 33     26 die "Missing read-until sequence!" if !defined $seq || !length $seq;
108              
109 5         13 my $at = index( $self->{'_read_buffer'}, $seq );
110              
111 5 100       15 if ($at > -1) {
112 1         7 return substr( $self->{'_read_buffer'}, 0, $at + length($seq), q<> );
113             }
114              
115 4         9 local $self->{'_return'};
116              
117 4         10 $self->_expand_read_buffer( 65536 );
118              
119 3 100       62 return q<> if $self->{'_return'};
120              
121 2         6 $at = index( $self->{'_read_buffer'}, $seq );
122              
123 2 100       5 if ($at > -1) {
124 1         9 return substr( $self->{'_read_buffer'}, 0, $at + length($seq), q<> );
125             }
126              
127 1         7 return undef;
128             }
129              
130             #----------------------------------------------------------------------
131              
132             1;