Completed
Push — master ( 07c484...6b7b8a )
by Eugene
02:21
created

Packer::packFloat32()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

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
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\CanPack;
17
18
class Packer
19
{
20
    private 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 CanPack[]
39
     */
40
    private $transformers = [];
41
42
    /**
43
     * @param PackOptions|int|null $options
44
     * @param CanPack[] $transformers
45
     *
46
     * @throws InvalidOptionException
47
     */
48 197
    public function __construct($options = null, array $transformers = [])
49
    {
50 197
        if (\is_null($options)) {
51 196
            $options = PackOptions::fromDefaults();
52 25
        } elseif (!$options instanceof PackOptions) {
53 20
            $options = PackOptions::fromBitmask($options);
54
        }
55
56 197
        $this->isDetectStrBin = $options->isDetectStrBinMode();
57 197
        $this->isForceStr = $options->isForceStrMode();
58 197
        $this->isDetectArrMap = $options->isDetectArrMapMode();
59 197
        $this->isForceArr = $options->isForceArrMode();
60 197
        $this->isForceFloat32 = $options->isForceFloat32Mode();
61
62 197
        if ($transformers) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $transformers of type MessagePack\TypeTransformer\CanPack[] 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...
63 1
            $this->transformers = $transformers;
64
        }
65 197
    }
66
67 2
    public function extendWith(CanPack $transformer, CanPack ...$transformers) : self
68
    {
69 2
        $new = clone $this;
70 2
        $new->transformers[] = $transformer;
71
72 2
        if ($transformers) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $transformers of type MessagePack\TypeTransformer\CanPack[] 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...
73 1
            $new->transformers = \array_merge($new->transformers, $transformers);
74
        }
75
76 2
        return $new;
77
    }
78
79 123
    public function pack($value)
80
    {
81 123
        if (\is_int($value)) {
82 65
            return $this->packInt($value);
83
        }
84 83
        if (\is_string($value)) {
85 37
            if ('' === $value) {
86 1
                return $this->isForceStr || $this->isDetectStrBin ? "\xa0" : "\xc4\x00";
87
            }
88 36
            if ($this->isForceStr) {
89 3
                return $this->packStr($value);
90
            }
91 33
            if ($this->isDetectStrBin && \preg_match(self::UTF8_REGEX, $value)) {
92 22
                return $this->packStr($value);
93
            }
94
95 15
            return $this->packBin($value);
96
        }
97 58
        if (\is_array($value)) {
98 31
            if ([] === $value) {
99 1
                return $this->isDetectArrMap || $this->isForceArr ? "\x90" : "\x80";
100
            }
101 30
            if ($this->isDetectArrMap) {
102 23
                if (!isset($value[0]) && !\array_key_exists(0, $value)) {
103 14
                    return $this->packMap($value);
104
                }
105
106 13
                return \array_values($value) === $value
107 13
                    ? $this->packArray($value)
108 13
                    : $this->packMap($value);
109
            }
110
111 7
            return $this->isForceArr ? $this->packArray($value) : $this->packMap($value);
112
        }
113 34
        if (\is_null($value)) {
114 7
            return "\xc0";
115
        }
116 31
        if (\is_bool($value)) {
117 10
            return $value ? "\xc3" : "\xc2";
118
        }
119 23
        if (\is_float($value)) {
120 7
            return $this->packFloat($value);
121
        }
122 16
        if ($this->transformers) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->transformers of type MessagePack\TypeTransformer\CanPack[] 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...
123 3
            foreach ($this->transformers as $transformer) {
124 3
                if (!\is_null($packed = $transformer->pack($this, $value))) {
125 2
                    return $packed;
126
                }
127
            }
128
        }
129 14
        if ($value instanceof Ext) {
130 11
            return $this->packExt($value->type, $value->data);
131
        }
132
133 3
        throw PackingFailedException::unsupportedType($value);
134
    }
135
136 1
    public function packNil()
137
    {
138 1
        return "\xc0";
139
    }
140
141 2
    public function packBool($bool)
142
    {
143 2
        return $bool ? "\xc3" : "\xc2";
144
    }
145
146 100
    public function packInt($int)
147
    {
148 100
        if ($int >= 0) {
149 68
            if ($int <= 0x7f) {
150 42
                return \chr($int);
151
            }
152 34
            if ($int <= 0xff) {
153 12
                return "\xcc".\chr($int);
154
            }
155 26
            if ($int <= 0xffff) {
156 12
                return "\xcd".\chr($int >> 8).\chr($int);
157
            }
158 16
            if ($int <= 0xffffffff) {
159 10
                return \pack('CN', 0xce, $int);
160
            }
161
162 8
            return \pack('CJ', 0xcf, $int);
163
        }
164
165 34
        if ($int >= -0x20) {
166 8
            return \chr(0xe0 | $int);
167
        }
168 26
        if ($int >= -0x80) {
169 6
            return "\xd0".\chr($int);
170
        }
171 20
        if ($int >= -0x8000) {
172 6
            return "\xd1".\chr($int >> 8).\chr($int);
173
        }
174 14
        if ($int >= -0x80000000) {
175 6
            return \pack('CN', 0xd2, $int);
176
        }
177
178 8
        return \pack('CJ', 0xd3, $int);
179
    }
