Completed
Pull Request — master (#6)
by Michele
03:20
created

BitReader::peek()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 5
cts 6
cp 0.8333
rs 9.9332
c 0
b 0
f 0
cc 3
nc 3
nop 2
crap 3.0416
1
<?php
2
3
namespace CHMLib\Reader;
4
5
use Exception;
6
7
/**
8
 * Read data from a raw string of bytes.
9
 */
10
class BitReader extends StringReader
11
{
12
    /**
13
     * The maximum size of the buffer in bits.
14
     *
15
     * @var int
16
     */
17
    const BUFFER_BITS = 32;
18
19
    /**
20
     * Pre-calculated mask to make sure we get.
21
     *
22
     * @var int[]
23
     */
24
    protected static $UNSIGNED_MASK = array(
25
        0x0000,
26
        0x0001, 0x0003, 0x0007, 0x000f,
27
        0x001f, 0x003f, 0x007f, 0x00ff,
28
        0x01ff, 0x03ff, 0x07ff, 0x0fff,
29
        0x1fff, 0x3fff, 0x7fff, 0xffff,
30
    );
31
32
    /**
33
     * The current bit buffer.
34
     *
35
     * @var int
36
     */
37
    protected $buffer;
38
39
    /**
40
     * The number of bits in the buffer.
41
     *
42
     * @var int
43
     */
44
    protected $bufferSize;
45
46
    /**
47
     * Initializes the instance.
48
     *
49
     * @param string $string The raw string containing the data to be read.
50
     */
51 26
    public function __construct($string)
52
    {
53 26
        parent::__construct($string);
54 26
        $this->buffer = 0;
55 26
        $this->bufferSize = 0;
56 26
    }
57
58
    /**
59
     * Make sure that there are at least $n (<=16) bits in the buffer.
60
     * If less than $n bits are there, read a 16-bit little-endian word from the byte array.
61
     *
62
     * @param int $n The minimum number of bits that should be available in the remaining bit buffer.
63
     *
64
     * @return int Return the number of remaining bits in the buffer.
65
     */
66 26
    public function ensure($n)
67
    {
68 26
        while ($this->bufferSize < $n) {
69 26
            if (($this->length - $this->position) < 2) {
70 23
                $this->position = $this->length;
71 23
                break;
72
            }
73 26
            $word = $this->readUInt16();
74 26
            $this->buffer |= $word << (static::BUFFER_BITS - 16 - $this->bufferSize);
75 26
            $this->bufferSize += 16;
76 26
        }
77
78 26
        return $this->bufferSize;
79
    }
80
81
    /**
82
     * Peek n bits, may raise an EOF Exception if there are not enough bits and $suppressException is false.
83
     *
84
     * @param int $n The number of bits to peek.
85
     * @param bool $suppressException Set to true to avoid raising an exception if there are not enough bits.
86
     *
87
     * @return int
88
     */
89 26
    public function peek($n, $suppressException = false)
90
    {
91 26
        if ($this->ensure($n) < $n) {
92 23
            if (!$suppressException) {
93
                throw new Exception('EOF');
94
            }
95 23
        }
96
97 26
        return (($this->buffer >> (static::BUFFER_BITS - $n))) & static::$UNSIGNED_MASK[$n];
98
    }
99
100
    /**
101
     * Read no more than 16 bits (represented as a little endian integer).
102
     *
103
     * @param int $n The number of bits to retrieve.
104
     *
105
     * @return int
106
     */
107 26
    public function readLE($n)
108
    {
109 26
        $result = $this->peek($n);
110 26
        $this->buffer <<= $n;
111 26
        $this->bufferSize -= $n;
112
113 26
        return $result;
114
    }
115
116
    /**
117
     * Reset the bits buffer and flush $n bytes.
118
     *
119
     * @param int $n The number of bytes to skip.
120
     *
121
     * @throws \Exception Throws an Exception in case of errors.
122
     */
123
    public function skip($n)
124
    {
125
        $this->buffer = 0;
126
        $this->bufferSize = 0;
127
        $this->setPosition($this->position + $n);
128
    }
129
130
    /**
131
     * Read a number of bytes and insert them in an array at a specified position.
132
     *
133
     * @param array $buffer The buffer where the data should be inserted.
134
     * @param int $offset The offset at which the data should be inserted.
135
     * @param int $length The number of bytes to read.
136
     *
137
     * @throws \Exception Throws an Exception in case of errors.
138
     *
139
     * @return array Return the $buffer array with filled-in bytes.
140
     */
141
    public function readFully($buffer, $offset, $length)
142
    {
143
        if ($length === 0) {
144
            $result = $buffer;
145
        } else {
146
            $bufferSize = count($buffer);
147
            $postSize = $bufferSize - $offset - $length;
148
            if ($postSize < 0) {
149
                throw new Exception('Buffer too small');
150
            }
151
            $pre = ($offset === 0) ? array() : array_slice($buffer, 0, $offset);
152
            $mid = $this->readBytes($length);
153
            $post = ($postSize === 0) ? array() : array_slice($buffer, -$postSize);
154
            $result = array_merge($pre, $mid, $post);
155
        }
156
157
        return $result;
158
    }
159
}
160