TCompactProtocol.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. <?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. namespace Thrift\Protocol;
  23. use Thrift\Type\TType;
  24. use Thrift\Exception\TProtocolException;
  25. use Thrift\Factory\TStringFuncFactory;
  26. /**
  27. * Compact implementation of the Thrift protocol.
  28. *
  29. */
  30. class TCompactProtocol extends TProtocol
  31. {
  32. const COMPACT_STOP = 0x00;
  33. const COMPACT_TRUE = 0x01;
  34. const COMPACT_FALSE = 0x02;
  35. const COMPACT_BYTE = 0x03;
  36. const COMPACT_I16 = 0x04;
  37. const COMPACT_I32 = 0x05;
  38. const COMPACT_I64 = 0x06;
  39. const COMPACT_DOUBLE = 0x07;
  40. const COMPACT_BINARY = 0x08;
  41. const COMPACT_LIST = 0x09;
  42. const COMPACT_SET = 0x0A;
  43. const COMPACT_MAP = 0x0B;
  44. const COMPACT_STRUCT = 0x0C;
  45. const STATE_CLEAR = 0;
  46. const STATE_FIELD_WRITE = 1;
  47. const STATE_VALUE_WRITE = 2;
  48. const STATE_CONTAINER_WRITE = 3;
  49. const STATE_BOOL_WRITE = 4;
  50. const STATE_FIELD_READ = 5;
  51. const STATE_CONTAINER_READ = 6;
  52. const STATE_VALUE_READ = 7;
  53. const STATE_BOOL_READ = 8;
  54. const VERSION_MASK = 0x1f;
  55. const VERSION = 1;
  56. const PROTOCOL_ID = 0x82;
  57. const TYPE_MASK = 0xe0;
  58. const TYPE_BITS = 0x07;
  59. const TYPE_SHIFT_AMOUNT = 5;
  60. protected static $ctypes = array(
  61. TType::STOP => TCompactProtocol::COMPACT_STOP,
  62. TType::BOOL => TCompactProtocol::COMPACT_TRUE, // used for collection
  63. TType::BYTE => TCompactProtocol::COMPACT_BYTE,
  64. TType::I16 => TCompactProtocol::COMPACT_I16,
  65. TType::I32 => TCompactProtocol::COMPACT_I32,
  66. TType::I64 => TCompactProtocol::COMPACT_I64,
  67. TType::DOUBLE => TCompactProtocol::COMPACT_DOUBLE,
  68. TType::STRING => TCompactProtocol::COMPACT_BINARY,
  69. TType::STRUCT => TCompactProtocol::COMPACT_STRUCT,
  70. TType::LST => TCompactProtocol::COMPACT_LIST,
  71. TType::SET => TCompactProtocol::COMPACT_SET,
  72. TType::MAP => TCompactProtocol::COMPACT_MAP,
  73. );
  74. protected static $ttypes = array(
  75. TCompactProtocol::COMPACT_STOP => TType::STOP ,
  76. TCompactProtocol::COMPACT_TRUE => TType::BOOL, // used for collection
  77. TCompactProtocol::COMPACT_FALSE => TType::BOOL,
  78. TCompactProtocol::COMPACT_BYTE => TType::BYTE,
  79. TCompactProtocol::COMPACT_I16 => TType::I16,
  80. TCompactProtocol::COMPACT_I32 => TType::I32,
  81. TCompactProtocol::COMPACT_I64 => TType::I64,
  82. TCompactProtocol::COMPACT_DOUBLE => TType::DOUBLE,
  83. TCompactProtocol::COMPACT_BINARY => TType::STRING,
  84. TCompactProtocol::COMPACT_STRUCT => TType::STRUCT,
  85. TCompactProtocol::COMPACT_LIST => TType::LST,
  86. TCompactProtocol::COMPACT_SET => TType::SET,
  87. TCompactProtocol::COMPACT_MAP => TType::MAP,
  88. );
  89. protected $state = TCompactProtocol::STATE_CLEAR;
  90. protected $lastFid = 0;
  91. protected $boolFid = null;
  92. protected $boolValue = null;
  93. protected $structs = array();
  94. protected $containers = array();
  95. // Some varint / zigzag helper methods
  96. public function toZigZag($n, $bits)
  97. {
  98. return ($n << 1) ^ ($n >> ($bits - 1));
  99. }
  100. public function fromZigZag($n)
  101. {
  102. return ($n >> 1) ^ -($n & 1);
  103. }
  104. public function getVarint($data)
  105. {
  106. $out = "";
  107. while (true) {
  108. if (($data & ~0x7f) === 0) {
  109. $out .= chr($data);
  110. break;
  111. } else {
  112. $out .= chr(($data & 0xff) | 0x80);
  113. $data = $data >> 7;
  114. }
  115. }
  116. return $out;
  117. }
  118. public function writeVarint($data)
  119. {
  120. $out = $this->getVarint($data);
  121. $result = TStringFuncFactory::create()->strlen($out);
  122. $this->trans_->write($out, $result);
  123. return $result;
  124. }
  125. public function readVarint(&$result)
  126. {
  127. $idx = 0;
  128. $shift = 0;
  129. $result = 0;
  130. while (true) {
  131. $x = $this->trans_->readAll(1);
  132. $arr = unpack('C', $x);
  133. $byte = $arr[1];
  134. $idx += 1;
  135. $result |= ($byte & 0x7f) << $shift;
  136. if (($byte >> 7) === 0) {
  137. return $idx;
  138. }
  139. $shift += 7;
  140. }
  141. return $idx;
  142. }
  143. public function __construct($trans)
  144. {
  145. parent::__construct($trans);
  146. }
  147. public function writeMessageBegin($name, $type, $seqid)
  148. {
  149. $written =
  150. $this->writeUByte(TCompactProtocol::PROTOCOL_ID) +
  151. $this->writeUByte(TCompactProtocol::VERSION |
  152. ($type << TCompactProtocol::TYPE_SHIFT_AMOUNT)) +
  153. $this->writeVarint($seqid) +
  154. $this->writeString($name);
  155. $this->state = TCompactProtocol::STATE_VALUE_WRITE;
  156. return $written;
  157. }
  158. public function writeMessageEnd()
  159. {
  160. $this->state = TCompactProtocol::STATE_CLEAR;
  161. return 0;
  162. }
  163. public function writeStructBegin($name)
  164. {
  165. $this->structs[] = array($this->state, $this->lastFid);
  166. $this->state = TCompactProtocol::STATE_FIELD_WRITE;
  167. $this->lastFid = 0;
  168. return 0;
  169. }
  170. public function writeStructEnd()
  171. {
  172. $old_values = array_pop($this->structs);
  173. $this->state = $old_values[0];
  174. $this->lastFid = $old_values[1];
  175. return 0;
  176. }
  177. public function writeFieldStop()
  178. {
  179. return $this->writeByte(0);
  180. }
  181. public function writeFieldHeader($type, $fid)
  182. {
  183. $written = 0;
  184. $delta = $fid - $this->lastFid;
  185. if (0 < $delta && $delta <= 15) {
  186. $written = $this->writeUByte(($delta << 4) | $type);
  187. } else {
  188. $written = $this->writeByte($type) +
  189. $this->writeI16($fid);
  190. }
  191. $this->lastFid = $fid;
  192. return $written;
  193. }
  194. public function writeFieldBegin($field_name, $field_type, $field_id)
  195. {
  196. if ($field_type == TTYPE::BOOL) {
  197. $this->state = TCompactProtocol::STATE_BOOL_WRITE;
  198. $this->boolFid = $field_id;
  199. return 0;
  200. } else {
  201. $this->state = TCompactProtocol::STATE_VALUE_WRITE;
  202. return $this->writeFieldHeader(self::$ctypes[$field_type], $field_id);
  203. }
  204. }
  205. public function writeFieldEnd()
  206. {
  207. $this->state = TCompactProtocol::STATE_FIELD_WRITE;
  208. return 0;
  209. }
  210. public function writeCollectionBegin($etype, $size)
  211. {
  212. $written = 0;
  213. if ($size <= 14) {
  214. $written = $this->writeUByte($size << 4 |
  215. self::$ctypes[$etype]);
  216. } else {
  217. $written = $this->writeUByte(0xf0 |
  218. self::$ctypes[$etype]) +
  219. $this->writeVarint($size);
  220. }
  221. $this->containers[] = $this->state;
  222. $this->state = TCompactProtocol::STATE_CONTAINER_WRITE;
  223. return $written;
  224. }
  225. public function writeMapBegin($key_type, $val_type, $size)
  226. {
  227. $written = 0;
  228. if ($size == 0) {
  229. $written = $this->writeByte(0);
  230. } else {
  231. $written = $this->writeVarint($size) +
  232. $this->writeUByte(self::$ctypes[$key_type] << 4 |
  233. self::$ctypes[$val_type]);
  234. }
  235. $this->containers[] = $this->state;
  236. return $written;
  237. }
  238. public function writeCollectionEnd()
  239. {
  240. $this->state = array_pop($this->containers);
  241. return 0;
  242. }
  243. public function writeMapEnd()
  244. {
  245. return $this->writeCollectionEnd();
  246. }
  247. public function writeListBegin($elem_type, $size)
  248. {
  249. return $this->writeCollectionBegin($elem_type, $size);
  250. }
  251. public function writeListEnd()
  252. {
  253. return $this->writeCollectionEnd();
  254. }
  255. public function writeSetBegin($elem_type, $size)
  256. {
  257. return $this->writeCollectionBegin($elem_type, $size);
  258. }
  259. public function writeSetEnd()
  260. {
  261. return $this->writeCollectionEnd();
  262. }
  263. public function writeBool($value)
  264. {
  265. if ($this->state == TCompactProtocol::STATE_BOOL_WRITE) {
  266. $ctype = TCompactProtocol::COMPACT_FALSE;
  267. if ($value) {
  268. $ctype = TCompactProtocol::COMPACT_TRUE;
  269. }
  270. return $this->writeFieldHeader($ctype, $this->boolFid);
  271. } elseif ($this->state == TCompactProtocol::STATE_CONTAINER_WRITE) {
  272. return $this->writeByte($value ? 1 : 0);
  273. } else {
  274. throw new TProtocolException('Invalid state in compact protocol');
  275. }
  276. }
  277. public function writeByte($value)
  278. {
  279. $data = pack('c', $value);
  280. $this->trans_->write($data, 1);
  281. return 1;
  282. }
  283. public function writeUByte($byte)
  284. {
  285. $this->trans_->write(pack('C', $byte), 1);
  286. return 1;
  287. }
  288. public function writeI16($value)
  289. {
  290. $thing = $this->toZigZag($value, 16);
  291. return $this->writeVarint($thing);
  292. }
  293. public function writeI32($value)
  294. {
  295. $thing = $this->toZigZag($value, 32);
  296. return $this->writeVarint($thing);
  297. }
  298. public function writeDouble($value)
  299. {
  300. $data = pack('d', $value);
  301. $this->trans_->write($data, 8);
  302. return 8;
  303. }
  304. public function writeString($value)
  305. {
  306. $len = TStringFuncFactory::create()->strlen($value);
  307. $result = $this->writeVarint($len);
  308. if ($len) {
  309. $this->trans_->write($value, $len);
  310. }
  311. return $result + $len;
  312. }
  313. public function readFieldBegin(&$name, &$field_type, &$field_id)
  314. {
  315. $result = $this->readUByte($compact_type_and_delta);
  316. $compact_type = $compact_type_and_delta & 0x0f;
  317. if ($compact_type == TType::STOP) {
  318. $field_type = $compact_type;
  319. $field_id = 0;
  320. return $result;
  321. }
  322. $delta = $compact_type_and_delta >> 4;
  323. if ($delta == 0) {
  324. $result += $this->readI16($field_id);
  325. } else {
  326. $field_id = $this->lastFid + $delta;
  327. }
  328. $this->lastFid = $field_id;
  329. $field_type = $this->getTType($compact_type);
  330. if ($compact_type == TCompactProtocol::COMPACT_TRUE) {
  331. $this->state = TCompactProtocol::STATE_BOOL_READ;
  332. $this->boolValue = true;
  333. } elseif ($compact_type == TCompactProtocol::COMPACT_FALSE) {
  334. $this->state = TCompactProtocol::STATE_BOOL_READ;
  335. $this->boolValue = false;
  336. } else {
  337. $this->state = TCompactProtocol::STATE_VALUE_READ;
  338. }
  339. return $result;
  340. }
  341. public function readFieldEnd()
  342. {
  343. $this->state = TCompactProtocol::STATE_FIELD_READ;
  344. return 0;
  345. }
  346. public function readUByte(&$value)
  347. {
  348. $data = $this->trans_->readAll(1);
  349. $arr = unpack('C', $data);
  350. $value = $arr[1];
  351. return 1;
  352. }
  353. public function readByte(&$value)
  354. {
  355. $data = $this->trans_->readAll(1);
  356. $arr = unpack('c', $data);
  357. $value = $arr[1];
  358. return 1;
  359. }
  360. public function readZigZag(&$value)
  361. {
  362. $result = $this->readVarint($value);
  363. $value = $this->fromZigZag($value);
  364. return $result;
  365. }
  366. public function readMessageBegin(&$name, &$type, &$seqid)
  367. {
  368. $protoId = 0;
  369. $result = $this->readUByte($protoId);
  370. if ($protoId != TCompactProtocol::PROTOCOL_ID) {
  371. throw new TProtocolException('Bad protocol id in TCompact message');
  372. }
  373. $verType = 0;
  374. $result += $this->readUByte($verType);
  375. $type = ($verType >> TCompactProtocol::TYPE_SHIFT_AMOUNT) & TCompactProtocol::TYPE_BITS;
  376. $version = $verType & TCompactProtocol::VERSION_MASK;
  377. if ($version != TCompactProtocol::VERSION) {
  378. throw new TProtocolException('Bad version in TCompact message');
  379. }
  380. $result += $this->readVarint($seqid);
  381. $result += $this->readString($name);
  382. return $result;
  383. }
  384. public function readMessageEnd()
  385. {
  386. return 0;
  387. }
  388. public function readStructBegin(&$name)
  389. {
  390. $name = ''; // unused
  391. $this->structs[] = array($this->state, $this->lastFid);
  392. $this->state = TCompactProtocol::STATE_FIELD_READ;
  393. $this->lastFid = 0;
  394. return 0;
  395. }
  396. public function readStructEnd()
  397. {
  398. $last = array_pop($this->structs);
  399. $this->state = $last[0];
  400. $this->lastFid = $last[1];
  401. return 0;
  402. }
  403. public function readCollectionBegin(&$type, &$size)
  404. {
  405. $sizeType = 0;
  406. $result = $this->readUByte($sizeType);
  407. $size = $sizeType >> 4;
  408. $type = $this->getTType($sizeType);
  409. if ($size == 15) {
  410. $result += $this->readVarint($size);
  411. }
  412. $this->containers[] = $this->state;
  413. $this->state = TCompactProtocol::STATE_CONTAINER_READ;
  414. return $result;
  415. }
  416. public function readMapBegin(&$key_type, &$val_type, &$size)
  417. {
  418. $result = $this->readVarint($size);
  419. $types = 0;
  420. if ($size > 0) {
  421. $result += $this->readUByte($types);
  422. }
  423. $val_type = $this->getTType($types);
  424. $key_type = $this->getTType($types >> 4);
  425. $this->containers[] = $this->state;
  426. $this->state = TCompactProtocol::STATE_CONTAINER_READ;
  427. return $result;
  428. }
  429. public function readCollectionEnd()
  430. {
  431. $this->state = array_pop($this->containers);
  432. return 0;
  433. }
  434. public function readMapEnd()
  435. {
  436. return $this->readCollectionEnd();
  437. }
  438. public function readListBegin(&$elem_type, &$size)
  439. {
  440. return $this->readCollectionBegin($elem_type, $size);
  441. }
  442. public function readListEnd()
  443. {
  444. return $this->readCollectionEnd();
  445. }
  446. public function readSetBegin(&$elem_type, &$size)
  447. {
  448. return $this->readCollectionBegin($elem_type, $size);
  449. }
  450. public function readSetEnd()
  451. {
  452. return $this->readCollectionEnd();
  453. }
  454. public function readBool(&$value)
  455. {
  456. if ($this->state == TCompactProtocol::STATE_BOOL_READ) {
  457. $value = $this->boolValue;
  458. return 0;
  459. } elseif ($this->state == TCompactProtocol::STATE_CONTAINER_READ) {
  460. return $this->readByte($value);
  461. } else {
  462. throw new TProtocolException('Invalid state in compact protocol');
  463. }
  464. }
  465. public function readI16(&$value)
  466. {
  467. return $this->readZigZag($value);
  468. }
  469. public function readI32(&$value)
  470. {
  471. return $this->readZigZag($value);
  472. }
  473. public function readDouble(&$value)
  474. {
  475. $data = $this->trans_->readAll(8);
  476. $arr = unpack('d', $data);
  477. $value = $arr[1];
  478. return 8;
  479. }
  480. public function readString(&$value)
  481. {
  482. $result = $this->readVarint($len);
  483. if ($len) {
  484. $value = $this->trans_->readAll($len);
  485. } else {
  486. $value = '';
  487. }
  488. return $result + $len;
  489. }
  490. public function getTType($byte)
  491. {
  492. return self::$ttypes[$byte & 0x0f];
  493. }
  494. // If we are on a 32bit architecture we have to explicitly deal with
  495. // 64-bit twos-complement arithmetic since PHP wants to treat all ints
  496. // as signed and any int over 2^31 - 1 as a float
  497. // Read and write I64 as two 32 bit numbers $hi and $lo
  498. public function readI64(&$value)
  499. {
  500. // Read varint from wire
  501. $hi = 0;
  502. $lo = 0;
  503. $idx = 0;
  504. $shift = 0;
  505. while (true) {
  506. $x = $this->trans_->readAll(1);
  507. $arr = unpack('C', $x);
  508. $byte = $arr[1];
  509. $idx += 1;
  510. // Shift hi and lo together.
  511. if ($shift < 28) {
  512. $lo |= (($byte & 0x7f) << $shift);
  513. } elseif ($shift == 28) {
  514. $lo |= (($byte & 0x0f) << 28);
  515. $hi |= (($byte & 0x70) >> 4);
  516. } else {
  517. $hi |= (($byte & 0x7f) << ($shift - 32));
  518. }
  519. if (($byte >> 7) === 0) {
  520. break;
  521. }
  522. $shift += 7;
  523. }
  524. // Now, unzig it.
  525. $xorer = 0;
  526. if ($lo & 1) {
  527. $xorer = 0xffffffff;
  528. }
  529. $lo = ($lo >> 1) & 0x7fffffff;
  530. $lo = $lo | (($hi & 1) << 31);
  531. $hi = ($hi >> 1) ^ $xorer;
  532. $lo = $lo ^ $xorer;
  533. // Now put $hi and $lo back together
  534. $isNeg = $hi < 0 || $hi & 0x80000000;
  535. // Check for a negative
  536. if ($isNeg) {
  537. $hi = ~$hi & (int) 0xffffffff;
  538. $lo = ~$lo & (int) 0xffffffff;
  539. if ($lo == (int) 0xffffffff) {
  540. $hi++;
  541. $lo = 0;
  542. } else {
  543. $lo++;
  544. }
  545. }
  546. // Force 32bit words in excess of 2G to be positive - we deal with sign
  547. // explicitly below
  548. if ($hi & (int) 0x80000000) {
  549. $hi &= (int) 0x7fffffff;
  550. $hi += 0x80000000;
  551. }
  552. if ($lo & (int) 0x80000000) {
  553. $lo &= (int) 0x7fffffff;
  554. $lo += 0x80000000;
  555. }
  556. // Create as negative value first, since we can store -2^63 but not 2^63
  557. $value = -$hi * 4294967296 - $lo;
  558. if (!$isNeg) {
  559. $value = -$value;
  560. }
  561. return $idx;
  562. }
  563. public function writeI64($value)
  564. {
  565. // If we are in an I32 range, use the easy method below.
  566. if (($value > 4294967296) || ($value < -4294967296)) {
  567. // Convert $value to $hi and $lo
  568. $neg = $value < 0;
  569. if ($neg) {
  570. $value *= -1;
  571. }
  572. $hi = (int) $value >> 32;
  573. $lo = (int) $value & 0xffffffff;
  574. if ($neg) {
  575. $hi = ~$hi;
  576. $lo = ~$lo;
  577. if (($lo & (int) 0xffffffff) == (int) 0xffffffff) {
  578. $lo = 0;
  579. $hi++;
  580. } else {
  581. $lo++;
  582. }
  583. }
  584. // Now do the zigging and zagging.
  585. $xorer = 0;
  586. if ($neg) {
  587. $xorer = 0xffffffff;
  588. }
  589. $lowbit = ($lo >> 31) & 1;
  590. $hi = ($hi << 1) | $lowbit;
  591. $lo = ($lo << 1);
  592. $lo = ($lo ^ $xorer) & 0xffffffff;
  593. $hi = ($hi ^ $xorer) & 0xffffffff;
  594. // now write out the varint, ensuring we shift both hi and lo
  595. $out = "";
  596. while (true) {
  597. if (($lo & ~0x7f) === 0 &&
  598. $hi === 0) {
  599. $out .= chr($lo);
  600. break;
  601. } else {
  602. $out .= chr(($lo & 0xff) | 0x80);
  603. $lo = $lo >> 7;
  604. $lo = $lo | ($hi << 25);
  605. $hi = $hi >> 7;
  606. // Right shift carries sign, but we don't want it to.
  607. $hi = $hi & (127 << 25);
  608. }
  609. }
  610. $ret = TStringFuncFactory::create()->strlen($out);
  611. $this->trans_->write($out, $ret);
  612. return $ret;
  613. } else {
  614. return $this->writeVarint($this->toZigZag($value, 64));
  615. }
  616. }
  617. }