PageRenderTime 63ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/ext-libs/Protobuf-PHP/library/DrSlump/Protobuf/Codec/Binary.php

https://gitlab.com/billyprice1/app-download.org
PHP | 423 lines | 319 code | 62 blank | 42 comment | 36 complexity | 435b0adc97550908a785cc3b6fa69caf MD5 | raw file
  1. <?php
  2. namespace DrSlump\Protobuf\Codec;
  3. use DrSlump\Protobuf;
  4. class Binary implements Protobuf\CodecInterface
  5. {
  6. const WIRE_VARINT = 0;
  7. const WIRE_FIXED64 = 1;
  8. const WIRE_LENGTH = 2;
  9. const WIRE_GROUP_START = 3;
  10. const WIRE_GROUP_END = 4;
  11. const WIRE_FIXED32 = 5;
  12. const WIRE_UNKNOWN = -1;
  13. // Table map to know if a field type is "packable"
  14. static $PACKABLE = array(
  15. Protobuf::TYPE_DOUBLE => true,
  16. Protobuf::TYPE_FLOAT => true,
  17. Protobuf::TYPE_INT64 => true,
  18. Protobuf::TYPE_UINT64 => true,
  19. Protobuf::TYPE_INT32 => true,
  20. Protobuf::TYPE_FIXED64 => true,
  21. Protobuf::TYPE_FIXED32 => true,
  22. Protobuf::TYPE_BOOL => true,
  23. Protobuf::TYPE_STRING => false,
  24. Protobuf::TYPE_GROUP => false,
  25. Protobuf::TYPE_MESSAGE => false,
  26. Protobuf::TYPE_BYTES => false,
  27. Protobuf::TYPE_UINT32 => true,
  28. Protobuf::TYPE_ENUM => true,
  29. Protobuf::TYPE_SFIXED32 => true,
  30. Protobuf::TYPE_SFIXED64 => true,
  31. Protobuf::TYPE_SINT32 => true,
  32. Protobuf::TYPE_SINT64 => true
  33. );
  34. /**
  35. * @param \DrSlump\Protobuf\Message $message
  36. * @return string
  37. */
  38. public function encode(Protobuf\Message $message)
  39. {
  40. return $this->encodeMessage($message);
  41. }
  42. /**
  43. * @param String|Message $message
  44. * @param String $data
  45. * @return \DrSlump\Protobuf\Message
  46. */
  47. public function decode(Protobuf\Message $message, $data)
  48. {
  49. static $reader;
  50. // Create a single reader for all messages to be parsed
  51. if (!$reader) {
  52. $reader = new Protobuf\Codec\Binary\Reader();
  53. }
  54. // Initialize the reader with the current message data
  55. $reader->init($data);
  56. return $this->decodeMessage($reader, $message);
  57. }
  58. protected function encodeMessage(Protobuf\Message $message)
  59. {
  60. $writer = new Binary\Writer();
  61. // Get message descriptor
  62. $descriptor = Protobuf::getRegistry()->getDescriptor($message);
  63. foreach ($descriptor->getFields() as $tag=>$field) {
  64. $empty = !$message->_has($tag);
  65. if ($field->isRequired() && $empty) {
  66. throw new \UnexpectedValueException(
  67. 'Message ' . get_class($message) . '\'s field tag ' . $tag . '(' . $field->getName() . ') is required but has no value'
  68. );
  69. }
  70. // Skip empty fields
  71. if ($empty) {
  72. continue;
  73. }
  74. $type = $field->getType();
  75. $wire = $field->isPacked() ? self::WIRE_LENGTH : $this->getWireType($type, null);
  76. // Compute key with tag number and wire type
  77. $key = $tag << 3 | $wire;
  78. $value = $message->_get($tag);
  79. if ($field->isRepeated()) {
  80. // Packed fields are encoded as a length-delimited stream containing
  81. // the concatenated encoding of each value.
  82. if ($field->isPacked() && !empty($value)) {
  83. $subwriter = new Binary\Writer();
  84. foreach($value as $val) {
  85. $this->encodeSimpleType($subwriter, $type, $val);
  86. }
  87. $data = $subwriter->getBytes();
  88. $writer->varint($key);
  89. $writer->varint(strlen($data));
  90. $writer->write($data);
  91. } else {
  92. // Make sure the value is an array of values
  93. $value = is_array($value) ? $value : array($value);
  94. foreach($value as $val) {
  95. // Skip nullified repeated values
  96. if (NULL === $val) {
  97. continue;
  98. } else if ($type !== Protobuf::TYPE_MESSAGE) {
  99. $writer->varint($key);
  100. $this->encodeSimpleType($writer, $type, $val);
  101. } else {
  102. $writer->varint($key);
  103. $data = $this->encodeMessage($val);
  104. $writer->varint(strlen($data));
  105. $writer->write($data);
  106. }
  107. }
  108. }
  109. } else if ($type !== Protobuf::TYPE_MESSAGE) {
  110. $writer->varint($key);
  111. $this->encodeSimpleType($writer, $type, $value);
  112. } else {
  113. $writer->varint($key);
  114. $data = $this->encodeMessage($value);
  115. $writer->varint(strlen($data));
  116. $writer->write($data);
  117. }
  118. }
  119. return $writer->getBytes();
  120. }
  121. protected function encodeSimpleType($writer, $type, $value)
  122. {
  123. switch ($type) {
  124. case Protobuf::TYPE_INT32:
  125. case Protobuf::TYPE_INT64:
  126. case Protobuf::TYPE_UINT64:
  127. case Protobuf::TYPE_UINT32:
  128. $writer->varint($value);
  129. break;
  130. case Protobuf::TYPE_SINT32: // ZigZag
  131. $writer->zigzag($value, 32);
  132. break;
  133. case Protobuf::TYPE_SINT64 : // ZigZag
  134. $writer->zigzag($value, 64);
  135. break;
  136. case Protobuf::TYPE_DOUBLE:
  137. $writer->double($value);
  138. break;
  139. case Protobuf::TYPE_FIXED64:
  140. $writer->fixed64($value);
  141. break;
  142. case Protobuf::TYPE_SFIXED64:
  143. $writer->sFixed64($value);
  144. break;
  145. case Protobuf::TYPE_FLOAT:
  146. $writer->float($value);
  147. break;
  148. case Protobuf::TYPE_FIXED32:
  149. $writer->fixed32($value);
  150. break;
  151. case Protobuf::TYPE_SFIXED32:
  152. $writer->sFixed32($value);
  153. break;
  154. case Protobuf::TYPE_BOOL:
  155. $writer->varint($value ? 1 : 0);
  156. break;
  157. case Protobuf::TYPE_STRING:
  158. case Protobuf::TYPE_BYTES:
  159. $writer->varint(strlen($value));
  160. $writer->write($value);
  161. break;
  162. case Protobuf::TYPE_MESSAGE:
  163. // Messages are not supported in this method
  164. return null;
  165. case Protobuf::TYPE_ENUM:
  166. $writer->varint($value);
  167. break;
  168. default:
  169. throw new \Exception('Unknown field type ' . $type);
  170. }
  171. }
  172. /**
  173. * @param \DrSlump\Protobuf\Codec\Binary\Reader $reader
  174. * @param \DrSlump\Protobuf\Message $message
  175. * @param int $length
  176. * @return \DrSlump\Protobuf\Message
  177. */
  178. protected function decodeMessage($reader, \DrSlump\Protobuf\Message $message, $length = NULL)
  179. {
  180. /** @var $message \DrSlump\Protobuf\Message */
  181. /** @var $descriptor \DrSlump\Protobuf\Descriptor */
  182. // Get message descriptor
  183. $descriptor = Protobuf::getRegistry()->getDescriptor($message);
  184. // Cache locally the message fields
  185. $fields = $descriptor->getFields();
  186. // Calculate the maximum offset if we have defined a length
  187. $limit = !is_null($length) ? $reader->pos() + $length : NULL;
  188. $pos = $reader->pos();
  189. // Keep reading until we reach the end or the limit
  190. while ($limit === NULL && !$reader->eof() || $limit !== NULL && $reader->pos() < $limit) {
  191. // Get initial varint with tag number and wire type
  192. $key = $reader->varint();
  193. if ($reader->eof()) break;
  194. $wire = $key & 0x7;
  195. $tag = $key >> 3;
  196. // Find the matching field for the tag number
  197. if (!isset($fields[$tag])) {
  198. $data = $this->decodeUnknown($reader, $wire);
  199. $unknown = new Binary\Unknown($tag, $wire, $data);
  200. $message->addUnknown($unknown);
  201. continue;
  202. }
  203. $field = $fields[$tag];
  204. $type = $field->getType();
  205. // Check if we are dealing with a packed stream, we cannot rely on the packed
  206. // flag of the message since we cannot be certain if the creator of the message
  207. // was using it.
  208. if ($wire === self::WIRE_LENGTH && $field->isRepeated() && self::$PACKABLE[$type]) {
  209. $len = $reader->varint();
  210. $until = $reader->pos() + $len;
  211. $wire = $this->getWireType($type);
  212. while ($reader->pos() < $until) {
  213. $item = $this->decodeSimpleType($reader, $type, $wire);
  214. $message->_add($tag, $item);
  215. }
  216. } else {
  217. // Assert wire and type match
  218. $this->assertWireType($wire, $type);
  219. // Check if it's a sub-message
  220. if ($type === Protobuf::TYPE_MESSAGE) {
  221. $submessage = $field->getReference();
  222. $submessage = new $submessage;
  223. $len = $reader->varint();
  224. $value = $this->decodeMessage($reader, $submessage, $len);
  225. } else {
  226. $value = $this->decodeSimpleType($reader, $type, $wire);
  227. }
  228. // Support non-packed repeated fields
  229. if ($field->isRepeated()) {
  230. $message->_add($tag, $value);
  231. } else {
  232. $message->_set($tag, $value);
  233. }
  234. }
  235. }
  236. return $message;
  237. }
  238. protected function isPackable($type)
  239. {
  240. static $packable = array(
  241. Protobuf::TYPE_INT64,
  242. Protobuf::TYPE_UINT64,
  243. Protobuf::TYPE_INT32,
  244. Protobuf::TYPE_UINT32,
  245. Protobuf::TYPE_SINT32,
  246. Protobuf::TYPE_SINT64,
  247. Protobuf::TYPE_DOUBLE,
  248. Protobuf::TYPE_FIXED64,
  249. Protobuf::TYPE_SFIXED64,
  250. Protobuf::TYPE_FLOAT,
  251. Protobuf::TYPE_FIXED32,
  252. Protobuf::TYPE_SFIXED32,
  253. Protobuf::TYPE_BOOL,
  254. Protobuf::TYPE_ENUM
  255. );
  256. return in_array($type, $packable);
  257. }
  258. protected function decodeUnknown($reader, $wire)
  259. {
  260. switch ($wire) {
  261. case self::WIRE_VARINT:
  262. return $reader->varint();
  263. case self::WIRE_LENGTH:
  264. $length = $reader->varint();
  265. return $reader->read($length);
  266. case self::WIRE_FIXED32:
  267. return $reader->fixed32();
  268. case self::WIRE_FIXED64:
  269. return $reader->fixed64();
  270. case self::WIRE_GROUP_START:
  271. case self::WIRE_GROUP_END:
  272. throw new \RuntimeException('Groups are deprecated in Protocol Buffers and unsupported by this library');
  273. default:
  274. throw new \RuntimeException('Unsupported wire type (' . $wire . ') while consuming unknown field');
  275. }
  276. }
  277. protected function assertWireType($wire, $type)
  278. {
  279. $expected = $this->getWireType($type, $wire);
  280. if ($wire !== $expected) {
  281. throw new \RuntimeException("Expected wire type $expected but got $wire for type $type");
  282. }
  283. }
  284. protected function getWireType($type, $default = null)
  285. {
  286. static $map = array(
  287. Protobuf::TYPE_INT32 => self::WIRE_VARINT,
  288. Protobuf::TYPE_INT64 => self::WIRE_VARINT,
  289. Protobuf::TYPE_UINT32 => self::WIRE_VARINT,
  290. Protobuf::TYPE_UINT64 => self::WIRE_VARINT,
  291. Protobuf::TYPE_SINT32 => self::WIRE_VARINT,
  292. Protobuf::TYPE_SINT64 => self::WIRE_VARINT,
  293. Protobuf::TYPE_BOOL => self::WIRE_VARINT,
  294. Protobuf::TYPE_ENUM => self::WIRE_VARINT,
  295. Protobuf::TYPE_FIXED64 => self::WIRE_FIXED64,
  296. Protobuf::TYPE_SFIXED64 => self::WIRE_FIXED64,
  297. Protobuf::TYPE_DOUBLE => self::WIRE_FIXED64,
  298. Protobuf::TYPE_STRING => self::WIRE_LENGTH,
  299. Protobuf::TYPE_BYTES => self::WIRE_LENGTH,
  300. Protobuf::TYPE_MESSAGE => self::WIRE_LENGTH,
  301. Protobuf::TYPE_FIXED32 => self::WIRE_FIXED32,
  302. Protobuf::TYPE_SFIXED32 => self::WIRE_FIXED32,
  303. Protobuf::TYPE_FLOAT => self::WIRE_FIXED32
  304. );
  305. // Unknown types just return the reported wire type
  306. return isset($map[$type]) ? $map[$type] : $default;
  307. }
  308. protected function decodeSimpleType($reader, $type, $wireType)
  309. {
  310. switch ($type) {
  311. case Protobuf::TYPE_INT64:
  312. case Protobuf::TYPE_UINT64:
  313. case Protobuf::TYPE_INT32:
  314. case Protobuf::TYPE_UINT32:
  315. case Protobuf::TYPE_ENUM:
  316. return $reader->varint();
  317. case Protobuf::TYPE_SINT32: // ZigZag
  318. return $reader->zigzag();
  319. case Protobuf::TYPE_SINT64: // ZigZag
  320. return $reader->zigzag();
  321. case Protobuf::TYPE_DOUBLE:
  322. return $reader->double();
  323. case Protobuf::TYPE_FIXED64:
  324. return $reader->fixed64();
  325. case Protobuf::TYPE_SFIXED64:
  326. return $reader->sFixed64();
  327. case Protobuf::TYPE_FLOAT:
  328. return $reader->float();
  329. case Protobuf::TYPE_FIXED32:
  330. return $reader->fixed32();
  331. case Protobuf::TYPE_SFIXED32:
  332. return $reader->sFixed32();
  333. case Protobuf::TYPE_BOOL:
  334. return (bool)$reader->varint();
  335. case Protobuf::TYPE_STRING:
  336. case Protobuf::TYPE_BYTES:
  337. $length = $reader->varint();
  338. return $reader->read($length);
  339. case Protobuf::TYPE_MESSAGE:
  340. throw new \RuntimeException('Nested messages are not supported in this method');
  341. default:
  342. // Unknown type, follow wire type rules
  343. switch ($wireType) {
  344. case self::WIRE_VARINT:
  345. return $reader->varint();
  346. case self::WIRE_FIXED32:
  347. return $reader->fixed32();
  348. case self::WIRE_FIXED64:
  349. return $reader->fixed64();
  350. case self::WIRE_LENGTH:
  351. $length = $reader->varint();
  352. return $reader->read($length);
  353. case self::WIRE_GROUP_START:
  354. case self::WIRE_GROUP_END:
  355. throw new \RuntimeException('Group is deprecated and not supported');
  356. default:
  357. throw new \RuntimeException('Unsupported wire type number ' . $wireType);
  358. }
  359. }
  360. }
  361. }