<?php
 declare(strict_types=1); namespace SpomkyLabs\Pki\ASN1\Util; use Brick\Math\BigInteger; use OutOfBoundsException; use RuntimeException; use SpomkyLabs\Pki\ASN1\Type\Primitive\BitString; use function assert; use function count; use function is_array; use function ord; final class Flags { private string $_flags; private function __construct( BigInteger|int|string $flags, private readonly int $_width ) { if ($_width === 0) { $this->_flags = ''; return; } $last_octet_bits = $_width % 8; $unused_bits = $last_octet_bits !== 0 ? 8 - $last_octet_bits : 0; $num = BigInteger::of($flags); $mask = BigInteger::of(1)->shiftedLeft($_width)->minus(1); $num = $num->and($mask); $data = $num->shiftedLeft($unused_bits) ->toBytes(false); $octets = unpack('C*', $data); assert(is_array($octets), new RuntimeException('unpack() failed')); $bits = count($octets) * 8; while ($bits < $_width) { array_unshift($octets, 0); $bits += 8; } $this->_flags = pack('C*', ...$octets); } public static function create(BigInteger|int|string $flags, int $_width): self { return new self($flags, $_width); } public static function fromBitString(BitString $bs, int $width): self { $num_bits = $bs->numBits(); $data = $bs->string(); $num = $data === '' ? BigInteger::of(0) : BigInteger::fromBytes($bs->string(), false); $num = $num->shiftedRight($bs->unusedBits()); if ($num_bits < $width) { $num = $num->shiftedLeft($width - $num_bits); } return self::create($num, $width); } public function test(int $idx): bool { if ($idx >= $this->_width) { throw new OutOfBoundsException('Index is out of bounds.'); } $oi = (int) floor($idx / 8); $byte = $this->_flags[$oi]; $bi = $idx % 8; $mask = 0x01 << (7 - $bi); return (ord($byte) & $mask) > 0; } public function string(): string { return $this->_flags; } public function number(): string { $num = BigInteger::fromBytes($this->_flags, false); $last_octet_bits = $this->_width % 8; $unused_bits = $last_octet_bits !== 0 ? 8 - $last_octet_bits : 0; $num = $num->shiftedRight($unused_bits); return $num->toBase(10); } public function intNumber(): int { $num = BigInt::create($this->number()); return $num->toInt(); } public function bitString(): BitString { $last_octet_bits = $this->_width % 8; $unused_bits = $last_octet_bits !== 0 ? 8 - $last_octet_bits : 0; return BitString::create($this->_flags, $unused_bits); } } 