File Coverage

blib/lib/Stream/Reader.pm
Criterion Covered Total %
statement 12 163 7.3
branch 1 124 0.8
condition 1 69 1.4
subroutine 3 12 25.0
pod 3 3 100.0
total 20 371 5.3


line stmt bran cond sub pod time code
1             package Stream::Reader;
2              
3 1     1   13837 use 5.005;
  1         4  
  1         234  
4 1     1   7 use strict;
  1         2  
  1         548  
5              
6             our $VERSION = '0.09';
7              
8             # Global/system variables
9              
10             our $CODE;
11             our $AUTOLOAD;
12             our $Shift;
13              
14             # Autoloaded code
15             $CODE ||= {
16              
17             # Constructor
18             new => <<'ENDC',
19             my $class = shift;
20             my $input = shift;
21             my $attr = ( ref($Shift = shift) eq 'HASH' )? $Shift : {};
22             my $self = {
23             # System parameters
24             input => $input,
25             inpos => 0,
26             inlimit => ( defined($attr->{Limit}) and $attr->{Limit} >= 0 )? $attr->{Limit} : 1e10,
27             buffsize => ( defined($attr->{BuffSize}) and $attr->{BuffSize} >= 0 )? $attr->{BuffSize} : 32_768,
28             bufferA => '',
29             bufferB => '',
30             status => 1,
31             # System flags
32             mode_B => ( $attr->{Mode} and index(uc($attr->{Mode}),'B') != -1 ),
33             mode_U => ( $attr->{Mode} and index(uc($attr->{Mode}),'U') != -1 ),
34             # Statistic parameters
35             Match => '',
36             Readed => 0,
37             Stored => 0,
38             Total => 0,
39             Error => 0
40             };
41             return bless( $self => $class );
42             ENDC
43              
44             # Destructor
45             DESTROY => <<'ENDC',
46             return 1;
47             ENDC
48              
49             # Public method
50             readto => <<'ENDC',
51             my $self = shift;
52             my $delim = ( ref($Shift = shift) eq 'ARRAY' )? $Shift : [$Shift];
53             my $attr = ( ref($Shift = shift) eq 'HASH' )? $Shift : {};
54             my $limit = ( defined($attr->{Limit}) and $attr->{Limit} >= 0 )? $attr->{Limit} : 1e10;
55             my $wcase = ( $attr->{Mode} and index(uc($attr->{Mode}),'I') != -1 );
56             my $max_d = 0;
57             my $min_d = 1e10;
58             my $error;
59             my $rsize;
60              
61             # Preparing:
62             # - reseting some statistic variables
63             @$self{ qw(Readed Stored Match) } = ( (0)x2, '' );
64             # - initialize output stream, if this is SCALAR and initialization required
65             if( UNIVERSAL::isa($attr->{Out},'SCALAR')
66             and !( defined(${$attr->{Out}}) and $attr->{Mode} and index(uc($attr->{Mode}),'A') != -1 )
67             ) {
68             ${$attr->{Out}} = '';
69             }
70             # - maximal and minimal delimiter length detection
71             foreach( @$delim ) {
72             $max_d = length if $max_d < length;
73             $min_d = length if $min_d > length;
74             }
75             # - checking status and delimiter(s) presents
76             unless( $self->{status} and $max_d ) {
77             return $self->{status};
78             } else {
79             # Processing:
80             while(1) {
81             # - searching
82             if( length($self->{bufferA}) >= $min_d ) {
83             my $found = 1e10;
84             my $buffer;
85             if( $wcase ) {
86             $buffer = \( $self->{mode_B}? $self->{bufferB} : lc($self->{bufferA}) );
87             }
88             foreach( @$delim ) {
89             my $pos = $wcase? index($$buffer,lc) : index($self->{bufferA},$_);
90             if( $pos != -1 and $pos < $found ) {
91             $found = $pos;
92             $self->{Match} = $_;
93             }
94             }
95             if( $found < 1e10 ) {
96             if( !$error and $self->{Stored} < $limit ) {
97             $rsize = $found;
98             $rsize = $limit - $self->{Stored} if( $rsize > $limit - $self->{Stored} );
99             $error = !$self->_write( $attr->{Out}, \(substr( $self->{bufferA}, 0, $rsize )) );
100             $self->{Stored} += $rsize unless $error;
101             }
102             $self->{Readed} += $found;
103             $self->{Total} += $found;
104             my $psize = $found + length($self->{Match});
105             substr( $self->{bufferA}, 0, $psize, '' );
106             substr( $self->{bufferB}, 0, $psize, '' ) if $self->{mode_B};
107             return 1;
108             }
109             }
110             # - move part data to output stream
111             if( length($self->{bufferA}) >= $max_d ) {
112             my $psize = length($self->{bufferA}) - ($max_d - 1);
113             if( !$error and $self->{Stored} < $limit ) {
114             $rsize = $psize;
115             $rsize = $limit - $self->{Stored} if( $rsize > $limit - $self->{Stored} );
116             $error = !$self->_write( $attr->{Out}, \(substr( $self->{bufferA}, 0, $rsize )) );
117             $self->{Stored} += $rsize unless $error;
118             }
119             $self->{Readed} += $psize;
120             $self->{Total} += $psize;
121             substr( $self->{bufferA}, 0, $psize, '' );
122             substr( $self->{bufferB}, 0, $psize, '' ) if $self->{mode_B};
123             }
124             # - if limit not ended yet then trying to fill buffer
125             # - else move last data to output stream and finish
126             if( $self->{inlimit} ) {
127             return 0 unless $self->_fill_buffer();
128             } else {
129             if( length $self->{bufferA} ) {
130             $rsize = length $self->{bufferA};
131             $self->{Readed} += $rsize;
132             $self->{Total} += $rsize;
133             if( !$error and $self->{Stored} < $limit ) {
134             $rsize = $limit - $self->{Stored} if( $rsize > $limit - $self->{Stored} );
135             $error = !$self->_write( $attr->{Out}, \(substr( $self->{bufferA}, 0, $rsize )) );
136             $self->{Stored} += $rsize unless $error;
137             }
138             $self->{bufferA} = '';
139             $self->{bufferB} = '' if $self->{mode_B};
140             }
141             $self->{status} = 0;
142             return( ( $attr->{Mode} and index(uc($attr->{Mode}),'E') != -1 )? 0 : 1 );
143             }
144             }
145             }
146             ENDC
147              
148             # Public method
149             readsome => <<'ENDC',
150             my $self = shift;
151             my $limit = ( defined($Shift = shift) and $Shift >= 0 )? $Shift : 1e10;
152             my $attr = ( ref($Shift = shift) eq 'HASH' )? $Shift : {};
153             my $rsize;
154             my $error;
155              
156             # Preparing:
157             # - reseting some statistic variables
158             @$self{ qw(Readed Stored Match) } = ( (0)x2, '' );
159             # - initialize output stream, if this is SCALAR and initialization required
160             if( UNIVERSAL::isa($attr->{Out},'SCALAR')
161             and !( defined(${$attr->{Out}}) and $attr->{Mode} and index(uc($attr->{Mode}),'A') != -1 )
162             ) {
163             ${$attr->{Out}} = '';
164             }
165             # - checking status
166             unless( $self->{status} ) {
167             return 0;
168             } else {
169             # Processing:
170             while( $self->{Readed} < $limit ) {
171             # - trying to fill buffer
172             unless( length $self->{bufferA} ) {
173             return 0 unless $self->_fill_buffer();
174             }
175             # - if buffer still empty then break cycle
176             unless( length $self->{bufferA} ) {
177             $self->{status} = 0;
178             return( $self->{Readed} ? 1 : 0 );
179             # - else if not enouth data in buffer, then move all data from buffer to output stream
180             } elsif( length($self->{bufferA}) <= $limit - $self->{Readed} ) {
181             unless( $error ) {
182             $error = !$self->_write( $attr->{Out}, \($self->{bufferA}) );
183             }
184             $rsize = length $self->{bufferA};
185             $self->{Stored} += $rsize unless $error;
186             $self->{Readed} += $rsize;
187             $self->{Total} += $rsize;
188             $self->{bufferA} = '';
189             $self->{bufferB} = '' if $self->{mode_B};
190             # - else move necessary of characters to output stream and break cycle
191             } else {
192             $rsize = $limit - $self->{Readed};
193             unless( $error ) {
194             $error = !$self->_write( $attr->{Out}, \(substr( $self->{bufferA}, 0, $rsize )) );
195             }
196             $self->{Stored} += $rsize unless $error;
197             $self->{Readed} += $rsize;
198             $self->{Total} += $rsize;
199             substr( $self->{bufferA}, 0, $rsize, '' );
200             substr( $self->{bufferB}, 0, $rsize, '' ) if $self->{mode_B};
201             last;
202             }
203             }
204             }
205             return 1;
206             ENDC
207              
208             # Private method: BOOL = _fill_buffer()
209             # Trying to filling buffer with new portion of data. Returns false on errors
210             _fill_buffer => <<'ENDC',
211             my $self = shift;
212              
213             if( $self->{inlimit} ) { # checking stream limit
214             my $buffer;
215             my $result;
216             # Getting new portion of data
217             if( $self->{buffsize} > $self->{inlimit} ) {
218             $result = $self->_read( \$buffer, $self->{inlimit} );
219             } else {
220             $result = $self->_read( \$buffer, $self->{buffsize} );
221             }
222             # Checking data
223             if( !defined($result) or ($] >= 5.008001
224             and !$self->{mode_U} and $result and utf8::is_utf8($buffer) and !utf8::valid($buffer)
225             )) {
226             # Error reading or malformed data
227             @$self{ qw(Error status inlimit bufferA bufferB) } = ( qw(1 0 0), ('')x2 );
228             return undef;
229             } else {
230             # Fixing stream limit and appending data to buffers
231             $self->{inlimit} = $result? ( $self->{inlimit} - $result ) : 0;
232             $self->{bufferA} .= $buffer;
233             $self->{bufferB} .= lc($buffer) if $self->{mode_B};
234             }
235             }
236             return 1;
237             ENDC
238              
239             # Private method: LENGTH = SELF->_read(STRREF,LENGTH)
240             # Trying to reading data from input stream into STRREF
241             _read => <<'ENDC',
242             my $self = shift;
243             my $strref = shift;
244             my $length = shift;
245             my $result;
246              
247             # Checking type of stream:
248             # - if SCALAR, then copy part of data from SCALAR variable
249             if( UNIVERSAL::isa($self->{input},'SCALAR') ) {
250             $result = length(${$self->{input}}) - $self->{inpos};
251             $result = $length if $result > $length;
252             $result = 0 if $result < 0;
253             $$strref = substr( ${$self->{input}}, $self->{inpos}, $result );
254             $self->{inpos} += $result;
255             # - if TYPEGLOB, then reading next part of data from file stream
256             } elsif( UNIVERSAL::isa($self->{input},'GLOB') ) {
257             $result = read( $self->{input}, $$strref, $length );
258             }
259             return $result;
260             ENDC
261              
262             # Private method: BOOL = SELF->_write(OUTPUT,STRREF)
263             # Storing data in output stream
264             _write => <<'ENDC',
265             my $self = shift;
266             my $output = shift;
267             my $strref = shift;
268             my $result;
269              
270             # Checking type of reference:
271             # - if SCALAR, then appending data to SCALAR variable
272             # - if TYPEGLOB, then writing data to file stream
273             if( UNIVERSAL::isa($output,'SCALAR') ) {
274             $$output .= $$strref;
275             $result = 1; # alltimes true result
276             } elsif ( UNIVERSAL::isa($output,'GLOB') ) {
277             $result = print( {$output} $$strref );
278             }
279             return $result;
280             ENDC
281              
282             };
283              
284             # Compiling all under mod_perl
285             if( exists $ENV{MOD_PERL} ) {
286             _compile($_) foreach( keys %{$CODE} );
287             }
288              
289             # Standard function
290             sub AUTOLOAD {
291 0     0   0 my $name = substr(
292             $AUTOLOAD, rindex( $AUTOLOAD, ':' ) + 1
293             );
294 0 0       0 unless( _compile($name) ) {
295 0         0 _croak("Undefined subroutine &${AUTOLOAD} called");
296             } else {
297 0         0 goto &{$AUTOLOAD};
  0         0  
298             }
299             }
300              
301             # Private function: BOOL = _compile(NAME)
302             # Compiling, specified by NAME, subroutine from $CODE array
303             sub _compile {
304 7     7   10 my $name = shift;
305              
306 7 50       19 unless( exists $CODE->{$name} ) {
307 0         0 return undef;
308             } else {
309 7 0 0 0 1 2364 eval "sub $name { $CODE->{$name} }";
  0 0 0 0 1    
  0 0 0 0 1    
  0 0 0 0      
  0 0 0 0      
  0 0 0 0      
  0 0 0 0      
  0 0 0        
  0 0 0        
  0 0 0        
  0 0 0        
  0 0 0        
  0 0 0        
  0 0 0        
  0 0 0        
  0 0 0        
  0 0 0        
  0 0 0        
  0 0 0        
  0 0 0        
  0 0 0        
  0 0 0        
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0 0          
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
310 7 0 33     28 if( $@ ne '' and $^W ) {
311 0         0 warn $@; # warnings enable
312             }
313 7         14 delete $CODE->{$name};
314 7         28 return 1;
315             }
316             }
317              
318             # Handling fatals
319             sub _croak {
320 0     0     require Carp; Carp::croak(shift);
  0            
321             }
322              
323             1;
324              
325             __END__