File Coverage

blib/lib/IO/Handle/Prototype/Fallback.pm
Criterion Covered Total %
statement 125 152 82.2
branch 41 54 75.9
condition 7 8 87.5
subroutine 29 40 72.5
pod 0 1 0.0
total 202 255 79.2


line stmt bran cond sub pod time code
1             package IO::Handle::Prototype::Fallback;
2              
3 1     1   14588 use strict;
  1         1  
  1         23  
4 1     1   5 use warnings;
  1         1  
  1         25  
5              
6 1     1   3 use Carp ();
  1         1  
  1         13  
7              
8 1     1   343 use parent qw(IO::Handle::Prototype);
  1         397  
  1         6  
9              
10             sub new {
11 19     19 0 7010 my ( $class, @args ) = @_;
12              
13 19         47 $class->SUPER::new(
14             $class->_process_callbacks(@args),
15             );
16             }
17              
18 14     14   27 sub __write { shift->_cb(__write => @_) }
19 0     0   0 sub __read { shift->_cb(__read => @_) }
20              
21             sub _process_callbacks {
22 34     34   69 my ( $class, %user_cb ) = @_;
23              
24 34 100       67 if ( keys %user_cb == 1 ) {
25             # these callbacks require wrapping of the user's callback to add
26             # buffering, so we short circuit the entire process
27 19         31 foreach my $fallback (qw(__read read getline)) {
28 42 100       82 if ( my $cb = $user_cb{$fallback} ) {
29 15         28 my $method = "_default_${fallback}_callbacks";
30              
31 15         40 return $class->_process_callbacks(
32             $class->$method($cb),
33             );
34             }
35             }
36             }
37              
38 19         37 my @fallbacks = $class->_base_callbacks;
39              
40             # additional fallbacks based on explicitly provided callbacks
41              
42 19         40 foreach my $fallback (qw(__write print write syswrite)) {
43 70 100       104 if ( exists $user_cb{$fallback} ) {
44 4         11 push @fallbacks, $class->_default_write_callbacks($fallback);
45 4         9 last;
46             }
47             }
48              
49 19 100       84 if ( exists $user_cb{getline} ) {
50 15         26 push @fallbacks, $class->_simple_getline_callbacks;
51             }
52              
53 19 100       36 if ( exists $user_cb{read} ) {
54 15         24 push @fallbacks, $class->_simple_read_callbacks;
55             }
56              
57             # merge everything
58 19         117 my %cb = (
59             @fallbacks,
60             %user_cb,
61             );
62              
63 19         130 return \%cb;
64             }
65              
66             sub _base_callbacks {
67 19     19   25 my $class = shift;
68              
69             return (
70 0     0   0 fileno => sub { undef },
71 0     0   0 stat => sub { undef },
72 0     0   0 opened => sub { 1 },
73             blocking => sub {
74 0     0   0 my ( $self, @args ) = @_;
75              
76 0 0       0 Carp::croak("Can't set blocking mode on iterator") if @args;
77              
78 0         0 return 1;
79             },
80 19         122 );
81             }
82              
83             sub _make_read_callbacks {
84 15     15   15 my ( $class, $read ) = @_;
85              
86 1     1   363 no warnings 'uninitialized';
  1         1  
  1         978  
87              
88             return (
89             # these fallbacks must wrap the underlying reading mechanism
90             __read => sub {
91 0     0   0 my $self = shift;
92 0 0       0 if ( exists $self->{buf} ) {
93 0         0 return delete $self->{buf};
94             } else {
95 0         0 my $ret = $self->$read;
96              
97 0 0       0 unless ( defined $ret ) {
98 0         0 $self->{eof}++;
99             }
100              
101 0         0 return $ret;
102             }
103             },
104             getline => sub {
105 65     65   50 my $self = shift;
106              
107 65 100       140 return undef if $self->{eof};
108              
109 60 100       113 if ( ref $/ ) {
    100          
110 5         6 $self->read(my $ret, ${$/});
  5         16  
111 5         13 return $ret;
112             } elsif ( defined $/ ) {
113             getline: {
114 50 100 100     35 if ( defined $self->{buf} and (my $off = index($self->{buf}, $/)) > -1 ) {
  174         583  
115 45         188 return substr($self->{buf}, 0, $off + length($/), '');
116             } else {
117 129 100       150 if ( defined( my $chunk = $self->$read ) ) {
118 124         437 $self->{buf} .= $chunk;
119 124         125 redo getline;
120             } else {
121 5         22 $self->{eof}++;
122              
123 5 50       15 if ( length( my $buf = delete $self->{buf} ) ) {
124 0         0 return $buf;
125             } else {
126 5         14 return undef;
127             }
128             }
129             }
130             }
131             } else {
132 5         8 my $ret = delete $self->{buf};
133              
134 5         10 while ( defined( my $chunk = $self->$read ) ) {
135 26         84 $ret .= $chunk;
136             }
137              
138 5         21 $self->{eof}++;
139              
140 5         16 return $ret;
141             }
142             },
143             read => sub {
144 35     35   48 my ( $self, undef, $length, $offset ) = @_;
145              
146 35 50       66 return 0 if $self->{eof};
147              
148 35 50 66     76 if ( $offset and length($_[1]) < $offset ) {
149 0         0 $_[1] .= "\0" x ( $offset - length($_[1]) );
150             }
151              
152 35         67 while (length($self->{buf}) < $length) {
153 58 100       62 if ( defined(my $next = $self->$read) ) {
154 53         232 $self->{buf} .= $next;
155             } else {
156             # data ended but still under $length, return all that remains and
157             # empty the buffer
158 5         17 my $ret = length($self->{buf});
159              
160 5 50       10 if ( $offset ) {
161 0         0 substr($_[1], $offset) = delete $self->{buf};
162             } else {
163 5         7 $_[1] = delete $self->{buf};
164             }
165              
166 5         8 $self->{eof}++;
167 5         15 return $ret;
168             }
169             }
170              
171 30         22 my $read;
172 30 50       41 if ( $length > length($self->{buf}) ) {
173 0         0 $read = delete $self->{buf};
174             } else {
175 30         57 $read = substr($self->{buf}, 0, $length, '');
176             }
177              
178 30 100       36 if ( $offset ) {
179 5         6 substr($_[1], $offset) = $read;
180             } else {
181 25         22 $_[1] = $read;
182             }
183              
184 30         73 return length($read);
185             },
186             eof => sub {
187 50     50   46 my $self = shift;
188 50         155 $self->{eof};
189             },
190             ungetc => sub {
191 5     5   10 my ( $self, $ord ) = @_;
192              
193 5         16 substr( $self->{buf}, 0, 0, chr($ord) );
194              
195 5         10 return;
196             },
197 15         122 );
198             }
199              
200             sub _default___read_callbacks {
201 6     6   6 my ( $class, $read ) = @_;
202              
203 6         13 $class->_make_read_callbacks($read);
204             }
205              
206             sub _default_read_callbacks {
207 3     3   5 my ( $class, $read ) = @_;
208              
209             $class->_make_read_callbacks(sub {
210 6     6   7 my $self = shift;
211              
212 6 50       14 if ( $self->$read(my $buf, ref $/ ? ${ $/ } : 4096) ) {
  0 100       0  
213 3         22 return $buf;
214             } else {
215 3         20 return undef;
216             }
217 3         11 });
218             }
219              
220             sub _default_getline_callbacks {
221 6     6   8 my ( $class, $getline ) = @_;
222              
223             $class->_make_read_callbacks(sub {
224 106 100   106   194 local $/ = ref $/ ? $/ : \4096;
225 106         119 $_[0]->$getline;
226 6         19 });
227             }
228              
229             sub _simple_read_callbacks {
230 15     15   15 my $class = shift;
231              
232             return (
233             # these are generic fallbacks defined in terms of the wrapping ones
234             sysread => sub {
235 0     0   0 shift->read(@_);
236             },
237             getc => sub {
238 10     10   9 my $self = shift;
239              
240 10 50       24 if ( $self->read(my $str, 1) ) {
241 10         33 return $str;
242             } else {
243 0         0 return undef;
244             }
245             },
246 15         43 );
247             }
248              
249             sub _simple_getline_callbacks {
250 15     15   13 my $class = shift;
251              
252             return (
253             getlines => sub {
254 5     5   6 my $self = shift;
255              
256 5         9 my @accum;
257              
258 5         12 while ( defined(my $next = $self->getline) ) {
259 25         54 push @accum, $next;
260             }
261              
262 5         26 return @accum;
263             }
264 15         41 );
265             }
266              
267             sub _default_write_callbacks {
268 4     4   7 my ( $class, $canonical ) = @_;
269              
270             return (
271 0     0   0 autoflush => sub { 1 },
272       0     sync => sub { },
273       0     flush => sub { },
274              
275             # these are defined in terms of a canonical print method, either write,
276             # syswrite or print
277             __write => sub {
278 14     14   21 my ( $self, $str ) = @_;
279 14         26 local $\;
280 14         10 local $,;
281 14         32 $self->$canonical($str);
282             },
283             print => sub {
284 10     10   10 my $self = shift;
285 10 100       14 my $ofs = defined $, ? $, : '';
286 10 100       19 my $ors = defined $\ ? $\ : '';
287 10         37 $self->__write( join($ofs, @_) . $ors );
288             },
289              
290             (map { $_ => sub {
291 4     4   7 my ( $self, $str, $len, $offset ) = @_;
292 4 100       13 $len = length($str) unless defined $len;
293 4   100     12 $offset ||= 0;
294 4         12 $self->__write(substr($str, $offset, $len));
295 8         52 } } qw(write syswrite)),
296              
297             # wrappers for print
298             printf => sub {
299 3     3   5 my ( $self, $f, @args ) = @_;
300 3         15 $self->print(sprintf $f, @args);
301             },
302             say => sub {
303 3     3   8 local $\ = "\n";
304 3         6 shift->print(@_);
305             },
306             printflush => sub {
307 0     0     my $self = shift;
308 0           my $autoflush = $self->autoflush;
309 0           my $ret = $self->print(@_);
310 0           $self->autoflush($autoflush);
311 0           return $ret;
312             }
313 4         28 );
314             }
315              
316             __PACKAGE__
317              
318             # ex: set sw=4 et:
319              
320             __END__