aboutsummaryrefslogtreecommitdiff
path: root/php/ByteBuffer.php
diff options
context:
space:
mode:
Diffstat (limited to 'php/ByteBuffer.php')
-rw-r--r--php/ByteBuffer.php495
1 files changed, 495 insertions, 0 deletions
diff --git a/php/ByteBuffer.php b/php/ByteBuffer.php
new file mode 100644
index 00000000..4a583b7a
--- /dev/null
+++ b/php/ByteBuffer.php
@@ -0,0 +1,495 @@
+<?php
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Google\FlatBuffers;
+
+class ByteBuffer
+{
+ /**
+ * @var string $_buffer;
+ */
+ public $_buffer;
+
+ /**
+ * @var int $_pos;
+ */
+ private $_pos;
+
+ /**
+ * @var bool $_is_little_endian
+ */
+ private static $_is_little_endian = null;
+
+ public static function wrap($bytes)
+ {
+ $bb = new ByteBuffer(0);
+ $bb->_buffer = $bytes;
+
+ return $bb;
+ }
+
+ /**
+ * @param $size
+ */
+ public function __construct($size)
+ {
+ $this->_buffer = str_repeat("\0", $size);
+ }
+
+ /**
+ * @return int
+ */
+ public function capacity()
+ {
+ return strlen($this->_buffer);
+ }
+
+ /**
+ * @return int
+ */
+ public function getPosition()
+ {
+ return $this->_pos;
+ }
+
+ /**
+ * @param $pos
+ */
+ public function setPosition($pos)
+ {
+ $this->_pos = $pos;
+ }
+
+ /**
+ *
+ */
+ public function reset()
+ {
+ $this->_pos = 0;
+ }
+
+ /**
+ * @return int
+ */
+ public function length()
+ {
+ return strlen($this->_buffer);
+ }
+
+ /**
+ * @return string
+ */
+ public function data()
+ {
+ return substr($this->_buffer, $this->_pos);
+ }
+
+ /**
+ * @return bool
+ */
+ public static function isLittleEndian()
+ {
+ if (ByteBuffer::$_is_little_endian === null) {
+ ByteBuffer::$_is_little_endian = unpack('S', "\x01\x00")[1] === 1;
+ }
+
+ return ByteBuffer::$_is_little_endian;
+ }
+
+ /**
+ * write little endian value to the buffer.
+ *
+ * @param $offset
+ * @param $count byte length
+ * @param $data actual values
+ */
+ public function writeLittleEndian($offset, $count, $data)
+ {
+ if (ByteBuffer::isLittleEndian()) {
+ for ($i = 0; $i < $count; $i++) {
+ $this->_buffer[$offset + $i] = chr($data >> $i * 8);
+ }
+ } else {
+ for ($i = 0; $i < $count; $i++) {
+ $this->_buffer[$offset + $count - 1 - $i] = chr($data >> $i * 8);
+ }
+ }
+ }
+
+ /**
+ * read little endian value from the buffer
+ *
+ * @param $offset
+ * @param $count acutal size
+ * @return int
+ */
+ public function readLittleEndian($offset, $count, $force_bigendian = false)
+ {
+ $this->assertOffsetAndLength($offset, $count);
+ $r = 0;
+
+ if (ByteBuffer::isLittleEndian() && $force_bigendian == false) {
+ for ($i = 0; $i < $count; $i++) {
+ $r |= ord($this->_buffer[$offset + $i]) << $i * 8;
+ }
+ } else {
+ for ($i = 0; $i < $count; $i++) {
+ $r |= ord($this->_buffer[$offset + $count -1 - $i]) << $i * 8;
+ }
+ }
+
+ return $r;
+ }
+
+ /**
+ * @param $offset
+ * @param $length
+ */
+ public function assertOffsetAndLength($offset, $length)
+ {
+ if ($offset < 0 ||
+ $offset >= strlen($this->_buffer) ||
+ $offset + $length > strlen($this->_buffer)) {
+ throw new \OutOfRangeException(sprintf("offset: %d, length: %d, buffer; %d", $offset, $length, strlen($this->_buffer)));
+ }
+ }
+
+ /**
+ * @param $offset
+ * @param $value
+ * @return mixed
+ */
+ public function putSbyte($offset, $value)
+ {
+ self::validateValue(-128, 127, $value, "sbyte");
+
+ $length = strlen($value);
+ $this->assertOffsetAndLength($offset, $length);
+ return $this->_buffer[$offset] = $value;
+ }
+
+ /**
+ * @param $offset
+ * @param $value
+ * @return mixed
+ */
+ public function putByte($offset, $value)
+ {
+ self::validateValue(0, 255, $value, "byte");
+
+ $length = strlen($value);
+ $this->assertOffsetAndLength($offset, $length);
+ return $this->_buffer[$offset] = $value;
+ }
+
+ /**
+ * @param $offset
+ * @param $value
+ */
+ public function put($offset, $value)
+ {
+ $length = strlen($value);
+ $this->assertOffsetAndLength($offset, $length);
+ for ($i = 0; $i < $length; $i++) {
+ $this->_buffer[$offset + $i] = $value[$i];
+ }
+ }
+
+ /**
+ * @param $offset
+ * @param $value
+ */
+ public function putShort($offset, $value)
+ {
+ self::validateValue(-32768, 32767, $value, "short");
+
+ $this->assertOffsetAndLength($offset, 2);
+ $this->writeLittleEndian($offset, 2, $value);
+ }
+
+ /**
+ * @param $offset
+ * @param $value
+ */
+ public function putUshort($offset, $value)
+ {
+ self::validateValue(0, 65535, $value, "short");
+
+ $this->assertOffsetAndLength($offset, 2);
+ $this->writeLittleEndian($offset, 2, $value);
+ }
+
+ /**
+ * @param $offset
+ * @param $value
+ */
+ public function putInt($offset, $value)
+ {
+ self::validateValue(~PHP_INT_MAX, PHP_INT_MAX, $value, "int");
+
+ $this->assertOffsetAndLength($offset, 4);
+ $this->writeLittleEndian($offset, 4, $value);
+ }
+
+ /**
+ * @param $offset
+ * @param $value
+ */
+ public function putUint($offset, $value)
+ {
+ // NOTE: We can't put big integer value. this is PHP limitation.
+ self::validateValue(0, PHP_INT_MAX, $value, "uint", " php has big numbers limitation. check your PHP_INT_MAX");
+
+ $this->assertOffsetAndLength($offset, 4);
+ $this->writeLittleEndian($offset, 4, $value);
+ }
+
+ /**
+ * @param $offset
+ * @param $value
+ */
+ public function putLong($offset, $value)
+ {
+ // NOTE: We can't put big integer value. this is PHP limitation.
+ self::validateValue(~PHP_INT_MAX, PHP_INT_MAX, $value, "long", " php has big numbers limitation. check your PHP_INT_MAX");
+
+ $this->assertOffsetAndLength($offset, 8);
+ $this->writeLittleEndian($offset, 8, $value);
+ }
+
+ /**
+ * @param $offset
+ * @param $value
+ */
+ public function putUlong($offset, $value)
+ {
+ // NOTE: We can't put big integer value. this is PHP limitation.
+ self::validateValue(0, PHP_INT_MAX, $value, "long", " php has big numbers limitation. check your PHP_INT_MAX");
+
+ $this->assertOffsetAndLength($offset, 8);
+ $this->writeLittleEndian($offset, 8, $value);
+ }
+
+ /**
+ * @param $offset
+ * @param $value
+ */
+ public function putFloat($offset, $value)
+ {
+ $this->assertOffsetAndLength($offset, 4);
+
+ $floathelper = pack("f", $value);
+ $v = unpack("V", $floathelper);
+ $this->writeLittleEndian($offset, 4, $v[1]);
+ }
+
+ /**
+ * @param $offset
+ * @param $value
+ */
+ public function putDouble($offset, $value)
+ {
+ $this->assertOffsetAndLength($offset, 8);
+
+ $floathelper = pack("d", $value);
+ $v = unpack("V*", $floathelper);
+
+ $this->writeLittleEndian($offset, 4, $v[1]);
+ $this->writeLittleEndian($offset + 4, 4, $v[2]);
+ }
+
+ /**
+ * @param $index
+ * @return mixed
+ */
+ public function getByte($index)
+ {
+ return ord($this->_buffer[$index]);
+ }
+
+ /**
+ * @param $index
+ * @return mixed
+ */
+ public function getSbyte($index)
+ {
+ $v = unpack("c", $this->_buffer[$index]);
+ return $v[1];
+ }
+
+ /**
+ * @param $buffer
+ */
+ public function getX(&$buffer)
+ {
+ for ($i = $this->_pos, $j = 0; $j < strlen($buffer); $i++, $j++) {
+ $buffer[$j] = $this->_buffer[$i];
+ }
+ }
+
+ /**
+ * @param $index
+ * @return mixed
+ */
+ public function get($index)
+ {
+ $this->assertOffsetAndLength($index, 1);
+ return $this->_buffer[$index];
+ }
+
+
+ /**
+ * @param $index
+ * @return mixed
+ */
+ public function getBool($index)
+ {
+ return (bool)ord($this->_buffer[$index]);
+ }
+
+ /**
+ * @param $index
+ * @return int
+ */
+ public function getShort($index)
+ {
+ $result = $this->readLittleEndian($index, 2);
+
+ return self::convertHelper(self::__SHORT, $result);
+ }
+
+ /**
+ * @param $index
+ * @return int
+ */
+ public function getUShort($index)
+ {
+ return $this->readLittleEndian($index, 2);
+ }
+
+ /**
+ * @param $index
+ * @return int
+ */
+ public function getInt($index)
+ {
+ $result = $this->readLittleEndian($index, 4);
+
+ return self::convertHelper(self::__INT, $result);
+ }
+
+ /**
+ * @param $index
+ * @return int
+ */
+ public function getUint($index)
+ {
+ return $this->readLittleEndian($index, 4);
+ }
+
+ /**
+ * @param $index
+ * @return int
+ */
+ public function getLong($index)
+ {
+ $result = $this->readLittleEndian($index, 8);
+
+ return self::convertHelper(self::__LONG, $result);
+ }
+
+ /**
+ * @param $index
+ * @return int
+ */
+ public function getUlong($index)
+ {
+ return $this->readLittleEndian($index, 8);
+ }
+
+ /**
+ * @param $index
+ * @return mixed
+ */
+ public function getFloat($index)
+ {
+ $i = $this->readLittleEndian($index, 4);
+
+ return self::convertHelper(self::__FLOAT, $i);
+ }
+
+ /**
+ * @param $index
+ * @return float
+ */
+ public function getDouble($index)
+ {
+ $i = $this->readLittleEndian($index, 4);
+ $i2 = $this->readLittleEndian($index + 4, 4);
+
+ return self::convertHelper(self::__DOUBLE, $i, $i2);
+ }
+
+ const __SHORT = 1;
+ const __INT = 2;
+ const __LONG = 3;
+ const __FLOAT = 4;
+ const __DOUBLE = 5;
+ private static function convertHelper($type, $value, $value2 = null) {
+ // readLittleEndian construct unsigned integer value from bytes. we have to encode this value to
+ // correct bytes, and decode as expected types with `unpack` function.
+ // then it returns correct type value.
+ // see also: http://php.net/manual/en/function.pack.php
+
+ switch ($type) {
+ case self::__SHORT:
+ $helper = pack("v", $value);
+ $v = unpack("s", $helper);
+
+ return $v[1];
+ break;
+ case self::__INT:
+ $helper = pack("V", $value);
+ $v = unpack("l", $helper);
+ return $v[1];
+ break;
+ case self::__LONG:
+ $helper = pack("P", $value);
+ $v = unpack("q", $helper);
+ return $v[1];
+ break;
+ case self::__FLOAT:
+ $inthelper = pack("V", $value);
+ $v = unpack("f", $inthelper);
+ return $v[1];
+ break;
+ case self::__DOUBLE:
+ $inthelper = pack("VV", $value, $value2);
+ $v = unpack("d", $inthelper);
+ return $v[1];
+ break;
+ default:
+ throw new \Exception(sprintf("unexpected type %d specified", $type));
+ }
+ }
+
+ private static function validateValue($min, $max, $value, $type, $additional_notes = "") {
+ if(!($min <= $value && $value <= $max)) {
+ throw new \InvalidArgumentException(sprintf("bad number %s for type %s.%s", $value, $type, $additional_notes));
+ }
+ }
+}