PageRenderTime 53ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://gitlab.com/billyprice1/app-download.org
PHP | 317 lines | 193 code | 45 blank | 79 comment | 33 complexity | 5be4cbee35519f2894aa7ff0cac03f35 MD5 | raw file
  1. <?php
  2. namespace DrSlump\Protobuf\Codec\Binary;
  3. /**
  4. * Implements writing primitives for Protobuf binary streams
  5. *
  6. * @note Protobuf uses little-endian order
  7. */
  8. class Writer
  9. {
  10. /** @var resource */
  11. protected $_fd;
  12. public function __construct()
  13. {
  14. $this->_fd = fopen('php://memory', 'wb');
  15. }
  16. public function __destruct()
  17. {
  18. fclose($this->_fd);
  19. }
  20. /**
  21. * Get the current bytes in the stream
  22. *
  23. * @return string
  24. */
  25. public function getBytes()
  26. {
  27. fseek($this->_fd, 0, SEEK_SET);
  28. return stream_get_contents($this->_fd);
  29. }
  30. /**
  31. * Store the given bytes in the stream
  32. *
  33. * @throws \RuntimeException
  34. * @param string $bytes
  35. * @param int $length
  36. */
  37. public function write($bytes, $length = null)
  38. {
  39. if ($length === NULL) {
  40. $length = strlen($bytes);
  41. }
  42. $written = fwrite($this->_fd, $bytes, $length);
  43. if ($written !== $length) {
  44. throw new \RuntimeException('Failed to write ' . $length . ' bytes');
  45. }
  46. }
  47. /**
  48. * Store a single byte
  49. *
  50. * @param int $value
  51. */
  52. public function byte($value)
  53. {
  54. $this->write(chr($value), 1);
  55. }
  56. /**
  57. * Store an integer encoded as varint
  58. *
  59. * @throws \OutOfBoundsException
  60. * @param int $value
  61. */
  62. public function varint($value)
  63. {
  64. // Small values do not need to be encoded
  65. if ($value >= 0 && $value < 0x80) {
  66. $this->byte($value);
  67. return;
  68. }
  69. // Build an array of bytes with the encoded values
  70. if ($value > 0) {
  71. $values = array();
  72. while ($value > 0) {
  73. $values[] = 0x80 | ($value & 0x7f);
  74. $value = $value >> 7;
  75. }
  76. } else if (function_exists('gmp_init')) {
  77. $value = PHP_INT_SIZE < 8
  78. ? gmp_and($value, '0x0ffffffffffffffff')
  79. : sprintf('%u', $value);
  80. $values = $this->varint_gmp($value);
  81. } else if (PHP_INT_SIZE < 8) {
  82. throw new \OutOfBoundsException(
  83. "PHP versions compiled with 32bit integers can only support negative integer encoding with GMP extension ($value was given)"
  84. );
  85. } else if (function_exists('bccomp')) {
  86. $value = sprintf('%u', $value);
  87. $values = $this->varint_bc($value);
  88. } else {
  89. throw new \OutOfBoundsException("Varints of negative integers are only supported with GMP or BC big integers PHP extensions ($value was given)");
  90. }
  91. // Remove the MSB flag from the last byte
  92. $values[count($values)-1] &= 0x7f;
  93. // Convert the byte sized ints to actual bytes in a string
  94. //$bytes = implode('', array_map('chr', $values));
  95. $bytes = call_user_func_array('pack', array_merge(array('C*'), $values));;
  96. $this->write($bytes);
  97. }
  98. public function varint_gmp($value)
  99. {
  100. static $x00, $x7f, $x80;
  101. if (NULL === $x00) {
  102. $x00 = \gmp_init(0x00);
  103. $x7f = \gmp_init(0x7f);
  104. $x80 = \gmp_init(0x80);
  105. }
  106. $values = array();
  107. while (\gmp_cmp($value, $x00) > 0) {
  108. $values[] = \gmp_intval(\gmp_and($value, $x7f)) | 0x80;
  109. $value = \gmp_div_q($value, $x80);
  110. }
  111. return $values;
  112. }
  113. public function varint_bc($value)
  114. {
  115. $values = array();
  116. while (\bccomp($value, 0, 0) > 0) {
  117. // Get the last 7bits of the number
  118. $bin = '';
  119. $dec = $value;
  120. do {
  121. $rest = bcmod($dec, 2);
  122. $dec = bcdiv($dec, 2, 0);
  123. $bin = $rest . $bin;
  124. } while ($dec > 0 && strlen($bin) < 7);
  125. // Pack as a decimal and apply the flag
  126. $values[] = intval($bin, 2) | 0x80;
  127. $value = bcdiv($value, 0x80, 0);
  128. }
  129. return $values;
  130. }
  131. /**
  132. * Encodes an integer with zigzag
  133. *
  134. * @param int $value
  135. * @param int $base Either 32 or 64 bits
  136. */
  137. public function zigzag($value, $base = 32)
  138. {
  139. $value = ($value << 1) ^ ($value >> $base-1);
  140. $this->varint($value);
  141. }
  142. /**
  143. * Encode an integer as a fixed of 32bits with sign
  144. *
  145. * @param int $value
  146. */
  147. public function sFixed32($value)
  148. {
  149. $bytes = pack('l*', $value);
  150. if ($this->isBigEndian()) {
  151. $bytes = strrev($bytes);
  152. }
  153. $this->write($bytes, 4);
  154. }
  155. /**
  156. * Encode an integer as a fixed of 32bits without sign
  157. *
  158. * @param int $value
  159. */
  160. public function fixed32($value)
  161. {
  162. $bytes = pack('V*', $value);
  163. $this->write($bytes, 4);
  164. }
  165. /**
  166. * Encode an integer as a fixed of 64bits with sign
  167. *
  168. * @param int $value
  169. */
  170. public function sFixed64($value)
  171. {
  172. if ($value >= 0) {
  173. $this->fixed64($value);
  174. } else if (function_exists('gmp_init')) {
  175. $this->sFixed64_gmp($value);
  176. } else if (function_exists('bcadd')) {
  177. $this->sFixed64_bc($value);
  178. } else {
  179. throw new \OutOfBoundsException("The signed Fixed64 type with negative integers is only supported with GMP or BC big integers PHP extensions ($value was given)");
  180. }
  181. }
  182. public function sFixed64_gmp($value)
  183. {
  184. static $xff, $x100;
  185. if (NULL === $xff) {
  186. $xff = gmp_init(0xff);
  187. $x100 = gmp_init(0x100);
  188. }
  189. $value = PHP_INT_SIZE < 8
  190. ? gmp_and($value, '0x0ffffffffffffffff')
  191. : gmp_init(sprintf('%u', $value));
  192. $bytes = '';
  193. for ($i=0; $i<8; $i++) {
  194. $bytes .= chr(gmp_intval(gmp_and($value, $xff)));
  195. $value = gmp_div_q($value, $x100);
  196. }
  197. $this->write($bytes);
  198. }
  199. public function sFixed64_bc($value)
  200. {
  201. if (PHP_INT_SIZE < 8) {
  202. throw new \OutOfBoundsException(
  203. "PHP versions compiled with 32bit integers can only support negative integer encoding with GMP extension ($value was given)"
  204. );
  205. }
  206. $value = sprintf('%u', $value);
  207. $bytes = '';
  208. for ($i=0; $i<8; $i++) {
  209. // Get the last 8bits of the number
  210. $bin = '';
  211. $dec = $value;
  212. do {
  213. $bin = bcmod($dec, 2) . $bin;
  214. $dec = bcdiv($dec, 2, 0);
  215. } while (strlen($bin) < 8);
  216. // Pack the byte
  217. $bytes .= chr(intval($bin, 2));
  218. $value = bcdiv($value, 0x100, 0);
  219. }
  220. $this->write($bytes);
  221. }
  222. /**
  223. * Encode an integer as a fixed of 64bits without sign
  224. *
  225. * @param int $value
  226. */
  227. public function fixed64($value)
  228. {
  229. $bytes = pack('V*', $value & 0xffffffff, $value / (0xffffffff+1));
  230. $this->write($bytes, 8);
  231. }
  232. /**
  233. * Encode a number as a 32bit float
  234. *
  235. * @param float $value
  236. */
  237. public function float($value)
  238. {
  239. $bytes = pack('f*', $value);
  240. if ($this->isBigEndian()) {
  241. $bytes = strrev($bytes);
  242. }
  243. $this->write($bytes, 4);
  244. }
  245. /**
  246. * Encode a number as a 64bit double
  247. *
  248. * @param float $value
  249. */
  250. public function double($value)
  251. {
  252. $bytes = pack('d*', $value);
  253. if ($this->isBigEndian()) {
  254. $bytes = strrev($bytes);
  255. }
  256. $this->write($bytes, 8);
  257. }
  258. /**
  259. * Checks if the current architecture is Big Endian
  260. *
  261. * @return bool
  262. */
  263. public function isBigEndian()
  264. {
  265. static $endianness;
  266. if (NULL === $endianness) {
  267. list(,$result) = unpack('L', pack('V', 1));
  268. $endianness = $result !== 1;
  269. }
  270. return $endianness;
  271. }
  272. }