Completed
Pull Request — master (#20)
by Eugene
06:45
created

Packer::packFloat64()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
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\InvalidOptionException;
15
use MessagePack\Exception\PackingFailedException;
16
use MessagePack\TypeTransformer\Collection;
17
18
class Packer
19
{
20
    const UTF8_REGEX = '/\A(?:
21
          [\x00-\x7F]++                      # ASCII
22
        | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
23
        |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
24
        | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
25
        |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
26
        |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
27
        | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
28
        |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
29
        )*+\z/x';
30
31
    private $isDetectStrBin;
32
    private $isForceStr;
33
    private $isDetectArrMap;
34
    private $isForceArr;
35
    private $isForceFloat32;
36
37
    /**
38
     * @var Collection|null
39
     */
40
    private $transformers;
41
42
    /**
43
     * @param PackOptions|int|null $options
44
     *
45
     * @throws InvalidOptionException
46
     */
47
    public function __construct($options = null)
48
    {
49
        if (!$options instanceof PackOptions) {
50
            $options = PackOptions::fromBitmask($options);
51
        }
52
53
        $this->isDetectStrBin = $options->isDetectStrBinMode();
54
        $this->isForceStr = $options->isForceStrMode();
55 114
        $this->isDetectArrMap = $options->isDetectArrMapMode();
56
        $this->isForceArr = $options->isForceArrMode();
57 114
        $this->isForceFloat32 = $options->isForceFloat32Mode();
58 13
    }
59
60 114
    /**
61
     * @param Collection|null $transformers
62
     */
63
    public function setTransformers(Collection $transformers = null)
64
    {
65
        $this->transformers = $transformers;
66
    }
67 35
68
    /**
69 35
     * @return Collection|null
70 10
     */
71
    public function getTransformers()
72
    {
73 25
        return $this->transformers;
74 25
    }
75 25
76
    public function pack($value)
77
    {
78
        if (\is_int($value)) {
79
            return $this->packInt($value);
80 2
        }
81
        if (\is_string($value)) {
82 2
            if ($this->isDetectStrBin) {
83 2
                return \preg_match(self::UTF8_REGEX, $value)
84
                    ? $this->packStr($value)
85
                    : $this->packBin($value);
86
            }
87
88 1
            return $this->isForceStr ? $this->packStr($value) : $this->packBin($value);
89
        }
90 1
        if (\is_array($value)) {
91
            if ($this->isDetectArrMap) {
92
                return \array_values($value) === $value
93 103
                    ? $this->packArray($value)
94
                    : $this->packMap($value);
95 103
            }
96 57
97
            return $this->isForceArr ? $this->packArray($value) : $this->packMap($value);
98 74
        }
99 39
        if (null === $value) {
100 27
            return $this->packNil();
101 19
        }
102 27
        if (\is_bool($value)) {
103
            return $this->packBool($value);
104 12
        }
105 6
        if (\is_float($value)) {
106
            return $this->isForceFloat32
107
                ? $this->packFloat32($value)
108 6
                : $this->packFloat64($value);
109
        }
110 49
        if ($value instanceof Ext) {
111 32
            return $this->packExt($value);
112 19
        }
113 10
114 19
        if ($this->transformers && $transformer = $this->transformers->match($value)) {
115
            $ext = new Ext($transformer->getId(), $this->pack($transformer->transform($value)));
116 13
117 6
            return $this->packExt($ext);
118
        }
119
120 7
        throw new PackingFailedException($value, 'Unsupported type.');
121
    }
122 21
123 3
    public function packArray(array $array)
124
    {
125 20
        $size = \count($array);
126 6
        $data = self::packArrayHeader($size);
127
128 15
        foreach ($array as $val) {
129 3
            $data .= $this->pack($val);
130
        }
131 12
132 9
        return $data;
133
    }
134
135 3 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...
136 1
    {
137
        if ($size <= 0xf) {
138 1
            return \chr(0x90 | $size);
139
        }
140
        if ($size <= 0xffff) {
141 2
            return "\xdc".\chr($size >> 8).\chr($size);
142
        }
143
144 16
        return \pack('CN', 0xdd, $size);
145
    }
146 16
147 16
    public function packMap(array $map)
148
    {
149 16
        $size = \count($map);
150 15
        $data = self::packMapHeader($size);
151
152
        foreach ($map as $key => $val) {
153 16
            $data .= $this->pack($key);
154
            $data .= $this->pack($val);
155
        }
156 16
157
        return $data;
158 16
    }
159 13
160 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...
161 3
    {
162 2
        if ($size <= 0xf) {
163
            return \chr(0x80 | $size);
164
        }
165 1
        if ($size <= 0xffff) {
166
            return "\xde".\chr($size >> 8).\chr($size);
167
        }
168 18
169
        return \pack('CN', 0xdf, $size);
170 18
    }
171 18
172
    public function packStr($str)
173 18
    {
174 18
        $len = \strlen($str);
175 18
176
        if ($len < 32) {
177
            return \chr(0xa0 | $len).$str;
178 18
        }
179
        if ($len <= 0xff) {
180
            return "\xd9".\chr($len).$str;
181 18
        }
182
        if ($len <= 0xffff) {
183 18
            return "\xda".\chr($len >> 8).\chr($len).$str;
184 15
        }
185
186 3
        return \pack('CN', 0xdb, $len).$str;
187 2
    }
188
189
    public function packBin($str)
190 1
    {
191
        $len = \strlen($str);
192
193 25
        if ($len <= 0xff) {
194
            return "\xc4".\chr($len).$str;
195 25
        }
196
        if ($len <= 0xffff) {
197 25
            return "\xc5".\chr($len >> 8).\chr($len).$str;
198 18
        }
199
200 7
        return \pack('CN', 0xc6, $len).$str;
201 4
    }
202
203 3
    public function packExt(Ext $ext)
204 2
    {
205
        $type = $ext->getType();
206
        $data = $ext->getData();
207 1
        $len = \strlen($data);
208
209
        switch ($len) {
210 16
            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...
211
            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...
212 16
            case 4: return "\xd6".\chr($type).$data;
213
            case 8: return "\xd7".\chr($type).$data;
214 16
            case 16: return "\xd8".\chr($type).$data;
215 14
        }
216
217 2
        if ($len <= 0xff) {
218 1
            return "\xc7".\chr($len).\chr($type).$data;
219
        }
220
        if ($len <= 0xffff) {
221 1
            return \pack('CnC', 0xc8, $len, $type).$data;
222
        }
223
224 10
        return \pack('CNC', 0xc9, $len, $type).$data;
225
    }
226 10
227 10
    public function packNil()
228 10
    {
229
        return "\xc0";
230
    }
231 10
232 8
    public function packBool($val)
233 7
    {
234 6
        return $val ? "\xc3" : "\xc2";
235 5
    }
236
237
    public function packFloat32($num)
238 4
    {
239 2
        return "\xca".\strrev(\pack('f', $num));
240
    }
241 2
242 1
    public function packFloat64($num)
243
    {
244
        return "\xcb".\strrev(\pack('d', $num));
245 1
    }
246
247
    public function packInt($num)
248 3
    {
249
        if ($num >= 0) {
250 3
            if ($num <= 0x7f) {
251
                return \chr($num);
252
            }
253 6
            if ($num <= 0xff) {
254
                return "\xcc".\chr($num);
255 6
            }
256
            if ($num <= 0xffff) {
257
                return "\xcd".\chr($num >> 8).\chr($num);
258 3
            }
259
            if ($num <= 0xffffffff) {
260 3
                return \pack('CN', 0xce, $num);
261
            }
262
263 57
            return self::packUint64(0xcf, $num);
264
        }
265 57
266 42
        if ($num >= -0x20) {
267 30
            return \chr(0xe0 | $num);
268
        }
269 16
        if ($num >= -0x80) {
270 6
            return "\xd0".\chr($num);
271
        }
272 12
        if ($num >= -0x8000) {
273 6
            return "\xd1".\chr($num >> 8).\chr($num);
274
        }
275 7
        if ($num >= -0x80000000) {
276 5
            return \pack('CN', 0xd2, $num);
277
        }
278
279 3
        return self::packUint64(0xd3, $num);
280
    }
281
282 16
    private static function packUint64($code, $num)
283 4
    {
284
        $hi = ($num & 0xffffffff00000000) >> 32;
285 12
        $lo = $num & 0x00000000ffffffff;
286 3
287
        return \pack('CNN', $code, $hi, $lo);
288 9
    }
289
}
290