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

Oh Dispensers of Wisdom, please come to my aid

Using the neat Win32::TieRegistry module I'm reading stuff from the Windows registry. The data gets written to a file, which is later read and processed. I'm having trouble with bit-twiddling after reading the data file, because of my limited understanding of pack, unpack and perl's internal data representation. I'm just missing something here -- possibly a piece of grey matter.

We're using ActiveState perl: "This is perl, version 5.005_03 built for MSWin32-x86-object" (but will probably be moving to a newer version soon.)

For REG_DWORD items read by Win32::TieRegistry's GetValue, the value is read thus:

my ( $value, $type ) = $key->GetValue( "$_" ); # ---- format the value as text if ( $type == 4 ) # reg_dword { $value = hex( $value ) }
So for a reg_dword value 0x00500000 in the registry, the string 5242880 is written to file.

Later I want to test and set bits in the value against a mask, so presumably it needs to be packed back into four bytes. Unfortunately none of my attempts to do this work, and I can't figure out how to "see" how perl is storing the data. Please can someone who has seen the light tell me how?

maybe something like this:

$dword = pack <somehow> ( $value ); # eg $value = 5242880 dec $mask = pack <somehow> ( 4194304 ); # 0x00400000 $flags = pack <somehow> ( 1048576 ); # 0x00100000 $testdw = $dword & $mask; $newdw = $dword | $flags; $newval = hex ( $newdw ); # gets written to file
Many thanks

Replies are listed 'Best First'.
Re: how to pack bits, flags and masks
by Zaxo (Archbishop) on Nov 15, 2004 at 18:42 UTC

    You're making this too difficult. The dword is a 32-bit native integer -

    perl -e'print 0x00500000,$/ 5242880 $
    All you need to test or set bits are the bitwise operators &, |, and ^, and their assignment forms, &=, |=, and ^=.

    For example, to test if bit 7 is set in $value,

    if ($value & 1<<6) { #do something }
    The bit manipulations you show should work fine on your data as it is.

    Even if you read your number as a decimal string, it should automatically be converted to an unsigned numeric type by the bitwise operators.

    After Compline,
    Zaxo

      thanks, it does seem simpler without 'pack' and I can even count the set bits with a for loop to test each in turn.

      I'd been hoping to use this fragment from Programming Perl (ch.29.2.189. unpack):
      "The following efficiently counts the number of set bits in a bitstring:
      $setbits = unpack "%32b*", $selectmask;

      but for me

      $value = 5242880; $value = $value | 0x0011; $setbits = unpack "%32b*", $value; printf "$setbits bits are set in %#.8x\n", $value;
      gives
      25 bits are set in 0x00500011

      so my understanding is still inadequate. If you can bear to explain, it would be much appreciated (though I can use the for loop to count bits instead)

      thanks

        $setbits = unpack "%32b*", $selectmask;
        should be:
        $setbits = unpack "%32b*", $packedmask;
        so:
        $setbits = unpack('%32b*', pack('N', $selectmask));
        does the trick
        print(unpack("%32b*", pack('N', 0x00500011)), $/);  # 4

Re: how to pack bits, flags and masks
by jimbojones (Friar) on Nov 15, 2004 at 19:04 UTC
    Hi

    I'm confused. Why can't you just leave everything in Perl's internal format?

    #-- set up the vars to be like what Win32::TieRegistry returns (a stri +ng and a number); my ($dword, $type) = ( '0x00500000', 4); if ( $type == 4 ) { $dword = hex $dword; } printf "Dword:\tdec:%d\thex:%x\n", $dword, $dword; my $mask = 0x00400000; printf "mask:\tdec:%d\thex:%x\n", $mask, $mask; my $flags = 0x00100000; printf "mask:\tdec:%d\thex:%x\n", $flags, $flags; my $testdw = $dword & $mask; my $newdw = $dword | $flags; printf "TestDW:\tdec:%d\thex:%x\n", $testdw, $testdw; printf "NewDW:\tdec:%d\thex:%x\n", $newdw, $newdw;
    Results

    Dword: dec:5242880 hex:500000 mask: dec:4194304 hex:400000 mask: dec:1048576 hex:100000 TestDW: dec:4194304 hex:400000 NewDW: dec:5242880 hex:500000
    I think you still need the first $dword = hex $dword; as the return from Win32::TieRegistry is a string.

    - j