Passed
Push — v5 ( 81dcab...700af4 )
by smiley
01:48
created

BitBuffer::available()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 2
rs 10
1
<?php
2
/**
3
 * Class BitBuffer
4
 *
5
 * @filesource   BitBuffer.php
6
 * @created      25.11.2015
7
 * @package      chillerlan\QRCode\Common
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2015 Smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\QRCode\Common;
14
15
use InvalidArgumentException;
16
use function count, floor;
17
18
/**
19
 * Holds the raw binary data
20
 */
21
final class BitBuffer{
22
23
	/**
24
	 * The buffer content
25
	 *
26
	 * @var int[]
27
	 */
28
	private array $buffer;
29
30
	/**
31
	 * Length of the content (bits)
32
	 */
33
	private int $length;
34
35
	private int $bytesRead = 0;
36
	private int $bitsRead  = 0;
37
38
	/**
39
	 * BitBuffer constructor.
40
	 */
41
	public function __construct(array $bytes = null){
42
		$this->buffer = $bytes ?? [];
43
		$this->length = count($this->buffer);
44
	}
45
46
	/**
47
	 * clears the buffer
48
	 */
49
	public function clear():BitBuffer{
50
		$this->buffer = [];
51
		$this->length = 0;
52
53
		return $this;
54
	}
55
56
	/**
57
	 * appends a sequence of bits
58
	 */
59
	public function put(int $num, int $length):BitBuffer{
60
61
		for($i = 0; $i < $length; $i++){
62
			$this->putBit((($num >> ($length - $i - 1)) & 1) === 1);
63
		}
64
65
		return $this;
66
	}
67
68
	/**
69
	 * appends a single bit
70
	 */
71
	public function putBit(bool $bit):BitBuffer{
72
		$bufIndex = (int)floor($this->length / 8);
73
74
		if(count($this->buffer) <= $bufIndex){
75
			$this->buffer[] = 0;
76
		}
77
78
		if($bit === true){
79
			$this->buffer[$bufIndex] |= (0x80 >> ($this->length % 8));
80
		}
81
82
		$this->length++;
83
84
		return $this;
85
	}
86
87
	/**
88
	 * returns the current buffer length
89
	 */
90
	public function getLength():int{
91
		return $this->length;
92
	}
93
94
	/**
95
	 * returns the buffer content
96
	 */
97
	public function getBuffer():array{
98
		return $this->buffer;
99
	}
100
101
	/**
102
	 * @return int number of bits that can be read successfully
103
	 */
104
	public function available():int{
105
		return 8 * ($this->length - $this->bytesRead) - $this->bitsRead;
106
	}
107
108
	/**
109
	 * @author Sean Owen, ZXing
110
	 *
111
	 * @param int $numBits number of bits to read
112
	 *
113
	 * @return int representing the bits read. The bits will appear as the least-significant
114
	 *         bits of the int
115
	 * @throws InvalidArgumentException if numBits isn't in [1,32] or more than is available
116
	 */
117
	public function read(int $numBits):int{
118
119
		if($numBits < 1 || $numBits > 32 || $numBits > $this->available()){
120
			throw new InvalidArgumentException('invalid $numBits: '.$numBits);
121
		}
122
123
		$result = 0;
124
125
		// First, read remainder from current byte
126
		if($this->bitsRead > 0){
127
			$bitsLeft       = 8 - $this->bitsRead;
128
			$toRead         = $numBits < $bitsLeft ? $numBits : $bitsLeft;
129
			$bitsToNotRead  = $bitsLeft - $toRead;
130
			$mask           = (0xff >> (8 - $toRead)) << $bitsToNotRead;
131
			$result         = ($this->buffer[$this->bytesRead] & $mask) >> $bitsToNotRead;
132
			$numBits        -= $toRead;
133
			$this->bitsRead += $toRead;
134
135
			if($this->bitsRead == 8){
136
				$this->bitsRead = 0;
137
				$this->bytesRead++;
138
			}
139
		}
140
141
		// Next read whole bytes
142
		if($numBits > 0){
143
144
			while($numBits >= 8){
145
				$result = ($result << 8) | ($this->buffer[$this->bytesRead] & 0xff);
146
				$this->bytesRead++;
147
				$numBits -= 8;
148
			}
149
150
			// Finally read a partial byte
151
			if($numBits > 0){
152
				$bitsToNotRead  = 8 - $numBits;
153
				$mask           = (0xff >> $bitsToNotRead) << $bitsToNotRead;
154
				$result         = ($result << $numBits) | (($this->buffer[$this->bytesRead] & $mask) >> $bitsToNotRead);
155
				$this->bitsRead += $numBits;
156
			}
157
		}
158
159
		return $result;
160
	}
161
162
}
163