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