blob: 7bf17e77ae34746b1a367fe619569a95dee7c0a3 [file] [log] [blame]
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* https://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 Apache\Avro\Datum;
// @todo Implement JSON encoding, as is required by the Avro spec.
use Apache\Avro\Avro;
use Apache\Avro\AvroException;
use Apache\Avro\AvroGMP;
use Apache\Avro\AvroIO;
/**
* Decodes and reads Avro data from an AvroIO object encoded using
* Avro binary encoding.
*
* @package Avro
*/
class AvroIOBinaryDecoder
{
/**
* @var AvroIO
*/
private $io;
/**
* @param AvroIO $io object from which to read.
*/
public function __construct($io)
{
Avro::checkPlatform();
$this->io = $io;
}
/**
* @returns null
*/
public function readNull()
{
return null;
}
/**
* @returns boolean
*/
public function readBoolean()
{
return (bool) (1 == ord($this->nextByte()));
}
/**
* @returns string the next byte from $this->io.
* @throws AvroException if the next byte cannot be read.
*/
private function nextByte()
{
return $this->read(1);
}
/**
* @param int $len count of bytes to read
* @returns string
*/
public function read($len)
{
return $this->io->read($len);
}
/**
* @returns int
*/
public function readInt()
{
return (int) $this->readLong();
}
/**
* @returns string|int
*/
public function readLong()
{
$byte = ord($this->nextByte());
$bytes = array($byte);
while (0 != ($byte & 0x80)) {
$byte = ord($this->nextByte());
$bytes [] = $byte;
}
if (Avro::usesGmp()) {
return AvroGMP::decodeLongFromArray($bytes);
}
return self::decodeLongFromArray($bytes);
}
/**
* @param int[] array of byte ascii values
* @returns long decoded value
* @internal Requires 64-bit platform
*/
public static function decodeLongFromArray($bytes)
{
$b = array_shift($bytes);
$n = $b & 0x7f;
$shift = 7;
while (0 != ($b & 0x80)) {
$b = array_shift($bytes);
$n |= (($b & 0x7f) << $shift);
$shift += 7;
}
return (($n >> 1) ^ -($n & 1));
}
/**
* @returns float
*/
public function readFloat()
{
return self::intBitsToFloat($this->read(4));
}
/**
* Performs decoding of the binary string to a float value.
*
* XXX: This is <b>not</b> endian-aware! See comments in
* {@link AvroIOBinaryEncoder::floatToIntBits()} for details.
*
* @param string $bits
* @returns float
*/
public static function intBitsToFloat($bits)
{
$float = unpack('g', $bits);
return (float) $float[1];
}
/**
* @returns double
*/
public function readDouble()
{
return self::longBitsToDouble($this->read(8));
}
/**
* Performs decoding of the binary string to a double value.
*
* XXX: This is <b>not</b> endian-aware! See comments in
* {@link AvroIOBinaryEncoder::floatToIntBits()} for details.
*
* @param string $bits
* @returns float
*/
public static function longBitsToDouble($bits)
{
$double = unpack('e', $bits);
return (double) $double[1];
}
/**
* A string is encoded as a long followed by that many bytes
* of UTF-8 encoded character data.
* @returns string
*/
public function readString()
{
return $this->readBytes();
}
/**
* @returns string
*/
public function readBytes()
{
return $this->read($this->readLong());
}
public function skipNull()
{
return null;
}
public function skipBoolean()
{
return $this->skip(1);
}
/**
* @param int $len count of bytes to skip
* @uses AvroIO::seek()
*/
public function skip($len)
{
$this->seek($len, AvroIO::SEEK_CUR);
}
/**
* @param int $offset
* @param int $whence
* @returns boolean true upon success
* @uses AvroIO::seek()
*/
private function seek($offset, $whence)
{
return $this->io->seek($offset, $whence);
}
public function skipInt()
{
return $this->skipLong();
}
public function skipLong()
{
$b = ord($this->nextByte());
while (0 != ($b & 0x80)) {
$b = ord($this->nextByte());
}
}
public function skipFloat()
{
return $this->skip(4);
}
public function skipDouble()
{
return $this->skip(8);
}
public function skipString()
{
return $this->skipBytes();
}
public function skipBytes()
{
return $this->skip($this->readLong());
}
public function skipFixed($writers_schema, AvroIOBinaryDecoder $decoder)
{
$decoder->skip($writers_schema->size());
}
public function skipEnum($writers_schema, AvroIOBinaryDecoder $decoder)
{
$decoder->skipInt();
}
public function skipUnion($writers_schema, AvroIOBinaryDecoder $decoder)
{
$index = $decoder->readLong();
AvroIODatumReader::skipData($writers_schema->schemaByIndex($index), $decoder);
}
public function skipRecord($writers_schema, AvroIOBinaryDecoder $decoder)
{
foreach ($writers_schema->fields() as $f) {
AvroIODatumReader::skipData($f->type(), $decoder);
}
}
public function skipArray($writers_schema, AvroIOBinaryDecoder $decoder)
{
$block_count = $decoder->readLong();
while (0 !== $block_count) {
if ($block_count < 0) {
$decoder->skip($this->readLong());
}
for ($i = 0; $i < $block_count; $i++) {
AvroIODatumReader::skipData($writers_schema->items(), $decoder);
}
$block_count = $decoder->readLong();
}
}
public function skipMap($writers_schema, AvroIOBinaryDecoder $decoder)
{
$block_count = $decoder->readLong();
while (0 !== $block_count) {
if ($block_count < 0) {
$decoder->skip($this->readLong());
}
for ($i = 0; $i < $block_count; $i++) {
$decoder->skipString();
AvroIODatumReader::skipData($writers_schema->values(), $decoder);
}
$block_count = $decoder->readLong();
}
}
/**
* @returns int position of pointer in AvroIO instance
* @uses AvroIO::tell()
*/
private function tell()
{
return $this->io->tell();
}
}