1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of the rybakit/msgpack.php package. |
5
|
|
|
* |
6
|
|
|
* (c) Eugene Leonovich <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace MessagePack; |
13
|
|
|
|
14
|
|
|
use Decimal\Decimal; |
15
|
|
|
use MessagePack\Exception\InsufficientDataException; |
16
|
|
|
use MessagePack\Exception\InvalidOptionException; |
17
|
|
|
use MessagePack\Exception\UnpackingFailedException; |
18
|
|
|
use MessagePack\TypeTransformer\Extension; |
19
|
|
|
|
20
|
|
|
class BufferUnpacker |
21
|
|
|
{ |
22
|
|
|
private $buffer; |
23
|
|
|
private $offset = 0; |
24
|
|
|
private $isBigIntAsDec; |
25
|
|
|
private $isBigIntAsGmp; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @var Extension[] |
29
|
|
|
*/ |
30
|
|
|
private $extensions = []; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @param UnpackOptions|int|null $options |
34
|
|
|
* @param Extension[] $extensions |
35
|
|
|
* |
36
|
|
|
* @throws InvalidOptionException |
37
|
|
|
*/ |
38
|
320 |
|
public function __construct(string $buffer = '', $options = null, array $extensions = []) |
39
|
|
|
{ |
40
|
320 |
|
if (\is_null($options)) { |
41
|
319 |
|
$options = UnpackOptions::fromDefaults(); |
42
|
8 |
|
} elseif (!$options instanceof UnpackOptions) { |
43
|
8 |
|
$options = UnpackOptions::fromBitmask($options); |
44
|
|
|
} |
45
|
|
|
|
46
|
320 |
|
$this->isBigIntAsDec = $options->isBigIntAsDecMode(); |
47
|
320 |
|
$this->isBigIntAsGmp = $options->isBigIntAsGmpMode(); |
48
|
|
|
|
49
|
320 |
|
$this->buffer = $buffer; |
50
|
|
|
|
51
|
320 |
|
if ($extensions) { |
|
|
|
|
52
|
1 |
|
foreach ($extensions as $extension) { |
53
|
1 |
|
$this->extensions[$extension->getType()] = $extension; |
54
|
|
|
} |
55
|
|
|
} |
56
|
320 |
|
} |
57
|
|
|
|
58
|
1 |
|
public function extendWith(Extension $extension, Extension ...$extensions) : self |
59
|
|
|
{ |
60
|
1 |
|
$new = clone $this; |
61
|
1 |
|
$new->extensions[$extension->getType()] = $extension; |
62
|
|
|
|
63
|
1 |
|
if ($extensions) { |
|
|
|
|
64
|
1 |
|
foreach ($extensions as $extraExtension) { |
65
|
1 |
|
$new->extensions[$extraExtension->getType()] = $extraExtension; |
66
|
|
|
} |
67
|
|
|
} |
68
|
|
|
|
69
|
1 |
|
return $new; |
70
|
|
|
} |
71
|
|
|
|
72
|
2 |
|
public function withBuffer(string $buffer) : self |
73
|
|
|
{ |
74
|
2 |
|
$new = clone $this; |
75
|
2 |
|
$new->buffer = $buffer; |
76
|
2 |
|
$new->offset = 0; |
77
|
|
|
|
78
|
2 |
|
return $new; |
79
|
|
|
} |
80
|
|
|
|
81
|
10 |
|
public function append(string $data) : self |
82
|
|
|
{ |
83
|
10 |
|
$this->buffer .= $data; |
84
|
|
|
|
85
|
10 |
|
return $this; |
86
|
|
|
} |
87
|
|
|
|
88
|
293 |
|
public function reset(string $buffer = '') : self |
89
|
|
|
{ |
90
|
293 |
|
$this->buffer = $buffer; |
91
|
293 |
|
$this->offset = 0; |
92
|
|
|
|
93
|
293 |
|
return $this; |
94
|
|
|
} |
95
|
|
|
|
96
|
3 |
|
public function seek(int $offset) : self |
97
|
|
|
{ |
98
|
3 |
|
if ($offset < 0) { |
99
|
1 |
|
$offset += \strlen($this->buffer); |
100
|
|
|
} |
101
|
|
|
|
102
|
3 |
|
if (!isset($this->buffer[$offset])) { |
103
|
1 |
|
throw new InsufficientDataException("Unable to seek to position $offset"); |
104
|
|
|
} |
105
|
|
|
|
106
|
2 |
|
$this->offset = $offset; |
107
|
|
|
|
108
|
2 |
|
return $this; |
109
|
|
|
} |
110
|
|
|
|
111
|
2 |
View Code Duplication |
public function skip(int $length) : self |
|
|
|
|
112
|
|
|
{ |
113
|
2 |
|
$offset = $this->offset + $length; |
114
|
|
|
|
115
|
2 |
|
if (!isset($this->buffer[$offset])) { |
116
|
1 |
|
throw new InsufficientDataException("Unable to seek to position $offset"); |
117
|
|
|
} |
118
|
|
|
|
119
|
1 |
|
$this->offset = $offset; |
120
|
|
|
|
121
|
1 |
|
return $this; |
122
|
|
|
} |
123
|
|
|
|
124
|
2 |
|
public function getRemainingCount() : int |
125
|
|
|
{ |
126
|
2 |
|
return \strlen($this->buffer) - $this->offset; |
127
|
|
|
} |
128
|
|
|
|
129
|
176 |
|
public function hasRemaining() : bool |
130
|
|
|
{ |
131
|
176 |
|
return isset($this->buffer[$this->offset]); |
132
|
|
|
} |
133
|
|
|
|
134
|
1 |
|
public function release() : int |
135
|
|
|
{ |
136
|
1 |
|
if (0 === $this->offset) { |
137
|
1 |
|
return 0; |
138
|
|
|
} |
139
|
|
|
|
140
|
1 |
|
$releasedBytesCount = $this->offset; |
141
|
1 |
|
$this->buffer = isset($this->buffer[$this->offset]) ? \substr($this->buffer, $this->offset) : ''; |
142
|
1 |
|
$this->offset = 0; |
143
|
|
|
|
144
|
1 |
|
return $releasedBytesCount; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* @param int $length |
149
|
|
|
* |
150
|
|
|
* @return string |
151
|
|
|
*/ |
152
|
47 |
View Code Duplication |
public function read($length) |
|
|
|
|
153
|
|
|
{ |
154
|
47 |
|
if (!isset($this->buffer[$this->offset + $length - 1])) { |
155
|
1 |
|
throw new InsufficientDataException(); |
156
|
|
|
} |
157
|
|
|
|
158
|
47 |
|
$data = \substr($this->buffer, $this->offset, $length); |
159
|
47 |
|
$this->offset += $length; |
160
|
|
|
|
161
|
47 |
|
return $data; |
162
|
|
|
} |
163
|
|
|
|
164
|
3 |
|
public function tryUnpack() : array |
165
|
|
|
{ |
166
|
3 |
|
$data = []; |
167
|
3 |
|
$offset = $this->offset; |
168
|
|
|
|
169
|
|
|
try { |
170
|
|
|
do { |
171
|
3 |
|
$data[] = $this->unpack(); |
172
|
3 |
|
$offset = $this->offset; |
173
|
3 |
|
} while (isset($this->buffer[$this->offset])); |
174
|
1 |
|
} catch (InsufficientDataException $e) { |
175
|
1 |
|
$this->offset = $offset; |
176
|
|
|
} |
177
|
|
|
|
178
|
3 |
|
return $data; |
179
|
|
|
} |
180
|
|
|
|
181
|
189 |
|
public function unpack() |
182
|
|
|
{ |
183
|
189 |
|
$buffer = &$this->buffer; |
184
|
4 |
|
$offset = &$this->offset; |
185
|
|
|
$stack = []; |
186
|
|
|
$top = 0; |
187
|
187 |
|
|
188
|
187 |
|
begin: |
189
|
|
|
if (!isset($buffer[$offset])) { |
190
|
|
|
throw new InsufficientDataException(); |
191
|
187 |
|
} |
192
|
29 |
|
|
193
|
|
|
$c = \ord($buffer[$offset]); |
194
|
|
|
++$offset; |
195
|
175 |
|
|
196
|
16 |
|
// fixint |
197
|
|
|
if ($c <= 0x7f) { |
198
|
|
|
$value = $c; |
199
|
169 |
|
goto end; |
200
|
5 |
|
} |
201
|
|
|
|
202
|
|
|
// fixstr |
203
|
|
|
if ($c >= 0xa0 && $c <= 0xbf) { |
204
|
165 |
|
if ($c & 0x1f) { |
205
|
163 |
|
$length = $c & 0x1f; |
206
|
159 |
|
goto read; |
207
|
|
|
} |
208
|
151 |
|
$value = ''; |
209
|
150 |
|
goto end; |
210
|
117 |
|
} |
211
|
116 |
|
|
212
|
116 |
|
// negfixint |
213
|
116 |
|
if ($c >= 0xe0) { |
214
|
116 |
|
$value = $c - 0x100; |
215
|
116 |
|
goto end; |
216
|
116 |
|
} |
217
|
116 |
|
|
218
|
116 |
|
switch ($c) { |
219
|
116 |
|
case 0xc0: $value = null; goto end; |
220
|
116 |
|
case 0xc2: $value = false; goto end; |
221
|
116 |
|
case 0xc3: $value = true; goto end; |
222
|
116 |
|
|
223
|
116 |
|
// fixmap |
224
|
|
|
case 0x80: $value = []; goto end; |
225
|
116 |
|
case 0x81: $stack[++$top] = new Container(2 /* map key */, 1); goto begin; |
226
|
115 |
|
case 0x82: $stack[++$top] = new Container(2 /* map key */, 2); goto begin; |
227
|
115 |
|
case 0x83: $stack[++$top] = new Container(2 /* map key */, 3); goto begin; |
228
|
110 |
|
case 0x84: $stack[++$top] = new Container(2 /* map key */, 4); goto begin; |
229
|
109 |
|
case 0x85: $stack[++$top] = new Container(2 /* map key */, 5); goto begin; |
230
|
109 |
|
case 0x86: $stack[++$top] = new Container(2 /* map key */, 6); goto begin; |
231
|
109 |
|
case 0x87: $stack[++$top] = new Container(2 /* map key */, 7); goto begin; |
232
|
109 |
|
case 0x88: $stack[++$top] = new Container(2 /* map key */, 8); goto begin; |
233
|
109 |
|
case 0x89: $stack[++$top] = new Container(2 /* map key */, 9); goto begin; |
234
|
109 |
|
case 0x8a: $stack[++$top] = new Container(2 /* map key */, 10); goto begin; |
235
|
109 |
|
case 0x8b: $stack[++$top] = new Container(2 /* map key */, 11); goto begin; |
236
|
109 |
|
case 0x8c: $stack[++$top] = new Container(2 /* map key */, 12); goto begin; |
237
|
109 |
|
case 0x8d: $stack[++$top] = new Container(2 /* map key */, 13); goto begin; |
238
|
109 |
|
case 0x8e: $stack[++$top] = new Container(2 /* map key */, 14); goto begin; |
239
|
109 |
|
case 0x8f: $stack[++$top] = new Container(2 /* map key */, 15); goto begin; |
240
|
109 |
|
|
241
|
|
|
// fixarray |
242
|
109 |
|
case 0x90: $value = []; goto end; |
243
|
104 |
|
case 0x91: $stack[++$top] = new Container(1 /* array item */, 1); goto begin; |
244
|
103 |
|
case 0x92: $stack[++$top] = new Container(1 /* array item */, 2); goto begin; |
245
|
|
|
case 0x93: $stack[++$top] = new Container(1 /* array item */, 3); goto begin; |
246
|
102 |
|
case 0x94: $stack[++$top] = new Container(1 /* array item */, 4); goto begin; |
247
|
99 |
|
case 0x95: $stack[++$top] = new Container(1 /* array item */, 5); goto begin; |
248
|
|
|
case 0x96: $stack[++$top] = new Container(1 /* array item */, 6); goto begin; |
249
|
95 |
|
case 0x97: $stack[++$top] = new Container(1 /* array item */, 7); goto begin; |
250
|
91 |
|
case 0x98: $stack[++$top] = new Container(1 /* array item */, 8); goto begin; |
251
|
85 |
|
case 0x99: $stack[++$top] = new Container(1 /* array item */, 9); goto begin; |
252
|
81 |
|
case 0x9a: $stack[++$top] = new Container(1 /* array item */, 10); goto begin; |
253
|
|
|
case 0x9b: $stack[++$top] = new Container(1 /* array item */, 11); goto begin; |
254
|
71 |
|
case 0x9c: $stack[++$top] = new Container(1 /* array item */, 12); goto begin; |
255
|
66 |
|
case 0x9d: $stack[++$top] = new Container(1 /* array item */, 13); goto begin; |
256
|
61 |
|
case 0x9e: $stack[++$top] = new Container(1 /* array item */, 14); goto begin; |
257
|
56 |
|
case 0x9f: $stack[++$top] = new Container(1 /* array item */, 15); goto begin; |
258
|
|
|
|
259
|
48 |
|
// bin |
260
|
44 |
|
case 0xc4: $length = self::unpackUint8($buffer, $offset); goto read; |
261
|
42 |
|
case 0xc5: $length = self::unpackUint16($buffer, $offset); goto read; |
262
|
|
|
case 0xc6: $length = self::unpackUint32($buffer, $offset); goto read; |
263
|
41 |
|
|
264
|
39 |
|
// float |
265
|
|
|
case 0xca: $value = self::unpackFloat32($buffer, $offset); goto end; |
266
|
38 |
|
case 0xcb: $value = self::unpackFloat64($buffer, $offset); goto end; |
267
|
36 |
|
|
268
|
|
|
// uint |
269
|
35 |
|
case 0xcc: $value = self::unpackUint8($buffer, $offset); goto end; |
270
|
30 |
|
case 0xcd: $value = self::unpackUint16($buffer, $offset); goto end; |
271
|
27 |
|
case 0xce: $value = self::unpackUint32($buffer, $offset); goto end; |
272
|
24 |
|
case 0xcf: $value = $this->unpackUint64(); goto end; |
273
|
21 |
|
|
274
|
18 |
|
// int |
275
|
11 |
|
case 0xd0: $value = self::unpackInt8($buffer, $offset); goto end; |
276
|
6 |
|
case 0xd1: $value = self::unpackInt16($buffer, $offset); goto end; |
277
|
|
|
case 0xd2: $value = self::unpackInt32($buffer, $offset); goto end; |
278
|
|
|
case 0xd3: $value = self::unpackInt64($buffer, $offset); goto end; |
279
|
1 |
|
|
280
|
|
|
// str |
281
|
|
|
case 0xd9: $length = self::unpackUint8($buffer, $offset); goto read; |
282
|
3 |
|
case 0xda: $length = self::unpackUint16($buffer, $offset); goto read; |
283
|
|
|
case 0xdb: $length = self::unpackUint32($buffer, $offset); goto read; |
284
|
3 |
|
|
285
|
1 |
|
// array |
286
|
|
|
case 0xdc: $stack[++$top] = new Container(1 /* array item */, self::unpackUint16($buffer, $offset)); goto begin; |
287
|
|
|
case 0xdd: $stack[++$top] = new Container(1 /* array item */, self::unpackUint32($buffer, $offset)); goto begin; |
288
|
2 |
|
|
289
|
1 |
|
// map |
290
|
|
|
case 0xde: $stack[++$top] = new Container(2 /* map key */, self::unpackUint16($buffer, $offset)); goto begin; |
291
|
1 |
|
case 0xdf: $stack[++$top] = new Container(2 /* map key */, self::unpackUint32($buffer, $offset)); goto begin; |
292
|
|
|
|
293
|
|
|
// ext |
294
|
1 |
|
case 0xd4: $value = $this->unpackExtData(1); goto end; |
295
|
|
|
case 0xd5: $value = $this->unpackExtData(2); goto end; |
296
|
|
|
case 0xd6: $value = $this->unpackExtData(4); goto end; |
297
|
5 |
|
case 0xd7: $value = $this->unpackExtData(8); goto end; |
298
|
|
|
case 0xd8: $value = $this->unpackExtData(16); goto end; |
299
|
5 |
|
case 0xc7: $value = $this->unpackExtData(self::unpackUint8($buffer, $offset)); goto end; |
300
|
1 |
|
case 0xc8: $value = $this->unpackExtData(self::unpackUint16($buffer, $offset)); goto end; |
301
|
|
|
case 0xc9: $value = $this->unpackExtData(self::unpackUint32($buffer, $offset)); goto end; |
302
|
|
|
} |
303
|
4 |
|
|
304
|
4 |
|
throw UnpackingFailedException::unknownCode($c); |
305
|
|
|
|
306
|
4 |
|
read: |
307
|
2 |
|
if (!isset($buffer[$offset + $length - 1])) { |
308
|
|
|
throw new InsufficientDataException(); |
309
|
2 |
|
} |
310
|
1 |
|
|
311
|
|
|
$value = \substr($buffer, $offset, $length); |
312
|
|
|
$offset += $length; |
313
|
1 |
|
|
314
|
|
|
end: |
315
|
|
|
while ($stack) { |
|
|
|
|
316
|
40 |
|
/** @var Container $state */ |
317
|
|
|
$state = $stack[$top]; |
318
|
40 |
|
if ($state->type === 1 /* array item */) { |
319
|
1 |
|
$state->value[] = $value; |
320
|
|
|
++$state->count; |
321
|
|
|
} else if ($state->type === 2 /* map key */) { |
322
|
39 |
|
$state->type = 3 /* map value */; |
323
|
39 |
|
$state->key = $value; |
324
|
|
|
goto begin; |
325
|
|
|
} else if ($state->type === 3 /* map value */) { |
326
|
39 |
|
$state->type = 2 /* map key */; |
327
|
3 |
|
$state->value[$state->key] = $value; |
328
|
|
|
++$state->count; |
329
|
|
|
} |
330
|
36 |
|
|
331
|
3 |
|
if ($state->size !== $state->count) { |
332
|
|
|
goto begin; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
unset($stack[$top]); |
336
|
33 |
|
--$top; |
337
|
30 |
|
$value = $state->value; |
338
|
27 |
|
} |
339
|
24 |
|
|
340
|
|
|
return $value; |
341
|
20 |
|
} |
342
|
16 |
|
|
343
|
12 |
|
public function unpackNil() |
344
|
8 |
|
{ |
345
|
|
|
$buffer = &$this->buffer; |
346
|
|
|
$offset = &$this->offset; |
347
|
1 |
|
|
348
|
|
|
if (!isset($buffer[$offset])) { |
349
|
|
|
throw new InsufficientDataException(); |
350
|
7 |
|
} |
351
|
|
|
|
352
|
7 |
|
if ("\xc0" === $buffer[$offset]) { |
353
|
1 |
|
++$offset; |
354
|
|
|
|
355
|
|
|
return null; |
356
|
6 |
|
} |
357
|
6 |
|
|
358
|
|
|
throw UnpackingFailedException::unexpectedCode(\ord($buffer[$offset++]), 'nil'); |
359
|
6 |
|
} |
360
|
3 |
|
|
361
|
|
|
public function unpackBool() |
362
|
3 |
|
{ |
363
|
2 |
|
$buffer = &$this->buffer; |
364
|
|
|
$offset = &$this->offset; |
365
|
|
|
|
366
|
1 |
|
if (!isset($buffer[$offset])) { |
367
|
|
|
throw new InsufficientDataException(); |
368
|
|
|
} |
369
|
14 |
|
|
370
|
|
|
$c = \ord($buffer[$offset]); |
371
|
14 |
|
++$offset; |
372
|
1 |
|
|
373
|
|
|
if (0xc2 === $c) { |
374
|
|
|
return false; |
375
|
13 |
|
} |
376
|
13 |
|
if (0xc3 === $c) { |
377
|
|
|
return true; |
378
|
13 |
|
} |
379
|
5 |
|
|
380
|
|
|
throw UnpackingFailedException::unexpectedCode($c, 'bool'); |
381
|
8 |
|
} |
382
|
4 |
|
|
383
|
|
|
public function unpackInt() |
384
|
4 |
|
{ |
385
|
2 |
|
$buffer = &$this->buffer; |
386
|
|
|
$offset = &$this->offset; |
387
|
2 |
|
|
388
|
1 |
|
if (!isset($buffer[$offset])) { |
389
|
|
|
throw new InsufficientDataException(); |
390
|
|
|
} |
391
|
1 |
|
|
392
|
|
|
$c = \ord($buffer[$offset]); |
393
|
|
|
++$offset; |
394
|
7 |
|
|
395
|
|
|
// fixint |
396
|
7 |
|
if ($c <= 0x7f) { |
397
|
1 |
|
return $c; |
398
|
|
|
} |
399
|
|
|
// negfixint |
400
|
6 |
|
if ($c >= 0xe0) { |
401
|
6 |
|
return $c - 0x100; |
402
|
|
|
} |
403
|
6 |
|
|
404
|
3 |
|
switch ($c) { |
405
|
|
|
// uint |
406
|
3 |
|
case 0xcc: return self::unpackUint8($buffer, $offset); |
407
|
1 |
|
case 0xcd: return self::unpackUint16($buffer, $offset); |
408
|
|
|
case 0xce: return self::unpackUint32($buffer, $offset); |
409
|
2 |
|
case 0xcf: return $this->unpackUint64(); |
410
|
1 |
|
// int |
411
|
|
|
case 0xd0: return self::unpackInt8($buffer, $offset); |
412
|
|
|
case 0xd1: return self::unpackInt16($buffer, $offset); |
413
|
1 |
|
case 0xd2: return self::unpackInt32($buffer, $offset); |
414
|
|
|
case 0xd3: return self::unpackInt64($buffer, $offset); |
415
|
|
|
} |
416
|
9 |
|
|
417
|
|
|
throw UnpackingFailedException::unexpectedCode($c, 'int'); |
418
|
9 |
|
} |
419
|
|
|
|
420
|
7 |
|
public function unpackFloat() |
421
|
7 |
|
{ |
422
|
6 |
|
$buffer = &$this->buffer; |
423
|
|
|
$offset = &$this->offset; |
424
|
|
|
|
425
|
7 |
|
if (!isset($buffer[$offset])) { |
426
|
|
|
throw new InsufficientDataException(); |
427
|
|
|
} |
428
|
9 |
|
|
429
|
|
|
$c = \ord($buffer[$offset]); |
430
|
9 |
|
++$offset; |
431
|
1 |
|
|
432
|
|
|
if (0xcb === $c) { |
433
|
|
|
return self::unpackFloat64($buffer, $offset); |
434
|
8 |
|
} |
435
|
8 |
|
if (0xca === $c) { |
436
|
|
|
return self::unpackFloat32($buffer, $offset); |
437
|
8 |
|
} |
438
|
4 |
|
|
439
|
|
|
throw UnpackingFailedException::unexpectedCode($c, 'float'); |
440
|
4 |
|
} |
441
|
2 |
|
|
442
|
|
|
public function unpackStr() |
443
|
2 |
|
{ |
444
|
1 |
|
$buffer = &$this->buffer; |
445
|
|
|
$offset = &$this->offset; |
446
|
|
|
|
447
|
1 |
|
if (!isset($buffer[$offset])) { |
448
|
|
|
throw new InsufficientDataException(); |
449
|
|
|
} |
450
|
43 |
|
|
451
|
|
|
$c = \ord($buffer[$offset]); |
452
|
43 |
|
++$offset; |
453
|
|
|
|
454
|
41 |
|
if ($c >= 0xa0 && $c <= 0xbf) { |
455
|
41 |
|
return ($c & 0x1f) ? $this->read($c & 0x1f) : ''; |
456
|
40 |
|
} |
457
|
|
|
if (0xd9 === $c) { |
458
|
|
|
return $this->read(self::unpackUint8($buffer, $offset)); |
459
|
9 |
|
} |
460
|
|
|
if (0xda === $c) { |
461
|
|
|
return $this->read(self::unpackUint16($buffer, $offset)); |
462
|
43 |
|
} |
463
|
|
|
if (0xdb === $c) { |
464
|
43 |
|
return $this->read(self::unpackUint32($buffer, $offset)); |
465
|
1 |
|
} |
466
|
|
|
|
467
|
|
|
throw UnpackingFailedException::unexpectedCode($c, 'str'); |
468
|
42 |
|
} |
469
|
42 |
|
|
470
|
|
|
public function unpackBin() |
471
|
42 |
|
{ |
472
|
38 |
|
$buffer = &$this->buffer; |
473
|
|
|
$offset = &$this->offset; |
474
|
4 |
|
|
475
|
2 |
|
if (!isset($buffer[$offset])) { |
476
|
|
|
throw new InsufficientDataException(); |
477
|
2 |
|
} |
478
|
1 |
|
|
479
|
|
|
$c = \ord($buffer[$offset]); |
480
|
|
|
++$offset; |
481
|
1 |
|
|
482
|
|
|
if (0xc4 === $c) { |
483
|
|
|
return $this->read(self::unpackUint8($buffer, $offset)); |
484
|
13 |
|
} |
485
|
|
|
if (0xc5 === $c) { |
486
|
13 |
|
return $this->read(self::unpackUint16($buffer, $offset)); |
487
|
1 |
|
} |
488
|
|
|
if (0xc6 === $c) { |
489
|
|
|
return $this->read(self::unpackUint32($buffer, $offset)); |
490
|
12 |
|
} |
491
|
12 |
|
|
492
|
|
|
throw UnpackingFailedException::unexpectedCode($c, 'bin'); |
493
|
|
|
} |
494
|
12 |
|
|
495
|
11 |
|
public function unpackArray() |
496
|
10 |
|
{ |
497
|
9 |
|
$size = $this->unpackArrayHeader(); |
498
|
8 |
|
|
499
|
7 |
|
$array = []; |
500
|
5 |
|
while ($size--) { |
501
|
3 |
|
$array[] = $this->unpack(); |
502
|
|
|
} |
503
|
|
|
|
504
|
1 |
|
return $array; |
505
|
|
|
} |
506
|
|
|
|
507
|
38 |
View Code Duplication |
public function unpackArrayHeader() |
|
|
|
|
508
|
|
|
{ |
509
|
38 |
|
$buffer = &$this->buffer; |
510
|
2 |
|
$offset = &$this->offset; |
511
|
|
|
|
512
|
|
|
if (!isset($buffer[$offset])) { |
513
|
36 |
|
throw new InsufficientDataException(); |
514
|
|
|
} |
515
|
|
|
|
516
|
32 |
|
$c = \ord($buffer[$offset]); |
517
|
|
|
++$offset; |
518
|
32 |
|
|
519
|
2 |
|
if ($c >= 0x90 && $c <= 0x9f) { |
520
|
|
|
return $c & 0xf; |
521
|
|
|
} |
522
|
30 |
|
if (0xdc === $c) { |
523
|
30 |
|
return self::unpackUint16($buffer, $offset); |
524
|
|
|
} |
525
|
|
|
if (0xdd === $c) { |
526
|
24 |
|
return self::unpackUint32($buffer, $offset); |
527
|
|
|
} |
528
|
24 |
|
|
529
|
2 |
|
throw UnpackingFailedException::unexpectedCode($c, 'array'); |
530
|
|
|
} |
531
|
|
|
|
532
|
22 |
|
public function unpackMap() |
533
|
22 |
|
{ |
534
|
22 |
|
$size = $this->unpackMapHeader(); |
535
|
22 |
|
|
536
|
|
|
$map = []; |
537
|
|
|
while ($size--) { |
538
|
16 |
|
//$map[$this->unpackMapKey()] = $this->unpack(); |
539
|
|
|
$map[$this->unpack()] = $this->unpack(); |
540
|
16 |
|
} |
541
|
1 |
|
|
542
|
|
|
return $map; |
543
|
|
|
} |
544
|
15 |
|
|
545
|
15 |
View Code Duplication |
public function unpackMapHeader() |
|
|
|
|
546
|
|
|
{ |
547
|
15 |
|
$buffer = &$this->buffer; |
548
|
10 |
|
$offset = &$this->offset; |
549
|
|
|
|
550
|
5 |
|
if (!isset($buffer[$offset])) { |
551
|
1 |
|
throw new InsufficientDataException(); |
552
|
|
|
} |
553
|
4 |
|
|
554
|
1 |
|
$c = \ord($buffer[$offset]); |
555
|
|
|
++$offset; |
556
|
|
|
|
557
|
3 |
|
if ($c >= 0x80 && $c <= 0x8f) { |
558
|
|
|
return $c & 0xf; |
559
|
|
|
} |
560
|
9 |
|
if (0xde === $c) { |
561
|
|
|
return self::unpackUint16($buffer, $offset); |
562
|
9 |
|
} |
563
|
1 |
|
if (0xdf === $c) { |
564
|
|
|
return self::unpackUint32($buffer, $offset); |
565
|
|
|
} |
566
|
8 |
|
|
567
|
8 |
|
throw UnpackingFailedException::unexpectedCode($c, 'map'); |
568
|
|
|
} |
569
|
8 |
|
|
570
|
|
|
public function unpackExt() |
571
|
|
|
{ |
572
|
9 |
|
$buffer = &$this->buffer; |
573
|
|
|
$offset = &$this->offset; |
574
|
9 |
|
|
575
|
1 |
|
if (!isset($buffer[$offset])) { |
576
|
|
|
throw new InsufficientDataException(); |
577
|
|
|
} |
578
|
8 |
|
|
579
|
8 |
|
$c = \ord($buffer[$offset]); |
580
|
8 |
|
++$offset; |
581
|
|
|
|
582
|
8 |
|
switch ($c) { |
583
|
|
|
case 0xd4: return $this->unpackExtData(1); |
584
|
|
|
case 0xd5: return $this->unpackExtData(2); |
585
|
9 |
|
case 0xd6: return $this->unpackExtData(4); |
586
|
|
|
case 0xd7: return $this->unpackExtData(8); |
587
|
9 |
|
case 0xd8: return $this->unpackExtData(16); |
588
|
1 |
|
case 0xc7: return $this->unpackExtData(self::unpackUint8($buffer, $offset)); |
589
|
|
|
case 0xc8: return $this->unpackExtData(self::unpackUint16($buffer, $offset)); |
590
|
|
|
case 0xc9: return $this->unpackExtData(self::unpackUint32($buffer, $offset)); |
591
|
8 |
|
} |
592
|
8 |
|
|
593
|
8 |
|
throw UnpackingFailedException::unexpectedCode($c, 'ext'); |
594
|
8 |
|
} |
595
|
8 |
|
|
596
|
|
|
private static function unpackUint8($buffer, &$offset) |
597
|
8 |
|
{ |
598
|
|
|
if (!isset($buffer[$offset])) { |
599
|
|
|
throw new InsufficientDataException(); |
600
|
15 |
|
} |
601
|
|
|
|
602
|
15 |
|
return \ord($buffer[$offset++]); |
603
|
1 |
|
} |
604
|
|
|
|
605
|
|
|
private static function unpackUint16($buffer, &$offset) |
606
|
14 |
|
{ |
607
|
14 |
|
if (!isset($buffer[$offset + 1])) { |
608
|
|
|
throw new InsufficientDataException(); |
609
|
14 |
|
} |
610
|
|
|
|
611
|
|
|
return \ord($buffer[$offset++]) << 8 |
612
|
5 |
|
| \ord($buffer[$offset++]); |
613
|
|
|
} |
614
|
5 |
|
|
615
|
1 |
|
private static function unpackUint32($buffer, &$offset) |
616
|
|
|
{ |
617
|
|
|
if (!isset($buffer[$offset + 3])) { |
618
|
4 |
|
throw new InsufficientDataException(); |
619
|
4 |
|
} |
620
|
|
|
|
621
|
4 |
|
return \ord($buffer[$offset++]) << 24 |
622
|
|
|
| \ord($buffer[$offset++]) << 16 |
623
|
|
|
| \ord($buffer[$offset++]) << 8 |
624
|
7 |
|
| \ord($buffer[$offset++]); |
625
|
|
|
} |
626
|
7 |
|
|
627
|
1 |
|
private function unpackUint64() |
628
|
|
|
{ |
629
|
|
|
$buffer = &$this->buffer; |
630
|
6 |
|
$offset = &$this->offset; |
631
|
6 |
|
|
632
|
|
|
if (!isset($buffer[$offset + 7])) { |
633
|
6 |
|
throw new InsufficientDataException(); |
634
|
|
|
} |
635
|
|
|
|
636
|
4 |
|
$num = \unpack('J', $buffer, $offset)[1]; |
637
|
|
|
$offset += 8; |
638
|
4 |
|
|
639
|
4 |
|
if ($num >= 0) { |
640
|
4 |
|
return $num; |
641
|
|
|
} |
642
|
|
|
if ($this->isBigIntAsDec) { |
643
|
4 |
|
return new Decimal(\sprintf('%u', $num)); |
644
|
|
|
} |
645
|
|
|
if ($this->isBigIntAsGmp) { |
646
|
5 |
|
return \gmp_import(\substr($buffer, $offset - 8, 8)); |
647
|
|
|
} |
648
|
5 |
|
|
649
|
5 |
|
return \sprintf('%u', $num); |
650
|
5 |
|
} |
651
|
|
|
|
652
|
|
|
private static function unpackInt8($buffer, &$offset) |
653
|
5 |
|
{ |
654
|
|
|
if (!isset($buffer[$offset])) { |
655
|
|
|
throw new InsufficientDataException(); |
656
|
82 |
|
} |
657
|
|
|
|
658
|
82 |
|
$num = \ord($buffer[$offset]); |
659
|
|
|
++$offset; |
660
|
|
|
|
661
|
|
|
return $num > 0x7f ? $num - 0x100 : $num; |
662
|
82 |
|
} |
663
|
82 |
|
|
664
|
|
|
private static function unpackInt16($buffer, &$offset) |
665
|
|
|
{ |
666
|
82 |
|
if (!isset($buffer[$offset + 1])) { |
667
|
14 |
|
throw new InsufficientDataException(); |
668
|
|
|
} |
669
|
|
|
|
670
|
76 |
|
$num = \ord($buffer[$offset]) << 8 |
671
|
2 |
|
| \ord($buffer[++$offset]); |
672
|
|
|
++$offset; |
673
|
|
|
|
674
|
74 |
|
return $num > 0x7fff ? $num - 0x10000 : $num; |
675
|
2 |
|
} |
676
|
|
|
|
677
|
|
|
private static function unpackInt32($buffer, &$offset) |
678
|
|
|
{ |
679
|
|
|
if (!isset($buffer[$offset + 3])) { |
680
|
72 |
|
throw new InsufficientDataException(); |
681
|
72 |
|
} |
682
|
70 |
|
|
683
|
68 |
|
$num = \ord($buffer[$offset]) << 24 |
684
|
|
|
| \ord($buffer[++$offset]) << 16 |
685
|
66 |
|
| \ord($buffer[++$offset]) << 8 |
686
|
66 |
|
| \ord($buffer[++$offset]); |
687
|
66 |
|
++$offset; |
688
|
66 |
|
|
689
|
|
|
return $num > 0x7fffffff ? $num - 0x100000000 : $num; |
690
|
66 |
|
} |
691
|
66 |
|
|
692
|
66 |
View Code Duplication |
private static function unpackInt64($buffer, &$offset) |
|
|
|
|
693
|
|
|
{ |
694
|
66 |
|
if (!isset($buffer[$offset + 7])) { |
695
|
64 |
|
throw new InsufficientDataException(); |
696
|
64 |
|
} |
697
|
|
|
|
698
|
|
|
$num = \unpack('J', $buffer, $offset)[1]; |
699
|
64 |
|
$offset += 8; |
700
|
|
|
|
701
|
|
|
return $num; |
702
|
42 |
|
} |
703
|
|
|
|
704
|
42 |
View Code Duplication |
private static function unpackFloat32($buffer, &$offset) |
|
|
|
|
705
|
16 |
|
{ |
706
|
|
|
if (!isset($buffer[$offset + 3])) { |
707
|
|
|
throw new InsufficientDataException(); |
708
|
|
|
} |
709
|
26 |
|
|
710
|
26 |
|
$num = \unpack('G', $buffer, $offset)[1]; |
711
|
|
|
$offset += 4; |
712
|
26 |
|
|
713
|
|
|
return $num; |
714
|
|
|
} |
715
|
|
|
|
716
|
26 |
View Code Duplication |
private static function unpackFloat64($buffer, &$offset) |
|
|
|
|
717
|
2 |
|
{ |
718
|
|
|
if (!isset($buffer[$offset + 7])) { |
719
|
|
|
throw new InsufficientDataException(); |
720
|
24 |
|
} |
721
|
24 |
|
|
722
|
|
|
$num = \unpack('E', $buffer, $offset)[1]; |
723
|
24 |
|
$offset += 8; |
724
|
|
|
|
725
|
|
|
return $num; |
726
|
|
|
} |
727
|
|
|
|
728
|
|
|
private function unpackExtData($length) |
729
|
|
|
{ |
730
|
|
|
$buffer = &$this->buffer; |
731
|
|
|
$offset = &$this->offset; |
732
|
|
|
|
733
|
|
|
if (!isset($buffer[$offset + $length])) { // 1 (type) + length - 1 |
734
|
|
|
throw new InsufficientDataException(); |
735
|
|
|
} |
736
|
|
|
|
737
|
|
|
// int8 |
738
|
|
|
$type = \ord($buffer[$offset]); |
739
|
|
|
++$offset; |
740
|
|
|
|
741
|
|
|
if ($type > 0x7f) { |
742
|
|
|
$type -= 0x100; |
743
|
|
|
} |
744
|
|
|
|
745
|
|
|
if (isset($this->extensions[$type])) { |
746
|
|
|
return $this->extensions[$type]->unpackExt($this, $length); |
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
$data = \substr($buffer, $offset, $length); |
750
|
|
|
$offset += $length; |
751
|
|
|
|
752
|
|
|
return new Ext($type, $data); |
753
|
|
|
} |
754
|
|
|
} |
755
|
|
|
|
756
|
|
|
final class Container |
757
|
|
|
{ |
758
|
|
|
public $type; |
759
|
|
|
public $size; |
760
|
|
|
public $count = 0; |
761
|
|
|
public $value; |
762
|
|
|
public $key; |
763
|
|
|
|
764
|
|
|
public function __construct($type, $size) |
765
|
|
|
{ |
766
|
|
|
$this->type = $type; |
767
|
|
|
$this->size = $size; |
768
|
|
|
} |
769
|
|
|
} |
770
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.