File Coverage

lib/IO/ScalarArray.pm
Criterion Covered Total %
statement 136 164 82.9
branch 36 50 72.0
condition 8 16 50.0
subroutine 27 42 64.2
pod 20 20 100.0
total 227 292 77.7


line stmt bran cond sub pod time code
1             package IO::ScalarArray;
2              
3              
4             =head1 NAME
5              
6             IO::ScalarArray - IO:: interface for reading/writing an array of scalars
7              
8              
9             =head1 SYNOPSIS
10              
11             Perform I/O on strings, using the basic OO interface...
12              
13             use IO::ScalarArray;
14             @data = ("My mes", "sage:\n");
15              
16             ### Open a handle on an array, and append to it:
17             $AH = new IO::ScalarArray \@data;
18             $AH->print("Hello");
19             $AH->print(", world!\nBye now!\n");
20             print "The array is now: ", @data, "\n";
21              
22             ### Open a handle on an array, read it line-by-line, then close it:
23             $AH = new IO::ScalarArray \@data;
24             while (defined($_ = $AH->getline)) {
25             print "Got line: $_";
26             }
27             $AH->close;
28              
29             ### Open a handle on an array, and slurp in all the lines:
30             $AH = new IO::ScalarArray \@data;
31             print "All lines:\n", $AH->getlines;
32              
33             ### Get the current position (either of two ways):
34             $pos = $AH->getpos;
35             $offset = $AH->tell;
36              
37             ### Set the current position (either of two ways):
38             $AH->setpos($pos);
39             $AH->seek($offset, 0);
40              
41             ### Open an anonymous temporary array:
42             $AH = new IO::ScalarArray;
43             $AH->print("Hi there!");
44             print "I printed: ", @{$AH->aref}, "\n"; ### get at value
45              
46              
47             Don't like OO for your I/O? No problem.
48             Thanks to the magic of an invisible tie(), the following now
49             works out of the box, just as it does with IO::Handle:
50            
51             use IO::ScalarArray;
52             @data = ("My mes", "sage:\n");
53              
54             ### Open a handle on an array, and append to it:
55             $AH = new IO::ScalarArray \@data;
56             print $AH "Hello";
57             print $AH ", world!\nBye now!\n";
58             print "The array is now: ", @data, "\n";
59              
60             ### Open a handle on a string, read it line-by-line, then close it:
61             $AH = new IO::ScalarArray \@data;
62             while (<$AH>) {
63             print "Got line: $_";
64             }
65             close $AH;
66              
67             ### Open a handle on a string, and slurp in all the lines:
68             $AH = new IO::ScalarArray \@data;
69             print "All lines:\n", <$AH>;
70              
71             ### Get the current position (WARNING: requires 5.6):
72             $offset = tell $AH;
73              
74             ### Set the current position (WARNING: requires 5.6):
75             seek $AH, $offset, 0;
76              
77             ### Open an anonymous temporary scalar:
78             $AH = new IO::ScalarArray;
79             print $AH "Hi there!";
80             print "I printed: ", @{$AH->aref}, "\n"; ### get at value
81              
82              
83             And for you folks with 1.x code out there: the old tie() style still works,
84             though this is I:
85              
86             use IO::ScalarArray;
87              
88             ### Writing to a scalar...
89             my @a;
90             tie *OUT, 'IO::ScalarArray', \@a;
91             print OUT "line 1\nline 2\n", "line 3\n";
92             print "Array is now: ", @a, "\n"
93              
94             ### Reading and writing an anonymous scalar...
95             tie *OUT, 'IO::ScalarArray';
96             print OUT "line 1\nline 2\n", "line 3\n";
97             tied(OUT)->seek(0,0);
98             while () {
99             print "Got line: ", $_;
100             }
101              
102              
103              
104             =head1 DESCRIPTION
105              
106             This class is part of the IO::Stringy distribution;
107             see L for change log and general information.
108              
109             The IO::ScalarArray class implements objects which behave just like
110             IO::Handle (or FileHandle) objects, except that you may use them
111             to write to (or read from) arrays of scalars. Logically, an
112             array of scalars defines an in-core "file" whose contents are
113             the concatenation of the scalars in the array. The handles created by
114             this class are automatically tiehandle'd (though please see L<"WARNINGS">
115             for information relevant to your Perl version).
116              
117             For writing large amounts of data with individual print() statements,
118             this class is likely to be more efficient than IO::Scalar.
119              
120             Basically, this:
121              
122             my @a;
123             $AH = new IO::ScalarArray \@a;
124             $AH->print("Hel", "lo, "); ### OO style
125             $AH->print("world!\n"); ### ditto
126              
127             Or this:
128              
129             my @a;
130             $AH = new IO::ScalarArray \@a;
131             print $AH "Hel", "lo, "; ### non-OO style
132             print $AH "world!\n"; ### ditto
133              
134             Causes @a to be set to the following array of 3 strings:
135              
136             ( "Hel" ,
137             "lo, " ,
138             "world!\n" )
139              
140             See L and compare with this class.
141              
142              
143             =head1 PUBLIC INTERFACE
144              
145             =cut
146              
147 3     3   3129 use Carp;
  3         5  
  3         200  
