Passed
Push — master ( 342a74...4ed09e )
by Eugene
02:09
created

BufferUnpacker::handleIntOverflow()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 1
dl 0
loc 10
ccs 6
cts 6
cp 1
crap 3
rs 10
c 0
b 0
f 0
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 MessagePack\Exception\InsufficientDataException;
15
use MessagePack\Exception\InvalidOptionException;
16
use MessagePack\Exception\UnpackingFailedException;
17
use MessagePack\TypeTransformer\Extension;
18
19
class BufferUnpacker
20
{
21
    private $buffer;
22
    private $offset = 0;
23
    private $isBigIntAsStr;
24
    private $isBigIntAsGmp;
25
26
    /**
27
     * @var Extension[]
28
     */
29
    private $extensions = [];
30
31
    /**
32
     * @param UnpackOptions|int|null $options
33
     * @param Extension[] $extensions
34
     *
35
     * @throws InvalidOptionException
36
     */
37 320
    public function __construct(string $buffer = '', $options = null, array $extensions = [])
38
    {
39 320
        if (\is_null($options)) {
40 319
            $options = UnpackOptions::fromDefaults();
41 8
        } elseif (!$options instanceof PackOptions) {
0 ignored issues
show
introduced by
$options is never a sub-type of MessagePack\PackOptions.
Loading history...
42 8
            $options = UnpackOptions::fromBitmask($options);
0 ignored issues
show
Bug introduced by
It seems like $options can also be of type MessagePack\UnpackOptions; however, parameter $bitmask of MessagePack\UnpackOptions::fromBitmask() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

42
            $options = UnpackOptions::fromBitmask(/** @scrutinizer ignore-type */ $options);
Loading history...
43
        }
44
45 320
        $this->isBigIntAsStr = $options->isBigIntAsStrMode();
46 320
        $this->isBigIntAsGmp = $options->isBigIntAsGmpMode();
47
48 320
        $this->buffer = $buffer;
49
50 320
        if ([] !== $extensions) {
51 1
            $this->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
    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
    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
        if (!isset($this->buffer[$this->offset])) {
184 4
            throw new InsufficientDataException();
185
        }
186
187 187
        $c = \ord($this->buffer[$this->offset]);
188 187
        ++$this->offset;
189
190
        // fixint
191 187
        if ($c <= 0x7f) {
192 29
            return $c;
193
        }
194
        // fixstr
195 175
        if ($c >= 0xa0 && $c <= 0xbf) {
196 16
            return ($c & 0x1f) ? $this->read($c & 0x1f) : '';
197
        }
198
        // negfixint
199 169
        if ($c >= 0xe0) {
200 5
            return $c - 0x100;
201
        }
202
203
        switch ($c) {
204 165
            case 0xc0: return null;
205 163
            case 0xc2: return false;
206 159
            case 0xc3: return true;
207
208
            // fixmap
209 151
            case 0x80: return [];
210 150
            case 0x81: return [$this->unpackMapKey() => $this->unpack()];
211 117
            case 0x82: return [$this->unpackMapKey() => $this->unpack(), $this->unpackMapKey() => $this->unpack()];
212 116
            case 0x83: return [$this->unpackMapKey() => $this->unpack(), $this->unpackMapKey() => $this->unpack(), $this->unpackMapKey() => $this->unpack()];
213 116
            case 0x84: return $this->unpackMapData(4);
214 116
            case 0x85: return $this->unpackMapData(5);
215 116
            case 0x86: return $this->unpackMapData(6);
216 116
            case 0x87: return $this->unpackMapData(7);
217 116
            case 0x88: return $this->unpackMapData(8);
218 116
            case 0x89: return $this->unpackMapData(9);
219 116
            case 0x8a: return $this->unpackMapData(10);
220 116
            case 0x8b: return $this->unpackMapData(11);
221 116
            case 0x8c: return $this->unpackMapData(12);
222 116
            case 0x8d: return $this->unpackMapData(13);
223 116
            case 0x8e: return $this->unpackMapData(14);
224 116
            case 0x8f: return $this->unpackMapData(15);
225
226
            // fixarray
227 116
            case 0x90: return [];
228 115
            case 0x91: return [$this->unpack()];
229 115
            case 0x92: return [$this->unpack(), $this->unpack()];
230 110
            case 0x93: return [$this->unpack(), $this->unpack(), $this->unpack()];
231 109
            case 0x94: return $this->unpackArrayData(4);
232 109
            case 0x95: return $this->unpackArrayData(5);
233 109
            case 0x96: return $this->unpackArrayData(6);
234 109
            case 0x97: return $this->unpackArrayData(7);
235 109
            case 0x98: return $this->unpackArrayData(8);
236 109
            case 0x99: return $this->unpackArrayData(9);
237 109
            case 0x9a: return $this->unpackArrayData(10);
238 109
            case 0x9b: return $this->unpackArrayData(11);
239 109
            case 0x9c: return $this->unpackArrayData(12);
240 109
            case 0x9d: return $this->unpackArrayData(13);
241 109
            case 0x9e: return $this->unpackArrayData(14);
242 109
            case 0x9f: return $this->unpackArrayData(15);
243
244
            // bin
245 109
            case 0xc4: return $this->read($this->unpackUint8());
246 104
            case 0xc5: return $this->read($this->unpackUint16());
247 103
            case 0xc6: return $this->read($this->unpackUint32());
248
249
            // float
250 102
            case 0xca: return $this->unpackFloat32();
251 99
            case 0xcb: return $this->unpackFloat64();
252
253
            // uint
254 95
            case 0xcc: return $this->unpackUint8();
255 91
            case 0xcd: return $this->unpackUint16();
256 85
            case 0xce: return $this->unpackUint32();
257 81
            case 0xcf: return $this->unpackUint64();
258
259
            // int
260 71
            case 0xd0: return $this->unpackInt8();
261 66
            case 0xd1: return $this->unpackInt16();
262 61
            case 0xd2: return $this->unpackInt32();
263 56
            case 0xd3: return $this->unpackInt64();
264
265
            // str
266 48
            case 0xd9: return $this->read($this->unpackUint8());
267 44
            case 0xda: return $this->read($this->unpackUint16());
268 42
            case 0xdb: return $this->read($this->unpackUint32());
269
270
            // array
271 41
            case 0xdc: return $this->unpackArrayData($this->unpackUint16());
272 39
            case 0xdd: return $this->unpackArrayData($this->unpackUint32());
273
274
            // map
275 38
            case 0xde: return $this->unpackMapData($this->unpackUint16());
276 36
            case 0xdf: return $this->unpackMapData($this->unpackUint32());
277
278
            // ext
279 35
            case 0xd4: return $this->unpackExtData(1);
280 30
            case 0xd5: return $this->unpackExtData(2);
281 27
            case 0xd6: return $this->unpackExtData(4);
282 24
            case 0xd7: return $this->unpackExtData(8);
283 21
            case 0xd8: return $this->unpackExtData(16);
284 18
            case 0xc7: return $this->unpackExtData($this->unpackUint8());
285 11
            case 0xc8: return $this->unpackExtData($this->unpackUint16());
286 6
            case 0xc9: return $this->unpackExtData($this->unpackUint32());
287
        }
288
289 1
        throw UnpackingFailedException::unknownCode($c);
290
    }
291
292 3
    public function unpackNil()
293
    {
294 3
        if (!isset($this->buffer[$this->offset])) {
295 1
            throw new InsufficientDataException();
296
        }
297
298 2
        if ("\xc0" === $this->buffer[$this->offset]) {
299 1
            ++$this->offset;
300
301 1
            return null;
302
        }
303
304 1
        throw UnpackingFailedException::unexpectedCode(\ord($this->buffer[$this->offset++]), 'nil');
305
    }
306
307 5
    public function unpackBool()
308
    {
309 5
        if (!isset($this->buffer[$this->offset])) {
310 1
            throw new InsufficientDataException();
311
        }
312
313 4
        $c = \ord($this->buffer[$this->offset]);
314 4
        ++$this->offset;
315
316 4
        if (0xc2 === $c) {
317 2
            return false;
318
        }
319 2
        if (0xc3 === $c) {
320 1
            return true;
321
        }
322
323 1
        throw UnpackingFailedException::unexpectedCode($c, 'bool');
324
    }
325
326 40
    public function unpackInt()
327
    {
328 40
        if (!isset($this->buffer[$this->offset])) {
329 1
            throw new InsufficientDataException();
330
        }
331
332 39
        $c = \ord($this->buffer[$this->offset]);
333 39
        ++$this->offset;
334
335
        // fixint
336 39
        if ($c <= 0x7f) {
337 3
            return $c;
338
        }
339
        // negfixint
340 36
        if ($c >= 0xe0) {
341 3
            return $c - 0x100;
342
        }
343
344
        switch ($c) {
345
            // uint
346 33
            case 0xcc: return $this->unpackUint8();
347 30
            case 0xcd: return $this->unpackUint16();
348 27
            case 0xce: return $this->unpackUint32();
349 24
            case 0xcf: return $this->unpackUint64();
350
351
            // int
352 20
            case 0xd0: return $this->unpackInt8();
353 16
            case 0xd1: return $this->unpackInt16();
354 12
            case 0xd2: return $this->unpackInt32();
355 8
            case 0xd3: return $this->unpackInt64();
356
        }
357
358 1
        throw UnpackingFailedException::unexpectedCode($c, 'int');
359
    }
360
361 7
    public function unpackFloat()
362
    {
363 7
        if (!isset($this->buffer[$this->offset])) {
364 1
            throw new InsufficientDataException();
365
        }
366
367 6
        $c = \ord($this->buffer[$this->offset]);
368 6
        ++$this->offset;
369
370 6
        if (0xcb === $c) {
371 3
            return $this->unpackFloat64();
372
        }
373 3
        if (0xca === $c) {
374 2
            return $this->unpackFloat32();
375
        }
376
377 1
        throw UnpackingFailedException::unexpectedCode($c, 'float');
378
    }
379
380 14
    public function unpackStr()
381
    {
382 14
        if (!isset($this->buffer[$this->offset])) {
383 1
            throw new InsufficientDataException();
384
        }
385
386 13
        $c = \ord($this->buffer[$this->offset]);
387 13
        ++$this->offset;
388
389 13
        if ($c >= 0xa0 && $c <= 0xbf) {
390 5
            return ($c & 0x1f) ? $this->read($c & 0x1f) : '';
391
        }
392 8
        if (0xd9 === $c) {
393 4
            return $this->read($this->unpackUint8());
394
        }
395 4
        if (0xda === $c) {
396 2
            return $this->read($this->unpackUint16());
397
        }
398 2
        if (0xdb === $c) {
399 1
            return $this->read($this->unpackUint32());
400
        }
401
402 1
        throw UnpackingFailedException::unexpectedCode($c, 'str');
403
    }
404
405 7
    public function unpackBin()
406
    {
407 7
        if (!isset($this->buffer[$this->offset])) {
408 1
            throw new InsufficientDataException();
409
        }
410
411 6
        $c = \ord($this->buffer[$this->offset]);
412 6
        ++$this->offset;
413
414 6
        if (0xc4 === $c) {
415 3
            return $this->read($this->unpackUint8());
416
        }
417 3
        if (0xc5 === $c) {
418 1
            return $this->read($this->unpackUint16());
419
        }
420 2
        if (0xc6 === $c) {
421 1
            return $this->read($this->unpackUint32());
422
        }
423
424 1
        throw UnpackingFailedException::unexpectedCode($c, 'bin');
425
    }
426
427 9
    public function unpackArray()
428
    {
429 9
        $size = $this->unpackArrayHeader();
430
431 7
        $array = [];
432 7
        while ($size--) {
433 6
            $array[] = $this->unpack();
434
        }
435
436 7
        return $array;
437
    }
438
439 9
    public function unpackArrayHeader()
440
    {
441 9
        if (!isset($this->buffer[$this->offset])) {
442 1
            throw new InsufficientDataException();
443
        }
444
445 8
        $c = \ord($this->buffer[$this->offset]);
446 8
        ++$this->offset;
447
448 8
        if ($c >= 0x90 && $c <= 0x9f) {
449 4
            return $c & 0xf;
450
        }
451 4
        if (0xdc === $c) {
452 2
            return $this->unpackUint16();
453
        }
454 2
        if (0xdd === $c) {
455 1
            return $this->unpackUint32();
456
        }
457
458 1
        throw UnpackingFailedException::unexpectedCode($c, 'array');
459
    }
460
461 43
    public function unpackMap()
462
    {
463 43
        $size = $this->unpackMapHeader();
464
465 41
        $map = [];
466 41
        while ($size--) {
467 40
            $map[$this->unpackMapKey()] = $this->unpack();
468
        }
469
470 9
        return $map;
471
    }
472
473 43
    public function unpackMapHeader()
474
    {
475 43
        if (!isset($this->buffer[$this->offset])) {
476 1
            throw new InsufficientDataException();
477
        }
478
479 42
        $c = \ord($this->buffer[$this->offset]);
480 42
        ++$this->offset;
481
482 42
        if ($c >= 0x80 && $c <= 0x8f) {
483 38
            return $c & 0xf;
484
        }
485 4
        if (0xde === $c) {
486 2
            return $this->unpackUint16();
487
        }
488 2
        if (0xdf === $c) {
489 1
            return $this->unpackUint32();
490
        }
491
492 1
        throw UnpackingFailedException::unexpectedCode($c, 'map');
493
    }
494
495 13
    public function unpackExt()
496
    {
497 13
        if (!isset($this->buffer[$this->offset])) {
498 1
            throw new InsufficientDataException();
499
        }
500
501 12
        $c = \ord($this->buffer[$this->offset]);
502 12
        ++$this->offset;
503
504
        switch ($c) {
505 12
            case 0xd4: return $this->unpackExtData(1);
506 11
            case 0xd5: return $this->unpackExtData(2);
507 10
            case 0xd6: return $this->unpackExtData(4);
508 9
            case 0xd7: return $this->unpackExtData(8);
509 8
            case 0xd8: return $this->unpackExtData(16);
510 7
            case 0xc7: return $this->unpackExtData($this->unpackUint8());
511 5
            case 0xc8: return $this->unpackExtData($this->unpackUint16());
512 3
            case 0xc9: return $this->unpackExtData($this->unpackUint32());
513
        }
514
515 1
        throw UnpackingFailedException::unexpectedCode($c, 'ext');
516
    }
517
518 38
    private function unpackUint8()
519
    {
520 38
        if (!isset($this->buffer[$this->offset])) {
521 2
            throw new InsufficientDataException();
522
        }
523
524 36
        return \ord($this->buffer[$this->offset++]);
525
    }
526
527 32
    private function unpackUint16()
528
    {
529 32
        if (!isset($this->buffer[$this->offset + 1])) {
530 2
            throw new InsufficientDataException();
531
        }
532
533 30
        return \ord($this->buffer[$this->offset++]) << 8
534 30
            | \ord($this->buffer[$this->offset++]);
535
    }
536
537 24
    private function unpackUint32()
538
    {
539 24
        if (!isset($this->buffer[$this->offset + 3])) {
540 2
            throw new InsufficientDataException();
541
        }
542
543 22
        return \ord($this->buffer[$this->offset++]) << 24
544 22
            | \ord($this->buffer[$this->offset++]) << 16
545 22
            | \ord($this->buffer[$this->offset++]) << 8
546 22
            | \ord($this->buffer[$this->offset++]);
547
    }
548
549 16
    private function unpackUint64()
550
    {
551 16
        if (!isset($this->buffer[$this->offset + 7])) {
552 1
            throw new InsufficientDataException();
553
        }
554
555 15
        $num = \unpack('J', $this->buffer, $this->offset)[1];
556 15
        $this->offset += 8;
557
558 15
        if ($num >= 0) {
559 10
            return $num;
560
        }
561 5
        if ($this->isBigIntAsStr) {
562 2
            return \sprintf('%u', $num);
563
        }
564 3
        if ($this->isBigIntAsGmp) {
565 1
            return \gmp_init(\sprintf('%u', $num));
566
        }
567
568 2
        return (float) \sprintf('%u', $num);
569
    }
570
571 9
    private function unpackInt8()
572
    {
573 9
        if (!isset($this->buffer[$this->offset])) {
574 1
            throw new InsufficientDataException();
575
        }
576
577 8
        $num = \ord($this->buffer[$this->offset]);
578 8
        ++$this->offset;
579
580 8
        return $num > 0x7f ? $num - 0x100 : $num;
581
    }
582
583 9
    private function unpackInt16()
584
    {
585 9
        if (!isset($this->buffer[$this->offset + 1])) {
586 1
            throw new InsufficientDataException();
587
        }
588
589 8
        $num = \ord($this->buffer[$this->offset]) << 8
590 8
            | \ord($this->buffer[++$this->offset]);
591 8
        ++$this->offset;
592
593 8
        return $num > 0x7fff ? $num - 0x10000 : $num;
594
    }
595
596 9
    private function unpackInt32()
597
    {
598 9
        if (!isset($this->buffer[$this->offset + 3])) {
599 1
            throw new InsufficientDataException();
600
        }
601
602 8
        $num = \ord($this->buffer[$this->offset]) << 24
603 8
            | \ord($this->buffer[++$this->offset]) << 16
604 8
            | \ord($this->buffer[++$this->offset]) << 8
605 8
            | \ord($this->buffer[++$this->offset]);
606 8
        ++$this->offset;
607
608 8
        return $num > 0x7fffffff ? $num - 0x100000000 : $num;
609
    }
610
611 15
    private function unpackInt64()
612
    {
613 15
        if (!isset($this->buffer[$this->offset + 7])) {
614 1
            throw new InsufficientDataException();
615
        }
616
617 14
        $num = \unpack('J', $this->buffer, $this->offset)[1];
618 14
        $this->offset += 8;
619
620 14
        return $num;
621
    }
622
623 5
    private function unpackFloat32()
624
    {
625 5
        if (!isset($this->buffer[$this->offset + 3])) {
626 1
            throw new InsufficientDataException();
627
        }
628
629 4
        $num = \unpack('G', $this->buffer, $this->offset)[1];
630 4
        $this->offset += 4;
631
632 4
        return $num;
633
    }
634
635 7
    private function unpackFloat64()
636
    {
637 7
        if (!isset($this->buffer[$this->offset + 7])) {
638 1
            throw new InsufficientDataException();
639
        }
640
641 6
        $num = \unpack('E', $this->buffer, $this->offset)[1];
642 6
        $this->offset += 8;
643
644 6
        return $num;
645
    }
646
647 4
    private function unpackArrayData($size)
648
    {
649 4
        $array = [];
650 4
        while ($size--) {
651 4
            $array[] = $this->unpack();
652
        }
653
654 4
        return $array;
655
    }
656
657 5
    private function unpackMapData($size)
658
    {
659 5
        $map = [];
660 5
        while ($size--) {
661 5
            $map[$this->unpackMapKey()] = $this->unpack();
662
        }
663
664 5
        return $map;
665
    }
666
667 82
    private function unpackMapKey()
668
    {
669 82
        if (!isset($this->buffer[$this->offset])) {
670
            throw new InsufficientDataException();
671
        }
672
673 82
        $c = \ord($this->buffer[$this->offset]);
674 82
        ++$this->offset;
675
676
        // fixint
677 82
        if ($c <= 0x7f) {
678 14
            return $c;
679
        }
680
        // fixstr
681 76
        if ($c >= 0xa0 && $c <= 0xbf) {
682 2
            return ($c & 0x1f) ? $this->read($c & 0x1f) : '';
683
        }
684
        // negfixint
685 74
        if ($c >= 0xe0) {
686 2
            return $c - 0x100;
687
        }
688
689
        switch ($c) {
690
            // uint
691 72
            case 0xcc: return $this->unpackUint8();
692 72
            case 0xcd: return $this->unpackUint16();
693 70
            case 0xce: return $this->unpackUint32();
694 68
            case 0xcf: return $this->unpackUint64();
695
696
            // int
697 66
            case 0xd0: return $this->unpackInt8();
698 66
            case 0xd1: return $this->unpackInt16();
699 66
            case 0xd2: return $this->unpackInt32();
700 66
            case 0xd3: return $this->unpackInt64();
701
702
            // str
703 66
            case 0xd9: return $this->read($this->unpackUint8());
704 66
            case 0xda: return $this->read($this->unpackUint16());
705 66
            case 0xdb: return $this->read($this->unpackUint32());
706
707
            // bin
708 66
            case 0xc4: return $this->read($this->unpackUint8());
709 64
            case 0xc5: return $this->read($this->unpackUint16());
710 64
            case 0xc6: return $this->read($this->unpackUint32());
711
        }
712
713 64
        throw UnpackingFailedException::invalidMapKeyType($c);
714
    }
715
716 42
    private function unpackExtData($length)
717
    {
718 42
        if (!isset($this->buffer[$this->offset + $length])) { // 1 (type) + length - 1
719 16
            throw new InsufficientDataException();
720
        }
721
722
        // int8
723 26
        $type = \ord($this->buffer[$this->offset]);
724 26
        ++$this->offset;
725
726 26
        if ($type > 0x7f) {
727
            $type -= 0x100;
728
        }
729
730 26
        if (isset($this->extensions[$type])) {
731 2
            return $this->extensions[$type]->unpackExt($this, $length);
732
        }
733
734 24
        $data = \substr($this->buffer, $this->offset, $length);
735 24
        $this->offset += $length;
736
737 24
        return new Ext($type, $data);
738
    }
739
}
740