package IO::Buffer; use strict; use warnings; use Carp; ################# # Class Methods # ################# sub new { my $self = bless({}, shift); @{$self->{'queue'}} = (); #an array that will hold complete lines $self->{'fract'} = ''; #a scalar to store a fragment of a line $self->{'size'} = 1024; #default amount of bytes read per sysread() return $self; } ################## # Object Methods # ################## sub set_handle { my($self, $fh) = @_; unless(ref($fh) eq 'GLOB') { carp "argument to \'new\' must be a filehandle"; return; } else { $self->{'fh'} = $fh; return 1; } } sub set_size { my($self, $size) = @_; unless($size =~ /^\d+$/) { carp "argument to \'set_size\' must be integer"; return; } else { $self->{'size'} = $size; return 1; } } sub sysreadline { my $self = shift; my($chunk, @lines); #if there still are lines in the queue, simply shift one off and return it if(@{$self->{'queue'}}) { return shift(@{$self->{'queue'}}); } #otherwise attempt to perform a sysread on the filehandle and process the data # if the read fails it will return 0 for end of file or undef for a read error # while setting $! to the reason for the error unless(my $read = sysread($self->{'fh'}, $chunk, $self->{'size'})) { return $read; } #the sysread was succesful, so let's move on @lines = split(/\r\n|\r|\n/, $chunk); #if the first character of $chunk is a newline, then what we currently have listed as a broken line # from the last read is in fact a regular full line, so we can go ahead and add it to the queue. if(($chunk =~ /^[\r\n|\r|\n]/) && ($self->{'fract'})) { push(@{$self->{'queue'}}, $self->{'fract'}); $self->{'fract'} = ''; } #otherwise if we still have a broken line sitting around, merge it with the first line of @lines # and then add it to the queue if($self->{'fract'}) { push(@{$self->{'queue'}}, $self->{'fract'} . shift(@lines)); $self->{'fract'} = ''; } #if the last character of $chunk is a newline, then there is no actual broken line in this batch, # so we can simply process all the lines in order. otherwise make sure the last line in $chunk is # treated as a fragment unless($chunk =~ /[\r\n|\r|\n]$/) { $self->{'fract'} = pop(@lines); } #now simply push all of @lines into the queue push(@{$self->{'queue'}}, @lines); #and last but not least, return the first line of the queue return(shift(@{$self->{'queue'}})); } 1;