148 3     3   15 use strict;
  3         4  
  3         85  
149 3     3   14 use vars qw($VERSION @ISA);
  3         4  
  3         151  
150 3     3   2249 use IO::Handle;
  3         24166  
  3         6610  
151              
152             # The package version, both in 1.23 style *and* usable by MakeMaker:
153             $VERSION = "2.110";
154              
155             # Inheritance:
156             @ISA = qw(IO::Handle);
157             require IO::WrapTie and push @ISA, 'IO::WrapTie::Slave' if ($] >= 5.004);
158              
159              
160             #==============================
161              
162             =head2 Construction
163              
164             =over 4
165              
166             =cut
167              
168             #------------------------------
169              
170             =item new [ARGS...]
171              
172             I
173             Return a new, unattached array handle.
174             If any arguments are given, they're sent to open().
175              
176             =cut
177              
178             sub new {
179 12     12 1 23944 my $proto = shift;
180 12   33     73 my $class = ref($proto) || $proto;
181 12         16 my $self = bless \do { local *FH }, $class;
  12         60  
182 12         125 tie *$self, $class, $self;
183 12         55 $self->open(@_); ### open on anonymous by default
184 12         39 $self;
185             }
186             sub DESTROY {
187 10     10   357 shift->close;
188             }
189              
190              
191             #------------------------------
192              
193             =item open [ARRAYREF]
194              
195             I
196             Open the array handle on a new array, pointed to by ARRAYREF.
197             If no ARRAYREF is given, a "private" array is created to hold
198             the file data.
199              
200             Returns the self object on success, undefined on error.
201              
202             =cut
203              
204             sub open {
205 12     12 1 31 my ($self, $aref) = @_;
206              
207             ### Sanity:
208 12 100       32 defined($aref) or do {my @a; $aref = \@a};
  2         2  
  2         3  
209 12 50       42 (ref($aref) eq "ARRAY") or croak "open needs a ref to a array";
210              
211             ### Setup:
212 12         70 $self->setpos([0,0]);
213 12         61 *$self->{AR} = $aref;
214 12         22 $self;
215             }
216              
217             #------------------------------
218              
219             =item opened
220              
221             I
222             Is the array handle opened on something?
223              
224             =cut
225              
226             sub opened {
227 0     0 1 0 *{shift()}->{AR};
  0         0  
228             }
229              
230             #------------------------------
231              
232             =item close
233              
234             I
235             Disassociate the array handle from its underlying array.
236             Done automatically on destroy.
237              
238             =cut
239              
240             sub close {
241 10     10 1 18 my $self = shift;
242 10         13 %{*$self} = ();
  10         39  
243 10         56 1;
244             }
245              
246             =back
247              
248             =cut
249              
250              
251              
252             #==============================
253              
254             =head2 Input and output
255              
256             =over 4
257              
258             =cut
259              
260             #------------------------------
261              
262             =item flush
263              
264             I
265             No-op, provided for OO compatibility.
266              
267             =cut
268              
269 0     0 1 0 sub flush { "0 but true" }
270              
271             #------------------------------
272              
273             =item getc
274              
275             I
276             Return the next character, or undef if none remain.
277             This does a read(1), which is somewhat costly.
278              
279             =cut
280              
281             sub getc {
282 6     6 1 28 my $buf = '';
283 6 50       25 ($_[0]->read($buf, 1) ? $buf : undef);
284             }
285              
286             #------------------------------
287              
288             =item getline
289              
290             I
291             Return the next line, or undef on end of data.
292             Can safely be called in an array context.
293             Currently, lines are delimited by "\n".
294              
295             =cut
296              
297             sub getline {
298 41     41 1 1116 my $self = shift;
299 41         64 my ($str, $line) = (undef, '');
300              
301              
302             ### Minimal impact implementation!
303             ### We do the fast fast thing (no regexps) if using the
304             ### classic input record separator.
305              
306             ### Case 1: $/ is undef: slurp all...
307 41 100       151 if (!defined($/)) {
    50          
    0          
308              
309 2 50       10 return undef if ($self->eof);
310              
311             ### Get the rest of the current string, followed by remaining strings:
312 2         11 my $ar = *$self->{AR};
313 2         27 my @slurp = (
314             substr($ar->[*$self->{Str}], *$self->{Pos}),
315             @$ar[(1 + *$self->{Str}) .. $#$ar ]
316             );
317            
318             ### Seek to end:
319 2         11 $self->_setpos_to_eof;
320 2         30 return join('', @slurp);
321             }
322              
323             ### Case 2: $/ is "\n":
324             elsif ($/ eq "\012") {
325            
326             ### Until we hit EOF (or exitted because of a found line):
327 39         88 until ($self->eof) {
328             ### If at end of current string, go fwd to next one (won't be EOF):
329 47 100       181 if ($self->_eos) {++*$self->{Str}, *$self->{Pos}=0};
  28         69  
330              
331             ### Get ref to current string in array, and set internal pos mark:
332 47         171 $str = \(*$self->{AR}[*$self->{Str}]); ### get current string
333 47         175 pos($$str) = *$self->{Pos}; ### start matching from here
334            
335             ### Get from here to either \n or end of string, and add to line:
336 47         323 $$str =~ m/\G(.*?)((\n)|\Z)/g; ### match to 1st \n or EOS
337 47         145 $line .= $1.$2; ### add it
338 47         145 *$self->{Pos} += length($1.$2); ### move fwd by len matched
339 47 100       297 return $line if $3; ### done, got line with "\n"
340             }
341 7 100       41 return ($line eq '') ? undef : $line; ### return undef if EOF
342             }
343              
344             ### Case 3: $/ is ref to int. Bail out.
345             elsif (ref($/)) {
346 0         0 croak '$/ given as a ref to int; currently unsupported';
347             }
348              
349             ### Case 4: $/ is either "" (paragraphs) or something weird...
350             ### Bail for now.
351             else {
352 0         0 croak '$/ as given is currently unsupported';
353             }
354             }
355              
356             #------------------------------
357              
358             =item getlines
359              
360             I
361             Get all remaining lines.
362             It will croak() if accidentally called in a scalar context.
363              
364             =cut
365              
366             sub getlines {
367 2     2 1 7 my $self = shift;
368 2 50       6 wantarray or croak("can't call getlines in scalar context!");
369 2         3 my ($line, @lines);
370 2         7 push @lines, $line while (defined($line = $self->getline));
371 2         15 @lines;
372             }
373              
374             #------------------------------
375              
376             =item print ARGS...
377              
378             I
379             Print ARGS to the underlying array.
380              
381             Currently, this always causes a "seek to the end of the array"
382             and generates a new array entry. This may change in the future.
383              
384             =cut
385              
386             sub print {
387 7     7 1 174 my $self = shift;
388 7 50       10 push @{*$self->{AR}}, join('', @_) . (defined($\) ? $\ : ""); ### add the data
  7         57  
389 7         25 $self->_setpos_to_eof;
390 7         30 1;
391             }
392              
393             #------------------------------
394              
395             =item read BUF, NBYTES, [OFFSET];
396              
397             I
398             Read some bytes from the array.
399             Returns the number of bytes actually read, 0 on end-of-file, undef on error.
400              
401             =cut
402              
403             sub read {
404 18     18 1 147 my $self = $_[0];
405             ### we must use $_[1] as a ref
406 18         45 my $n = $_[2];
407 18   50     71 my $off = $_[3] || 0;
408              
409             ### print "getline\n";
410 18         20 my $justread;
411             my $len;
412 18 50       43 ($off ? substr($_[1], $off) : $_[1]) = '';
413              
414             ### Stop when we have zero bytes to go, or when we hit EOF:
415 18         18 my @got;
416 18   100     78 until (!$n or $self->eof) {
417             ### If at end of current string, go forward to next one (won't be EOF):
418 30 100       96 if ($self->_eos) {
419 12         32 ++*$self->{Str};
420 12         30 *$self->{Pos} = 0;
421             }
422              
423             ### Get longest possible desired substring of current string:
424 30         140 $justread = substr(*$self->{AR}[*$self->{Str}], *$self->{Pos}, $n);
425 30         47 $len = length($justread);
426 30         56 push @got, $justread;
427 30         43 $n -= $len;
428 30         140 *$self->{Pos} += $len;
429             }
430 18         42 $_[1] .= join('', @got);
431 18         67 return length($_[1])-$off;
432             }
433              
434             #------------------------------
435              
436             =item write BUF, NBYTES, [OFFSET];
437              
438             I
439             Write some bytes into the array.
440              
441             =cut
442              
443             sub write {
444 0     0 1 0 my $self = $_[0];
445 0         0 my $n = $_[2];
446 0   0     0 my $off = $_[3] || 0;
447              
448 0         0 my $data = substr($_[1], $n, $off);
449 0         0 $n = length($data);
450 0         0 $self->print($data);
451 0         0 return $n;
452             }
453              
454              
455             =back
456              
457             =cut
458              
459              
460              
461             #==============================
462              
463             =head2 Seeking/telling and other attributes
464              
465             =over 4
466              
467             =cut
468              
469             #------------------------------
470              
471             =item autoflush
472              
473             I
474             No-op, provided for OO compatibility.
475              
476             =cut
477              
478 0     0 1 0 sub autoflush {}
479              
480             #------------------------------
481              
482             =item binmode
483              
484             I
485             No-op, provided for OO compatibility.
486              
487             =cut
488              
489 0     0 1 0 sub binmode {}
490              
491             #------------------------------
492              
493             =item clearerr
494              
495             I Clear the error and EOF flags. A no-op.
496              
497             =cut
498              
499 0     0 1 0 sub clearerr { 1 }
500              
501             #------------------------------
502              
503             =item eof
504              
505             I Are we at end of file?
506              
507             =cut
508              
509             sub eof {
510             ### print "checking EOF [*$self->{Str}, *$self->{Pos}]\n";
511             ### print "SR = ", $#{*$self->{AR}}, "\n";
512              
513 88 100   88 1 356 return 0 if (*{$_[0]}->{Str} < $#{*{$_[0]}->{AR}}); ### before EOA
  88         228  
  88         244  
  88         454  
514 13 50       24 return 1 if (*{$_[0]}->{Str} > $#{*{$_[0]}->{AR}}); ### after EOA
  13         23  
  13         17  
  13         43  
515             ### ### at EOA, past EOS:
516 13 50       21 ((*{$_[0]}->{Str} == $#{*{$_[0]}->{AR}}) && ($_[0]->_eos));
  13         29  
  13         14  
  13         65  
517             }
518              
519             #------------------------------
520             #
521             # _eos
522             #
523             # I Are we at end of the CURRENT string?
524             #
525             sub _eos {
526 90     90   104 (*{$_[0]}->{Pos} >= length(*{$_[0]}->{AR}[*{$_[0]}->{Str}])); ### past last char
  90         190  
  90         450  
  90         169  
527             }
528              
529             #------------------------------
530              
531             =item seek POS,WHENCE
532              
533             I
534             Seek to a given position in the stream.
535             Only a WHENCE of 0 (SEEK_SET) is supported.
536              
537             =cut
538              
539             sub seek {
540 22     22 1 5180 my ($self, $pos, $whence) = @_;
541              
542             ### Seek:
543 22 100       62 if ($whence == 0) { $self->_seek_set($pos); }
  18 100       59  
    50          
544 2         11 elsif ($whence == 1) { $self->_seek_cur($pos); }
545 2         13 elsif ($whence == 2) { $self->_seek_end($pos); }
546 0         0 else { croak "bad seek whence ($whence)" }
547 22         73 return 1;
548             }
549              
550             #------------------------------
551             #
552             # _seek_set POS
553             #
554             # Instance method, private.
555             # Seek to $pos relative to start:
556             #
557             sub _seek_set {
558 22     22   30 my ($self, $pos) = @_;
559              
560             ### Advance through array until done:
561 22         27 my $istr = 0;
562 22   33     85 while (($pos >= 0) && ($istr < scalar(@{*$self->{AR}}))) {
  44         198  
563 44 100       142 if (length(*$self->{AR}[$istr]) > $pos) { ### it's in this string!
564 22         74 return $self->setpos([$istr, $pos]);
565             }
566             else { ### it's in next string
567 22         91 $pos -= length(*$self->{AR}[$istr++]); ### move forward one string
568             }
569             }
570             ### If we reached this point, pos is at or past end; zoom to EOF:
571 0         0 return $self->_setpos_to_eof;
572             }
573              
574             #------------------------------
575             #
576             # _seek_cur POS
577             #
578             # Instance method, private.
579             # Seek to $pos relative to current position.
580             #
581             sub _seek_cur {
582 2     2   3 my ($self, $pos) = @_;
583 2         7 $self->_seek_set($self->tell + $pos);
584             }
585              
586             #------------------------------
587             #
588             # _seek_end POS
589             #
590             # Instance method, private.
591             # Seek to $pos relative to end.
592             # We actually seek relative to beginning, which is simple.
593             #
594             sub _seek_end {
595 2     2   5 my ($self, $pos) = @_;
596 2         26 $self->_seek_set($self->_tell_eof + $pos);
597             }
598              
599             #------------------------------
600              
601             =item tell
602              
603             I
604             Return the current position in the stream, as a numeric offset.
605              
606             =cut
607              
608             sub tell {
609 4     4 1 107 my $self = shift;
610 4         8 my $off = 0;
611 4         6 my ($s, $str_s);
612 4         22 for ($s = 0; $s < *$self->{Str}; $s++) { ### count all "whole" scalars
613 12 50       37 defined($str_s = *$self->{AR}[$s]) or $str_s = '';
614             ###print STDERR "COUNTING STRING $s (". length($str_s) . ")\n";
615 12         68 $off += length($str_s);
616             }
617             ###print STDERR "COUNTING POS ($self->{Pos})\n";
618 4         24 return ($off += *$self->{Pos}); ### plus the final, partial one
619             }
620              
621             #------------------------------
622             #
623             # _tell_eof
624             #
625             # Instance method, private.
626             # Get position of EOF, as a numeric offset.
627             # This is identical to the size of the stream - 1.
628             #
629             sub _tell_eof {
630 2     2   5 my $self = shift;
631 2         4 my $len = 0;
632 2         4 foreach (@{*$self->{AR}}) { $len += length($_) }
  2         9  
  13         29  
633 2         11 $len;
634             }
635              
636             #------------------------------
637              
638             =item setpos POS
639              
640             I
641             Seek to a given position in the array, using the opaque getpos() value.
642             Don't expect this to be a number.
643              
644             =cut
645              
646             sub setpos {
647 43     43 1 87 my ($self, $pos) = @_;
648 43 50       120 (ref($pos) eq 'ARRAY') or
649             die "setpos: only use a value returned by getpos!\n";
650 43         224 (*$self->{Str}, *$self->{Pos}) = @$pos;
651             }
652              
653             #------------------------------
654             #
655             # _setpos_to_eof
656             #
657             # Fast-forward to EOF.
658             #
659             sub _setpos_to_eof {
660 9     9   16 my $self = shift;
661 9         15 $self->setpos([scalar(@{*$self->{AR}}), 0]);
  9         766  
662             }
663              
664             #------------------------------
665              
666             =item getpos
667              
668             I
669             Return the current position in the array, as an opaque value.
670             Don't expect this to be a number.
671              
672             =cut
673              
674             sub getpos {
675 0     0 1 0 [*{$_[0]}->{Str}, *{$_[0]}->{Pos}];
  0         0  
  0         0  
676             }
677              
678             #------------------------------
679              
680             =item aref
681              
682             I
683             Return a reference to the underlying array.
684              
685             =cut
686              
687             sub aref {
688 2     2 1 20 *{shift()}->{AR};
  2         12  
689             }
690              
691             =back
692              
693             =cut
694              
695             #------------------------------
696             # Tied handle methods...
697             #------------------------------
698              
699             ### Conventional tiehandle interface:
700 14 100 66 14   696 sub TIEHANDLE { (defined($_[1]) && UNIVERSAL::isa($_[1],"IO::ScalarArray"))
701             ? $_[1]
702             : shift->new(@_) }
703 0     0   0 sub GETC { shift->getc(@_) }
704 10     10   263 sub PRINT { shift->print(@_) }
705 0     0   0 sub PRINTF { shift->print(sprintf(shift, @_)) }
706 0     0   0 sub READ { shift->read(@_) }
707 16 100   16   318 sub READLINE { wantarray ? shift->getlines(@_) : shift->getline(@_) }
708 0     0     sub WRITE { shift->write(@_); }
709 0     0     sub CLOSE { shift->close(@_); }
710 0     0     sub SEEK { shift->seek(@_); }
711 0     0     sub TELL { shift->tell(@_); }
712 0     0     sub EOF { shift->eof(@_); }
713              
714             #------------------------------------------------------------
715              
716             1;
717             __END__