1 | <?php |
||
9 | class Bit implements TypeInterface |
||
10 | { |
||
11 | /** |
||
12 | * @var bool |
||
13 | */ |
||
14 | private $signed = false; |
||
15 | |||
16 | /** |
||
17 | * Returns an unsigned integer from the bit level |
||
18 | * |
||
19 | * @param \PhpBinaryReader\BinaryReader $br |
||
20 | * @param int $length |
||
21 | * @throws \OutOfBoundsException |
||
22 | * @throws InvalidDataException |
||
23 | * @return int |
||
24 | */ |
||
25 | 76 | public function read(BinaryReader &$br, $length) |
|
26 | { |
||
27 | 76 | if (!is_int($length)) { |
|
28 | 4 | throw new InvalidDataException('The length parameter must be an integer'); |
|
29 | } |
||
30 | |||
31 | 72 | $bitmask = new BitMask(); |
|
32 | 72 | $result = 0; |
|
33 | 72 | $bits = $length; |
|
34 | 72 | $shift = $br->getCurrentBit(); |
|
35 | |||
36 | 72 | if ($shift != 0) { |
|
37 | 8 | $bitsLeft = 8 - $shift; |
|
38 | |||
39 | 8 | if ($bitsLeft < $bits) { |
|
40 | 4 | $bits -= $bitsLeft; |
|
41 | 4 | $result = ($br->getNextByte() >> $shift) << $bits; |
|
42 | 8 | } elseif ($bitsLeft > $bits) { |
|
43 | 8 | $br->setCurrentBit($br->getCurrentBit() + $bits); |
|
44 | |||
45 | 8 | return ($br->getNextByte() >> $shift) & $bitmask->getMask($bits, BitMask::MASK_LO); |
|
46 | } else { |
||
47 | 8 | $br->setCurrentBit(0); |
|
48 | |||
49 | 8 | return $br->getNextByte() >> $shift; |
|
50 | } |
||
51 | } |
||
52 | |||
53 | 72 | if (!$br->canReadBytes($length / 8)) { |
|
54 | 8 | throw new \OutOfBoundsException('Cannot read bits, it exceeds the boundary of the file'); |
|
55 | } |
||
56 | |||
57 | 68 | if ($bits >= 8) { |
|
58 | 38 | $bytes = intval($bits / 8); |
|
59 | |||
60 | 38 | if ($bytes == 1) { |
|
61 | 4 | $bits -= 8; |
|
62 | 4 | $result |= ($this->getSigned() ? $br->readInt8() : $br->readUInt8()) << $bits; |
|
63 | 38 | } elseif ($bytes == 2) { |
|
64 | 4 | $bits -= 16; |
|
65 | 4 | $result |= ($this->getSigned() ? $br->readInt16() : $br->readUInt16()) << $bits; |
|
66 | 38 | } elseif ($bytes == 4) { |
|
67 | 6 | $bits -= 32; |
|
68 | 6 | $result |= ($this->getSigned() ? $br->readInt32() : $br->readUInt32()) << $bits; |
|
69 | } else { |
||
70 | 36 | while ($bits > 8) { |
|
71 | 36 | $bits -= 8; |
|
72 | 36 | $result |= ($this->getSigned() ? $br->readInt8() : $br->readUInt8()) << 8; |
|
73 | } |
||
74 | } |
||
75 | } |
||
76 | |||
77 | 68 | if ($bits != 0) { |
|
78 | 66 | $code = $this->getSigned() ? 'c' : 'C'; |
|
79 | 66 | $data = unpack($code, $br->readFromHandle(1)); |
|
80 | 66 | $br->setNextByte($data[1]); |
|
81 | 66 | $result |= $br->getNextByte() & $bitmask->getMask($bits, BitMask::MASK_LO); |
|
82 | } |
||
83 | |||
84 | 68 | $br->setCurrentBit($bits); |
|
85 | |||
86 | 68 | return $result; |
|
87 | } |
||
88 | |||
89 | /** |
||
90 | * Returns a signed integer from the bit level |
||
91 | * |
||
92 | * @param \PhpBinaryReader\BinaryReader $br |
||
93 | * @param int $length |
||
94 | * @return int |
||
95 | */ |
||
96 | 74 | public function readSigned(&$br, $length) |
|
104 | |||
105 | /** |
||
106 | * @param boolean $signed |
||
107 | */ |
||
108 | 74 | public function setSigned($signed) |
|
112 | |||
113 | /** |
||
114 | * @return boolean |
||
115 | */ |
||
116 | 68 | public function getSigned() |
|
120 | } |
||
121 |