PocketMine-MP 5.23.3 git-f7687af337d001ddbcc47b8e773f014a33faa662
Loading...
Searching...
No Matches
BitSet.php
1<?php
2
3/*
4 * This file is part of BedrockProtocol.
5 * Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
6 *
7 * BedrockProtocol is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 */
12
13declare(strict_types=1);
14
15namespace pocketmine\network\mcpe\protocol\serializer;
16
17use function array_pad;
18use function array_slice;
19use function array_values;
20use function count;
21use function intdiv;
22
23class BitSet{
24 private const INT_BITS = PHP_INT_SIZE * 8;
25 private const SHIFT = 7;
26
30 public function __construct(
31 private readonly int $length,
32 private array $parts = []
33 ){
34 $expectedPartsCount = self::getExpectedPartsCount($length);
35 $partsCount = count($parts);
36
37 if($partsCount > $expectedPartsCount){
38 throw new \InvalidArgumentException("Too many parts");
39 }elseif($partsCount < $expectedPartsCount){
40 $parts = array_pad($parts, $expectedPartsCount, 0);
41 }
42
43 $this->parts = array_values($parts);
44 }
45
46 public function get(int $index) : bool{
47 [$partIndex, $bitIndex] = $this->getPartIndex($index);
48
49 return ($this->parts[$partIndex] & (1 << $bitIndex)) !== 0;
50 }
51
52 public function set(int $index, bool $value) : void{
53 [$partIndex, $bitIndex] = $this->getPartIndex($index);
54
55 if($value){
56 $this->parts[$partIndex] |= 1 << $bitIndex;
57 }else{
58 $this->parts[$partIndex] &= ~(1 << $bitIndex);
59 }
60 }
61
67 private function getPartIndex(int $index) : array{
68 if($index < 0 or $index >= $this->length){
69 throw new \InvalidArgumentException("Index out of bounds");
70 }
71
72 return [
73 intdiv($index, self::INT_BITS),
74 $index % self::INT_BITS
75 ];
76 }
77
81 public function getPartsCount() : int{
82 return count($this->parts);
83 }
84
85 private static function getExpectedPartsCount(int $length) : int{
86 return intdiv($length + self::INT_BITS - 1, self::INT_BITS);
87 }
88
89 public static function read(PacketSerializer $in, int $length) : self{
90 $result = [0];
91
92 $currentIndex = 0;
93 $currentShift = 0;
94
95 for($i = 0; $i < $length; $i += self::SHIFT){
96 $b = $in->getByte();
97 $bits = $b & 0x7f;
98
99 $result[$currentIndex] |= $bits << $currentShift; //extra bits will be discarded
100 $nextShift = $currentShift + self::SHIFT;
101 if($nextShift >= self::INT_BITS){
102 $nextShift -= self::INT_BITS;
103 $rightShift = self::SHIFT - $nextShift;
104 $result[++$currentIndex] = $bits >> $rightShift;
105 }
106 $currentShift = $nextShift;
107
108 if(($b & 0x80) === 0){
109 return new self($length, array_slice($result, 0, self::getExpectedPartsCount($length)));
110 }
111 }
112
113 return new self($length, array_slice($result, 0, self::getExpectedPartsCount($length)));
114 }
115
116 public function write(PacketSerializer $out) : void{
117 $parts = $this->parts;
118 $length = $this->length;
119
120 $currentIndex = 0;
121 $currentShift = 0;
122
123 for($i = 0; $i < $length; $i += self::SHIFT){
124 $bits = $parts[$currentIndex] >> $currentShift;
125 $nextShift = $currentShift + self::SHIFT;
126 if($nextShift >= self::INT_BITS){
127 $nextShift -= self::INT_BITS;
128 $bits |= ($parts[++$currentIndex] ?? 0) << (self::SHIFT - $nextShift);
129 }
130 $currentShift = $nextShift;
131
132 $last = $i + self::SHIFT >= $length;
133 $bits |= $last ? 0 : 0x80;
134
135 $out->putByte($bits);
136 if($last){
137 break;
138 }
139 }
140 }
141
142 public function getLength() : int{
143 return $this->length;
144 }
145}
__construct(private readonly int $length, private array $parts=[])
Definition BitSet.php:30