| 1 | <?php |
||
| 11 | */ |
||
| 12 | class Flags |
||
| 13 | { |
||
| 14 | /** |
||
| 15 | * Flag octets. |
||
| 16 | * |
||
| 17 | * @var string |
||
| 18 | */ |
||
| 19 | protected $_flags; |
||
| 20 | |||
| 21 | /** |
||
| 22 | * Number of flags. |
||
| 23 | * |
||
| 24 | * @var int |
||
| 25 | */ |
||
| 26 | protected $_width; |
||
| 27 | |||
| 28 | /** |
||
| 29 | * Constructor. |
||
| 30 | * |
||
| 31 | * @param int|string $flags Flags |
||
| 32 | * @param int $width The number of flags. If width is larger than |
||
| 33 | * number of bits in $flags, zeroes are prepended |
||
| 34 | * to flag field. |
||
| 35 | */ |
||
| 36 | 62 | public function __construct($flags, int $width) |
|
| 37 | { |
||
| 38 | 62 | if (!$width) { |
|
| 39 | 2 | $this->_flags = ''; |
|
| 40 | } else { |
||
| 41 | // calculate number of unused bits in last octet |
||
| 42 | 60 | $last_octet_bits = $width % 8; |
|
| 43 | 60 | $unused_bits = $last_octet_bits ? 8 - $last_octet_bits : 0; |
|
| 44 | 60 | $num = gmp_init($flags); |
|
| 45 | // mask bits outside bitfield width |
||
| 46 | 60 | $mask = gmp_sub(gmp_init(1) << $width, 1); |
|
| 47 | 60 | $num &= $mask; |
|
| 48 | // shift towards MSB if needed |
||
| 49 | 60 | $data = gmp_export($num << $unused_bits, 1, |
|
|
1 ignored issue
–
show
|
|||
| 50 | 60 | GMP_MSW_FIRST | GMP_BIG_ENDIAN); |
|
| 51 | 60 | $octets = unpack('C*', $data); |
|
| 52 | 60 | $bits = count($octets) * 8; |
|
| 53 | // pad with zeroes |
||
| 54 | 60 | while ($bits < $width) { |
|
| 55 | 20 | array_unshift($octets, 0); |
|
| 56 | 20 | $bits += 8; |
|
| 57 | } |
||
| 58 | 60 | $this->_flags = pack('C*', ...$octets); |
|
| 59 | } |
||
| 60 | 62 | $this->_width = $width; |
|
| 61 | 62 | } |
|
| 62 | |||
| 63 | /** |
||
| 64 | * Initialize from BitString. |
||
| 65 | * |
||
| 66 | * @param BitString $bs |
||
| 67 | * @param int $width |
||
| 68 | * |
||
| 69 | * @return self |
||
| 70 | */ |
||
| 71 | 8 | public static function fromBitString(BitString $bs, int $width): self |
|
| 80 | } |
||
| 81 | |||
| 82 | /** |
||
| 83 | * Check whether a bit at given index is set. |
||
| 84 | * |
||
| 85 | * Index 0 is the leftmost bit. |
||
| 86 | * |
||
| 87 | * @param int $idx |
||
| 88 | * |
||
| 89 | * @throws \OutOfBoundsException |
||
| 90 | * |
||
| 91 | * @return bool |
||
| 92 | */ |
||
| 93 | 15 | public function test(int $idx): bool |
|
| 106 | } |
||
| 107 | |||
| 108 | /** |
||
| 109 | * Get flags as an octet string. |
||
| 110 | * |
||
| 111 | * Zeroes are appended to the last octet if width is not divisible by 8. |
||
| 112 | * |
||
| 113 | * @return string |
||
| 114 | */ |
||
| 115 | 23 | public function string(): string |
|
| 116 | { |
||
| 117 | 23 | return $this->_flags; |
|
| 118 | } |
||
| 119 | |||
| 120 | /** |
||
| 121 | * Get flags as a base 10 integer. |
||
| 122 | * |
||
| 123 | * @return string Integer as a string |
||
| 124 | */ |
||
| 125 | 15 | public function number(): string |
|
| 126 | { |
||
| 127 | 15 | $num = gmp_import($this->_flags, 1, GMP_MSW_FIRST | GMP_BIG_ENDIAN); |
|
| 128 | 15 | $last_octet_bits = $this->_width % 8; |
|
| 129 | 15 | $unused_bits = $last_octet_bits ? 8 - $last_octet_bits : 0; |
|
| 130 | 15 | $num >>= $unused_bits; |
|
| 131 | 15 | return gmp_strval($num, 10); |
|
| 132 | } |
||
| 133 | |||
| 134 | /** |
||
| 135 | * Get flags as an integer. |
||
| 136 | * |
||
| 137 | * @return int |
||
| 138 | */ |
||
| 139 | 1 | public function intNumber(): int |
|
| 143 | } |
||
| 144 | |||
| 145 | /** |
||
| 146 | * Get flags as a BitString. |
||
| 147 | * |
||
| 148 | * Unused bits are set accordingly. Trailing zeroes are not stripped. |
||
| 149 | * |
||
| 150 | * @return BitString |
||
| 151 | */ |
||
| 159 |