* @copyright 2015 Smiley * @license MIT */ namespace WCPOS\Vendor\chillerlan\QRCode\Common; use WCPOS\Vendor\chillerlan\QRCode\QRCodeException; use function count, floor, min; /** * Holds the raw binary data */ final class BitBuffer { /** * The buffer content * * @var int[] */ private array $buffer; /** * Length of the content (bits) */ private int $length; /** * Read count (bytes) */ private int $bytesRead = 0; /** * Read count (bits) */ private int $bitsRead = 0; /** * BitBuffer constructor. * * @param int[] $bytes */ public function __construct(array $bytes = []) { $this->buffer = $bytes; $this->length = count($this->buffer); } /** * appends a sequence of bits */ public function put(int $bits, int $length) : self { for ($i = 0; $i < $length; $i++) { $this->putBit(($bits >> $length - $i - 1 & 1) === 1); } return $this; } /** * appends a single bit */ public function putBit(bool $bit) : self { $bufIndex = (int) floor($this->length / 8); if (count($this->buffer) <= $bufIndex) { $this->buffer[] = 0; } if ($bit === \true) { $this->buffer[$bufIndex] |= 0x80 >> $this->length % 8; } $this->length++; return $this; } /** * returns the current buffer length */ public function getLength() : int { return $this->length; } /** * returns the buffer content * * to debug: array_map(fn($v) => sprintf('%08b', $v), $bitBuffer->getBuffer()) */ public function getBuffer() : array { return $this->buffer; } /** * @return int number of bits that can be read successfully */ public function available() : int { return 8 * ($this->length - $this->bytesRead) - $this->bitsRead; } /** * @author Sean Owen, ZXing * * @param int $numBits number of bits to read * * @return int representing the bits read. The bits will appear as the least-significant bits of the int * @throws \chillerlan\QRCode\QRCodeException if numBits isn't in [1,32] or more than is available */ public function read(int $numBits) : int { if ($numBits < 1 || $numBits > $this->available()) { throw new QRCodeException('invalid $numBits: ' . $numBits); } $result = 0; // First, read remainder from current byte if ($this->bitsRead > 0) { $bitsLeft = 8 - $this->bitsRead; $toRead = min($numBits, $bitsLeft); $bitsToNotRead = $bitsLeft - $toRead; $mask = 0xff >> 8 - $toRead << $bitsToNotRead; $result = ($this->buffer[$this->bytesRead] & $mask) >> $bitsToNotRead; $numBits -= $toRead; $this->bitsRead += $toRead; if ($this->bitsRead === 8) { $this->bitsRead = 0; $this->bytesRead++; } } // Next read whole bytes if ($numBits > 0) { while ($numBits >= 8) { $result = $result << 8 | $this->buffer[$this->bytesRead] & 0xff; $this->bytesRead++; $numBits -= 8; } // Finally read a partial byte if ($numBits > 0) { $bitsToNotRead = 8 - $numBits; $mask = 0xff >> $bitsToNotRead << $bitsToNotRead; $result = $result << $numBits | ($this->buffer[$this->bytesRead] & $mask) >> $bitsToNotRead; $this->bitsRead += $numBits; } } return $result; } /** * Clears the buffer and resets the stats */ public function clear() : self { $this->buffer = []; $this->length = 0; return $this->rewind(); } /** * Resets the read-counters */ public function rewind() : self { $this->bytesRead = 0; $this->bitsRead = 0; return $this; } }