|
|
<?php |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace Zxing\Common; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final class BitArray |
|
|
{ |
|
|
|
|
|
private $bits; |
|
|
private $size; |
|
|
|
|
|
|
|
|
public function __construct($bits = [], $size = 0) |
|
|
{ |
|
|
|
|
|
if (!$bits && !$size) { |
|
|
$this->$size = 0; |
|
|
$this->bits = []; |
|
|
} elseif ($bits && !$size) { |
|
|
$this->size = $bits; |
|
|
$this->bits = $this->makeArray($bits); |
|
|
} else { |
|
|
$this->bits = $bits; |
|
|
$this->size = $size; |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
private static function makeArray($size) |
|
|
{ |
|
|
return []; |
|
|
} |
|
|
|
|
|
public function getSize() |
|
|
{ |
|
|
return $this->size; |
|
|
} |
|
|
|
|
|
public function getSizeInBytes() |
|
|
{ |
|
|
return ($this->size + 7) / 8; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function set($i) |
|
|
{ |
|
|
$this->bits[(int)($i / 32)] |= 1 << ($i & 0x1F); |
|
|
$this->bits[(int)($i / 32)] = ($this->bits[(int)($i / 32)]); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function flip($i) |
|
|
{ |
|
|
$this->bits[(int)($i / 32)] ^= 1 << ($i & 0x1F); |
|
|
$this->bits[(int)($i / 32)] = ($this->bits[(int)($i / 32)]); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function getNextSet($from) |
|
|
{ |
|
|
if ($from >= $this->size) { |
|
|
return $this->size; |
|
|
} |
|
|
$bitsOffset = (int)($from / 32); |
|
|
$currentBits = (int)$this->bits[$bitsOffset]; |
|
|
|
|
|
$currentBits &= ~((1 << ($from & 0x1F)) - 1); |
|
|
while ($currentBits == 0) { |
|
|
if (++$bitsOffset == count($this->bits)) { |
|
|
return $this->size; |
|
|
} |
|
|
$currentBits = $this->bits[$bitsOffset]; |
|
|
} |
|
|
$result = ($bitsOffset * 32) + numberOfTrailingZeros($currentBits); |
|
|
|
|
|
return $result > $this->size ? $this->size : $result; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function getNextUnset($from) |
|
|
{ |
|
|
if ($from >= $this->size) { |
|
|
return $this->size; |
|
|
} |
|
|
$bitsOffset = (int)($from / 32); |
|
|
$currentBits = ~$this->bits[$bitsOffset]; |
|
|
|
|
|
$currentBits &= ~((1 << ($from & 0x1F)) - 1); |
|
|
while ($currentBits == 0) { |
|
|
if (++$bitsOffset == count($this->bits)) { |
|
|
return $this->size; |
|
|
} |
|
|
$currentBits = (~$this->bits[$bitsOffset]); |
|
|
} |
|
|
$result = ($bitsOffset * 32) + numberOfTrailingZeros($currentBits); |
|
|
|
|
|
return $result > $this->size ? $this->size : $result; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function setBulk($i, $newBits) |
|
|
{ |
|
|
$this->bits[(int)($i / 32)] = $newBits; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function setRange($start, $end) |
|
|
{ |
|
|
if ($end < $start) { |
|
|
throw new \InvalidArgumentException(); |
|
|
} |
|
|
if ($end == $start) { |
|
|
return; |
|
|
} |
|
|
$end--; |
|
|
$firstInt = (int)($start / 32); |
|
|
$lastInt = (int)($end / 32); |
|
|
for ($i = $firstInt; $i <= $lastInt; $i++) { |
|
|
$firstBit = $i > $firstInt ? 0 : $start & 0x1F; |
|
|
$lastBit = $i < $lastInt ? 31 : $end & 0x1F; |
|
|
$mask = 0; |
|
|
if ($firstBit == 0 && $lastBit == 31) { |
|
|
$mask = -1; |
|
|
} else { |
|
|
$mask = 0; |
|
|
for ($j = $firstBit; $j <= $lastBit; $j++) { |
|
|
$mask |= 1 << $j; |
|
|
} |
|
|
} |
|
|
$this->bits[$i] = ($this->bits[$i] | $mask); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function clear() |
|
|
{ |
|
|
$max = count($this->bits); |
|
|
for ($i = 0; $i < $max; $i++) { |
|
|
$this->bits[$i] = 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function isRange($start, $end, $value) |
|
|
{ |
|
|
if ($end < $start) { |
|
|
throw new \InvalidArgumentException(); |
|
|
} |
|
|
if ($end == $start) { |
|
|
return true; |
|
|
} |
|
|
$end--; |
|
|
$firstInt = (int)($start / 32); |
|
|
$lastInt = (int)($end / 32); |
|
|
for ($i = $firstInt; $i <= $lastInt; $i++) { |
|
|
$firstBit = $i > $firstInt ? 0 : $start & 0x1F; |
|
|
$lastBit = $i < $lastInt ? 31 : $end & 0x1F; |
|
|
$mask = 0; |
|
|
if ($firstBit == 0 && $lastBit == 31) { |
|
|
$mask = -1; |
|
|
} else { |
|
|
$mask = 0; |
|
|
for ($j = $firstBit; $j <= $lastBit; $j++) { |
|
|
$mask = ($mask | (1 << $j)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (($this->bits[$i] & $mask) != ($value ? $mask : 0)) { |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
return true; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function appendBits($value, $numBits) |
|
|
{ |
|
|
if ($numBits < 0 || $numBits > 32) { |
|
|
throw new \InvalidArgumentException("Num bits must be between 0 and 32"); |
|
|
} |
|
|
$this->ensureCapacity($this->size + $numBits); |
|
|
for ($numBitsLeft = $numBits; $numBitsLeft > 0; $numBitsLeft--) { |
|
|
$this->appendBit((($value >> ($numBitsLeft - 1)) & 0x01) == 1); |
|
|
} |
|
|
} |
|
|
|
|
|
private function ensureCapacity($size) |
|
|
{ |
|
|
if ($size > count($this->bits) * 32) { |
|
|
$newBits = $this->makeArray($size); |
|
|
$newBits = arraycopy($this->bits, 0, $newBits, 0, count($this->bits)); |
|
|
$this->bits = $newBits; |
|
|
} |
|
|
} |
|
|
|
|
|
public function appendBit($bit) |
|
|
{ |
|
|
$this->ensureCapacity($this->size + 1); |
|
|
if ($bit) { |
|
|
$this->bits[(int)($this->size / 32)] |= 1 << ($this->size & 0x1F); |
|
|
} |
|
|
$this->size++; |
|
|
} |
|
|
|
|
|
public function appendBitArray($other) |
|
|
{ |
|
|
$otherSize = $other->size; |
|
|
$this->ensureCapacity($this->size + $otherSize); |
|
|
for ($i = 0; $i < $otherSize; $i++) { |
|
|
$this->appendBit($other->get($i)); |
|
|
} |
|
|
} |
|
|
|
|
|
public function _xor($other) |
|
|
{ |
|
|
if (count($this->bits) !== count($other->bits)) { |
|
|
throw new \InvalidArgumentException("Sizes don't match"); |
|
|
} |
|
|
$count = count($this->bits); |
|
|
for ($i = 0; $i < $count; $i++) { |
|
|
|
|
|
|
|
|
$this->bits[$i] ^= $other->bits[$i]; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function toBytes($bitOffset, &$array, $offset, $numBytes) |
|
|
{ |
|
|
for ($i = 0; $i < $numBytes; $i++) { |
|
|
$theByte = 0; |
|
|
for ($j = 0; $j < 8; $j++) { |
|
|
if ($this->get($bitOffset)) { |
|
|
$theByte |= 1 << (7 - $j); |
|
|
} |
|
|
$bitOffset++; |
|
|
} |
|
|
$array[(int)($offset + $i)] = $theByte; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function get($i) |
|
|
{ |
|
|
$key = (int)($i / 32); |
|
|
|
|
|
return ($this->bits[$key] & (1 << ($i & 0x1F))) != 0; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function getBitArray() |
|
|
{ |
|
|
return $this->bits; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function reverse() |
|
|
{ |
|
|
$newBits = []; |
|
|
|
|
|
$len = (($this->size - 1) / 32); |
|
|
$oldBitsLen = $len + 1; |
|
|
for ($i = 0; $i < $oldBitsLen; $i++) { |
|
|
$x = $this->bits[$i]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$x = (($x >> 1) & 0x55555555) | (($x & 0x55555555) << 1); |
|
|
$x = (($x >> 2) & 0x33333333) | (($x & 0x33333333) << 2); |
|
|
$x = (($x >> 4) & 0x0f0f0f0f) | (($x & 0x0f0f0f0f) << 4); |
|
|
$x = (($x >> 8) & 0x00ff00ff) | (($x & 0x00ff00ff) << 8); |
|
|
$x = (($x >> 16) & 0x0000ffff) | (($x & 0x0000ffff) << 16); |
|
|
$newBits[(int)$len - $i] = (int)$x; |
|
|
} |
|
|
|
|
|
if ($this->size != $oldBitsLen * 32) { |
|
|
$leftOffset = $oldBitsLen * 32 - $this->size; |
|
|
$mask = 1; |
|
|
for ($i = 0; $i < 31 - $leftOffset; $i++) { |
|
|
$mask = ($mask << 1) | 1; |
|
|
} |
|
|
$currentInt = ($newBits[0] >> $leftOffset) & $mask; |
|
|
for ($i = 1; $i < $oldBitsLen; $i++) { |
|
|
$nextInt = $newBits[$i]; |
|
|
$currentInt |= $nextInt << (32 - $leftOffset); |
|
|
$newBits[(int)($i) - 1] = $currentInt; |
|
|
$currentInt = ($nextInt >> $leftOffset) & $mask; |
|
|
} |
|
|
$newBits[(int)($oldBitsLen) - 1] = $currentInt; |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
public function equals($o) |
|
|
{ |
|
|
if (!($o instanceof BitArray)) { |
|
|
return false; |
|
|
} |
|
|
$other = $o; |
|
|
|
|
|
return $this->size == $other->size && $this->bits === $other->bits; |
|
|
} |
|
|
|
|
|
public function hashCode() |
|
|
{ |
|
|
return 31 * $this->size + hashCode($this->bits); |
|
|
} |
|
|
|
|
|
public function toString() |
|
|
{ |
|
|
$result = ''; |
|
|
for ($i = 0; $i < $this->size; $i++) { |
|
|
if (($i & 0x07) == 0) { |
|
|
$result .= ' '; |
|
|
} |
|
|
$result .= ($this->get($i) ? 'X' : '.'); |
|
|
} |
|
|
|
|
|
return (string)$result; |
|
|
} |
|
|
|
|
|
public function _clone() |
|
|
{ |
|
|
return new BitArray($this->bits, $this->size); |
|
|
} |
|
|
} |
|
|
|