StreamReader::readVarint()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 1
Metric Value
c 3
b 1
f 1
dl 0
loc 4
rs 10
cc 1
eloc 3
nc 1
nop 0
1
<?php
2
namespace Cassandra\Response;
3
4
use Cassandra\Type;
5
6
class StreamReader {
7
8
    /**
9
     * @var string
10
     */
11
    protected $data;
12
    
13
    /**
14
     * @var int
15
     */
16
    protected $offset = 0;
17
18
    public function __construct($data){
19
        $this->data = $data;
20
    }
21
22
    /**
23
     * Read data from stream.
24
     * 
25
     * NOTICE When $this->offset == strlen($this->data), substr() will return false.  You'd better avoid call read() when $length == 0.
26
     *
27
     * @param int $length  $length should be > 0.
28
     * @return string
29
     */
30
    protected function read($length) {
31
        $output = substr($this->data, $this->offset, $length);
32
        $this->offset += $length;
33
        return $output;
34
    }
35
36
    public function offset($offset){
37
        $this->offset = $offset;
38
    }
39
    
40
    public function reset(){
41
        $this->offset = 0;
42
    }
43
44
    /**
45
     * Read single character.
46
     *
47
     * @return int
48
     */
49
    public function readChar() {
50
        return unpack('C', $this->read(1))[1];
51
    }
52
53
    /**
54
     * Read unsigned short.
55
     *
56
     * @return int
57
     */
58
    public function readShort() {
59
        return unpack('n', $this->read(2))[1];
60
    }
61
62
    /**
63
     * Read unsigned int.
64
     *
65
     * @return int
66
     */
67
    public function readInt() {
68
        return unpack('N', $this->read(4))[1];
69
    }
70
71
    /**
72
     * Read string.
73
     *
74
     * @return string
75
     */
76
    public function readString() {
77
        $length = unpack('n', $this->read(2))[1];
78
        return $length === 0 ? '' : $this->read($length);
79
    }
80
81
    /**
82
     * Read long string.
83
     *
84
     * @return string
85
     */
86
    public function readLongString() {
87
        $length = unpack('N', $this->read(4))[1];
88
        return $length === 0 ? '' : $this->read($length);
89
    }
90
91
    /**
92
     * Read bytes.
93
     *
94
     * @return string
95
     */
96
    public function readBytes() {
97
        $binaryLength = $this->read(4);
98
        if ($binaryLength === "\xff\xff\xff\xff")
99
            return null;
100
101
        $length = unpack('N', $binaryLength)[1];
102
        return $length === 0 ? '' : $this->read($length);
103
    }
104
105
    /**
106
     * Read uuid.
107
     *
108
     * @return string
109
     */
110
    public function readUuid() {
111
        $data = unpack('n8', $this->read(16));
112
113
        return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', $data[1], $data[2], $data[3], $data[4], $data[5], $data[6], $data[7], $data[8]);
114
    }
115
116
    /**
117
     * Read list.
118
     *
119
     * @param array $definition [$valueType]
120
     * @return array
121
     */
122 View Code Duplication
    public function readList(array $definition) {
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...
123
        list($valueType) = $definition;
124
        $list = [];
125
        $count = $this->readInt();
126
        for ($i = 0; $i < $count; ++$i) {
127
            $list[] = $this->readValue($valueType);
128
        }
129
        return $list;
130
    }
131
132
    /**
133
     * Read map.
134
     *
135
     * @param array $definition [$keyType, $valueType]
136
     * @return array
137
     */
138 View Code Duplication
    public function readMap(array $definition) {
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...
139
        list($keyType, $valueType) = $definition;
140
        $map = [];
141
        $count = $this->readInt();
142
        for ($i = 0; $i < $count; ++$i) {
143
            $map[$this->readValue($keyType)] = $this->readValue($valueType);
144
        }
145
        return $map;
146
    }
147
148
    /**
149
     * 
150
     * @param array $definition ['key1'=>$valueType1, 'key2'=>$valueType2, ...]
151
     * @return array
152
     */
153
    public function readTuple(array $definition) {
154
        $tuple = [];
155
        $dataLength = strlen($this->data);
156
        foreach ($definition as $key => $type) {
157
            if ($this->offset < $dataLength)
158
                $tuple[$key] = $this->readValue($type);
159
            else
160
                $tuple[$key] = null;
161
        }
162
        return $tuple;
163
    }
164
165
    /**
166
     * Read float.
167
     *
168
     * @return float
169
     */
170
    public function readFloat() {
171
        return unpack('f', strrev($this->read(4)))[1];
172
    }
173
174
    /**
175
     * Read double.
176
     *
177
     * @return double
178
     */
179
    public function readDouble() {
180
        return unpack('d', strrev($this->read(8)))[1];
181
    }
182
183
    /**
184
     * Read boolean.
185
     *
186
     * @return bool
187
     */
188
    public function readBoolean() {
189
        return (bool)$this->readChar();
190
    }
191
192
    /**
193
     * Read inet.
194
     *
195
     * @return string
196
     */
197
    public function readInet() {
198
        return inet_ntop($this->data);
199
    }
200
201
    /**
202
     * Read variable length integer.
203
     *
204
     * @return string
205
     */
206
    public function readVarint() {
207
        list($higher, $lower) = array_values(unpack('N2', $this->data));
208
        return $higher << 32 | $lower;
209
    }
210
211
    /**
212
     * Read variable length decimal.
213
     *
214
     * @return string
215
     */
216
    public function readDecimal() {
217
        $scale = $this->readInt();
218
        $value = $this->readVarint();
219
        $len = strlen($value);
220
        return substr($value, 0, $len - $scale) . '.' . substr($value, $len - $scale);
221
    }
222
    
223
    public function readStringMultimap(){
224
        $map = [];
225
        $count = $this->readShort();
226
        for($i = 0; $i < $count; $i++){
227
            $key = $this->readString();
228
                
229
            $listLength = $this->readShort();
230
            $list = [];
231
            for($j = 0; $j < $listLength; $j++)
232
                $list[] = $this->readString();
233
                    
234
            $map[$key] = $list;
235
        }
236
        return $map;
237
    }
238
239
    /**
240
     * alias of readValue()
241
     * @deprecated
242
     * 
243
     * @param int|array $type
244
     * @return mixed
245
     */
246
    public function readBytesAndConvertToType($type){
247
        return $this->readValue($type);
248
    }
249
    
250
    /**
251
     * read a [bytes] and read by type
252
     *
253
     * @param int|array $type
254
     * @return mixed
255
     */
256
    public function readValue($type){
257
        $binaryLength = substr($this->data, $this->offset, 4);
258
        $this->offset += 4;
259
260
        if ($binaryLength === "\xff\xff\xff\xff")
261
            return null;
262
263
        $length = unpack('N', $binaryLength)[1];
264
265
        // do not use $this->read() for performance
266
        // substr() returns FALSE when OFFSET is equal to the length of data
267
        $data = ($length == 0) ? '' : substr($this->data, $this->offset, $length);
268
        $this->offset += $length;
269
        if(!is_array($type)){
270
            $class = Type\Base::$typeClassMap[$type];
271
            return $class::parse($data);
272
        }
273
        else{
274
            if (!isset(Type\Base::$typeClassMap[$type['type']]))
275
                throw new Type\Exception('Unknown type ' . var_export($type, true));
276
            $class = Type\Base::$typeClassMap[$type['type']];
277
            return $class::parse($data, $type['definition']);
278
        }
279
    }
280
281
    /**
282
     * @return int|array
283
     */
284
    public function readType(){
285
        $type = $this->readShort();
286
        switch ($type) {
287
            case Type\Base::CUSTOM:
288
                return [
289
                    'type'    => $type,
290
                    'definition'=> [$this->readString()],
291
                ];
292
            case Type\Base::COLLECTION_LIST:
293
            case Type\Base::COLLECTION_SET:
294
                return [
295
                    'type'    => $type,
296
                    'definition'    => [$this->readType()],
297
                ];
298
            case Type\Base::COLLECTION_MAP:
299
                return [
300
                    'type'    => $type,
301
                    'definition'=> [$this->readType(), $this->readType()],
302
                ];
303
            case Type\Base::UDT:
304
                $data = [
305
                    'type'        => $type,
306
                    'keyspace'    => $this->readString(),
307
                    'name'        => $this->readString(),
308
                    'definition'    => [],
309
                ];
310
                $length = $this->readShort();
311 View Code Duplication
                for($i = 0; $i < $length; ++$i){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
312
                    $key = $this->readString();
313
                    $data['definition'][$key] = $this->readType();
314
                }
315
                return $data;
316
            case Type\Base::TUPLE:
317
                $data = [
318
                    'type'    => $type,
319
                    'definition'    =>    [],
320
                ];
321
                $length = $this->readShort();
322 View Code Duplication
                for($i = 0; $i < $length; ++$i){
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
323
                    $data['definition'][] = $this->readType();
324
                }
325
                return $data;
326
            default:
327
                return $type;
328
        }
329
    }
330
}
331