26 private const INT_BITS = PHP_INT_SIZE * 8;
27 private const SHIFT = 7;
33 private readonly
int $length,
34 private array $parts = []
36 $expectedPartsCount = self::getExpectedPartsCount($length);
37 $partsCount = count($parts);
39 if($partsCount > $expectedPartsCount){
40 throw new \InvalidArgumentException(
"Too many parts");
41 }elseif($partsCount < $expectedPartsCount){
42 $parts = array_pad($parts, $expectedPartsCount, 0);
45 $this->parts = array_values($parts);
48 public function get(
int $index) : bool{
49 [$partIndex, $bitIndex] = $this->getPartIndex($index);
51 return ($this->parts[$partIndex] & (1 << $bitIndex)) !== 0;
54 public function set(
int $index,
bool $value) :
void{
55 [$partIndex, $bitIndex] = $this->getPartIndex($index);
58 $this->parts[$partIndex] |= 1 << $bitIndex;
60 $this->parts[$partIndex] &= ~(1 << $bitIndex);
69 private function getPartIndex(
int $index) : array{
70 if($index < 0 or $index >= $this->length){
71 throw new \InvalidArgumentException(
"Index out of bounds");
75 intdiv($index, self::INT_BITS),
76 $index % self::INT_BITS
83 public function getPartsCount() : int{
84 return count($this->parts);
87 private static function getExpectedPartsCount(
int $length) : int{
88 return intdiv($length + self::INT_BITS - 1, self::INT_BITS);
91 public static function read(ByteBufferReader $in,
int $length) : self{
97 for($i = 0; $i < $length; $i += self::SHIFT){
98 $b = Byte::readUnsigned($in);
101 $result[$currentIndex] |= $bits << $currentShift;
102 $nextShift = $currentShift + self::SHIFT;
103 if($nextShift >= self::INT_BITS){
104 $nextShift -= self::INT_BITS;
105 $rightShift = self::SHIFT - $nextShift;
106 $result[++$currentIndex] = $bits >> $rightShift;
108 $currentShift = $nextShift;
110 if(($b & 0x80) === 0){
111 return new self($length, array_slice($result, 0, self::getExpectedPartsCount($length)));
115 return new self($length, array_slice($result, 0, self::getExpectedPartsCount($length)));
118 public function write(ByteBufferWriter $out) : void{
119 $parts = $this->parts;
120 $length = $this->length;
125 for($i = 0; $i < $length; $i += self::SHIFT){
126 $bits = $parts[$currentIndex] >> $currentShift;
127 $nextShift = $currentShift + self::SHIFT;
128 if($nextShift >= self::INT_BITS){
129 $nextShift -= self::INT_BITS;
130 $bits |= ($parts[++$currentIndex] ?? 0) << (self::SHIFT - $nextShift);
132 $currentShift = $nextShift;
134 $last = $i + self::SHIFT >= $length;
135 $bits |= $last ? 0 : 0x80;
137 Byte::writeUnsigned($out, $bits);
144 public function getLength() : int{
145 return $this->length;
148 public function equals(BitSet $that) : bool{
149 return $this->length === $that->length && $this->parts === $that->parts;