...

Text file src/github.com/google/flatbuffers/php/ByteBuffer.php

Documentation: github.com/google/flatbuffers/php

     1<?php
     2/*
     3 * Copyright 2015 Google Inc.
     4 *
     5 * Licensed under the Apache License, Version 2.0 (the "License");
     6 * you may not use this file except in compliance with the License.
     7 * You may obtain a copy of the License at
     8 *
     9 *     http://www.apache.org/licenses/LICENSE-2.0
    10 *
    11 * Unless required by applicable law or agreed to in writing, software
    12 * distributed under the License is distributed on an "AS IS" BASIS,
    13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14 * See the License for the specific language governing permissions and
    15 * limitations under the License.
    16 */
    17
    18namespace Google\FlatBuffers;
    19
    20class ByteBuffer
    21{
    22    /**
    23     * @var string $_buffer;
    24     */
    25    public $_buffer;
    26
    27    /**
    28     * @var int $_pos;
    29     */
    30    private $_pos;
    31
    32    /**
    33     * @var bool $_is_little_endian
    34     */
    35    private static $_is_little_endian = null;
    36
    37    public static function wrap($bytes)
    38    {
    39        $bb = new ByteBuffer(0);
    40        $bb->_buffer = $bytes;
    41
    42        return $bb;
    43    }
    44
    45    /**
    46     * @param $size
    47     */
    48    public function __construct($size)
    49    {
    50        $this->_buffer = str_repeat("\0", $size);
    51    }
    52
    53    /**
    54     * @return int
    55     */
    56    public function capacity()
    57    {
    58        return strlen($this->_buffer);
    59    }
    60
    61    /**
    62     * @return int
    63     */
    64    public function getPosition()
    65    {
    66        return $this->_pos;
    67    }
    68
    69    /**
    70     * @param $pos
    71     */
    72    public function setPosition($pos)
    73    {
    74        $this->_pos = $pos;
    75    }
    76
    77    /**
    78     *
    79     */
    80    public function reset()
    81    {
    82        $this->_pos = 0;
    83    }
    84
    85    /**
    86     * @return int
    87     */
    88    public function length()
    89    {
    90        return strlen($this->_buffer);
    91    }
    92
    93    /**
    94     * @return string
    95     */
    96    public function data()
    97    {
    98        return substr($this->_buffer, $this->_pos);
    99    }
   100
   101    /**
   102     * @return bool
   103     */
   104    public static function isLittleEndian()
   105    {
   106        if (ByteBuffer::$_is_little_endian === null) {
   107            ByteBuffer::$_is_little_endian = unpack('S', "\x01\x00")[1] === 1;
   108        }
   109
   110        return ByteBuffer::$_is_little_endian;
   111    }
   112
   113    /**
   114     * write little endian value to the buffer.
   115     *
   116     * @param $offset
   117     * @param $count byte length
   118     * @param $data actual values
   119     */
   120    public function writeLittleEndian($offset, $count, $data)
   121    {
   122        if (ByteBuffer::isLittleEndian()) {
   123            for ($i = 0; $i < $count; $i++) {
   124                $this->_buffer[$offset + $i] = chr($data >> $i * 8);
   125            }
   126        } else {
   127            for ($i = 0; $i < $count; $i++) {
   128                $this->_buffer[$offset + $count - 1 - $i] = chr($data >> $i * 8);
   129            }
   130        }
   131    }
   132
   133    /**
   134     * read little endian value from the buffer
   135     *
   136     * @param $offset
   137     * @param $count acutal size
   138     * @return int
   139     */
   140    public function readLittleEndian($offset, $count, $force_bigendian = false)
   141    {
   142        $this->assertOffsetAndLength($offset, $count);
   143        $r = 0;
   144
   145        if (ByteBuffer::isLittleEndian() && $force_bigendian == false) {
   146            for ($i = 0; $i < $count; $i++) {
   147                $r |= ord($this->_buffer[$offset + $i]) << $i * 8;
   148            }
   149        } else {
   150            for ($i = 0; $i < $count; $i++) {
   151                $r |= ord($this->_buffer[$offset + $count -1 - $i]) << $i * 8;
   152            }
   153        }
   154
   155        return $r;
   156    }
   157
   158    /**
   159     * @param $offset
   160     * @param $length
   161     */
   162    public function assertOffsetAndLength($offset, $length)
   163    {
   164        if ($offset < 0 ||
   165            $offset >= strlen($this->_buffer) ||
   166            $offset + $length > strlen($this->_buffer)) {
   167            throw new \OutOfRangeException(sprintf("offset: %d, length: %d, buffer; %d", $offset, $length, strlen($this->_buffer)));
   168        }
   169    }
   170
   171    /**
   172     * @param $offset
   173     * @param $value
   174     * @return mixed
   175     */
   176    public function putSbyte($offset, $value)
   177    {
   178        self::validateValue(-128, 127, $value, "sbyte");
   179
   180        $length = strlen($value);
   181        $this->assertOffsetAndLength($offset, $length);
   182        return $this->_buffer[$offset] = $value;
   183    }
   184
   185    /**
   186     * @param $offset
   187     * @param $value
   188     * @return mixed
   189     */
   190    public function putByte($offset, $value)
   191    {
   192        self::validateValue(0, 255, $value, "byte");
   193
   194        $length = strlen($value);
   195        $this->assertOffsetAndLength($offset, $length);
   196        return $this->_buffer[$offset] = $value;
   197    }
   198
   199    /**
   200     * @param $offset
   201     * @param $value
   202     */
   203    public function put($offset, $value)
   204    {
   205        $length = strlen($value);
   206        $this->assertOffsetAndLength($offset, $length);
   207        for ($i = 0; $i < $length; $i++) {
   208            $this->_buffer[$offset + $i] = $value[$i];
   209        }
   210    }
   211
   212    /**
   213     * @param $offset
   214     * @param $value
   215     */
   216    public function putShort($offset, $value)
   217    {
   218        self::validateValue(-32768, 32767, $value, "short");
   219
   220        $this->assertOffsetAndLength($offset, 2);
   221        $this->writeLittleEndian($offset, 2, $value);
   222    }
   223
   224    /**
   225     * @param $offset
   226     * @param $value
   227     */
   228    public function putUshort($offset, $value)
   229    {
   230        self::validateValue(0, 65535, $value, "short");
   231
   232        $this->assertOffsetAndLength($offset, 2);
   233        $this->writeLittleEndian($offset, 2, $value);
   234    }
   235
   236    /**
   237     * @param $offset
   238     * @param $value
   239     */
   240    public function putInt($offset, $value)
   241    {
   242        // 2147483647 = (1 << 31) -1 = Maximum signed 32-bit int
   243        // -2147483648 = -1 << 31 = Minimum signed 32-bit int
   244        self::validateValue(-2147483648, 2147483647, $value, "int");
   245
   246        $this->assertOffsetAndLength($offset, 4);
   247        $this->writeLittleEndian($offset, 4, $value);
   248    }
   249
   250    /**
   251     * @param $offset
   252     * @param $value
   253     */
   254    public function putUint($offset, $value)
   255    {
   256        // NOTE: We can't put big integer value. this is PHP limitation.
   257        // 4294967295 = (1 << 32) -1 = Maximum unsigned 32-bin int
   258        self::validateValue(0, 4294967295, $value, "uint",  " php has big numbers limitation. check your PHP_INT_MAX");
   259
   260        $this->assertOffsetAndLength($offset, 4);
   261        $this->writeLittleEndian($offset, 4, $value);
   262    }
   263
   264    /**
   265     * @param $offset
   266     * @param $value
   267     */
   268    public function putLong($offset, $value)
   269    {
   270        // NOTE: We can't put big integer value. this is PHP limitation.
   271        self::validateValue(~PHP_INT_MAX, PHP_INT_MAX, $value, "long",  " php has big numbers limitation. check your PHP_INT_MAX");
   272
   273        $this->assertOffsetAndLength($offset, 8);
   274        $this->writeLittleEndian($offset, 8, $value);
   275    }
   276
   277    /**
   278     * @param $offset
   279     * @param $value
   280     */
   281    public function putUlong($offset, $value)
   282    {
   283        // NOTE: We can't put big integer value. this is PHP limitation.
   284        self::validateValue(0, PHP_INT_MAX, $value, "long", " php has big numbers limitation. check your PHP_INT_MAX");
   285
   286        $this->assertOffsetAndLength($offset, 8);
   287        $this->writeLittleEndian($offset, 8, $value);
   288    }
   289
   290    /**
   291     * @param $offset
   292     * @param $value
   293     */
   294    public function putFloat($offset, $value)
   295    {
   296        $this->assertOffsetAndLength($offset, 4);
   297
   298        $floathelper = pack("f", $value);
   299        $v = unpack("V", $floathelper);
   300        $this->writeLittleEndian($offset, 4, $v[1]);
   301    }
   302
   303    /**
   304     * @param $offset
   305     * @param $value
   306     */
   307    public function putDouble($offset, $value)
   308    {
   309        $this->assertOffsetAndLength($offset, 8);
   310
   311        $floathelper = pack("d", $value);
   312        $v = unpack("V*", $floathelper);
   313
   314        $this->writeLittleEndian($offset, 4, $v[1]);
   315        $this->writeLittleEndian($offset + 4, 4, $v[2]);
   316    }
   317
   318    /**
   319     * @param $index
   320     * @return mixed
   321     */
   322    public function getByte($index)
   323    {
   324        return ord($this->_buffer[$index]);
   325    }
   326
   327    /**
   328     * @param $index
   329     * @return mixed
   330     */
   331    public function getSbyte($index)
   332    {
   333        $v = unpack("c", $this->_buffer[$index]);
   334        return $v[1];
   335    }
   336
   337    /**
   338     * @param $buffer
   339     */
   340    public function getX(&$buffer)
   341    {
   342        for ($i = $this->_pos, $j = 0; $j < strlen($buffer); $i++, $j++) {
   343            $buffer[$j] = $this->_buffer[$i];
   344        }
   345    }
   346
   347    /**
   348     * @param $index
   349     * @return mixed
   350     */
   351    public function get($index)
   352    {
   353        $this->assertOffsetAndLength($index, 1);
   354        return $this->_buffer[$index];
   355    }
   356
   357
   358    /**
   359     * @param $index
   360     * @return mixed
   361     */
   362    public function getBool($index)
   363    {
   364        return (bool)ord($this->_buffer[$index]);
   365    }
   366
   367    /**
   368     * @param $index
   369     * @return int
   370     */
   371    public function getShort($index)
   372    {
   373        $result = $this->readLittleEndian($index, 2);
   374
   375        $sign = $index + (ByteBuffer::isLittleEndian() ? 1 : 0);
   376        $issigned = isset($this->_buffer[$sign]) && ord($this->_buffer[$sign]) & 0x80;
   377
   378        // 65536 = 1 << 16 = Maximum unsigned 16-bit int
   379        return $issigned ? $result - 65536 : $result;
   380    }
   381
   382    /**
   383     * @param $index
   384     * @return int
   385     */
   386    public function getUShort($index)
   387    {
   388        return $this->readLittleEndian($index, 2);
   389    }
   390
   391    /**
   392     * @param $index
   393     * @return int
   394     */
   395    public function getInt($index)
   396    {
   397        $result = $this->readLittleEndian($index, 4);
   398
   399        $sign = $index + (ByteBuffer::isLittleEndian() ? 3 : 0);
   400        $issigned = isset($this->_buffer[$sign]) && ord($this->_buffer[$sign]) & 0x80;
   401
   402        if (PHP_INT_SIZE > 4) {
   403            // 4294967296 = 1 << 32 = Maximum unsigned 32-bit int
   404            return $issigned ? $result - 4294967296 : $result;
   405        } else {
   406            // 32bit / Windows treated number as signed integer.
   407            return $result;
   408        }
   409    }
   410
   411    /**
   412     * @param $index
   413     * @return int
   414     */
   415    public function getUint($index)
   416    {
   417        return $this->readLittleEndian($index, 4);
   418    }
   419
   420    /**
   421     * @param $index
   422     * @return int
   423     */
   424    public function getLong($index)
   425    {
   426        return $this->readLittleEndian($index, 8);
   427    }
   428
   429    /**
   430     * @param $index
   431     * @return int
   432     */
   433    public function getUlong($index)
   434    {
   435        return $this->readLittleEndian($index, 8);
   436    }
   437
   438    /**
   439     * @param $index
   440     * @return mixed
   441     */
   442    public function getFloat($index)
   443    {
   444        $i = $this->readLittleEndian($index, 4);
   445
   446        return self::convertHelper(self::__FLOAT, $i);
   447    }
   448
   449    /**
   450     * @param $index
   451     * @return float
   452     */
   453    public function getDouble($index)
   454    {
   455        $i = $this->readLittleEndian($index, 4);
   456        $i2 = $this->readLittleEndian($index + 4, 4);
   457
   458        return self::convertHelper(self::__DOUBLE, $i, $i2);
   459    }
   460
   461    const __SHORT = 1;
   462    const __INT = 2;
   463    const __LONG = 3;
   464    const __FLOAT = 4;
   465    const __DOUBLE = 5;
   466    private static function convertHelper($type, $value, $value2 = null) {
   467        // readLittleEndian construct unsigned integer value from bytes. we have to encode this value to
   468        // correct bytes, and decode as expected types with `unpack` function.
   469        // then it returns correct type value.
   470        // see also: http://php.net/manual/en/function.pack.php
   471
   472        switch ($type) {
   473            case self::__FLOAT:
   474                $inthelper = pack("V", $value);
   475                $v = unpack("f", $inthelper);
   476                return $v[1];
   477                break;
   478            case self::__DOUBLE:
   479                $inthelper = pack("VV", $value, $value2);
   480                $v = unpack("d", $inthelper);
   481                return $v[1];
   482                break;
   483            default:
   484                throw new \Exception(sprintf("unexpected type %d specified", $type));
   485        }
   486    }
   487
   488    private static function validateValue($min, $max, $value, $type, $additional_notes = "") {
   489        if (
   490          !(
   491            ($type === "byte" && $min <= ord($value) && ord($value) <= $max) ||
   492            ($min <= $value && $value <= $max)
   493          )
   494        ) {
   495            throw new \InvalidArgumentException(sprintf("bad number %s for type %s.%s", $value, $type, $additional_notes));
   496        }
   497    }
   498}

View as plain text