blob: 9b98ff31c2eb3dc780264c67fcae14cf6d3476bc [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\IO;
use Apache\Avro\AvroIO;
/**
* AvroIO wrapper for string access
* @package Avro
*/
class AvroStringIO extends AvroIO
{
/**
* @var string
*/
private $string_buffer;
/**
* @var int current position in string
*/
private $current_index;
/**
* @var boolean whether or not the string is closed.
*/
private $is_closed;
/**
* @param string $str initial value of AvroStringIO buffer. Regardless
* of the initial value, the pointer is set to the
* beginning of the buffer.
* @throws AvroIOException if a non-string value is passed as $str
*/
public function __construct($str = '')
{
$this->is_closed = false;
$this->string_buffer = '';
$this->current_index = 0;
if (is_string($str)) {
$this->string_buffer .= $str;
} else {
throw new AvroIOException(
sprintf('constructor argument must be a string: %s', gettype($str))
);
}
}
/**
* Append bytes to this buffer.
* (Nothing more is needed to support Avro.)
* @param string $arg bytes to write
* @returns int count of bytes written.
* @throws AvroIOException if $args is not a string value.
*/
public function write($arg)
{
$this->checkClosed();
if (is_string($arg)) {
return $this->appendStr($arg);
}
throw new AvroIOException(
sprintf(
'write argument must be a string: (%s) %s',
gettype($arg),
var_export($arg, true)
)
);
}
/**
* @throws AvroIOException if the buffer is closed.
*/
private function checkClosed()
{
if ($this->isClosed()) {
throw new AvroIOException('Buffer is closed');
}
}
/**
* @returns boolean true if this buffer is closed and false
* otherwise.
*/
public function isClosed()
{
return $this->is_closed;
}
/**
* Appends bytes to this buffer.
* @param string $str
* @returns integer count of bytes written.
*/
private function appendStr($str)
{
$this->checkClosed();
$this->string_buffer .= $str;
$len = strlen($str);
$this->current_index += $len;
return $len;
}
/**
* @returns string bytes read from buffer
* @todo test for fencepost errors wrt updating current_index
*/
public function read($len)
{
$this->checkClosed();
$read = '';
for ($i = $this->current_index; $i < ($this->current_index + $len); $i++) {
$read .= $this->string_buffer[$i] ?? '';
}
if (strlen($read) < $len) {
$this->current_index = $this->length();
} else {
$this->current_index += $len;
}
return $read;
}
/**
* @returns int count of bytes in the buffer
* @internal Could probably memoize length for performance, but
* no need do this yet.
*/
public function length()
{
return strlen($this->string_buffer);
}
/**
* @returns boolean true if successful
* @throws AvroIOException if the seek failed.
*/
public function seek($offset, $whence = self::SEEK_SET): bool
{
if (!is_int($offset)) {
throw new AvroIOException('Seek offset must be an integer.');
}
// Prevent seeking before BOF
switch ($whence) {
case self::SEEK_SET:
if (0 > $offset) {
throw new AvroIOException('Cannot seek before beginning of file.');
}
$this->current_index = $offset;
break;
case self::SEEK_CUR:
if (0 > $this->current_index + $whence) {
throw new AvroIOException('Cannot seek before beginning of file.');
}
$this->current_index += $offset;
break;
case self::SEEK_END:
if (0 > $this->length() + $offset) {
throw new AvroIOException('Cannot seek before beginning of file.');
}
$this->current_index = $this->length() + $offset;
break;
default:
throw new AvroIOException(sprintf('Invalid seek whence %d', $whence));
}
return true;
}
/**
* @returns int
* @see AvroIO::tell()
*/
public function tell()
{
return $this->current_index;
}
/**
* @returns boolean
* @see AvroIO::isEof()
*/
public function isEof()
{
return ($this->current_index >= $this->length());
}
/**
* No-op provided for compatibility with AvroIO interface.
* @returns boolean true
*/
public function flush()
{
return true;
}
/**
* Marks this buffer as closed.
* @returns boolean true
*/
public function close()
{
$this->checkClosed();
$this->is_closed = true;
return true;
}
/**
* Truncates the truncate buffer to 0 bytes and returns the pointer
* to the beginning of the buffer.
* @returns boolean true
*/
public function truncate()
{
$this->checkClosed();
$this->string_buffer = '';
$this->current_index = 0;
return true;
}
/**
* @returns string
* @uses self::__toString()
*/
public function string()
{
return (string) $this;
}
/**
* @returns string
*/
public function __toString()
{
return $this->string_buffer;
}
}