Article 3459 of comp.lang.perl: Xref: feenix.metronet.com comp.lang.perl:3459 Newsgroups: comp.lang.perl Path: feenix.metronet.com!news.utdallas.edu!tamsun.tamu.edu!cs.utexas.edu!swrinde!elroy.jpl.nasa.gov!ufo!hobbs!dnoble From: dnoble@devvax.jpl.nasa.gov (David Noble) Subject: Re: How to pack/unpack bits? Message-ID: <1993Jun16.164149.26542@jpl-devvax.jpl.nasa.gov> Originator: dnoble@hobbs Sender: usenet@jpl-devvax.jpl.nasa.gov (For NNTP so rrn will be able to post) Nntp-Posting-Host: hobbs Organization: Jet Propulsion Laboratory (NASA) References: <1993Jun16.140220.6372@prl.philips.nl> Date: Wed, 16 Jun 1993 16:41:49 GMT Lines: 212 niekerk@prl.philips.nl (Paul van Niekerk) writes: > it seems that all bit fields start at byte boundaries. yep > Can the above structure be handled by pack/unpack? nope The following routines will give you the pack/unpack behavior you want, as long as each collection of bitfields fits evenly into an 8, 16, or 32-bit block. I've never really had any use for templates using 'b' (lsb first), so it may break on them. I always use 'B' (msb first). Somebody let me know if there's an easy way to do this. This algorithm was coded for "programmer efficiency" according to the camel book definition, meaning 'use whatever you think of first'. Hope this helps... David Noble (dnoble@devvax.jpl.nasa.gov) ----SNIP----SNIP----SNIP----SNIP----SNIP----SNIP----SNIP----SNIP----SNIP---- ;# USAGE ;# $data = &pack('B3B5nAA', @fields); ;# @fields = &unpack('B3B5nAA', $data); sub pack { local ($template) = shift(@_); local (@data) = @_; local ($_); local ($type, $len); local ($new_tmplt); local ($new_field); local ($current_bits); local ($wrapup); local (@tmp_data); # see if the template has any bitfields (they make life difficult) if ($template =~ m/[Bb]/) { $_ = $template; # check each field, in order while (($type, $len) = m/([A-Za-z])([0-9*]*)/) { s/[A-Za-z][0-9*]*//; # see if this is one of those nasty bitfields if (($type eq 'b') || ($type eq 'B')) { if (!$len) { $len = 1; } # accept 'B' as 'B1' $current_bits += $len; $new_field .= shift(@data); if (($current_bits == 8) || ($current_bits == 16) || ($current_bits == 32)) { $new_tmplt .= $type . $current_bits; push(@tmp_data, $new_field); $current_bits = 0; $new_field = ''; } elsif ($current_bits > 32) { die 'bitfields must at least be aligned on 32-bit words, stopped'; } } # end of processing bit fields elsif ($current_bits) { die 'fields must at least be aligned on 32-bit words, stopped'; } # this was not a bitfield, so it gets passed through to the new template else { push(@tmp_data, shift(@data)); $new_tmplt .= $type . $len; } } pack($new_tmplt, @tmp_data); } # this is how easy it is without bitfields else { pack($template, @data); } } sub unpack { local ($template) = shift(@_); local ($data) = shift(@_); local ($_); local ($i); local ($type, $len); local ($pos); local ($new_tmplt); local ($wrapup); local ($current_bits); local ($next_bitfield); local ($field_with_bits); local ($bits_left); local ($tmp_field); local (@bitfields, @tmp_fields, @unfinished, @return); # see if the template has any bitfields (they make life difficult) if ($template =~ m/[Bb]/) { $_ = $template; # check each field, in order while (($type, $len) = m/([A-Za-z])([0-9*]*)/) { s/[A-Za-z][0-9*]*//; # see if this is one of those nasty bitfields if (($type eq 'b') || ($type eq 'B')) { if (!$len) { $len = 1; } # accept 'B' as 'B1' push(@tmp_fields, $len); # keep track of the number of bits # append this to any previous adjacent bitfield that is unaligned $current_bits += $len; if ($current_bits == 8) { $new_tmplt .= 'C'; $wrapup = 1; } elsif ($current_bits == 16) { $new_tmplt .= 'n'; $wrapup = 1; } elsif ($current_bits == 32) { $new_tmplt .= 'N'; $wrapup = 1; } elsif ($current_bits > 32) { die 'bitfields must at least be aligned on 32-bit words, stopped'; } # # Store the location of the collection of bitfields, # the aggregate size of the contiguous bitfields, and # IN REVERSE ORDER the size of each bitfield. # # This reverse order makes it a _lot_ easier to extract # the individual bitfields from the conglomerate. But, # it does make another layer of reversing necessary to # have the bitfields end up in the right order in the # final returned list. # if ($wrapup) { push(@bitfields, $pos); push(@bitfields, $current_bits); while (@tmp_fields) { push(@bitfields, pop(@tmp_fields)); # note the backwards order } ++$pos; $current_bits = 0; $wrapup = 0; } } # end of processing bit fields elsif ($current_bits) { die 'fields must at least be aligned on 32-bit words, stopped'; } # this was not a bitfield, so it gets passed through to the new template else { ++$pos; $new_tmplt .= $type . $len; } } # # the new template has been constructed, so unpack the structure # @unfinished = unpack($new_tmplt, $data); # # now find the bitfields and put them into the return list # $pos = 0; $next_bitfield = shift(@bitfields); while (@unfinished) { if ($pos == $next_bitfield) { $field_with_bits = shift(@unfinished); $bits_left = shift(@bitfields); while ($bits_left) { $len = shift(@bitfields); for ($tmp_field = '', $i = 0; $i < $len; ++$i) { $tmp_field = (($field_with_bits & 1) ? '1' : '0') . $tmp_field; $field_with_bits = $field_with_bits >> 1; } push(@tmp_fields, $tmp_field); $bits_left -= $len; } while (@tmp_fields) { push(@return, pop(@tmp_fields)); } $next_bitfield = shift(@bitfields); } else { push(@return, shift(@unfinished)); ++$pos; } } @return; } # this is how easy it is without bitfields else { unpack($template, $data); } } 1; Article 3465 of comp.lang.perl: Xref: feenix.metronet.com comp.lang.perl:3465 Path: feenix.metronet.com!news.ecn.bgu.edu!mp.cs.niu.edu!ux1.cso.uiuc.edu!uwm.edu!cs.utexas.edu!utnut!utcsri!csri.toronto.edu!acs Newsgroups: comp.lang.perl From: acs@csri.toronto.edu (Alvin Chia-Hua Shih) Subject: Re: How to pack/unpack bits? Message-ID: <1993Jun16.133329.8754@jarvis.csri.toronto.edu> References: <1993Jun16.140220.6372@prl.philips.nl> Date: 16 Jun 93 17:33:30 GMT Lines: 19 In <1993Jun16.140220.6372@prl.philips.nl> niekerk@prl.philips.nl (Paul van Niekerk) writes: [ structure diagram elided... ] >it seems that all bit fields start at byte boundaries. I can't find any >examples of packing/unpacking bits in the Camel book. Can the above >structure be handled by pack/unpack? How? Nope. Pack and unpack are for byte-sized thingies or bigger. What you need is the vec() operator. Have a look at the entry in the manpage. The thing to note is that a vec() can be an lvalue. ACS -- ___ ___ ___ ______________________________________________________________ | | | __| Democracy is not a way of getting better solutions. | | - | --|__ | It's just a way to spread the blame. | |_|_|___|___|______________________________________________________________| Alvin_C._Shih____________________acs@csri.utoronto.ca______________________| Hi there, your perl archive has under the "Assorted" scripts the unpack_bitfields.pl originally written by David Noble. I have modified his code as it had some idiosyncracies(sp?) depending on his type of application. I have generally cleaned it up and made it work with real binary data (his unpack version required the data to already be textualised to ASCII 1 and 0, while his pack worked with real binary).. David gave me permission to redistribute this. This will (hopefully) also be part of the nocol distribution, as I used it to write my ntpmon monitor... Maybe you could add it to the archive. Thx here it is: ;# unpack-bitfields.pl ;# original by David Noble ;# changes for real binary date in unpack plus minor changes ;# by Mathias Koerber ;# USAGE ;# $data = &bitpack('B3B5nAA', @fields); ;# @fields = &bitunpack('B3B5nAA', $data); sub bitpack { local ($template) = shift(@_); local (@data) = @_; local ($_); local ($type, $len); local ($new_tmplt); local ($new_field); local ($current_bits); local ($wrapup); local (@tmp_data); # see if the template has any bitfields (they make life difficult) if ($template =~ m/[Bb]/) { $_ = $template; # check each field, in order while (($type, $len) = m/([A-Za-z])([0-9*]*)/) { s/[A-Za-z][0-9*]*//; # see if this is one of those nasty bitfields if (($type eq 'b') || ($type eq 'B')) { if (!$len) { $len = 1; } # accept 'B' as 'B1' $current_bits += $len; $new_field .= shift(@data); if (($current_bits == 8) || ($current_bits == 16) || ($current_bits == 32)) { if ($current_bits == 8) { $new_tmplt .= 'C'; }; if ($current_bits == 16) { $new_tmplt .= 'I'; }; if ($current_bits == 32) { $new_tmplt .= 'L'; }; push(@tmp_data, oct($new_field)); $current_bits = 0; $new_field = ''; } elsif ($current_bits > 32) { die 'bitfields must at least be aligned on 32-bit words, stopped'; } } # end of processing bit fields elsif ($current_bits) { die 'fields must at least be aligned on 32-bit words, stopped'; } # this was not a bitfield, so it gets passed through to the new template else { push(@tmp_data, shift(@data)); $new_tmplt .= $type . $len; } } pack($new_tmplt, @tmp_data); } # this is how easy it is without bitfields else { pack($template, @data); } } sub bitunpack { local ($template) = shift(@_); local ($data) = shift(@_); local ($_); local ($i); local ($type, $len); local ($pos); local ($new_tmplt); local ($wrapup); local ($current_bits); local ($next_bitfield); local ($field_with_bits); local ($bits_left); local ($tmp_field); local (@bitfields, @tmp_fields, @unfinished, @return); # see if the template has any bitfields (they make life difficult) if ($template =~ m/[Bb]/) { $_ = $template; # check each field, in order while (($type, $len) = m/([A-Za-z])([0-9*]*)/) { s/[A-Za-z][0-9*]*//; # see if this is one of those nasty bitfields if (($type eq 'b') || ($type eq 'B')) { if (!$len) { $len = 1; } # accept 'B' as 'B1' push(@tmp_fields, $len); # keep track of the number of bits # append this to any previous adjacent bitfield that is unaligned $current_bits += $len; if ($current_bits == 8) { $new_tmplt .= 'C'; $wrapup = 1; } elsif ($current_bits == 16) { $new_tmplt .= 'n'; $wrapup = 1; } elsif ($current_bits == 32) { $new_tmplt .= 'N'; $wrapup = 1; } elsif ($current_bits > 32) { die 'bitfields must at least be aligned on 32-bit words, stopped'; } # # Store the location of the collection of bitfields, # the aggregate size of the contiguous bitfields, and # IN REVERSE ORDER the size of each bitfield. # # This reverse order makes it a _lot_ easier to extract # the individual bitfields from the conglomerate. But, # it does make another layer of reversing necessary to # have the bitfields end up in the right order in the # final returned list. # if ($wrapup) { push(@bitfields, $pos); push(@bitfields, $current_bits); while (@tmp_fields) { push(@bitfields, pop(@tmp_fields)); # note the backwards order } ++$pos; $current_bits = 0; $wrapup = 0; } } # end of processing bit fields elsif ($current_bits) { die 'fields must at least be aligned on 32-bit words, stopped'; } # this was not a bitfield, so it gets passed through to the new template else { ++$pos; $new_tmplt .= $type . $len; } } # # the new template has been constructed, so unpack the structure # @unfinished = unpack($new_tmplt, $data); # # now find the bitfields and put them into the return list # $pos = 0; $next_bitfield = shift(@bitfields); while (@unfinished) { if ($pos == $next_bitfield) { $field_with_bits = shift(@unfinished); $bits_left = shift(@bitfields); while ($bits_left) { $len = shift(@bitfields); # for ($tmp_field = '', $i = 0; $i < $len; ++$i) { # $tmp_field = (($field_with_bits & 1) ? '1' : '0') . $tmp_field; # $tmp_field += ($field_with_bits & 1); # $tmp_field >> 1; # $field_with_bits = $field_with_bits >> 1; # } $tmp_field = ($field_with_bits & ((2**$len)-1)); $field_with_bits = $field_with_bits >> $len; # $tmp_field << 1; # correct for the last shift push(@tmp_fields, $tmp_field); $bits_left -= $len; } while (@tmp_fields) { push(@return, pop(@tmp_fields)); } $next_bitfield = shift(@bitfields); $pos++; } else { push(@return, shift(@unfinished)); ++$pos; } } @return; } # this is how easy it is without bitfields else { unpack($template, $data); } } 1; -- Mathias Koerber at SWi Tel: +65 / 7780066 x 29 14 Science Park Drive Fax: +65 / 7779401 #04-01 The Maxwell email: Mathias.Koerber@SWi.com.sg Singapore Science Park S'pore 0511 MK * Eifersucht ist eine Leidenschaft, die mit Eifer sucht, was Leiden schafft *