package FileCook; $VERSION = '1.1'; use FileHandle; use Exporter; use strict; our @ISA = qw(Exporter); our @EXPORT=qw(WriteStruct ReadStruct); # NB: filehandles must be binmode! # Writes structure to filehandle. Returns number of bytes written. sub WriteStruct { my ($filehandle, $record) = @_; $filehandle or return undef; $record or return undef; my $output = &StructToString( $record ); if( defined( $output ) ) { $output = &Write32( length $output ) . $output; print $filehandle $output; return length $output; } return 0; } # Reads structure from filehandle and returns it. sub ReadStruct { my ($filehandle) = @_; my $record; $filehandle or return undef; read( $filehandle, $record, 4 ) or return undef; $record = &Read32( $record ); read( $filehandle, $record, $record ); my ($result, $count) = &StringToStruct( $record ); return $result; } # ----------------------- sub Write32 { (my $i) = @_; return (chr(($i)&0xFF). chr(($i>>8)&0xFF). chr(($i>>16)&0xFF). chr(($i>>24)&0xFF)); } sub Read32 { (my $c) = @_; return unpack("V", $c); } sub StructToString { (my $s)=@_; my $r = ''; if (ref($s) eq 'ARRAY') { $r .= '['; foreach my $item (@$s) { $r.=&StructToString($item); } $r .= ']'; }elsif (ref($s) eq 'HASH') { my @keys = keys %$s ; $r.='{'; foreach my $key (@keys) { $r.=&StructToString($key); $r.=&StructToString($s->{$key}); } $r.='}'; } else { $r.='$' . &Write32(length ($s)) . $s; } return $r; } sub StringToStruct { (my $s)=@_; my $r = substr($s, 0, 1); my $count; length($s) or return undef; if ($r eq '[') { my @a = ( ); my $i = 1; while(($i < length($s)) && (substr($s,$i,1) ne ']')) { my ($t, $c) = &StringToStruct(substr($s, $i)); push @a, $t; $i += $c; } $count = $i+1; $r = \@a; } elsif ($r eq '{') { my @a = ( ); my %b; my $i = 1; while(($i < length($s)) && (substr($s,$i,1) ne '}')) { my ($t, $c) = &StringToStruct(substr($s, $i)); push @a, $t; $i += $c; } $count = $i+1; %b = @a; $r = \%b; }elsif ($r eq '$') { $count = &Read32(substr($s, 1, 4)); $r = substr($s, 5, $count); $count += 5; } else { return undef; } return ($r, $count); } 1;