180
181 10
    public function packFloat($float)
182
    {
183 10
        return $this->isForceFloat32
184 1
            ? "\xca".\pack('G', $float)
185 10
            : "\xcb".\pack('E', $float);
186
    }
187
188 2
    public function packFloat32($float)
189
    {
190 2
        return "\xca".\pack('G', $float);
191
    }
192
193 3
    public function packFloat64($float)
194
    {
195 3
        return "\xcb".\pack('E', $float);
196
    }
197
198 39
    public function packStr($str)
199
    {
200 39
        $length = \strlen($str);
201
202 39
        if ($length < 32) {
203 25
            return \chr(0xa0 | $length).$str;
204
        }
205 14
        if ($length <= 0xff) {
206 8
            return "\xd9".\chr($length).$str;
207
        }
208 6
        if ($length <= 0xffff) {
209 4
            return "\xda".\chr($length >> 8).\chr($length).$str;
210
        }
211
212 2
        return \pack('CN', 0xdb, $length).$str;
213
    }
214
215 22
    public function packBin($str)
216
    {
217 22
        $length = \strlen($str);
218
219 22
        if ($length <= 0xff) {
220 18
            return "\xc4".\chr($length).$str;
221
        }
222 4
        if ($length <= 0xffff) {
223 2
            return "\xc5".\chr($length >> 8).\chr($length).$str;
224
        }
225
226 2
        return \pack('CN', 0xc6, $length).$str;
227
    }
228
229 22
    public function packArray($array)
230
    {
231 22
        $data = $this->packArrayHeader(\count($array));
232
233 22
        foreach ($array as $val) {
234 21
            $data .= $this->pack($val);
235
        }
236
237 22
        return $data;
238
    }
239
240 22
    public function packArrayHeader($size)
241
    {
242 22
        if ($size <= 0xf) {
243 16
            return \chr(0x90 | $size);
244
        }
245 6
        if ($size <= 0xffff) {
246 4
            return "\xdc".\chr($size >> 8).\chr($size);
247
        }
248
249 2
        return \pack('CN', 0xdd, $size);
250
    }
251
252 25
    public function packMap($map)
253
    {
254 25
        $data = $this->packMapHeader(\count($map));
255
256 25
        if ($this->isForceStr) {
257 1
            foreach ($map as $key => $val) {
258 1
                $data .= \is_string($key) ? $this->packStr($key) : $this->packInt($key);
259 1
                $data .= $this->pack($val);
260
            }
261
262 1
            return $data;
263
        }
264
265 24
        if ($this->isDetectStrBin) {
266 23
            foreach ($map as $key => $val) {
267 23
                $data .= \is_string($key)
268 4
                    ? (\preg_match(self::UTF8_REGEX, $key) ? $this->packStr($key) : $this->packBin($key))
269 23
                    : $this->packInt($key);
270 23
                $data .= $this->pack($val);
271
            }
272
273 23
            return $data;
274
        }
275
276 1
        foreach ($map as $key => $val) {
277 1
            $data .= \is_string($key) ? $this->packBin($key) : $this->packInt($key);
278 1
            $data .= $this->pack($val);
279
        }
280
281 1
        return $data;
282
    }
283
284 25
    public function packMapHeader($size)
285
    {
286 25
        if ($size <= 0xf) {
287 19
            return \chr(0x80 | $size);
288
        }
289 6
        if ($size <= 0xffff) {
290 4
            return "\xde".\chr($size >> 8).\chr($size);
291
        }
292
293 2
        return \pack('CN', 0xdf, $size);
294
    }
295
296 20
    public function packExt($type, $data)
297
    {
298 20
        $length = \strlen($data);
299
300
        switch ($length) {
301 20
            case 1: return "\xd4".\chr($type).$data;
302 18
            case 2: return "\xd5".\chr($type).$data;
303 16
            case 4: return "\xd6".\chr($type).$data;
304 14
            case 8: return "\xd7".\chr($type).$data;
305 12
            case 16: return "\xd8".\chr($type).$data;
306
        }
307
308 10
        if ($length <= 0xff) {
309 6
            return "\xc7".\chr($length).\chr($type).$data;
310
        }
311 4
        if ($length <= 0xffff) {
312 2
            return \pack('CnC', 0xc8, $length, $type).$data;
313
        }
314
315 2
        return \pack('CNC', 0xc9, $length, $type).$data;
316
    }
317
}
318