develooper Front page | perl.perl5.porters | Postings from December 2014

Re: All I want for Christmas is: Destructive unpack()

Thread Previous
From:
H.Merijn Brand
Date:
December 25, 2014 15:41
Subject:
Re: All I want for Christmas is: Destructive unpack()
Message ID:
20141225164036.2bbbb720@pc09.procura.nl
On Thu, 25 Dec 2014 15:03:15 +0000, "Paul \"LeoNerd\" Evans"
<leonerd@leonerd.org.uk> wrote:

> The pack() function is great for really easily building strings, or
> outputting to files, binary structured data:
> 
>   sub strpack_elem
>   {
>     my $self = shift;
>     my ( $key, $value ) = @_;
>     $self->{str} .= pack( "N/a* N/a*", $key, $value );
>   }
> 
>   sub fpack_elem
>   {
>     my $self = shift;
>     my ( $key, $value ) = @_;
>     $self->print( pack( "N/a* N/a*", $key, $value ) );
>   }
> 
> This works great because the string-contact operator and $fh->print
> operation both append new data to the output, and advances the position
> so the next append operation will work correctly.
> 
> The same however, sadly cannot be said of unpack(). All that unpack()
> can do is return values by looking at the string of input you give it.
> That string MUST be long enough, or it fails in non-obvious ways [1].
> Further, unpack gives no hint on how many bytes of input it wanted to
> consume, requiring any of several additional hacks to get that length
> somehow. About the neatest I could write the converse operations to
> theabove is

I want unpack on streams!

See for a great example http://www.perlmonks.org/?node_id=1104462

ikegami's answer:
--8<---
my $template = "iifff";
my $rec_size = template_len ($template); # not shown
while (1) {
   my $rv = read ($fh, my $rec, $rec_size);
   defined $rv     or  die "$!\n";
   $rv             or  last;
   $rv < $rec_size and die "Premature EOF\n";

   my @fields = unpack $template, $rec;
   ...
   }
-->8---

could be shortened to

while (my @fields = unpack "iifff" => $fh) {
   ...
   }

>   sub strunpack_elem
>   {
>     my $self = shift;
>     length $self->{str} >= 4 or die "Need more";
>     my ( $keylen ) = unpack( "N", $self->{str} );
>     length $self->{str} >= 8 + $keylen or die "Need more";
>     my ( $vallen ) = unpack( "N", substr $self->{str}, 4 + $keylen, 4 );
>     length $self->{str} >= 8 + $keylen + $vallen or die "Need more";
> 
>     # Now committed
>     substr( $self->{str}, 0, 4, "" ); # keylen
>     my $key = substr( $self->{str}, 0, $keylen, "" );
>     substr( $self->{str}, 0, 4, "" ); # vallen
>     my $val = substr( $self->{str}, 0, $vallen, "" );
> 
>     return ( $key, $val );
>   }
> 
>   sub funpack_elem
>   {
>     my $self = shift;
>     my $buf = $self->read( 4 );
>     my ( $keylen ) = unpack( "N", $buf );
>     my $key = $self->read( $keylen );
> 
>     $buf = $self->read( 4 );
>     my ( $vallen ) = unpack( "N", $buf );
>     my $val = $self->read( $vallen );
> 
>     return ( $key, $val );
>   }
> 
> I think we can all agree that the latter functions are much more
> horrible and less readable than the pack versions - these make the
> logic much harder to see, for all the machinery noise in the middle. 
> 
> I wrote a C library called "libpack", somewhat based on Perl's
> pack/unpack functions [2]. This library has a bunch of destructive
> unpack functions which consume bytes of the input string or filehandle
> as they go, meaning the equivalents of the above are much neater to
> write. It would be nice to have some of the neatness of that, in Perl
> as well.
> 
> -----
> 
> In summary:
> 
>   I'd like Perl to provide destructive unpack()-like functions to work
>   on a string buffer or a filehandle, atomically reading and consuming
>   input data as they go, to either return the full result or take no
>   action.
> 
> -----
> 
> [1]: consider the following:
> 
>   eval: [ unpack "N", "abcd" ]
>   [ '1633837924' ]                   # OK
> 
>   eval: [ unpack "N", "abc" ]
>   [  ]                               # Fair enough
> 
>   eval: [ unpack "N n", "abc" ]
>   [ '24930' ]                        # Uhhmm....
> 
>   eval: [ unpack "N a2", "abc" ]
>   [ 'ab' ]                           # Go home unpack(), you are drunk
> 
> 
> [2]: http://www.leonerd.org.uk/code/libpack/doc.html
> 
> 


-- 
H.Merijn Brand  http://tux.nl   Perl Monger  http://amsterdam.pm.org/
using perl5.00307 .. 5.21   porting perl5 on HP-UX, AIX, and openSUSE
http://mirrors.develooper.com/hpux/        http://www.test-smoke.org/
http://qa.perl.org   http://www.goldmark.org/jeff/stupid-disclaimers/

Thread Previous


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About