38    use LegacyRuntimeEnumDescriberTrait;
 
   40    private int $offset = 0;
 
   42    public function __construct(
 
   47    public function readInt(
int $bits) : 
int{
 
   48        $bitsLeft = $this->maxBits - $this->offset;
 
   49        if($bits > $bitsLeft){
 
   50            throw new \InvalidArgumentException(
"No bits left in buffer (need $bits, have $bitsLeft");
 
   53        $value = ($this->value >> $this->offset) & ~(~0 << $bits);
 
   54        $this->offset += $bits;
 
   58    public function int(
int $bits, 
int &$value) : 
void{
 
   59        $value = $this->readInt($bits);
 
   65    public function boundedInt(
int $bits, 
int $min, 
int $max, 
int &$value) : void{
 
   66        $offset = $this->offset;
 
   68        $actualBits = $this->offset - $offset;
 
   69        if($this->offset !== $offset + $bits){
 
   70            throw new \InvalidArgumentException(
"Bits should be $actualBits for the given bounds, but received $bits. Use boundedIntAuto() for automatic bits calculation.");
 
 
   74    private function readBoundedIntAuto(
int $min, 
int $max) : int{
 
   75        $bits = ((int) log($max - $min, 2)) + 1;
 
   76        $result = $this->readInt($bits) + $min;
 
   77        if($result < $min || $result > $max){
 
   78            throw new InvalidSerializedRuntimeDataException(
"Value is outside the range $min - $max");
 
   84        $value = $this->readBoundedIntAuto($min, $max);
 
 
   87    protected function readBool() : bool{
 
   88        return $this->readInt(1) === 1;
 
   91    public function bool(
bool &$value) : void{
 
   92        $value = $this->readBool();
 
   95    public function horizontalFacing(
int &$facing) : void{
 
   96        $facing = match($this->readInt(2)){
 
  110        foreach(Facing::ALL as $facing){
 
  111            if($this->readBool()){
 
  112                $result[$facing] = $facing;
 
 
  124        foreach(Facing::HORIZONTAL as $facing){
 
  125            if($this->readBool()){
 
  126                $result[$facing] = $facing;
 
 
  133    public function facing(
int &$facing) : void{
 
  134        $facing = match($this->readInt(3)){
 
  141            default => 
throw new InvalidSerializedRuntimeDataException(
"Invalid facing value")
 
  145    public function facingExcept(
int &$facing, 
int $except) : void{
 
  147        $this->facing($result);
 
  148        if($result === $except){
 
  149            throw new InvalidSerializedRuntimeDataException(
"Illegal facing value");
 
  155    public function axis(
int &$axis) : void{
 
  156        $axis = match($this->readInt(2)){
 
  160            default => 
throw new InvalidSerializedRuntimeDataException(
"Invalid axis value")
 
  164    public function horizontalAxis(
int &$axis) : void{
 
  165        $axis = match($this->readInt(1)){
 
  168            default => 
throw new AssumptionFailedError(
"Unreachable")
 
  179        $packed = $this->readBoundedIntAuto(0, (3 ** 4) - 1);
 
  180        foreach(Facing::HORIZONTAL as $facing){
 
  181            $type = intdiv($packed,  (3 ** $offset)) % 3;
 
  183                $result[$facing] = match($type){
 
  184                    1 => WallConnectionType::SHORT,
 
  185                    2 => WallConnectionType::TALL,
 
  192        $connections = $result;
 
 
  202        $this->enumSet($slots, BrewingStandSlot::cases());
 
 
  205    public function railShape(
int &$railShape) : void{
 
  206        $result = $this->readInt(4);
 
  207        if(!isset(RailConnectionInfo::CONNECTIONS[$result]) && !isset(RailConnectionInfo::CURVE_CONNECTIONS[$result])){
 
  211        $railShape = $result;
 
  214    public function straightOnlyRailShape(
int &$railShape) : void{
 
  215        $result = $this->readInt(3);
 
  216        if(!isset(RailConnectionInfo::CONNECTIONS[$result])){
 
  217            throw new InvalidSerializedRuntimeDataException(
"No rail shape matches meta $result");
 
  220        $railShape = $result;
 
  223    public function enum(\UnitEnum &$case) : 
void{
 
  224        $metadata = RuntimeEnumMetadata::from($case);
 
  225        $raw = $this->readInt($metadata->bits);
 
  226        $result = $metadata->intToEnum($raw);
 
  227        if($result === 
null){
 
 
  234    public function enumSet(array &$set, array $allCases) : void{
 
  236        foreach($allCases as $case){
 
  237            if($this->readBool()){
 
  238                $result[spl_object_id($case)] = $case;
 
 
  244    public function getOffset() : int{ return $this->offset; }