Completed
Push — master ( c3a26e...028647 )
by Eugene
04:48
created

Packer::packUint64()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 2
crap 1
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\PackingFailedException;
15
use MessagePack\TypeTransformer\Collection;
16
17
class Packer
18
{
19
    const FORCE_STR = 0b0001;
20
    const FORCE_BIN = 0b0010;
21
    const FORCE_ARR = 0b0100;
22
    const FORCE_MAP = 0b1000;
23
24
    const NON_UTF8_REGEX = '/(
25
        [\xC0-\xC1] # Invalid UTF-8 Bytes
26
        | [\xF5-\xFF] # Invalid UTF-8 Bytes
27
        | \xE0[\x80-\x9F] # Overlong encoding of prior code point
28
        | \xF0[\x80-\x8F] # Overlong encoding of prior code point
29
        | [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start
30
        | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start
31
        | [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start
32
        | (?<=[\x0-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle
33
        | (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]|[\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
34
        | (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
35
        | (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
36
        | (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
37
    )/x';
38
39
    /**
40
     * @var int
41
     */
42
    private $strDetectionMode;
43
44
    /**
45
     * @var int
46
     */
47
    private $arrDetectionMode;
48
49
    /**
50
     * @var Collection
51
     */
52
    private $transformers;
53
54
    /**
55
     * @param int|null $typeDetectionMode
56
     */
57 112
    public function __construct($typeDetectionMode = null)
58
    {
59 112
        if (null !== $typeDetectionMode) {
60 12
            $this->setTypeDetectionMode($typeDetectionMode);
61
        }
62 112
    }
63
64
    /**
65
     * @param int $mode
66
     */
67 34
    public function setTypeDetectionMode($mode)
68
    {
69 34
        if ($mode > 0b1010 || $mode < 0 || 0b11 === ($mode & 0b11)) {
70 10
            throw new \InvalidArgumentException('Invalid type detection mode.');
71
        }
72
73 24
        $this->strDetectionMode = $mode & 0b0011;
74 24
        $this->arrDetectionMode = $mode & 0b1100;
75 24
    }
76
77
    /**
78
     * @param Collection|null $transformers
79
     */
80 2
    public function setTransformers(Collection $transformers = null)
81
    {
82 2
        $this->transformers = $transformers;
83 2
    }
84
85
    /**
86
     * @return Collection|null
87
     */
88 1
    public function getTransformers()
89
    {
90 1
        return $this->transformers;
91
    }
92
93 101
    public function pack($value)
94
    {
95 101
        if (\is_int($value)) {
96 55
            return $this->packInt($value);
97
        }
98 72
        if (\is_string($value)) {
99 39
            if (!$this->strDetectionMode) {
100 27
                return \preg_match(self::NON_UTF8_REGEX, $value)
101 10
                    ? $this->packBin($value)
102 27
                    : $this->packStr($value);
103
            }
104 12
            if (self::FORCE_STR === $this->strDetectionMode) {
105 6
                return $this->packStr($value);
106
            }
107
108 6
            return $this->packBin($value);
109
        }
110 47
        if (\is_array($value)) {
111 30
            if (!$this->arrDetectionMode) {
112 18
                return \array_values($value) === $value
113 9
                    ? $this->packArray($value)
114 18
                    : $this->packMap($value);
115
            }
116 12
            if (self::FORCE_ARR === $this->arrDetectionMode) {
117 6
                return $this->packArray($value);
118
            }
119
120 6
            return $this->packMap($value);
121
        }
122 21
        if (null === $value) {
123 3
            return $this->packNil();
124
        }
125 20
        if (\is_bool($value)) {
126 6
            return $this->packBool($value);
127
        }
128 15
        if (\is_double($value)) {
129 3
            return $this->packFloat($value);
130
        }
131 12
        if ($value instanceof Ext) {
132 9
            return $this->packExt($value);
133
        }
134
135 3
        if ($this->transformers && $transformer = $this->transformers->match($value)) {
136 1
            $ext = new Ext($transformer->getId(), $this->pack($transformer->transform($value)));
137
138 1
            return $this->packExt($ext);
139
        }
140
141 2
        throw new PackingFailedException($value, 'Unsupported type.');
142
    }
143
144 15
    public function packArray(array $array)
145
    {
146 15
        $size = \count($array);
147 15
        $data = self::packArrayHeader($size);
148
149 15
        foreach ($array as $val) {
150 14
            $data .= $this->pack($val);
151
        }
152
153 15
        return $data;
154
    }
155
156 15 View Code Duplication
    private static function packArrayHeader($size)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
157
    {
158 15
        if ($size <= 0xf) {
159 12
            return \chr(0x90 | $size);
160
        }
161 3
        if ($size <= 0xffff) {
162 2
            return "\xdc".\chr($size >> 8).\chr($size);
163
        }
164
165 1
        return \pack('CN', 0xdd, $size);
166
    }
167
168 17
    public function packMap(array $map)
169
    {
170 17
        $size = \count($map);
171 17
        $data = self::packMapHeader($size);
172
173 17
        foreach ($map as $key => $val) {
174 17
            $data .= $this->pack($key);
175 17
            $data .= $this->pack($val);
176
        }
177
178 17
        return $data;
179
    }
180
181 17 View Code Duplication
    private static function packMapHeader($size)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
182
    {
183 17
        if ($size <= 0xf) {
184 14
            return \chr(0x80 | $size);
185
        }
186 3
        if ($size <= 0xffff) {
187 2
            return "\xde".\chr($size >> 8).\chr($size);
188
        }
189
190 1
        return \pack('CN', 0xdf, $size);
191
    }
192
193 25
    public function packStr($str)
194
    {
195 25
        $len = \strlen($str);
196
197 25
        if ($len < 32) {
198 18
            return \chr(0xa0 | $len).$str;
199
        }
200 7
        if ($len <= 0xff) {
201 4
            return "\xd9".\chr($len).$str;
202
        }
203 3
        if ($len <= 0xffff) {
204 2
            return "\xda".\chr($len >> 8).\chr($len).$str;
205
        }
206
207 1
        return \pack('CN', 0xdb, $len).$str;
208
    }
209
210 16
    public function packBin($str)
211
    {
212 16
        $len = \strlen($str);
213
214 16
        if ($len <= 0xff) {
215 14
            return "\xc4".\chr($len).$str;
216
        }
217 2
        if ($len <= 0xffff) {
218 1
            return "\xc5".\chr($len >> 8).\chr($len).$str;
219
        }
220
221 1
        return \pack('CN', 0xc6, $len).$str;
222
    }
223
224 10
    public function packExt(Ext $ext)
225
    {
226 10
        $type = $ext->getType();
227 10
        $data = $ext->getData();
228 10
        $len = \strlen($data);
229
230
        switch ($len) {
231 10
            case 1: return "\xd4".\chr($type).$data;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
232 8
            case 2: return "\xd5".\chr($type).$data;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
233 7
            case 4: return "\xd6".\chr($type).$data;
234 6
            case 8: return "\xd7".\chr($type).$data;
235 5
            case 16: return "\xd8".\chr($type).$data;
236
        }
237
238 4
        if ($len <= 0xff) {
239 2
            return "\xc7".\chr($len).\chr($type).$data;
240
        }
241 2
        if ($len <= 0xffff) {
242 1
            return \pack('CnC', 0xc8, $len, $type).$data;
243
        }
244
245 1
        return \pack('CNC', 0xc9, $len, $type).$data;
246
    }
247
248 3
    public function packNil()
249
    {
250 3
        return "\xc0";
251
    }
252
253 6
    public function packBool($val)
254
    {
255 6
        return $val ? "\xc3" : "\xc2";
256
    }
257
258 3
    public function packFloat($num)
259
    {
260 3
        return "\xcb".strrev(pack('d', $num));
261
    }
262
263 55
    public function packInt($num)
264
    {
265 55
        if ($num >= 0) {
266 40
            if ($num <= 0x7f) {
267 28
                return \chr($num);
268
            }
269 16
            if ($num <= 0xff) {
270 6
                return "\xcc".\chr($num);
271
            }
272 12
            if ($num <= 0xffff) {
273 6
                return "\xcd".\chr($num >> 8).\chr($num);
274
            }
275 7
            if ($num <= 0xffffffff) {
276 5
                return \pack('CN', 0xce, $num);
277
            }
278
279 3
            return self::packUint64(0xcf, $num);
280
        }
281
282 16
        if ($num >= -0x20) {
283 4
            return \chr(0xe0 | $num);
284
        }
285 12
        if ($num >= -0x80) {
286 3
            return "\xd0".\chr($num);
287
        }
288 9
        if ($num >= -0x8000) {
289 3
            return "\xd1".\chr($num >> 8).\chr($num);
290
        }
291 6
        if ($num >= -0x80000000) {
292 3
            return \pack('CN', 0xd2, $num);
293
        }
294
295 3
        return self::packUint64(0xd3, $num);
296
    }
297
298 6
    private static function packUint64($code, $num)
299
    {
300 6
        $hi = ($num & 0xffffffff00000000) >> 32;
301 6
        $lo = $num & 0x00000000ffffffff;
302
303 6
        return \pack('CNN', $code, $hi, $lo);
304
    }
305
}
306