From f161b4c3f7f76f8158b746c180f013873ca9d662 Mon Sep 17 00:00:00 2001 From: Saif Eddin Gmati <29315886+azjezz@users.noreply.github.com> Date: Wed, 27 Mar 2024 07:56:50 +0000 Subject: [PATCH] feat(io): introduce `ReadHandle::reachedEndOfDataSource` (#452) Signed-off-by: azjezz --- examples/channel/bounded.php | 7 +- src/Psl/File/ReadHandle.php | 8 ++ src/Psl/File/ReadWriteHandle.php | 8 ++ src/Psl/IO/CloseReadStreamHandle.php | 8 ++ src/Psl/IO/CloseReadWriteStreamHandle.php | 8 ++ src/Psl/IO/CloseSeekReadStreamHandle.php | 8 ++ src/Psl/IO/CloseSeekReadWriteStreamHandle.php | 8 ++ src/Psl/IO/Internal/ResourceHandle.php | 85 +++++++++++++------ src/Psl/IO/MemoryHandle.php | 29 ++++--- .../IO/ReadHandleConvenienceMethodsTrait.php | 6 +- src/Psl/IO/ReadHandleInterface.php | 28 +++++- src/Psl/IO/ReadStreamHandle.php | 8 ++ src/Psl/IO/ReadWriteStreamHandle.php | 8 ++ src/Psl/IO/Reader.php | 79 ++++++++--------- src/Psl/IO/SeekReadStreamHandle.php | 8 ++ src/Psl/IO/SeekReadWriteStreamHandle.php | 8 ++ src/Psl/Network/Internal/Socket.php | 8 ++ .../benchmark/Channel/CommunicationBench.php | 36 ++++---- tests/unit/IO/ReaderTest.php | 18 ++-- 19 files changed, 258 insertions(+), 118 deletions(-) diff --git a/examples/channel/bounded.php b/examples/channel/bounded.php index 0bbcd95b..79321817 100644 --- a/examples/channel/bounded.php +++ b/examples/channel/bounded.php @@ -30,12 +30,11 @@ for ($i = 0; $i < 10; $i++) { $file = File\open_read_only(__FILE__); - $reader = new IO\Reader($file); - while (!$reader->isEndOfFile()) { - $byte = $reader->readByte(); - + while ($byte = $file->readAll(1)) { $sender->send($byte); } + + $file->close(); } IO\write_error_line("[ sender ]: completed."); diff --git a/src/Psl/File/ReadHandle.php b/src/Psl/File/ReadHandle.php index 14389c38..cfcaa83b 100644 --- a/src/Psl/File/ReadHandle.php +++ b/src/Psl/File/ReadHandle.php @@ -39,6 +39,14 @@ public function __construct(string $file) parent::__construct($this->readHandle); } + /** + * {@inheritDoc} + */ + public function reachedEndOfDataSource(): bool + { + return $this->readHandle->reachedEndOfDataSource(); + } + /** * {@inheritDoc} */ diff --git a/src/Psl/File/ReadWriteHandle.php b/src/Psl/File/ReadWriteHandle.php index a5aec5ba..0795c412 100644 --- a/src/Psl/File/ReadWriteHandle.php +++ b/src/Psl/File/ReadWriteHandle.php @@ -67,6 +67,14 @@ public function __construct(string $file, WriteMode $write_mode = WriteMode::Ope parent::__construct($this->readWriteHandle); } + /** + * {@inheritDoc} + */ + public function reachedEndOfDataSource(): bool + { + return $this->readWriteHandle->reachedEndOfDataSource(); + } + /** * {@inheritDoc} */ diff --git a/src/Psl/IO/CloseReadStreamHandle.php b/src/Psl/IO/CloseReadStreamHandle.php index 3bc288c6..3ec42f13 100644 --- a/src/Psl/IO/CloseReadStreamHandle.php +++ b/src/Psl/IO/CloseReadStreamHandle.php @@ -23,6 +23,14 @@ public function __construct(mixed $stream) $this->handle = new Internal\ResourceHandle($stream, read: true, write: false, seek: false, close: true); } + /** + * {@inheritDoc} + */ + public function reachedEndOfDataSource(): bool + { + return $this->handle->reachedEndOfDataSource(); + } + /** * {@inheritDoc} */ diff --git a/src/Psl/IO/CloseReadWriteStreamHandle.php b/src/Psl/IO/CloseReadWriteStreamHandle.php index 5ffce07c..70d3fc6c 100644 --- a/src/Psl/IO/CloseReadWriteStreamHandle.php +++ b/src/Psl/IO/CloseReadWriteStreamHandle.php @@ -24,6 +24,14 @@ public function __construct(mixed $stream) $this->handle = new Internal\ResourceHandle($stream, read: true, write: true, seek: false, close: true); } + /** + * {@inheritDoc} + */ + public function reachedEndOfDataSource(): bool + { + return $this->handle->reachedEndOfDataSource(); + } + /** * {@inheritDoc} */ diff --git a/src/Psl/IO/CloseSeekReadStreamHandle.php b/src/Psl/IO/CloseSeekReadStreamHandle.php index ced72ded..fe1269b8 100644 --- a/src/Psl/IO/CloseSeekReadStreamHandle.php +++ b/src/Psl/IO/CloseSeekReadStreamHandle.php @@ -23,6 +23,14 @@ public function __construct(mixed $stream) $this->handle = new Internal\ResourceHandle($stream, read: true, write: false, seek: true, close: true); } + /** + * {@inheritDoc} + */ + public function reachedEndOfDataSource(): bool + { + return $this->handle->reachedEndOfDataSource(); + } + /** * {@inheritDoc} */ diff --git a/src/Psl/IO/CloseSeekReadWriteStreamHandle.php b/src/Psl/IO/CloseSeekReadWriteStreamHandle.php index ef665ce7..38928c26 100644 --- a/src/Psl/IO/CloseSeekReadWriteStreamHandle.php +++ b/src/Psl/IO/CloseSeekReadWriteStreamHandle.php @@ -24,6 +24,14 @@ public function __construct(mixed $stream) $this->handle = new Internal\ResourceHandle($stream, read: true, write: true, seek: true, close: true); } + /** + * {@inheritDoc} + */ + public function reachedEndOfDataSource(): bool + { + return $this->handle->reachedEndOfDataSource(); + } + /** * {@inheritDoc} */ diff --git a/src/Psl/IO/Internal/ResourceHandle.php b/src/Psl/IO/Internal/ResourceHandle.php index ca908ec7..a2664bf9 100644 --- a/src/Psl/IO/Internal/ResourceHandle.php +++ b/src/Psl/IO/Internal/ResourceHandle.php @@ -14,15 +14,19 @@ use function error_get_last; use function fclose; +use function feof; +use function fread; use function fseek; use function ftell; use function fwrite; use function is_resource; use function max; use function str_contains; +use function stream_get_contents; use function stream_get_meta_data; use function stream_set_blocking; use function stream_set_read_buffer; +use function stream_set_write_buffer; use function substr; /** @@ -46,54 +50,57 @@ class ResourceHandle implements IO\CloseSeekReadWriteStreamHandleInterface */ protected mixed $stream; - /** - * @var null|Async\Sequence, null|float}, string> - */ - private ?Async\Sequence $readSequence = null; - - private ?Suspension $readSuspension = null; - - /** - * @var string - */ - private string $readWatcher = 'invalid'; - /** * @var null|Async\Sequence> */ private ?Async\Sequence $writeSequence = null; - private ?Suspension $writeSuspension = null; + private string $writeWatcher = 'invalid'; /** - * @var string + * @var null|Async\Sequence, null|float}, string> */ - private string $writeWatcher = 'invalid'; + private ?Async\Sequence $readSequence = null; + private ?Suspension $readSuspension = null; + private string $readWatcher = 'invalid'; + + private bool $useSingleRead = false; + private bool $reachedEof = false; /** * @param resource $stream */ - public function __construct(mixed $stream, bool $read, bool $write, bool $seek, private bool $close) + public function __construct(mixed $stream, bool $read, bool $write, bool $seek, private readonly bool $close) { /** @psalm-suppress RedundantConditionGivenDocblockType - The stream is always a resource, but we want to make sure it is a stream resource. */ $this->stream = Type\resource('stream')->assert($stream); - /** @psalm-suppress UnusedFunctionCall */ - stream_set_read_buffer($stream, 0); stream_set_blocking($stream, false); $meta = stream_get_meta_data($stream); + if ($read) { + $this->useSingleRead = ($meta["stream_type"] === "udp_socket" || $meta["stream_type"] === "STDIO"); + } + $blocks = $meta['blocked'] || ($meta['wrapper_type'] ?? '') === 'plainfile'; if ($seek) { - Psl\invariant($meta['seekable'], 'Handle is not seekable.'); + $seekable = $meta['seekable']; + + Psl\invariant($seekable, 'Handle is not seekable.'); } if ($read) { - Psl\invariant(str_contains($meta['mode'], 'r') || str_contains($meta['mode'], '+'), 'Handle is not readable.'); + $readable = str_contains($meta['mode'], 'r') || str_contains($meta['mode'], '+'); + + Psl\invariant($readable, 'Handle is not readable.'); + + /** @psalm-suppress UnusedFunctionCall */ + stream_set_read_buffer($stream, 0); $this->readWatcher = EventLoop::onReadable($this->stream, function () { - $this->readSuspension?->resume(null); + $this->readSuspension?->resume(); }); + $this->readSequence = new Async\Sequence( /** * @param array{null|int<1, max>, null|float} $input @@ -142,11 +149,14 @@ function (array $input) use ($blocks): string { || str_contains($meta['mode'], 'a') || str_contains($meta['mode'], '+'); - Psl\invariant($writable, 'Handle is not writeable.'); + Psl\invariant($writable, 'Handle is not writeable.'); + + stream_set_write_buffer($stream, 0); - $this->writeWatcher = EventLoop::onReadable($this->stream, function () { - $this->writeSuspension?->resume(null); + $this->writeWatcher = EventLoop::onWritable($this->stream, function () { + $this->writeSuspension?->resume(); }); + $this->writeSequence = new Async\Sequence( /** * @param array{string, null|float} $input @@ -254,6 +264,22 @@ public function tell(): int return max($result, 0); } + /** + * {@inheritDoc} + */ + public function reachedEndOfDataSource(): bool + { + if (!is_resource($this->stream)) { + throw new Exception\AlreadyClosedException('Handle has already been closed.'); + } + + if ($this->reachedEof) { + return true; + } + + return $this->reachedEof = feof($this->stream); + } + /** * {@inheritDoc} */ @@ -279,7 +305,12 @@ public function tryRead(?int $max_bytes = null): string $max_bytes = self::MAXIMUM_READ_BUFFER_SIZE; } - $result = fread($this->stream, $max_bytes); + if ($this->useSingleRead) { + $result = fread($this->stream, $max_bytes); + } else { + $result = stream_get_contents($this->stream, $max_bytes); + } + if ($result === false) { /** @var array{message: string} $error */ $error = error_get_last(); @@ -287,6 +318,10 @@ public function tryRead(?int $max_bytes = null): string throw new Exception\RuntimeException($error['message'] ?? 'unknown error.'); } + if ($result === '' && feof($this->stream)) { + $this->reachedEof = true; + } + return $result; } diff --git a/src/Psl/IO/MemoryHandle.php b/src/Psl/IO/MemoryHandle.php index 4aa4c7c5..623b5a5b 100644 --- a/src/Psl/IO/MemoryHandle.php +++ b/src/Psl/IO/MemoryHandle.php @@ -21,6 +21,7 @@ final class MemoryHandle implements CloseSeekReadWriteHandleInterface private int $offset = 0; private string $buffer; private bool $closed = false; + private bool $reachedEof = false; public function __construct(string $buffer = '') { @@ -28,13 +29,17 @@ public function __construct(string $buffer = '') } /** - * Read from the handle. - * - * @param positive-int|null $max_bytes the maximum number of bytes to read. - * - * @throws Exception\AlreadyClosedException If the handle has been already closed. - * - * @return string the read data on success, or an empty string if the end of file is reached. + * {@inheritDoc} + */ + public function reachedEndOfDataSource(): bool + { + $this->assertHandleIsOpen(); + + return $this->reachedEof; + } + + /** + * {@inheritDoc} */ public function tryRead(?int $max_bytes = null): string { @@ -46,6 +51,8 @@ public function tryRead(?int $max_bytes = null): string $length = strlen($this->buffer); if ($this->offset >= $length) { + $this->reachedEof = true; + return ''; } @@ -58,13 +65,7 @@ public function tryRead(?int $max_bytes = null): string } /** - * Read from the handle. - * - * @param positive-int|null $max_bytes the maximum number of bytes to read. - * - * @throws Exception\AlreadyClosedException If the handle has been already closed. - * - * @return string the read data on success, or an empty string if the end of file is reached. + * {@inheritDoc} */ public function read(?int $max_bytes = null, ?float $timeout = null): string { diff --git a/src/Psl/IO/ReadHandleConvenienceMethodsTrait.php b/src/Psl/IO/ReadHandleConvenienceMethodsTrait.php index ad3d7d21..a2461676 100644 --- a/src/Psl/IO/ReadHandleConvenienceMethodsTrait.php +++ b/src/Psl/IO/ReadHandleConvenienceMethodsTrait.php @@ -17,7 +17,7 @@ trait ReadHandleConvenienceMethodsTrait /** * Read until there is no more data to read. * - * It is possible for this to never return, e.g. if called on a pipe or + * It is possible for this to never return, e.g. if called on a pipe * or socket which the other end keeps open forever. Set a timeout if you * do not want this to happen. * @@ -60,7 +60,7 @@ static function () use ($data): void { if ($to_read !== null) { $to_read -= strlen($chunk); } - } while (($to_read === null || $to_read > 0) && $chunk !== ''); + } while (($to_read === null || $to_read > 0) && !$this->reachedEndOfDataSource()); return $data->value; } @@ -68,7 +68,7 @@ static function () use ($data): void { /** * Read a fixed amount of data. * - * It is possible for this to never return, e.g. if called on a pipe or + * It is possible for this to never return, e.g. if called on a pipe * or socket which the other end keeps open forever. Set a timeout if you * do not want this to happen. * diff --git a/src/Psl/IO/ReadHandleInterface.php b/src/Psl/IO/ReadHandleInterface.php index 801d5804..01869e09 100644 --- a/src/Psl/IO/ReadHandleInterface.php +++ b/src/Psl/IO/ReadHandleInterface.php @@ -9,6 +9,26 @@ */ interface ReadHandleInterface extends HandleInterface { + /** + * Indicates whether the cursor has reached the end of the data source (EOF). + * + * This method returns `true` if an attempt to read beyond the end of the data source has been made, + * indicating that no more data is available for reading. Conversely, it returns `false` if the + * end of the data source has not been reached or if no data exists but no attempt to read has been made. + * + * It is important to note that a return value of `false` does not guarantee that subsequent calls to + * `read()` or `tryRead()` will return data. This could occur in situations where the handle is completely empty, + * and no read operations have been performed yet, or if the handle is not ready for reading. In such cases, + * calls to `read()` or `tryRead()` might return an empty string, reflecting the absence of available data + * at the time of the call. + * + * @throws Exception\RuntimeException If an error occurred during the operation. + * @throws Exception\AlreadyClosedException If the handle has already been closed. + * + * @return bool `true` if the cursor is at the end of the data source, otherwise `false`. + */ + public function reachedEndOfDataSource(): bool; + /** * Try to read from the handle immediately, without waiting. * @@ -20,7 +40,7 @@ interface ReadHandleInterface extends HandleInterface * @throws Exception\RuntimeException If an error occurred during the operation. * @throws Exception\AlreadyClosedException If the handle has been already closed. * - * @return string the read data on success, or an empty string if the handle is not ready for read, or the end of file is reached. + * @return string the read data on success, or an empty string if the handle is not ready for read, or the end of data source is reached. * * @see ReadStreamHandleInterface::read() * @see ReadStreamHandleInterface::readAll() @@ -36,7 +56,7 @@ public function tryRead(?int $max_bytes = null): string; * @throws Exception\RuntimeException If an error occurred during the operation. * @throws Exception\TimeoutException If $timeout is reached before being able to read from the handle. * - * @return string the read data on success, or an empty string if the end of file is reached. + * @return string the read data on success, or an empty string if the end of data source is reached. * * Up to `$max_bytes` may be allocated in a buffer; large values may lead to * unnecessarily hitting the request memory limit. @@ -46,7 +66,7 @@ public function read(?int $max_bytes = null, ?float $timeout = null): string; /** * Read until there is no more data to read. * - * It is possible for this to never return, e.g. if called on a pipe or + * It is possible for this to never return, e.g. if called on a pipe * or socket which the other end keeps open forever. Set a timeout if you * do not want this to happen. * @@ -64,7 +84,7 @@ public function readAll(?int $max_bytes = null, ?float $timeout = null): string; /** * Read a fixed amount of data. * - * It is possible for this to never return, e.g. if called on a pipe or + * It is possible for this to never return, e.g. if called on a pipe * or socket which the other end keeps open forever. Set a timeout if you * do not want this to happen. * diff --git a/src/Psl/IO/ReadStreamHandle.php b/src/Psl/IO/ReadStreamHandle.php index ad7269bc..6c71c877 100644 --- a/src/Psl/IO/ReadStreamHandle.php +++ b/src/Psl/IO/ReadStreamHandle.php @@ -23,6 +23,14 @@ public function __construct(mixed $stream) $this->handle = new Internal\ResourceHandle($stream, read: true, write: false, seek: false, close: false); } + /** + * {@inheritDoc} + */ + public function reachedEndOfDataSource(): bool + { + return $this->handle->reachedEndOfDataSource(); + } + /** * {@inheritDoc} */ diff --git a/src/Psl/IO/ReadWriteStreamHandle.php b/src/Psl/IO/ReadWriteStreamHandle.php index 3fae5178..fb4a67ba 100644 --- a/src/Psl/IO/ReadWriteStreamHandle.php +++ b/src/Psl/IO/ReadWriteStreamHandle.php @@ -24,6 +24,14 @@ public function __construct(mixed $stream) $this->handle = new Internal\ResourceHandle($stream, read: true, write: true, seek: false, close: false); } + /** + * {@inheritDoc} + */ + public function reachedEndOfDataSource(): bool + { + return $this->handle->reachedEndOfDataSource(); + } + /** * {@inheritDoc} */ diff --git a/src/Psl/IO/Reader.php b/src/Psl/IO/Reader.php index 11ec1462..2d801408 100644 --- a/src/Psl/IO/Reader.php +++ b/src/Psl/IO/Reader.php @@ -16,7 +16,7 @@ final class Reader implements ReadHandleInterface { use ReadHandleConvenienceMethodsTrait; - private ReadHandleInterface $handle; + private readonly ReadHandleInterface $handle; private bool $eof = false; private string $buffer = ''; @@ -26,6 +26,33 @@ public function __construct(ReadHandleInterface $handle) $this->handle = $handle; } + /** + * {@inheritDoc} + */ + public function reachedEndOfDataSource(): bool + { + if ($this->eof) { + return true; + } + + if ($this->buffer !== '') { + return false; + } + + // @codeCoverageIgnoreStart + try { + $this->buffer = $this->handle->read(); + if ($this->buffer === '') { + return $this->eof = $this->handle->reachedEndOfDataSource(); + } + } catch (Exception\ExceptionInterface) { + // ignore; it'll be thrown again when attempting a real read. + } + // @codeCoverageIgnoreEnd + + return false; + } + /** * {@inheritDoc} */ @@ -65,23 +92,6 @@ function (): void { return $ret; } - /** - * @param null|positive-int $desired_bytes - * - * @throws Exception\AlreadyClosedException If the handle has been already closed. - * @throws Exception\RuntimeException If an error occurred during the operation. - * @throws Exception\TimeoutException If $timeout is reached before being able to read from the handle. - */ - private function fillBuffer(?int $desired_bytes, ?float $timeout): void - { - $chunk = $this->handle->read($desired_bytes, $timeout); - if ($chunk === '') { - $this->eof = true; - } - - $this->buffer .= $chunk; - } - /** * Read a single byte from the handle. * @@ -102,7 +112,6 @@ public function readByte(?float $timeout = null): string $ret = $this->buffer[0]; if ($ret === $this->buffer) { $this->buffer = ''; - $this->eof = true; return $ret; } @@ -246,33 +255,17 @@ public function getHandle(): ReadHandleInterface } /** - * @throws Exception\RuntimeException If an error occurred during the operation. - * @throws Exception\AlreadyClosedException If the handle has been already closed. + * @param null|positive-int $desired_bytes * - * @return bool true if EOL has been reached, false otherwise. + * @throws Exception\AlreadyClosedException If the handle has been already closed. + * @throws Exception\RuntimeException If an error occurred during the operation. + * @throws Exception\TimeoutException If $timeout is reached before being able to read from the handle. */ - public function isEndOfFile(): bool + private function fillBuffer(?int $desired_bytes, ?float $timeout): void { - if ($this->eof) { - return true; - } - - if ($this->buffer !== '') { - return false; - } - - // @codeCoverageIgnoreStart - try { - $this->buffer = $this->handle->read(); - if ($this->buffer === '') { - $this->eof = true; - return true; - } - } catch (Exception\ExceptionInterface) { - // ignore; it'll be thrown again when attempting a real read. + $this->buffer .= $chunk = $this->handle->read($desired_bytes, $timeout); + if ($chunk === '') { + $this->eof = $this->handle->reachedEndOfDataSource(); } - // @codeCoverageIgnoreEnd - - return false; } } diff --git a/src/Psl/IO/SeekReadStreamHandle.php b/src/Psl/IO/SeekReadStreamHandle.php index aa4d7e02..ca74f2e3 100644 --- a/src/Psl/IO/SeekReadStreamHandle.php +++ b/src/Psl/IO/SeekReadStreamHandle.php @@ -23,6 +23,14 @@ public function __construct(mixed $stream) $this->handle = new Internal\ResourceHandle($stream, read: true, write: false, seek: true, close: false); } + /** + * {@inheritDoc} + */ + public function reachedEndOfDataSource(): bool + { + return $this->handle->reachedEndOfDataSource(); + } + /** * {@inheritDoc} */ diff --git a/src/Psl/IO/SeekReadWriteStreamHandle.php b/src/Psl/IO/SeekReadWriteStreamHandle.php index 260edcbf..ea78e8ce 100644 --- a/src/Psl/IO/SeekReadWriteStreamHandle.php +++ b/src/Psl/IO/SeekReadWriteStreamHandle.php @@ -24,6 +24,14 @@ public function __construct(mixed $stream) $this->handle = new Internal\ResourceHandle($stream, read: true, write: true, seek: true, close: false); } + /** + * {@inheritDoc} + */ + public function reachedEndOfDataSource(): bool + { + return $this->handle->reachedEndOfDataSource(); + } + /** * {@inheritDoc} */ diff --git a/src/Psl/Network/Internal/Socket.php b/src/Psl/Network/Internal/Socket.php index 489735af..25f61908 100644 --- a/src/Psl/Network/Internal/Socket.php +++ b/src/Psl/Network/Internal/Socket.php @@ -32,6 +32,14 @@ public function __construct($stream) $this->handle = new Internal\ResourceHandle($stream, read: true, write: true, seek: false, close: true); } + /** + * {@inheritDoc} + */ + public function reachedEndOfDataSource(): bool + { + return $this->handle->reachedEndOfDataSource(); + } + /** * {@inheritDoc} */ diff --git a/tests/benchmark/Channel/CommunicationBench.php b/tests/benchmark/Channel/CommunicationBench.php index 78dd86d0..ae68fa75 100644 --- a/tests/benchmark/Channel/CommunicationBench.php +++ b/tests/benchmark/Channel/CommunicationBench.php @@ -13,10 +13,18 @@ #[Groups(['channel'])] final class CommunicationBench { + /** + * @throws Channel\Exception\ExceptionInterface + * @throws IO\Exception\ExceptionInterface + * @throws File\Exception\ExceptionInterface + * + * @psalm-suppress UnnecessaryVarAnnotation + */ public function benchBoundedCommunication(): void { /** - * @psalm-suppress MissingThrowsDocblock - $capacity is always > 0 + * @var Channel\ReceiverInterface $receiver + * @var Channel\SenderInterface $sender */ [$receiver, $sender] = Channel\bounded(10); @@ -30,14 +38,8 @@ public function benchBoundedCommunication(): void } }); - /** @psalm-suppress MissingThrowsDocblock */ $file = File\open_read_only(__FILE__); - $reader = new IO\Reader($file); - /** @psalm-suppress MissingThrowsDocblock */ - while (!$reader->isEndOfFile()) { - $byte = $reader->readByte(); - - /** @psalm-suppress InvalidArgument */ + while ($byte = $file->readAll(1)) { $sender->send($byte); } @@ -46,10 +48,18 @@ public function benchBoundedCommunication(): void Async\Scheduler::run(); } + /** + * @throws Channel\Exception\ExceptionInterface + * @throws IO\Exception\ExceptionInterface + * @throws File\Exception\ExceptionInterface + * + * @psalm-suppress UnnecessaryVarAnnotation + */ public function benchUnboundedCommunication(): void { /** - * @psalm-suppress MissingThrowsDocblock - $capacity is always > 0 + * @var Channel\ReceiverInterface $receiver + * @var Channel\SenderInterface $sender */ [$receiver, $sender] = Channel\bounded(10); @@ -63,14 +73,8 @@ public function benchUnboundedCommunication(): void } }); - /** @psalm-suppress MissingThrowsDocblock */ $file = File\open_read_only(__FILE__); - $reader = new IO\Reader($file); - /** @psalm-suppress MissingThrowsDocblock */ - while (!$reader->isEndOfFile()) { - $byte = $reader->readByte(); - - /** @psalm-suppress InvalidArgument */ + while ($byte = $file->readAll(1)) { $sender->send($byte); } diff --git a/tests/unit/IO/ReaderTest.php b/tests/unit/IO/ReaderTest.php index 62f08390..09b6709d 100644 --- a/tests/unit/IO/ReaderTest.php +++ b/tests/unit/IO/ReaderTest.php @@ -16,7 +16,7 @@ public function testReadByteOnAnEmptyBufferFillsTheInternalBufferAndMarksTheRead $reader = new IO\Reader($handle); static::assertSame('a', $reader->readByte()); - static::assertTrue($reader->isEndOfFile()); + static::assertTrue($reader->reachedEndOfDataSource()); } public function testReadByteOnAnEmptyBufferThrows(): void @@ -35,7 +35,7 @@ public function testReadEmptyHandle(): void $reader = new IO\Reader($handle); static::assertEmpty($reader->tryRead()); - static::assertTrue($reader->isEndOfFile()); + static::assertTrue($reader->reachedEndOfDataSource()); } public function testReadingFile(): void @@ -66,7 +66,7 @@ public function testReadingFile(): void /** * Handle has reached EOL, but the buffer still contains content. */ - static::assertFalse($reader->isEndOfFile()); + static::assertFalse($reader->reachedEndOfDataSource()); static::assertSame(' ', $reader->readByte()); static::assertSame('ReaderTest', $reader->readFixedSize(10)); } @@ -77,7 +77,7 @@ public function testReadEof(): void $reader = new IO\Reader($handle); static::assertSame('hello', $reader->read()); static::assertSame('', $reader->read()); - static::assertTrue($reader->isEndOfFile()); + static::assertTrue($reader->reachedEndOfDataSource()); static::assertSame('', $reader->read()); } @@ -90,7 +90,7 @@ public function testReadSome(): void static::assertSame('ll', $reader->read(2)); static::assertSame('o,', $reader->read(2)); static::assertSame(' world!', $reader->read(10)); - static::assertTrue($reader->isEndOfFile()); + static::assertTrue($reader->reachedEndOfDataSource()); static::assertSame('', $reader->read()); } @@ -130,7 +130,7 @@ public function testIsEndOfLineWithEofHandle(): void $reader = new IO\Reader($handle); - static::assertTrue($reader->isEndOfFile()); + static::assertTrue($reader->reachedEndOfDataSource()); } public function testIsEndOfLineWithEmptyHandle(): void @@ -138,8 +138,8 @@ public function testIsEndOfLineWithEmptyHandle(): void $handle = new IO\MemoryHandle(); $reader = new IO\Reader($handle); - static::assertTrue($reader->isEndOfFile()); - static::assertTrue($reader->isEndOfFile()); + static::assertTrue($reader->reachedEndOfDataSource()); + static::assertTrue($reader->reachedEndOfDataSource()); } public function testIsEndOfLineWithNonEmptyHandle(): void @@ -147,7 +147,7 @@ public function testIsEndOfLineWithNonEmptyHandle(): void $handle = new IO\MemoryHandle('hello'); $reader = new IO\Reader($handle); - static::assertFalse($reader->isEndOfFile()); + static::assertFalse($reader->reachedEndOfDataSource()); static::assertSame('hello', $reader->readLine()); } }