Unpack by bits in PHP

前端 未结 1 474
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-01-26 19:09

I want to unpack a binary string into into an array by a weird sequence of 8-8-8-7 bits.

I could easily do something like this, for a normal 8-8-8-8 sequence:

         


        
相关标签:
1条回答
  • 2021-01-26 19:39

    Not sure if I completely understand, but if you have packed data in an unaligned / unpadded format, you will want to use some sort of bitstream.

    Here's a simple class that does that. Ideally it would be some sort of iterator that accepts a resource stream, but showing how to do it via a string directly is simpler:

    class BitStream
    {
      private $data, $byte, $byteCount, $bytePos, $bitPos;
      private $mask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80];
    
      public function __construct($data)
      {
        $this->data = $data;
        $this->byteCount = strlen($data);
        $this->bytePos = 0;
        $this->bitPos = 7;
    
        $this->byte = $this->byteCount ? ord($data[0]) : null;
      }
    
      // reads and returns 1 bit. null on no more bits
      public function readBit()
      {
        if ($this->byte === null) return null;
    
        // get current bit
        $bit = ($this->byte & $this->mask[$this->bitPos]) >> $this->bitPos;
    
        if (--$this->bitPos == -1)
        {
          // advance to next byte 
          $this->bitPos = 7;
          $this->bytePos++;
          $this->byte = $this->bytePos < $this->byteCount ? ord($this->data[$this->bytePos]) : null;
        }
    
        return $bit;
      }
    
      // reads up to $n bits, where 0 < $n < bit length of max int
      // returns null if not enough bits left
      public function readBits($n)
      {
        $val = 0;
        while ($n--)
        {
          $bit = $this->readBit();
          if ($bit === null) return null;      
    
          $val = ($val << 1) | $bit;
        }
    
        return $val;
      }
    }
    

    Then to use it:

    $bs = new BitStream($data);
    
    $out = [];
    while (true)
    {
      $a = $bs->readBits(8);
      $b = $bs->readBits(8);
      $c = $bs->readBits(8);
      $d = $bs->readBits(7);
    
      if ($d === null) break; // ran out of data
    
      $out[] = [$a, $b, $c, $d];
    }
    

    The readBits() function would be faster if it were optimized to read up to 8 bits at a time, but it's much simpler to understand as-is.

    0 讨论(0)
提交回复
热议问题