Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

OK, I admit it... pack and unpack confuse me... each time I think I have them figured out, I realize I really don't. Are there any resources (other then perldoc -f unpack) with some more advanced examples?

The particular problem I'm having at the moment is unpacking a C struct like this:

struct { unsigned long var1:16, var2:16; unsigned long var3:24, var4:8; unsigned long var5:6, var6:26; };

The first 2, I got (I think), the second 2 I THOUGHT I got, but was wrong.. the last 2... well, frankly I'm just not too sure. Here's what I tried:

$fh->read($buf, 12); @var = unpack("s s C3 C b6 b26", $buf);

Now, $var[0] and $var[1] end up as 2 byte shorts (as I expected), $var[2] ends up as 1 byte, not the 3 bytes I was expecting... it looks like the 2nd and 3rd bytes are put into $var[3] and $var[4]... which of course throws the rest off track.

How do I grab 3 bytes and put that into a single var, not split into 3 separate vars?

With regards to the "b6" and "b26"... well... I don't really want the value returned as binary data... I just want a 6 bit integer and a 26 bit integer respectively. How do I do that?

Replies are listed 'Best First'.
Re: Unpacking struct
by Zaxo (Archbishop) on Apr 01, 2005 at 03:26 UTC

    Bitwise operations may be your best choice. There are no 24-, 6-, or 26-bit types in unpack's formats, and vec demands its chunks be a power of two bits wide and aligned. Here is one way to do it, unpacking to three ints and doing the bit operations.

    $fh->read(my $buf, 12); my ($var1, $var2, @raw) = unpack 's s L2' $buf; my ($var3,$var4) = map { ($_ >> 8, $_ & 0xff) } $raw[0]; my ($var5,$var6) = map { ($_ >> 26, $_ & 0x3fffffff) } $raw[1];
    That's not quite as unportable as it looks, but it's still on the edge.

    Update: Differs from Stevie-O's by bit order of the C struct. I'm not sure which is correct, so I'd bet on his. Adjust to what works :-)

    After Compline,
    Zaxo

Re: Unpacking struct
by Stevie-O (Friar) on Apr 01, 2005 at 03:09 UTC
    pack and unpack can handle very many things, but bitfields are not one of them. You'll have to do some bit-level manipulation to extract the values you want; I would recommend you do this:
    $fh->read($buf, 12); # note that your original code used 's', which is signed, # but your struct declaration says unsigned! @var = unpack('S S L L', $buf); my ($var1, $var2) = ($var[0], $var[1]); my $var3 = $var[2] & 0xFFFFFF; my $var4 = $var[2] >> 24; my $var5 = $var[3] & 0x3F; my $var6 = $var[3] >> 6;
    --Stevie-O
    $"=$,,$_=q>|\p4<6 8p<M/_|<('=> .q>.<4-KI<l|2$<6%s!<qn#F<>;$, .=pack'N*',"@{[unpack'C*',$_] }"for split/</;$_=$,,y[A-Z a-z] {}cd;print lc
Re: Unpacking struct
by Random_Walk (Prior) on Apr 01, 2005 at 10:04 UTC
Re: Unpacking struct
by ikegami (Patriarch) on Apr 01, 2005 at 02:46 UTC
    Are those bit fields? Have you looked into vec instead of unpack?