BinaryDataReader::readCodedBinary()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 5
nop 0
dl 0
loc 17
ccs 11
cts 11
cp 1
crap 5
rs 9.6111
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace MySQLReplication\BinaryDataReader;
5
6
class BinaryDataReader
7
{
8
    public const NULL_COLUMN = 251;
9
    public const UNSIGNED_CHAR_COLUMN = 251;
10
    public const UNSIGNED_SHORT_COLUMN = 252;
11
    public const UNSIGNED_INT24_COLUMN = 253;
12
    public const UNSIGNED_INT64_COLUMN = 254;
13
    public const UNSIGNED_CHAR_LENGTH = 1;
14
    public const UNSIGNED_SHORT_LENGTH = 2;
15
    public const UNSIGNED_INT24_LENGTH = 3;
16
    public const UNSIGNED_INT32_LENGTH = 4;
17
    public const UNSIGNED_FLOAT_LENGTH = 4;
18
    public const UNSIGNED_DOUBLE_LENGTH = 8;
19
    public const UNSIGNED_INT40_LENGTH = 5;
20
    public const UNSIGNED_INT48_LENGTH = 6;
21
    public const UNSIGNED_INT56_LENGTH = 7;
22
    public const UNSIGNED_INT64_LENGTH = 8;
23
24
    private $data;
25
26
    /**
27
     * @var int
28
     */
29
    private $readBytes = 0;
30
31 88
    public function __construct(string $data)
32
    {
33 88
        $this->data = $data;
34
    }
35
36 3
    public static function pack64bit(int $value): string
37
    {
38 3
        return pack(
39 3
            'C8',
40 3
            ($value >> 0) & 0xFF,
41 3
            ($value >> 8) & 0xFF,
42 3
            ($value >> 16) & 0xFF,
43 3
            ($value >> 24) & 0xFF,
44 3
            ($value >> 32) & 0xFF,
45 3
            ($value >> 40) & 0xFF,
46 3
            ($value >> 48) & 0xFF,
47 3
            ($value >> 56) & 0xFF
48 3
        );
49
    }
50
51 60
    public function advance(int $length): void
52
    {
53 60
        $this->read($length);
54
    }
55
56 2
    public function readInt16(): int
57
    {
58 2
        return unpack('s', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
59
    }
60
61 85
    public function read(int $length): string
62
    {
63 85
        $return = substr($this->data, 0, $length);
64 85
        $this->readBytes += $length;
65 85
        $this->data = substr($this->data, $length);
66
67 85
        return $return;
68
    }
69
70 11
    public function unread(string $data): void
71
    {
72 11
        $this->readBytes -= strlen($data);
73 11
        $this->data = $data . $this->data;
74
    }
75
76
    /**
77
     * @throws BinaryDataReaderException
78
     */
79 56
    public function readCodedBinary(): ?int
80
    {
81 56
        $c = ord($this->read(self::UNSIGNED_CHAR_LENGTH));
82 56
        if ($c === self::NULL_COLUMN) {
83 1
            return null;
84
        }
85 56
        if ($c < self::UNSIGNED_CHAR_COLUMN) {
86 55
            return $c;
87
        }
88 2
        if ($c === self::UNSIGNED_SHORT_COLUMN) {
89 1
            return $this->readUInt16();
90
        }
91 2
        if ($c === self::UNSIGNED_INT24_COLUMN) {
92 1
            return $this->readUInt24();
93
        }
94
95 1
        throw new BinaryDataReaderException('Column num ' . $c . ' not handled');
96
    }
97
98 60
    public function readUInt16(): int
99
    {
100 60
        return unpack('v', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
101
    }
102
103 8
    public function readUInt24(): int
104
    {
105 8
        $data = unpack('C3', $this->read(self::UNSIGNED_INT24_LENGTH));
106
107 8
        return $data[1] + ($data[2] << 8) + ($data[3] << 16);
108
    }
109
110 6
    public function readUInt64(): string
111
    {
112 6
        return $this->unpackUInt64($this->read(self::UNSIGNED_INT64_LENGTH));
113
    }
114
115 57
    public function unpackUInt64(string $binary): string
116
    {
117 57
        $data = unpack('V*', $binary);
118
119 57
        return bcadd((string)$data[1], bcmul((string)$data[2], bcpow('2', '32')));
120
    }
121
122 2
    public function readInt24(): int
123
    {
124 2
        $data = unpack('C3', $this->read(self::UNSIGNED_INT24_LENGTH));
125
126 2
        $res = $data[1] | ($data[2] << 8) | ($data[3] << 16);
127 2
        if ($res >= 0x800000) {
128 2
            $res -= 0x1000000;
129
        }
130
131 2
        return $res;
132
    }
133
134 3
    public function readInt64(): string
135
    {
136 3
        $data = unpack('V*', $this->read(self::UNSIGNED_INT64_LENGTH));
137
138 3
        return bcadd((string)$data[1], (string)($data[2] << 32));
139
    }
140
141
    /**
142
     * @throws BinaryDataReaderException
143
     */
144 14
    public function readLengthString(int $size): string
145
    {
146 14
        return $this->read($this->readUIntBySize($size));
147
    }
148
149
    /**
150
     * @throws BinaryDataReaderException
151
     */
152 24
    public function readUIntBySize(int $size): int
153
    {
154 24
        if ($size === self::UNSIGNED_CHAR_LENGTH) {
155 12
            return $this->readUInt8();
156
        }
157 12
        if ($size === self::UNSIGNED_SHORT_LENGTH) {
158 3
            return $this->readUInt16();
159
        }
160 9
        if ($size === self::UNSIGNED_INT24_LENGTH) {
161 2
            return $this->readUInt24();
162
        }
163 7
        if ($size === self::UNSIGNED_INT32_LENGTH) {
164 3
            return $this->readUInt32();
165
        }
166 4
        if ($size === self::UNSIGNED_INT40_LENGTH) {
167 1
            return $this->readUInt40();
168
        }
169 3
        if ($size === self::UNSIGNED_INT48_LENGTH) {
170 1
            return $this->readUInt48();
171
        }
172 2
        if ($size === self::UNSIGNED_INT56_LENGTH) {
173 1
            return $this->readUInt56();
174
        }
175
176 1
        throw new BinaryDataReaderException('$size ' . $size . ' not handled');
177
    }
178
179 60
    public function readUInt8(): int
180
    {
181 60
        return unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
182
    }
183
184 59
    public function readUInt32(): int
185
    {
186 59
        return unpack('I', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
187
    }
188
189 1
    public function readUInt40(): int
190
    {
191 1
        $data1 = unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
192 1
        $data2 = unpack('I', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
193
194 1
        return $data1 + ($data2 << 8);
195
    }
196
197 1
    public function readUInt48(): int
198
    {
199 1
        $data = unpack('v3', $this->read(self::UNSIGNED_INT48_LENGTH));
200
201 1
        return $data[1] + ($data[2] << 16) + ($data[3] << 32);
202
    }
203
204 1
    public function readUInt56(): int
205
    {
206 1
        $data1 = unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
207 1
        $data2 = unpack('S', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
208 1
        $data3 = unpack('I', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
209
210 1
        return $data1 + ($data2 << 8) + ($data3 << 24);
211
    }
212
213
    /**
214
     * @throws BinaryDataReaderException
215
     */
216 21
    public function readIntBeBySize(int $size): int
217
    {
218 21
        if ($size === self::UNSIGNED_CHAR_LENGTH) {
219 7
            return $this->readInt8();
220
        }
221 16
        if ($size === self::UNSIGNED_SHORT_LENGTH) {
222 3
            return $this->readInt16Be();
223
        }
224 15
        if ($size === self::UNSIGNED_INT24_LENGTH) {
225 7
            return $this->readInt24Be();
226
        }
227 8
        if ($size === self::UNSIGNED_INT32_LENGTH) {
228 2
            return $this->readInt32Be();
229
        }
230 6
        if ($size === self::UNSIGNED_INT40_LENGTH) {
231 5
            return $this->readInt40Be();
232
        }
233
234 1
        throw new BinaryDataReaderException('$size ' . $size . ' not handled');
235
    }
236
237 11
    public function readInt8(): int
238
    {
239 11
        $re = unpack('c', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
240 11
        return $re >= 0x80 ? $re - 0x100 : $re;
241
    }
242
243 3
    public function readInt16Be(): int
244
    {
245 3
        $re = unpack('n', $this->read(self::UNSIGNED_SHORT_LENGTH))[1];
246 3
        return $re >= 0x8000 ? $re - 0x10000 : $re;
247
    }
248
249 9
    public function readInt24Be(): int
250
    {
251 9
        $data = unpack('C3', $this->read(self::UNSIGNED_INT24_LENGTH));
252 9
        $re = ($data[1] << 16) | ($data[2] << 8) | $data[3];
253 9
        return $re >= 0x800000 ? $re - 0x1000000 : $re;
254
    }
255
256 12
    public function readInt32Be(): int
257
    {
258 12
        $re = unpack('N', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
259 12
        return $re >= 0x80000000 ? $re - 0x100000000 : $re;
260
    }
261
262 5
    public function readInt40Be(): int
263
    {
264 5
        $data1 = unpack('N', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
265 5
        $data2 = unpack('C', $this->read(self::UNSIGNED_CHAR_LENGTH))[1];
266
267 5
        return $data2 + ($data1 << 8);
268
    }
269
270 59
    public function readInt32(): int
271
    {
272 59
        return unpack('i', $this->read(self::UNSIGNED_INT32_LENGTH))[1];
273
    }
274
275 2
    public function readFloat(): float
276
    {
277 2
        return unpack('f', $this->read(self::UNSIGNED_FLOAT_LENGTH))[1];
278
    }
279
280 2
    public function readDouble(): float
281
    {
282 2
        return unpack('d', $this->read(self::UNSIGNED_DOUBLE_LENGTH))[1];
283
    }
284
285 55
    public function readTableId(): string
286
    {
287 55
        return $this->unpackUInt64($this->read(self::UNSIGNED_INT48_LENGTH) . chr(0) . chr(0));
288
    }
289
290 53
    public function isComplete(int $size): bool
291
    {
292 53
        return ! ($this->readBytes - 20 < $size);
293
    }
294
295 1
    public function getBinaryDataLength(): int
296
    {
297 1
        return strlen($this->data);
298
    }
299
300 1
    public function getData(): string
301
    {
302 1
        return $this->data;
303
    }
304
305 6
    public function getBinarySlice(int $binary, int $start, int $size, int $binaryLength): int
306
    {
307 6
        $binary >>= $binaryLength - ($start + $size);
308 6
        $mask = ((1 << $size) - 1);
309
310 6
        return $binary & $mask;
311
    }
312
313 1
    public function getReadBytes(): int
314
    {
315 1
        return $this->readBytes;
316
    }
317
}
318