1
0
mirror of https://github.com/danog/byte-stream.git synced 2024-11-26 11:54:54 +01:00

Add LineReader (#64)

This commit is contained in:
Niklas Keller 2019-08-22 22:37:48 +02:00 committed by GitHub
parent 5d602c5c7a
commit 38d13dbb5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 156 additions and 0 deletions

62
lib/LineReader.php Normal file
View File

@ -0,0 +1,62 @@
<?php
namespace Amp\ByteStream;
use Amp\Promise;
use function Amp\call;
final class LineReader
{
/** @var string */
private $buffer = "";
/** @var InputStream */
private $source;
public function __construct(InputStream $inputStream)
{
$this->source = $inputStream;
}
/**
* @return Promise<string|null>
*/
public function readLine(): Promise
{
return call(function () {
if (($pos = \strpos($this->buffer, "\n")) !== false) {
$line = \substr($this->buffer, 0, $pos);
$this->buffer = \substr($this->buffer, $pos + 1);
return \rtrim($line, "\r");
}
while (null !== $chunk = yield $this->source->read()) {
$this->buffer .= $chunk;
if (($pos = \strpos($this->buffer, "\n")) !== false) {
$line = \substr($this->buffer, 0, $pos);
$this->buffer = \substr($this->buffer, $pos + 1);
return \rtrim($line, "\r");
}
}
if ($this->buffer === "") {
return null;
}
$line = $this->buffer;
$this->buffer = "";
return \rtrim($line, "\r");
});
}
public function getBuffer(): string
{
return $this->buffer;
}
public function clearBuffer()
{
$this->buffer = "";
}
}

94
test/LineReaderTest.php Normal file
View File

@ -0,0 +1,94 @@
<?php /** @noinspection PhpUnhandledExceptionInspection */
namespace Amp\ByteStream;
use Amp\Iterator;
use Amp\PHPUnit\TestCase;
use function Amp\call;
use function Amp\Promise\wait;
class LineReaderTest extends TestCase
{
public function testSingleLine()
{
$this->check(["abc"], ["abc"]);
}
public function testMultiLineLf()
{
$this->check(["abc\nef"], ["abc", "ef"]);
}
public function testMultiLineCrLf()
{
$this->check(["abc\r\nef"], ["abc", "ef"]);
}
public function testMultiLineEmptyNewlineStart()
{
$this->check(["\r\nabc\r\nef\r\n"], ["", "abc", "ef"]);
}
public function testMultiLineEmptyNewlineEnd()
{
$this->check(["abc\r\nef\r\n"], ["abc", "ef"]);
}
public function testMultiLineEmptyNewlineMiddle()
{
$this->check(["abc\r\n\r\nef\r\n"], ["abc", "", "ef"]);
}
public function testEmpty()
{
$this->check([], []);
}
public function testEmptyCrLf()
{
$this->check(["\r\n"], [""]);
}
public function testEmptyCr()
{
$this->check(["\r"], [""]);
}
public function testMultiLineSlow()
{
$this->check(["a", "bc", "\r", "\n\r\nef\r", "\n"], ["abc", "", "ef"]);
}
public function testClearBuffer()
{
wait(call(static function () {
$inputStream = new IteratorStream(Iterator\fromIterable(["a\nb\nc"]));
$reader = new LineReader($inputStream);
self::assertSame("a", yield $reader->readLine());
self::assertSame("b\nc", $reader->getBuffer());
$reader->clearBuffer();
self::assertSame("", $reader->getBuffer());
self::assertNull(yield $reader->readLine());
}));
}
private function check(array $chunks, array $expectedLines)
{
wait(call(static function () use ($chunks, $expectedLines) {
$inputStream = new IteratorStream(Iterator\fromIterable($chunks));
$reader = new LineReader($inputStream);
$lines = [];
while (null !== $line = yield $reader->readLine()) {
$lines[] = $line;
}
self::assertSame($expectedLines, $lines);
self::assertSame("", $reader->getBuffer());
}));
}
}