Passed
Push — main ( 688ffa...59318b )
by smiley
14:40
created

BitBuffer::put()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 7
rs 10
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
	public function getBuffer():array{
95
		return $this->buffer;
96
	}
97
98
	/**
99
	 * @return int number of bits that can be read successfully
100
	 */
101
	public function available():int{
102
		return 8 * ($this->length - $this->bytesRead) - $this->bitsRead;
103
	}
104
105
	/**
106
	 * @author Sean Owen, ZXing
107
	 *
108
	 * @param int $numBits number of bits to read
109
	 *
110
	 * @return int representing the bits read. The bits will appear as the least-significant bits of the int
111
	 * @throws \chillerlan\QRCode\QRCodeException if numBits isn't in [1,32] or more than is available
112
	 */
113
	public function read(int $numBits):int{
114
115
		if($numBits < 1 || $numBits > 32 || $numBits > $this->available()){
116
			throw new QRCodeException('invalid $numBits: '.$numBits);
117
		}
118
119
		$result = 0;
120
121
		// First, read remainder from current byte
122
		if($this->bitsRead > 0){
123
			$bitsLeft       = 8 - $this->bitsRead;
124
			$toRead         = min($numBits, $bitsLeft);
125
			$bitsToNotRead  = $bitsLeft - $toRead;
126
			$mask           = (0xff >> (8 - $toRead)) << $bitsToNotRead;
127
			$result         = ($this->buffer[$this->bytesRead] & $mask) >> $bitsToNotRead;
128
			$numBits        -= $toRead;
129
			$this->bitsRead += $toRead;
130
131
			if($this->bitsRead === 8){
132
				$this->bitsRead = 0;
133
				$this->bytesRead++;
134
			}
135
		}
136
137
		// Next read whole bytes
138
		if($numBits > 0){
139
140
			while($numBits >= 8){
141
				$result = ($result << 8) | ($this->buffer[$this->bytesRead] & 0xff);
142
				$this->bytesRead++;
143
				$numBits -= 8;
144
			}
145
146
			// Finally read a partial byte
147
			if($numBits > 0){
148
				$bitsToNotRead  = 8 - $numBits;
149
				$mask           = (0xff >> $bitsToNotRead) << $bitsToNotRead;
150
				$result         = ($result << $numBits) | (($this->buffer[$this->bytesRead] & $mask) >> $bitsToNotRead);
151
				$this->bitsRead += $numBits;
152
			}
153
		}
154
155
		return $result;
156
	}
157
158
	/**
159
	 * Clears the buffer and resets the stats
160
	 */
161
	public function clear():self{
162
		$this->buffer    = [];
163
		$this->length    = 0;
164
		$this->bytesRead = 0;
165
		$this->bitsRead  = 0;
166
167
		return $this;
168
	}
169
170
}
171