35 private int $value = 0;
36 private int $offset = 0;
38 public function __construct(
42 public function writeInt(
int $bits,
int $value) :
void{
43 if($this->offset + $bits > $this->maxBits){
44 throw new \InvalidArgumentException(
"Bit buffer cannot be larger than $this->maxBits bits (already have $this->offset bits)");
46 if(($value & (~0 << $bits)) !== 0){
47 throw new \InvalidArgumentException(
"Value $value does not fit into $bits bits");
50 $this->value |= ($value << $this->offset);
51 $this->offset += $bits;
54 public function int(
int $bits,
int &$value) :
void{
55 $this->writeInt($bits, $value);
61 public function boundedInt(
int $bits,
int $min,
int $max,
int &$value) : void{
62 $offset = $this->offset;
63 $this->writeBoundedIntAuto($min, $max, $value);
64 $actualBits = $this->offset - $offset;
65 if($actualBits !== $bits){
66 throw new \InvalidArgumentException(
"Bits should be $actualBits for the given bounds, but received $bits. Use boundedIntAuto() for automatic bits calculation.");
70 private function writeBoundedIntAuto(
int $min,
int $max,
int $value) : void{
71 if($value < $min || $value > $max){
72 throw new \InvalidArgumentException(
"Value $value is outside the range $min - $max");
74 $bits = ((int) log($max - $min, 2)) + 1;
75 $this->writeInt($bits, $value - $min);
79 $this->writeBoundedIntAuto($min, $max, $value);
82 protected function writeBool(
bool $value) : void{
83 $this->writeInt(1, $value ? 1 : 0);
86 public function bool(
bool &$value) : void{
87 $this->writeBool($value);
90 public function horizontalFacing(
int &$facing) : void{
91 $this->writeInt(2, match($facing){
96 default =>
throw new \InvalidArgumentException(
"Invalid horizontal facing $facing")
104 $uniqueFaces = array_flip($faces);
105 foreach(Facing::ALL as $facing){
106 $this->writeBool(isset($uniqueFaces[$facing]));
114 $uniqueFaces = array_flip($faces);
115 foreach(Facing::HORIZONTAL as $facing){
116 $this->writeBool(isset($uniqueFaces[$facing]));
120 public function facing(
int &$facing) : void{
121 $this->writeInt(3, match($facing){
128 default =>
throw new \InvalidArgumentException(
"Invalid facing $facing")
132 public function facingExcept(
int &$facing,
int $except) : void{
133 $this->facing($facing);
136 public function axis(
int &$axis) : void{
137 $this->writeInt(2, match($axis){
141 default =>
throw new \InvalidArgumentException(
"Invalid axis $axis")
145 public function horizontalAxis(
int &$axis) : void{
146 $this->writeInt(1, match($axis){
149 default =>
throw new \InvalidArgumentException(
"Invalid horizontal axis $axis")
160 foreach(Facing::HORIZONTAL as $facing){
161 $packed += match($connections[$facing] ??
null){
163 WallConnectionType::SHORT => 1,
164 WallConnectionType::TALL => 2,
168 $this->writeBoundedIntAuto(0, (3 ** 4) - 1, $packed);
178 $this->enumSet($slots, BrewingStandSlot::cases());
181 public function railShape(
int &$railShape) : void{
182 $this->int(4, $railShape);
185 public function straightOnlyRailShape(
int &$railShape) : void{
186 $this->int(3, $railShape);
189 public function enum(\UnitEnum &$case) :
void{
190 $metadata = RuntimeEnumMetadata::from($case);
191 $this->writeInt($metadata->bits, $metadata->enumToInt($case));
194 public function enumSet(array &$set, array $allCases) : void{
195 foreach($allCases as $case){
196 $this->writeBool(isset($set[spl_object_id($case)]));
200 public function getValue() : int{ return $this->value; }
202 public function getOffset() : int{ return $this->offset; }