BitBuffer   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 159
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
eloc 50
dl 0
loc 159
rs 10
c 2
b 0
f 1
wmc 19

9 Methods

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