Issues (4)

src/BufferUnpacker.php (2 issues)

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\Type\Ext;
19
20
class BufferUnpacker
21
{
22
    /** @var string */
23
    private $buffer;
24
25
    /** @var int */
26
    private $offset = 0;
27
28
    /** @var bool */
29
    private $isBigIntAsDec;
30
31
    /** @var bool */
32
    private $isBigIntAsGmp;
33
34
    /** @var Extension[] */
35
    private $extensions = [];
36
37
    /**
38
     * @param UnpackOptions|int|null $options
39
     * @param Extension[] $extensions
40
     *
41
     * @throws InvalidOptionException
42
     */
43 346
    public function __construct(string $buffer = '', $options = null, array $extensions = [])
44
    {
45 346
        if (\is_null($options)) {
46 345
            $options = UnpackOptions::fromDefaults();
47 11
        } elseif (!$options instanceof UnpackOptions) {
48 10
            $options = UnpackOptions::fromBitmask($options);
49
        }
50
51 346
        $this->isBigIntAsDec = $options->isBigIntAsDecMode();
52 346
        $this->isBigIntAsGmp = $options->isBigIntAsGmpMode();
53
54 346
        $this->buffer = $buffer;
55
56 346
        if ($extensions) {
57 341
            foreach ($extensions as $extension) {
58 341
                $this->extensions[$extension->getType()] = $extension;
59
            }
60
        }
61
    }
62
63 1
    public function extendWith(Extension $extension, Extension ...$extensions) : self
64
    {
65 1
        $new = clone $this;
66 1
        $new->extensions[$extension->getType()] = $extension;
67
68 1
        if ($extensions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $extensions of type array<integer,MessagePack\Extension> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

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