_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)); } } }