Add php protobuffer support for transition to GTFS-realtime
[busui.git] / lib / Protobuf-PHP / library / DrSlump / Protobuf / Codec / Binary / Reader.php
blob:a/lib/Protobuf-PHP/library/DrSlump/Protobuf/Codec/Binary/Reader.php -> blob:b/lib/Protobuf-PHP/library/DrSlump/Protobuf/Codec/Binary/Reader.php
  <?php
   
  namespace DrSlump\Protobuf\Codec\Binary;
   
  /**
  * Implements reading primitives for Protobuf binary streams
  *
  * Important: There are no checks in place for overflows, so you must
  * be aware of PHP's integer and floating point limits.
  *
  * @note Protobuf uses little-endian order
  */
  class Reader
  {
  /** @var resource */
  protected $_fd;
   
  /**
  * Create a new reader from a file descriptor or a string of bytes
  *
  * @param resource|string $fdOrString
  */
  public function __construct($fdOrString)
  {
  if (is_resource($fdOrString)) {
  $this->_fd = $fdOrString;
  } else {
  // @todo Could this be faster by using a custom String wrapper?
  $this->_fd = fopen('data://text/plain,' . urlencode($fdOrString), 'rb');
  }
  }
   
  public function __destruct()
  {
  fclose($this->_fd);
  }
   
  /**
  * Obtain a number of bytes from the string
  *
  * @throws \RuntimeException
  * @param int $length
  * @return string
  */
  public function read($length)
  {
  // Protect against 0 byte reads when an EOF
  if ($length < 1) return '';
   
  $bytes = fread($this->_fd, $length);
  if (FALSE === $bytes) {
  throw new \RuntimeException('Failed to read ' . $length . ' bytes');
  }
   
  return $bytes;
  }
   
  /**
  * Check if we have reached the end of the stream
  *
  * @return bool
  */
  public function eof()
  {
  return feof($this->_fd);
  }
   
  /**
  * Obtain the current position in the stream
  *
  * @return int
  */
  public function pos()
  {
  return ftell($this->_fd);
  }
   
  /**
  * Obtain a byte
  *
  * @return int
  */
  public function byte()
  {
  return ord($this->read(1));
  }
   
  /**
  * Decode a varint
  *
  * @return int
  */
  public function varint()
  {
  $result = $shift = 0;
  do {
  $byte = $this->byte();
  $result |= ($byte & 0x7f) << $shift;
  $shift += 7;
  } while ($byte > 0x7f);
   
  return $result;
  }
   
  /**
  * Decodes a zigzag integer of the given bits
  *
  * @param int $bits - Either 32 or 64
  */
  public function zigzag()
  {
  $number = $this->varint();
  return ($number >> 1) ^ (-($number & 1));
  }
   
  /**
  * Decode a fixed 32bit integer with sign
  *
  * @return int
  */
  public function sFixed32()
  {
  $bytes = $this->read(4);
  if ($this->isBigEndian()) {
  $bytes = strrev($bytes);
  }
   
  list(, $result) = unpack('l', $bytes);
  return $result;
  }
   
  /**
  * Decode a fixed 32bit integer without sign
  *
  * @return int
  */
  public function fixed32()
  {
  $bytes = $this->read(4);
   
  if (PHP_INT_SIZE < 8) {
  list(, $lo, $hi) = unpack('v*', $bytes);
  $result = $hi << 16 | $lo;
  } else {
  list(, $result) = unpack('V*', $bytes);
  }
   
  return $result;
  }
   
  /**
  * Decode a fixed 62bit integer with sign
  *
  * @return int
  */
  public function sFixed64()
  {
  $bytes = $this->read(8);
   
  list(, $lo0, $lo1, $hi0, $hi1) = unpack('v*', $bytes);
  return ($hi1 << 16 | $hi0) << 32 | ($lo1 << 16 | $lo0);
  }
   
  /**
  * Decode a fixed 62bit integer without sign
  *
  * @return int
  */
  public function fixed64()
  {
  return $this->sFixed64();
  }
   
  /**
  * Decode a 32bit float
  *
  * @return float
  */
  public function float()
  {
  $bytes = $this->read(4);
  if ($this->isBigEndian()) {
  $bytes = strrev($bytes);
  }
   
  list(, $result) = unpack('f', $bytes);
  return $result;
  }
   
  /**
  * Decode a 64bit double
  *
  * @return float
  */
  public function double()
  {
  $bytes = $this->read(8);
  if ($this->isBigEndian()) {
  $bytes = strrev($bytes);
  }
   
  list(, $result) = unpack('d', $bytes);
  return $result;
  }
   
  /**
  * Check if the current architecture is Big Endian
  *
  * @return bool
  */
  public function isBigEndian()
  {
  static $endianness;
   
  if (NULL === $endianness) {
  list(,$result) = unpack('L', pack('V', 1));
  $endianness = $result !== 1;
  }
   
  return $endianness;
  }
  }