File Coverage

lib/IO/ScalarArray.pm
Criterion Covered Total %
statement 137 166 82.5
branch 36 50 72.0
condition 8 16 50.0
subroutine 28 44 63.6
pod 21 21 100.0
total 230 297 77.4


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   1799 use Carp;
  3         4  
  3         148  
148 3     3   11 use strict;
  3         3  
  3         60  
149 3     3   9 use vars qw($VERSION @ISA);
  3         3  
  3         134  
150 3     3   943 use IO::Handle;
  3         9008  
  3         3809  
151              
152             # The package version, both in 1.23 style *and* usable by MakeMaker:
153             $VERSION = "2.111";
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 1363 my $proto = shift;
180 12   33     44 my $class = ref($proto) || $proto;
181 12         12 my $self = bless \do { local *FH }, $class;
  12         37  
182 12         74 tie *$self, $class, $self;
183 12         33 $self->open(@_); ### open on anonymous by default
184 12         21 $self;
185             }
186             sub DESTROY {
187 10     10   240 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 11 my ($self, $aref) = @_;
206              
207             ### Sanity:
208 12 100       23 defined($aref) or do {my @a; $aref = \@a};
  2         2  
  2         4  
209 12 50       26 (ref($aref) eq "ARRAY") or croak "open needs a ref to a array";
210              
211             ### Setup:
212 12         35 $self->setpos([0,0]);
213 12         34 *$self->{AR} = $aref;
214 12         11 $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 13 my $self = shift;
242 10         8 %{*$self} = ();
  10         23  
243 10         105 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 fileno
274              
275             I
276             No-op, returns undef
277              
278             =cut
279              
280 1     1 1 35 sub fileno { }
281              
282             #------------------------------
283              
284             =item getc
285              
286             I
287             Return the next character, or undef if none remain.
288             This does a read(1), which is somewhat costly.
289              
290             =cut
291              
292             sub getc {
293 6     6 1 22 my $buf = '';
294 6 50       17 ($_[0]->read($buf, 1) ? $buf : undef);
295             }
296              
297             #------------------------------
298              
299             =item getline
300              
301             I
302             Return the next line, or undef on end of data.
303             Can safely be called in an array context.
304             Currently, lines are delimited by "\n".
305              
306             =cut
307              
308             sub getline {
309 41     41 1 747 my $self = shift;
310 41         43 my ($str, $line) = (undef, '');
311              
312              
313             ### Minimal impact implementation!
314             ### We do the fast thing (no regexps) if using the
315             ### classic input record separator.
316              
317             ### Case 1: $/ is undef: slurp all...
318 41 100       77 if (!defined($/)) {
    50          
    0          
319              
320 2 50       3 return undef if ($self->eof);
321              
322             ### Get the rest of the current string, followed by remaining strings:
323 2         3 my $ar = *$self->{AR};
324 2         11 my @slurp = (
325             substr($ar->[*$self->{Str}], *$self->{Pos}),
326             @$ar[(1 + *$self->{Str}) .. $#$ar ]
327             );
328            
329             ### Seek to end:
330 2         3 $self->_setpos_to_eof;
331 2         10 return join('', @slurp);
332             }
333              
334             ### Case 2: $/ is "\n":
335             elsif ($/ eq "\012") {
336            
337             ### Until we hit EOF (or exited because of a found line):
338 39         50 until ($self->eof) {
339             ### If at end of current string, go fwd to next one (won't be EOF):
340 47 100       90 if ($self->_eos) {++*$self->{Str}, *$self->{Pos}=0};
  28         34  
341              
342             ### Get ref to current string in array, and set internal pos mark:
343 47         77 $str = \(*$self->{AR}[*$self->{Str}]); ### get current string
344 47         84 pos($$str) = *$self->{Pos}; ### start matching from here
345            
346             ### Get from here to either \n or end of string, and add to line:
347 47         158 $$str =~ m/\G(.*?)((\n)|\Z)/g; ### match to 1st \n or EOS
348 47         73 $line .= $1.$2; ### add it
349 47         69 *$self->{Pos} += length($1.$2); ### move fwd by len matched
350 47 100       255 return $line if $3; ### done, got line with "\n"
351             }
352 7 100       17 return ($line eq '') ? undef : $line; ### return undef if EOF
353             }
354              
355             ### Case 3: $/ is ref to int. Bail out.
356             elsif (ref($/)) {
357 0         0 croak '$/ given as a ref to int; currently unsupported';
358             }
359              
360             ### Case 4: $/ is either "" (paragraphs) or something weird...
361             ### Bail for now.
362             else {
363 0         0 croak '$/ as given is currently unsupported';
364             }
365             }
366              
367             #------------------------------
368              
369             =item getlines
370              
371             I
372             Get all remaining lines.
373             It will croak() if accidentally called in a scalar context.
374              
375             =cut
376              
377             sub getlines {
378 2     2 1 4 my $self = shift;
379 2 50       4 wantarray or croak("can't call getlines in scalar context!");
380 2         2 my ($line, @lines);
381 2         3 push @lines, $line while (defined($line = $self->getline));
382 2         7 @lines;
383             }
384              
385             #------------------------------
386              
387             =item print ARGS...
388              
389             I
390             Print ARGS to the underlying array.
391              
392             Currently, this always causes a "seek to the end of the array"
393             and generates a new array entry. This may change in the future.
394              
395             =cut
396              
397             sub print {
398 7     7 1 14 my $self = shift;
399 7 50       7 push @{*$self->{AR}}, join('', @_) . (defined($\) ? $\ : ""); ### add the data
  7         29  
400 7         14 $self->_setpos_to_eof;
401 7         15 1;
402             }
403              
404             #------------------------------
405              
406             =item read BUF, NBYTES, [OFFSET];
407              
408             I
409             Read some bytes from the array.
410             Returns the number of bytes actually read, 0 on end-of-file, undef on error.
411              
412             =cut
413              
414             sub read {
415 18     18 1 70 my $self = $_[0];
416             ### we must use $_[1] as a ref
417 18         16 my $n = $_[2];
418 18   50     49 my $off = $_[3] || 0;
419              
420             ### print "getline\n";
421 18         14 my $justread;
422             my $len;
423 18 50       24 ($off ? substr($_[1], $off) : $_[1]) = '';
424              
425             ### Stop when we have zero bytes to go, or when we hit EOF:
426 18         15 my @got;
427 18   100     43 until (!$n or $self->eof) {
428             ### If at end of current string, go forward to next one (won't be EOF):
429 30 100       45 if ($self->_eos) {
430 12         11 ++*$self->{Str};
431 12         13 *$self->{Pos} = 0;
432             }
433              
434             ### Get longest possible desired substring of current string:
435 30         65 $justread = substr(*$self->{AR}[*$self->{Str}], *$self->{Pos}, $n);
436 30         23 $len = length($justread);
437 30         25 push @got, $justread;
438 30         24 $n -= $len;
439 30         61 *$self->{Pos} += $len;
440             }
441 18         24 $_[1] .= join('', @got);
442 18         33 return length($_[1])-$off;
443             }
444              
445             #------------------------------
446              
447             =item write BUF, NBYTES, [OFFSET];
448              
449             I
450             Write some bytes into the array.
451              
452             =cut
453              
454             sub write {
455 0     0 1 0 my $self = $_[0];
456 0         0 my $n = $_[2];
457 0   0     0 my $off = $_[3] || 0;
458              
459 0         0 my $data = substr($_[1], $n, $off);
460 0         0 $n = length($data);
461 0         0 $self->print($data);
462 0         0 return $n;
463             }
464              
465              
466             =back
467              
468             =cut
469              
470              
471              
472             #==============================
473              
474             =head2 Seeking/telling and other attributes
475              
476             =over 4
477              
478             =cut
479              
480             #------------------------------
481              
482             =item autoflush
483              
484             I
485             No-op, provided for OO compatibility.
486              
487             =cut
488              
489 0     0 1 0 sub autoflush {}
490              
491             #------------------------------
492              
493             =item binmode
494              
495             I
496             No-op, provided for OO compatibility.
497              
498             =cut
499              
500 0     0 1 0 sub binmode {}
501              
502             #------------------------------
503              
504             =item clearerr
505              
506             I Clear the error and EOF flags. A no-op.
507              
508             =cut
509              
510 0     0 1 0 sub clearerr { 1 }
511              
512             #------------------------------
513              
514             =item eof
515              
516             I Are we at end of file?
517              
518             =cut
519              
520             sub eof {
521             ### print "checking EOF [*$self->{Str}, *$self->{Pos}]\n";
522             ### print "SR = ", $#{*$self->{AR}}, "\n";
523              
524 88 100   88 1 50 return 0 if (*{$_[0]}->{Str} < $#{*{$_[0]}->{AR}}); ### before EOA
  88         115  
  88         54  
  88         235  
525 13 50       13 return 1 if (*{$_[0]}->{Str} > $#{*{$_[0]}->{AR}}); ### after EOA
  13         17  
  13         11  
  13         21  
526             ### ### at EOA, past EOS:
527 13 50       11 ((*{$_[0]}->{Str} == $#{*{$_[0]}->{AR}}) && ($_[0]->_eos));
  13         13  
  13         7  
  13         36  
528             }
529              
530             #------------------------------
531             #
532             # _eos
533             #
534             # I Are we at end of the CURRENT string?
535             #
536             sub _eos {
537 90     90   63 (*{$_[0]}->{Pos} >= length(*{$_[0]}->{AR}[*{$_[0]}->{Str}])); ### past last char
  90         93  
  90         202  
  90         79  
538             }
539              
540             #------------------------------
541              
542             =item seek POS,WHENCE
543              
544             I
545             Seek to a given position in the stream.
546             Only a WHENCE of 0 (SEEK_SET) is supported.
547              
548             =cut
549              
550             sub seek {
551 22     22 1 1148 my ($self, $pos, $whence) = @_;
552              
553             ### Seek:
554 22 100       39 if ($whence == 0) { $self->_seek_set($pos); }
  18 100       33  
    50          
555 2         9 elsif ($whence == 1) { $self->_seek_cur($pos); }
556 2         8 elsif ($whence == 2) { $self->_seek_end($pos); }
557 0         0 else { croak "bad seek whence ($whence)" }
558 22         37 return 1;
559             }
560              
561             #------------------------------
562             #
563             # _seek_set POS
564             #
565             # Instance method, private.
566             # Seek to $pos relative to start:
567             #
568             sub _seek_set {
569 22     22   22 my ($self, $pos) = @_;
570              
571             ### Advance through array until done:
572 22         21 my $istr = 0;
573 22   33     42 while (($pos >= 0) && ($istr < scalar(@{*$self->{AR}}))) {
  44         120  
574 44 100       81 if (length(*$self->{AR}[$istr]) > $pos) { ### it's in this string!
575 22         49 return $self->setpos([$istr, $pos]);
576             }
577             else { ### it's in next string
578 22         51 $pos -= length(*$self->{AR}[$istr++]); ### move forward one string
579             }
580             }
581             ### If we reached this point, pos is at or past end; zoom to EOF:
582 0         0 return $self->_setpos_to_eof;
583             }
584              
585             #------------------------------
586             #
587             # _seek_cur POS
588             #
589             # Instance method, private.
590             # Seek to $pos relative to current position.
591             #
592             sub _seek_cur {
593 2     2   2 my ($self, $pos) = @_;
594 2         4 $self->_seek_set($self->tell + $pos);
595             }
596              
597             #------------------------------
598             #
599             # _seek_end POS
600             #
601             # Instance method, private.
602             # Seek to $pos relative to end.
603             # We actually seek relative to beginning, which is simple.
604             #
605             sub _seek_end {
606 2     2   8 my ($self, $pos) = @_;
607 2         7 $self->_seek_set($self->_tell_eof + $pos);
608             }
609              
610             #------------------------------
611              
612             =item tell
613              
614             I
615             Return the current position in the stream, as a numeric offset.
616              
617             =cut
618              
619             sub tell {
620 4     4 1 49 my $self = shift;
621 4         4 my $off = 0;
622 4         3 my ($s, $str_s);
623 4         13 for ($s = 0; $s < *$self->{Str}; $s++) { ### count all "whole" scalars
624 12 50       22 defined($str_s = *$self->{AR}[$s]) or $str_s = '';
625             ###print STDERR "COUNTING STRING $s (". length($str_s) . ")\n";
626 12         20 $off += length($str_s);
627             }
628             ###print STDERR "COUNTING POS ($self->{Pos})\n";
629 4         13 return ($off += *$self->{Pos}); ### plus the final, partial one
630             }
631              
632             #------------------------------
633             #
634             # _tell_eof
635             #
636             # Instance method, private.
637             # Get position of EOF, as a numeric offset.
638             # This is identical to the size of the stream - 1.
639             #
640             sub _tell_eof {
641 2     2   3 my $self = shift;
642 2         2 my $len = 0;
643 2         3 foreach (@{*$self->{AR}}) { $len += length($_) }
  2         7  
  13         11  
644 2         6 $len;
645             }
646              
647             #------------------------------
648              
649             =item setpos POS
650              
651             I
652             Seek to a given position in the array, using the opaque getpos() value.
653             Don't expect this to be a number.
654              
655             =cut
656              
657             sub setpos {
658 43     43 1 35 my ($self, $pos) = @_;
659 43 50       77 (ref($pos) eq 'ARRAY') or
660             die "setpos: only use a value returned by getpos!\n";
661 43         103 (*$self->{Str}, *$self->{Pos}) = @$pos;
662             }
663              
664             #------------------------------
665             #
666             # _setpos_to_eof
667             #
668             # Fast-forward to EOF.
669             #
670             sub _setpos_to_eof {
671 9     9   9 my $self = shift;
672 9         7 $self->setpos([scalar(@{*$self->{AR}}), 0]);
  9         23  
673             }
674              
675             #------------------------------
676              
677             =item getpos
678              
679             I
680             Return the current position in the array, as an opaque value.
681             Don't expect this to be a number.
682              
683             =cut
684              
685             sub getpos {
686 0     0 1 0 [*{$_[0]}->{Str}, *{$_[0]}->{Pos}];
  0         0  
  0         0  
687             }
688              
689             #------------------------------
690              
691             =item aref
692              
693             I
694             Return a reference to the underlying array.
695              
696             =cut
697              
698             sub aref {
699 2     2 1 11 *{shift()}->{AR};
  2         11  
700             }
701              
702             =back
703              
704             =cut
705              
706             #------------------------------
707             # Tied handle methods...
708             #------------------------------
709              
710             ### Conventional tiehandle interface:
711 14 100 66 14   165 sub TIEHANDLE { (defined($_[1]) && UNIVERSAL::isa($_[1],"IO::ScalarArray"))
712             ? $_[1]
713             : shift->new(@_) }
714 0     0   0 sub GETC { shift->getc(@_) }
715 10     10   137 sub PRINT { shift->print(@_) }
716 0     0   0 sub PRINTF { shift->print(sprintf(shift, @_)) }
717 0     0   0 sub READ { shift->read(@_) }
718 16 100   16   68 sub READLINE { wantarray ? shift->getlines(@_) : shift->getline(@_) }
719 0     0     sub WRITE { shift->write(@_); }
720 0     0     sub CLOSE { shift->close(@_); }
721 0     0     sub SEEK { shift->seek(@_); }
722 0     0     sub TELL { shift->tell(@_); }
723 0     0     sub EOF { shift->eof(@_); }
724 0     0     sub BINMODE { 1; }
725              
726             #------------------------------------------------------------
727              
728             1;
729             __END__