Search code examples
phpperlpack

Perl pack() function and "B" format character to Php


What does the "B" do in this pack statement from Perl code?

$hce_hash=pack('B*', $hce_hash);

Is there an equivalent function in PHP?


Solution

  • PHP’s pack doesn’t support a format of B*, but it does support H*. In Perl, you could emulate it with

    sub pack_Bstar {
      my($bits) = @_;
      my $Hstar;
    
      my $nybble = 0;
      for (my $i = 0; $i < length $bits; ++$i) {
        $nybble *= 2;
        $nybble += int substr($bits, $i, 1);
        if ($i % 4 == 3) {
          $Hstar .= sprintf "%x", $nybble;
          $nybble = 0;
        }
      }
    
      my $pad = 4 - length($bits) % 4;
      if ($pad != 4) {
        $nybble = ($nybble << $pad);
        $Hstar .= sprintf "%x", $nybble;
      }
    
      pack "H*", $Hstar;
    }
    

    The code above is not idiomatic Perl, but translation to PHP should be straightforward.

    The H* format wants a hex string with high nybble (4 bits) first. The code above chews off four bits at a time to compute each nybble value. For example, for a bit string of 1011, tracing the algorithm gives

    1. nybble = 0
    2. nybble = 2 * 0 + 1 = 1
    3. nybble = 2 * 1 + 0 = 2
    4. nybble = 2 * 2 + 1 = 5
    5. nybble = 2 * 5 + 1 = 11

    10112 is indeed 1110, which is b16. If the last nybble is incomplete (between one and three bits), we left-shift the bit the appropriate number of places. This has the effect of zero-padding on the right.

    Tests:

    my @tests = (
      ["01001010011101010111001101110100"                               => "Just"],
      ["0110000101101110011011110111010001101000011001010111001"        => "another"],
      ["01010000010010000101000000101111010100000110010101110010011011" => "PHP/Perl"],
      ["01101000011000010110001101101011011001010111001000101100"       => "hacker,"],
    );
    
    for (@tests) {
      my($input,$expect) = @$_;
      my $got = pack_Bstar $input;
      print "$input: ", ($got eq $expect ? "PASS" : "FAIL"), " ($got)\n";
    }
    

    Output:

    01001010011101010111001101110100: PASS (Just)
    0110000101101110011011110111010001101000011001010111001: PASS (another)
    01010000010010000101000000101111010100000110010101110010011011: PASS (PHP/Perl)
    01101000011000010110001101101011011001010111001000101100: PASS (hacker,)