blob: c25b0501b72cd38313057a2c7cdca3d13e890036 [file] [log] [blame]
Roger Meier21c0a852012-09-05 19:47:14 +00001<?php
2/*
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 * @package thrift.protocol
21 */
22
23namespace Thrift\Protocol;
24
Roger Meier21c0a852012-09-05 19:47:14 +000025use Thrift\Type\TType;
26use Thrift\Exception\TProtocolException;
27use Thrift\Factory\TStringFuncFactory;
28
29/**
30 * Compact implementation of the Thrift protocol.
31 *
32 */
Roger Thomas6fb59232014-11-04 10:09:23 +000033class TCompactProtocol extends TProtocol
34{
Roger Meier21c0a852012-09-05 19:47:14 +000035 const COMPACT_STOP = 0x00;
36 const COMPACT_TRUE = 0x01;
37 const COMPACT_FALSE = 0x02;
38 const COMPACT_BYTE = 0x03;
39 const COMPACT_I16 = 0x04;
40 const COMPACT_I32 = 0x05;
41 const COMPACT_I64 = 0x06;
42 const COMPACT_DOUBLE = 0x07;
43 const COMPACT_BINARY = 0x08;
44 const COMPACT_LIST = 0x09;
45 const COMPACT_SET = 0x0A;
46 const COMPACT_MAP = 0x0B;
47 const COMPACT_STRUCT = 0x0C;
48
49 const STATE_CLEAR = 0;
50 const STATE_FIELD_WRITE = 1;
51 const STATE_VALUE_WRITE = 2;
52 const STATE_CONTAINER_WRITE = 3;
53 const STATE_BOOL_WRITE = 4;
54 const STATE_FIELD_READ = 5;
55 const STATE_CONTAINER_READ = 6;
56 const STATE_VALUE_READ = 7;
57 const STATE_BOOL_READ = 8;
58
59 const VERSION_MASK = 0x1f;
60 const VERSION = 1;
61 const PROTOCOL_ID = 0x82;
62 const TYPE_MASK = 0xe0;
Jens Geyera86886e2014-09-17 22:25:48 +020063 const TYPE_BITS = 0x07;
Roger Meier21c0a852012-09-05 19:47:14 +000064 const TYPE_SHIFT_AMOUNT = 5;
65
66 protected static $ctypes = array(
67 TType::STOP => TCompactProtocol::COMPACT_STOP,
68 TType::BOOL => TCompactProtocol::COMPACT_TRUE, // used for collection
69 TType::BYTE => TCompactProtocol::COMPACT_BYTE,
70 TType::I16 => TCompactProtocol::COMPACT_I16,
71 TType::I32 => TCompactProtocol::COMPACT_I32,
72 TType::I64 => TCompactProtocol::COMPACT_I64,
73 TType::DOUBLE => TCompactProtocol::COMPACT_DOUBLE,
74 TType::STRING => TCompactProtocol::COMPACT_BINARY,
75 TType::STRUCT => TCompactProtocol::COMPACT_STRUCT,
76 TType::LST => TCompactProtocol::COMPACT_LIST,
77 TType::SET => TCompactProtocol::COMPACT_SET,
78 TType::MAP => TCompactProtocol::COMPACT_MAP,
79 );
80
81 protected static $ttypes = array(
82 TCompactProtocol::COMPACT_STOP => TType::STOP ,
83 TCompactProtocol::COMPACT_TRUE => TType::BOOL, // used for collection
84 TCompactProtocol::COMPACT_FALSE => TType::BOOL,
85 TCompactProtocol::COMPACT_BYTE => TType::BYTE,
86 TCompactProtocol::COMPACT_I16 => TType::I16,
87 TCompactProtocol::COMPACT_I32 => TType::I32,
88 TCompactProtocol::COMPACT_I64 => TType::I64,
89 TCompactProtocol::COMPACT_DOUBLE => TType::DOUBLE,
90 TCompactProtocol::COMPACT_BINARY => TType::STRING,
91 TCompactProtocol::COMPACT_STRUCT => TType::STRUCT,
92 TCompactProtocol::COMPACT_LIST => TType::LST,
93 TCompactProtocol::COMPACT_SET => TType::SET,
94 TCompactProtocol::COMPACT_MAP => TType::MAP,
95 );
96
97 protected $state = TCompactProtocol::STATE_CLEAR;
98 protected $lastFid = 0;
99 protected $boolFid = null;
100 protected $boolValue = null;
101 protected $structs = array();
102 protected $containers = array();
103
104 // Some varint / zigzag helper methods
Roger Thomas6fb59232014-11-04 10:09:23 +0000105 public function toZigZag($n, $bits)
106 {
Roger Meier21c0a852012-09-05 19:47:14 +0000107 return ($n << 1) ^ ($n >> ($bits - 1));
108 }
109
Roger Thomas6fb59232014-11-04 10:09:23 +0000110 public function fromZigZag($n)
111 {
Roger Meier21c0a852012-09-05 19:47:14 +0000112 return ($n >> 1) ^ -($n & 1);
113 }
114
Roger Thomas6fb59232014-11-04 10:09:23 +0000115 public function getVarint($data)
116 {
Roger Meier21c0a852012-09-05 19:47:14 +0000117 $out = "";
118 while (true) {
119 if (($data & ~0x7f) === 0) {
120 $out .= chr($data);
121 break;
122 } else {
123 $out .= chr(($data & 0xff) | 0x80);
124 $data = $data >> 7;
125 }
126 }
Roger Thomas6fb59232014-11-04 10:09:23 +0000127
Roger Meier21c0a852012-09-05 19:47:14 +0000128 return $out;
129 }
130
Roger Thomas6fb59232014-11-04 10:09:23 +0000131 public function writeVarint($data)
132 {
Roger Meier21c0a852012-09-05 19:47:14 +0000133 $out = $this->getVarint($data);
134 $result = TStringFuncFactory::create()->strlen($out);
135 $this->trans_->write($out, $result);
Roger Thomas6fb59232014-11-04 10:09:23 +0000136
Roger Meier21c0a852012-09-05 19:47:14 +0000137 return $result;
138 }
139
Roger Thomas6fb59232014-11-04 10:09:23 +0000140 public function readVarint(&$result)
141 {
Roger Meier21c0a852012-09-05 19:47:14 +0000142 $idx = 0;
143 $shift = 0;
144 $result = 0;
145 while (true) {
146 $x = $this->trans_->readAll(1);
147 $arr = unpack('C', $x);
148 $byte = $arr[1];
149 $idx += 1;
150 $result |= ($byte & 0x7f) << $shift;
151 if (($byte >> 7) === 0) {
152 return $idx;
153 }
154 $shift += 7;
155 }
156
157 return $idx;
158 }
159
Roger Thomas6fb59232014-11-04 10:09:23 +0000160 public function __construct($trans)
161 {
Roger Meier21c0a852012-09-05 19:47:14 +0000162 parent::__construct($trans);
163 }
164
Roger Thomas6fb59232014-11-04 10:09:23 +0000165 public function writeMessageBegin($name, $type, $seqid)
166 {
Roger Meier21c0a852012-09-05 19:47:14 +0000167 $written =
168 $this->writeUByte(TCompactProtocol::PROTOCOL_ID) +
169 $this->writeUByte(TCompactProtocol::VERSION |
170 ($type << TCompactProtocol::TYPE_SHIFT_AMOUNT)) +
171 $this->writeVarint($seqid) +
172 $this->writeString($name);
173 $this->state = TCompactProtocol::STATE_VALUE_WRITE;
Roger Thomas6fb59232014-11-04 10:09:23 +0000174
Roger Meier21c0a852012-09-05 19:47:14 +0000175 return $written;
176 }
177
Roger Thomas6fb59232014-11-04 10:09:23 +0000178 public function writeMessageEnd()
179 {
Roger Meier21c0a852012-09-05 19:47:14 +0000180 $this->state = TCompactProtocol::STATE_CLEAR;
Roger Thomas6fb59232014-11-04 10:09:23 +0000181
Roger Meier21c0a852012-09-05 19:47:14 +0000182 return 0;
183 }
184
Roger Thomas6fb59232014-11-04 10:09:23 +0000185 public function writeStructBegin($name)
186 {
Roger Meier21c0a852012-09-05 19:47:14 +0000187 $this->structs[] = array($this->state, $this->lastFid);
188 $this->state = TCompactProtocol::STATE_FIELD_WRITE;
189 $this->lastFid = 0;
Roger Thomas6fb59232014-11-04 10:09:23 +0000190
Roger Meier21c0a852012-09-05 19:47:14 +0000191 return 0;
192 }
193
Roger Thomas6fb59232014-11-04 10:09:23 +0000194 public function writeStructEnd()
195 {
Roger Meier21c0a852012-09-05 19:47:14 +0000196 $old_values = array_pop($this->structs);
197 $this->state = $old_values[0];
198 $this->lastFid = $old_values[1];
Roger Thomas6fb59232014-11-04 10:09:23 +0000199
Roger Meier21c0a852012-09-05 19:47:14 +0000200 return 0;
201 }
202
Roger Thomas6fb59232014-11-04 10:09:23 +0000203 public function writeFieldStop()
204 {
Roger Meier21c0a852012-09-05 19:47:14 +0000205 return $this->writeByte(0);
206 }
207
Roger Thomas6fb59232014-11-04 10:09:23 +0000208 public function writeFieldHeader($type, $fid)
209 {
Roger Meier21c0a852012-09-05 19:47:14 +0000210 $written = 0;
211 $delta = $fid - $this->lastFid;
212 if (0 < $delta && $delta <= 15) {
213 $written = $this->writeUByte(($delta << 4) | $type);
214 } else {
215 $written = $this->writeByte($type) +
216 $this->writeI16($fid);
217 }
218 $this->lastFid = $fid;
Roger Thomas6fb59232014-11-04 10:09:23 +0000219
Roger Meier21c0a852012-09-05 19:47:14 +0000220 return $written;
221 }
222
Roger Thomas6fb59232014-11-04 10:09:23 +0000223 public function writeFieldBegin($field_name, $field_type, $field_id)
224 {
Roger Meier21c0a852012-09-05 19:47:14 +0000225 if ($field_type == TTYPE::BOOL) {
226 $this->state = TCompactProtocol::STATE_BOOL_WRITE;
227 $this->boolFid = $field_id;
Roger Thomas6fb59232014-11-04 10:09:23 +0000228
Roger Meier21c0a852012-09-05 19:47:14 +0000229 return 0;
230 } else {
231 $this->state = TCompactProtocol::STATE_VALUE_WRITE;
Roger Thomas6fb59232014-11-04 10:09:23 +0000232
Roger Meier21c0a852012-09-05 19:47:14 +0000233 return $this->writeFieldHeader(self::$ctypes[$field_type], $field_id);
234 }
235 }
236
Roger Thomas6fb59232014-11-04 10:09:23 +0000237 public function writeFieldEnd()
238 {
Roger Meier21c0a852012-09-05 19:47:14 +0000239 $this->state = TCompactProtocol::STATE_FIELD_WRITE;
Roger Thomas6fb59232014-11-04 10:09:23 +0000240
Roger Meier21c0a852012-09-05 19:47:14 +0000241 return 0;
242 }
243
Roger Thomas6fb59232014-11-04 10:09:23 +0000244 public function writeCollectionBegin($etype, $size)
245 {
Roger Meier21c0a852012-09-05 19:47:14 +0000246 $written = 0;
247 if ($size <= 14) {
248 $written = $this->writeUByte($size << 4 |
249 self::$ctypes[$etype]);
250 } else {
251 $written = $this->writeUByte(0xf0 |
252 self::$ctypes[$etype]) +
253 $this->writeVarint($size);
254 }
255 $this->containers[] = $this->state;
256 $this->state = TCompactProtocol::STATE_CONTAINER_WRITE;
257
258 return $written;
259 }
260
Roger Thomas6fb59232014-11-04 10:09:23 +0000261 public function writeMapBegin($key_type, $val_type, $size)
262 {
Roger Meier21c0a852012-09-05 19:47:14 +0000263 $written = 0;
264 if ($size == 0) {
265 $written = $this->writeByte(0);
266 } else {
267 $written = $this->writeVarint($size) +
268 $this->writeUByte(self::$ctypes[$key_type] << 4 |
269 self::$ctypes[$val_type]);
270 }
271 $this->containers[] = $this->state;
Roger Thomas6fb59232014-11-04 10:09:23 +0000272
Roger Meier21c0a852012-09-05 19:47:14 +0000273 return $written;
274 }
275
Roger Thomas6fb59232014-11-04 10:09:23 +0000276 public function writeCollectionEnd()
277 {
Roger Meier21c0a852012-09-05 19:47:14 +0000278 $this->state = array_pop($this->containers);
Roger Thomas6fb59232014-11-04 10:09:23 +0000279
Roger Meier21c0a852012-09-05 19:47:14 +0000280 return 0;
281 }
282
Roger Thomas6fb59232014-11-04 10:09:23 +0000283 public function writeMapEnd()
284 {
Roger Meier21c0a852012-09-05 19:47:14 +0000285 return $this->writeCollectionEnd();
286 }
287
Roger Thomas6fb59232014-11-04 10:09:23 +0000288 public function writeListBegin($elem_type, $size)
289 {
Roger Meier21c0a852012-09-05 19:47:14 +0000290 return $this->writeCollectionBegin($elem_type, $size);
291 }
292
Roger Thomas6fb59232014-11-04 10:09:23 +0000293 public function writeListEnd()
294 {
Roger Meier21c0a852012-09-05 19:47:14 +0000295 return $this->writeCollectionEnd();
296 }
297
Roger Thomas6fb59232014-11-04 10:09:23 +0000298 public function writeSetBegin($elem_type, $size)
299 {
Roger Meier21c0a852012-09-05 19:47:14 +0000300 return $this->writeCollectionBegin($elem_type, $size);
301 }
302
Roger Thomas6fb59232014-11-04 10:09:23 +0000303 public function writeSetEnd()
304 {
Roger Meier21c0a852012-09-05 19:47:14 +0000305 return $this->writeCollectionEnd();
306 }
307
Roger Thomas6fb59232014-11-04 10:09:23 +0000308 public function writeBool($value)
309 {
Roger Meier21c0a852012-09-05 19:47:14 +0000310 if ($this->state == TCompactProtocol::STATE_BOOL_WRITE) {
311 $ctype = TCompactProtocol::COMPACT_FALSE;
312 if ($value) {
313 $ctype = TCompactProtocol::COMPACT_TRUE;
314 }
Roger Thomas6fb59232014-11-04 10:09:23 +0000315
Roger Meier21c0a852012-09-05 19:47:14 +0000316 return $this->writeFieldHeader($ctype, $this->boolFid);
Roger Thomas6fb59232014-11-04 10:09:23 +0000317 } elseif ($this->state == TCompactProtocol::STATE_CONTAINER_WRITE) {
Roger Meier21c0a852012-09-05 19:47:14 +0000318 return $this->writeByte($value ? 1 : 0);
319 } else {
320 throw new TProtocolException('Invalid state in compact protocol');
321 }
322 }
323
Roger Thomas6fb59232014-11-04 10:09:23 +0000324 public function writeByte($value)
325 {
Roger Meier21c0a852012-09-05 19:47:14 +0000326 $data = pack('c', $value);
327 $this->trans_->write($data, 1);
Roger Thomas6fb59232014-11-04 10:09:23 +0000328
Roger Meier21c0a852012-09-05 19:47:14 +0000329 return 1;
330 }
331
Roger Thomas6fb59232014-11-04 10:09:23 +0000332 public function writeUByte($byte)
333 {
Roger Meier21c0a852012-09-05 19:47:14 +0000334 $this->trans_->write(pack('C', $byte), 1);
Roger Thomas6fb59232014-11-04 10:09:23 +0000335
Roger Meier21c0a852012-09-05 19:47:14 +0000336 return 1;
337 }
338
Roger Thomas6fb59232014-11-04 10:09:23 +0000339 public function writeI16($value)
340 {
Roger Meier21c0a852012-09-05 19:47:14 +0000341 $thing = $this->toZigZag($value, 16);
Roger Thomas6fb59232014-11-04 10:09:23 +0000342
Roger Meier21c0a852012-09-05 19:47:14 +0000343 return $this->writeVarint($thing);
344 }
345
Roger Thomas6fb59232014-11-04 10:09:23 +0000346 public function writeI32($value)
347 {
Roger Meier21c0a852012-09-05 19:47:14 +0000348 $thing = $this->toZigZag($value, 32);
Roger Thomas6fb59232014-11-04 10:09:23 +0000349
Roger Meier21c0a852012-09-05 19:47:14 +0000350 return $this->writeVarint($thing);
351 }
352
Roger Thomas6fb59232014-11-04 10:09:23 +0000353 public function writeDouble($value)
354 {
Roger Meier21c0a852012-09-05 19:47:14 +0000355 $data = pack('d', $value);
Jens Geyereb393ac2014-09-29 22:25:46 +0200356 $this->trans_->write($data, 8);
Roger Thomas6fb59232014-11-04 10:09:23 +0000357
Roger Meier21c0a852012-09-05 19:47:14 +0000358 return 8;
359 }
360
Roger Thomas6fb59232014-11-04 10:09:23 +0000361 public function writeString($value)
362 {
Roger Meier21c0a852012-09-05 19:47:14 +0000363 $len = TStringFuncFactory::create()->strlen($value);
364 $result = $this->writeVarint($len);
365 if ($len) {
366 $this->trans_->write($value, $len);
367 }
Roger Thomas6fb59232014-11-04 10:09:23 +0000368
Roger Meier21c0a852012-09-05 19:47:14 +0000369 return $result + $len;
370 }
371
Roger Thomas6fb59232014-11-04 10:09:23 +0000372 public function readFieldBegin(&$name, &$field_type, &$field_id)
373 {
Håkon Hitlandb7a213c2016-11-17 16:59:30 +0100374 $result = $this->readUByte($compact_type_and_delta);
Roger Meier21c0a852012-09-05 19:47:14 +0000375
Håkon Hitlandb7a213c2016-11-17 16:59:30 +0100376 $compact_type = $compact_type_and_delta & 0x0f;
377
378 if ($compact_type == TType::STOP) {
379 $field_type = $compact_type;
Roger Meier21c0a852012-09-05 19:47:14 +0000380 $field_id = 0;
Roger Thomas6fb59232014-11-04 10:09:23 +0000381
Roger Meier21c0a852012-09-05 19:47:14 +0000382 return $result;
383 }
Håkon Hitlandb7a213c2016-11-17 16:59:30 +0100384 $delta = $compact_type_and_delta >> 4;
Roger Meier21c0a852012-09-05 19:47:14 +0000385 if ($delta == 0) {
386 $result += $this->readI16($field_id);
387 } else {
388 $field_id = $this->lastFid + $delta;
389 }
390 $this->lastFid = $field_id;
Håkon Hitlandb7a213c2016-11-17 16:59:30 +0100391 $field_type = $this->getTType($compact_type);
392
393 if ($compact_type == TCompactProtocol::COMPACT_TRUE) {
Roger Meier21c0a852012-09-05 19:47:14 +0000394 $this->state = TCompactProtocol::STATE_BOOL_READ;
395 $this->boolValue = true;
Håkon Hitlandb7a213c2016-11-17 16:59:30 +0100396 } elseif ($compact_type == TCompactProtocol::COMPACT_FALSE) {
Roger Meier21c0a852012-09-05 19:47:14 +0000397 $this->state = TCompactProtocol::STATE_BOOL_READ;
398 $this->boolValue = false;
399 } else {
400 $this->state = TCompactProtocol::STATE_VALUE_READ;
401 }
Roger Thomas6fb59232014-11-04 10:09:23 +0000402
Roger Meier21c0a852012-09-05 19:47:14 +0000403 return $result;
404 }
405
Roger Thomas6fb59232014-11-04 10:09:23 +0000406 public function readFieldEnd()
407 {
Roger Meier21c0a852012-09-05 19:47:14 +0000408 $this->state = TCompactProtocol::STATE_FIELD_READ;
Roger Thomas6fb59232014-11-04 10:09:23 +0000409
Roger Meier21c0a852012-09-05 19:47:14 +0000410 return 0;
411 }
412
Roger Thomas6fb59232014-11-04 10:09:23 +0000413 public function readUByte(&$value)
414 {
Roger Meier21c0a852012-09-05 19:47:14 +0000415 $data = $this->trans_->readAll(1);
416 $arr = unpack('C', $data);
417 $value = $arr[1];
Roger Thomas6fb59232014-11-04 10:09:23 +0000418
Roger Meier21c0a852012-09-05 19:47:14 +0000419 return 1;
420 }
421
Roger Thomas6fb59232014-11-04 10:09:23 +0000422 public function readByte(&$value)
423 {
Roger Meier21c0a852012-09-05 19:47:14 +0000424 $data = $this->trans_->readAll(1);
425 $arr = unpack('c', $data);
426 $value = $arr[1];
Roger Thomas6fb59232014-11-04 10:09:23 +0000427
Roger Meier21c0a852012-09-05 19:47:14 +0000428 return 1;
429 }
430
Roger Thomas6fb59232014-11-04 10:09:23 +0000431 public function readZigZag(&$value)
432 {
Roger Meier21c0a852012-09-05 19:47:14 +0000433 $result = $this->readVarint($value);
434 $value = $this->fromZigZag($value);
Roger Thomas6fb59232014-11-04 10:09:23 +0000435
Roger Meier21c0a852012-09-05 19:47:14 +0000436 return $result;
437 }
438
Roger Thomas6fb59232014-11-04 10:09:23 +0000439 public function readMessageBegin(&$name, &$type, &$seqid)
440 {
Roger Meier21c0a852012-09-05 19:47:14 +0000441 $protoId = 0;
442 $result = $this->readUByte($protoId);
443 if ($protoId != TCompactProtocol::PROTOCOL_ID) {
444 throw new TProtocolException('Bad protocol id in TCompact message');
445 }
446 $verType = 0;
447 $result += $this->readUByte($verType);
Jens Geyera86886e2014-09-17 22:25:48 +0200448 $type = ($verType >> TCompactProtocol::TYPE_SHIFT_AMOUNT) & TCompactProtocol::TYPE_BITS;
Roger Meier21c0a852012-09-05 19:47:14 +0000449 $version = $verType & TCompactProtocol::VERSION_MASK;
450 if ($version != TCompactProtocol::VERSION) {
451 throw new TProtocolException('Bad version in TCompact message');
452 }
Roger Meier47b89b92014-02-11 21:28:56 +0100453 $result += $this->readVarint($seqid);
454 $result += $this->readString($name);
Roger Meier21c0a852012-09-05 19:47:14 +0000455
456 return $result;
457 }
458
Roger Thomas6fb59232014-11-04 10:09:23 +0000459 public function readMessageEnd()
460 {
Roger Meier21c0a852012-09-05 19:47:14 +0000461 return 0;
462 }
463
Roger Thomas6fb59232014-11-04 10:09:23 +0000464 public function readStructBegin(&$name)
465 {
Roger Meier21c0a852012-09-05 19:47:14 +0000466 $name = ''; // unused
467 $this->structs[] = array($this->state, $this->lastFid);
468 $this->state = TCompactProtocol::STATE_FIELD_READ;
469 $this->lastFid = 0;
Roger Thomas6fb59232014-11-04 10:09:23 +0000470
Roger Meier21c0a852012-09-05 19:47:14 +0000471 return 0;
472 }
473
Roger Thomas6fb59232014-11-04 10:09:23 +0000474 public function readStructEnd()
475 {
Roger Meier21c0a852012-09-05 19:47:14 +0000476 $last = array_pop($this->structs);
477 $this->state = $last[0];
478 $this->lastFid = $last[1];
Roger Thomas6fb59232014-11-04 10:09:23 +0000479
Roger Meier21c0a852012-09-05 19:47:14 +0000480 return 0;
481 }
482
Roger Thomas6fb59232014-11-04 10:09:23 +0000483 public function readCollectionBegin(&$type, &$size)
484 {
Roger Meier21c0a852012-09-05 19:47:14 +0000485 $sizeType = 0;
486 $result = $this->readUByte($sizeType);
487 $size = $sizeType >> 4;
488 $type = $this->getTType($sizeType);
489 if ($size == 15) {
490 $result += $this->readVarint($size);
491 }
492 $this->containers[] = $this->state;
493 $this->state = TCompactProtocol::STATE_CONTAINER_READ;
494
495 return $result;
496 }
497
Roger Thomas6fb59232014-11-04 10:09:23 +0000498 public function readMapBegin(&$key_type, &$val_type, &$size)
499 {
Roger Meier21c0a852012-09-05 19:47:14 +0000500 $result = $this->readVarint($size);
501 $types = 0;
502 if ($size > 0) {
503 $result += $this->readUByte($types);
504 }
505 $val_type = $this->getTType($types);
506 $key_type = $this->getTType($types >> 4);
507 $this->containers[] = $this->state;
508 $this->state = TCompactProtocol::STATE_CONTAINER_READ;
509
510 return $result;
511 }
512
Roger Thomas6fb59232014-11-04 10:09:23 +0000513 public function readCollectionEnd()
514 {
Roger Meier21c0a852012-09-05 19:47:14 +0000515 $this->state = array_pop($this->containers);
Roger Thomas6fb59232014-11-04 10:09:23 +0000516
Roger Meier21c0a852012-09-05 19:47:14 +0000517 return 0;
518 }
519
Roger Thomas6fb59232014-11-04 10:09:23 +0000520 public function readMapEnd()
521 {
Roger Meier21c0a852012-09-05 19:47:14 +0000522 return $this->readCollectionEnd();
523 }
524
Roger Thomas6fb59232014-11-04 10:09:23 +0000525 public function readListBegin(&$elem_type, &$size)
526 {
Roger Meier21c0a852012-09-05 19:47:14 +0000527 return $this->readCollectionBegin($elem_type, $size);
528 }
529
Roger Thomas6fb59232014-11-04 10:09:23 +0000530 public function readListEnd()
531 {
Roger Meier21c0a852012-09-05 19:47:14 +0000532 return $this->readCollectionEnd();
533 }
534
Roger Thomas6fb59232014-11-04 10:09:23 +0000535 public function readSetBegin(&$elem_type, &$size)
536 {
Roger Meier21c0a852012-09-05 19:47:14 +0000537 return $this->readCollectionBegin($elem_type, $size);
538 }
539
Roger Thomas6fb59232014-11-04 10:09:23 +0000540 public function readSetEnd()
541 {
Roger Meier21c0a852012-09-05 19:47:14 +0000542 return $this->readCollectionEnd();
543 }
544
Roger Thomas6fb59232014-11-04 10:09:23 +0000545 public function readBool(&$value)
546 {
Roger Meier21c0a852012-09-05 19:47:14 +0000547 if ($this->state == TCompactProtocol::STATE_BOOL_READ) {
548 $value = $this->boolValue;
Roger Thomas6fb59232014-11-04 10:09:23 +0000549
Roger Meier21c0a852012-09-05 19:47:14 +0000550 return 0;
Roger Thomas6fb59232014-11-04 10:09:23 +0000551 } elseif ($this->state == TCompactProtocol::STATE_CONTAINER_READ) {
Roger Meier21c0a852012-09-05 19:47:14 +0000552 return $this->readByte($value);
553 } else {
554 throw new TProtocolException('Invalid state in compact protocol');
555 }
556 }
557
Roger Thomas6fb59232014-11-04 10:09:23 +0000558 public function readI16(&$value)
559 {
Roger Meier21c0a852012-09-05 19:47:14 +0000560 return $this->readZigZag($value);
561 }
562
Roger Thomas6fb59232014-11-04 10:09:23 +0000563 public function readI32(&$value)
564 {
Roger Meier21c0a852012-09-05 19:47:14 +0000565 return $this->readZigZag($value);
566 }
567
Roger Thomas6fb59232014-11-04 10:09:23 +0000568 public function readDouble(&$value)
569 {
Jens Geyereb393ac2014-09-29 22:25:46 +0200570 $data = $this->trans_->readAll(8);
Roger Meier21c0a852012-09-05 19:47:14 +0000571 $arr = unpack('d', $data);
572 $value = $arr[1];
Roger Thomas6fb59232014-11-04 10:09:23 +0000573
Roger Meier21c0a852012-09-05 19:47:14 +0000574 return 8;
575 }
576
Roger Thomas6fb59232014-11-04 10:09:23 +0000577 public function readString(&$value)
578 {
Roger Meier21c0a852012-09-05 19:47:14 +0000579 $result = $this->readVarint($len);
580 if ($len) {
581 $value = $this->trans_->readAll($len);
582 } else {
583 $value = '';
584 }
Roger Thomas6fb59232014-11-04 10:09:23 +0000585
Roger Meier21c0a852012-09-05 19:47:14 +0000586 return $result + $len;
587 }
588
Roger Thomas6fb59232014-11-04 10:09:23 +0000589 public function getTType($byte)
590 {
Roger Meier21c0a852012-09-05 19:47:14 +0000591 return self::$ttypes[$byte & 0x0f];
592 }
593
594 // If we are on a 32bit architecture we have to explicitly deal with
595 // 64-bit twos-complement arithmetic since PHP wants to treat all ints
596 // as signed and any int over 2^31 - 1 as a float
597
598 // Read and write I64 as two 32 bit numbers $hi and $lo
599
Roger Thomas6fb59232014-11-04 10:09:23 +0000600 public function readI64(&$value)
601 {
Roger Meier21c0a852012-09-05 19:47:14 +0000602 // Read varint from wire
603 $hi = 0;
604 $lo = 0;
605
606 $idx = 0;
607 $shift = 0;
608
609 while (true) {
610 $x = $this->trans_->readAll(1);
611 $arr = unpack('C', $x);
612 $byte = $arr[1];
613 $idx += 1;
Roger Meier21c0a852012-09-05 19:47:14 +0000614 // Shift hi and lo together.
Nobuaki Sukegawa527637a2016-07-24 15:28:46 +0900615 if ($shift < 28) {
616 $lo |= (($byte & 0x7f) << $shift);
617 } elseif ($shift == 28) {
618 $lo |= (($byte & 0x0f) << 28);
619 $hi |= (($byte & 0x70) >> 4);
620 } else {
Roger Meier21c0a852012-09-05 19:47:14 +0000621 $hi |= (($byte & 0x7f) << ($shift - 32));
Roger Meier21c0a852012-09-05 19:47:14 +0000622 }
623 if (($byte >> 7) === 0) {
624 break;
625 }
626 $shift += 7;
627 }
628
629 // Now, unzig it.
630 $xorer = 0;
631 if ($lo & 1) {
632 $xorer = 0xffffffff;
633 }
634 $lo = ($lo >> 1) & 0x7fffffff;
635 $lo = $lo | (($hi & 1) << 31);
636 $hi = ($hi >> 1) ^ $xorer;
637 $lo = $lo ^ $xorer;
638
639 // Now put $hi and $lo back together
Nobuaki Sukegawa527637a2016-07-24 15:28:46 +0900640 $isNeg = $hi < 0 || $hi & 0x80000000;
Roger Meier21c0a852012-09-05 19:47:14 +0000641
Nobuaki Sukegawa527637a2016-07-24 15:28:46 +0900642 // Check for a negative
643 if ($isNeg) {
644 $hi = ~$hi & (int) 0xffffffff;
645 $lo = ~$lo & (int) 0xffffffff;
Roger Meier21c0a852012-09-05 19:47:14 +0000646
Nobuaki Sukegawa527637a2016-07-24 15:28:46 +0900647 if ($lo == (int) 0xffffffff) {
648 $hi++;
649 $lo = 0;
Roger Meier21c0a852012-09-05 19:47:14 +0000650 } else {
Nobuaki Sukegawa527637a2016-07-24 15:28:46 +0900651 $lo++;
Roger Meier21c0a852012-09-05 19:47:14 +0000652 }
653 }
654
Nobuaki Sukegawa527637a2016-07-24 15:28:46 +0900655 // Force 32bit words in excess of 2G to be positive - we deal with sign
656 // explicitly below
657
658 if ($hi & (int) 0x80000000) {
659 $hi &= (int) 0x7fffffff;
660 $hi += 0x80000000;
661 }
662
663 if ($lo & (int) 0x80000000) {
664 $lo &= (int) 0x7fffffff;
665 $lo += 0x80000000;
666 }
667
Håkon Hitlandb7a213c2016-11-17 16:59:30 +0100668 // Create as negative value first, since we can store -2^63 but not 2^63
669 $value = -$hi * 4294967296 - $lo;
Nobuaki Sukegawa527637a2016-07-24 15:28:46 +0900670
Håkon Hitlandb7a213c2016-11-17 16:59:30 +0100671 if (!$isNeg) {
672 $value = -$value;
Nobuaki Sukegawa527637a2016-07-24 15:28:46 +0900673 }
674
Roger Meier21c0a852012-09-05 19:47:14 +0000675 return $idx;
676 }
677
Roger Thomas6fb59232014-11-04 10:09:23 +0000678 public function writeI64($value)
679 {
Roger Meier21c0a852012-09-05 19:47:14 +0000680 // If we are in an I32 range, use the easy method below.
681 if (($value > 4294967296) || ($value < -4294967296)) {
682 // Convert $value to $hi and $lo
683 $neg = $value < 0;
684
685 if ($neg) {
686 $value *= -1;
687 }
688
Roger Thomas6fb59232014-11-04 10:09:23 +0000689 $hi = (int) $value >> 32;
690 $lo = (int) $value & 0xffffffff;
Roger Meier21c0a852012-09-05 19:47:14 +0000691
692 if ($neg) {
693 $hi = ~$hi;
694 $lo = ~$lo;
Roger Thomas6fb59232014-11-04 10:09:23 +0000695 if (($lo & (int) 0xffffffff) == (int) 0xffffffff) {
Roger Meier21c0a852012-09-05 19:47:14 +0000696 $lo = 0;
697 $hi++;
698 } else {
699 $lo++;
700 }
701 }
702
703 // Now do the zigging and zagging.
704 $xorer = 0;
705 if ($neg) {
706 $xorer = 0xffffffff;
707 }
708 $lowbit = ($lo >> 31) & 1;
709 $hi = ($hi << 1) | $lowbit;
710 $lo = ($lo << 1);
711 $lo = ($lo ^ $xorer) & 0xffffffff;
712 $hi = ($hi ^ $xorer) & 0xffffffff;
713
714 // now write out the varint, ensuring we shift both hi and lo
715 $out = "";
716 while (true) {
717 if (($lo & ~0x7f) === 0 &&
718 $hi === 0) {
719 $out .= chr($lo);
720 break;
721 } else {
722 $out .= chr(($lo & 0xff) | 0x80);
723 $lo = $lo >> 7;
724 $lo = $lo | ($hi << 25);
725 $hi = $hi >> 7;
726 // Right shift carries sign, but we don't want it to.
727 $hi = $hi & (127 << 25);
728 }
729 }
730
731 $ret = TStringFuncFactory::create()->strlen($out);
732 $this->trans_->write($out, $ret);
733
734 return $ret;
735 } else {
736 return $this->writeVarint($this->toZigZag($value, 64));
737 }
738 }
739}