Skip to content

Commit

Permalink
Add LineReader
Browse files Browse the repository at this point in the history
Closes #63.
  • Loading branch information
kelunik committed Aug 22, 2019
1 parent 5d602c5 commit a0a418e
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 0 deletions.
52 changes: 52 additions & 0 deletions lib/LineReader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?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 () {
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;
}

if (($pos = \strpos($this->buffer, "\n")) !== false) {
$line = \substr($this->buffer, 0, $pos);
$this->buffer = \substr($this->buffer, $pos + 1);
return \rtrim($line, "\r");
}

$line = $this->buffer;
$this->buffer = "";
return \rtrim($line, "\r");
});
}
}
72 changes: 72 additions & 0 deletions lib/LineReaderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?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(): void
{
$this->check(["abc"], ["abc"]);
}

public function testMultiLineLf(): void
{
$this->check(["abc\nef"], ["abc", "ef"]);
}

public function testMultiLineCrLf(): void
{
$this->check(["abc\r\nef"], ["abc", "ef"]);
}

public function testMultiLineEmptyNewlineStart(): void
{
$this->check(["\r\nabc\r\nef\r\n"], ["", "abc", "ef"]);
}

public function testMultiLineEmptyNewlineEnd(): void
{
$this->check(["abc\r\nef\r\n"], ["abc", "ef"]);
}

public function testMultiLineEmptyNewlineMiddle(): void
{
$this->check(["abc\r\n\r\nef\r\n"], ["abc", "", "ef"]);
}

public function testEmpty(): void
{
$this->check([], []);
}

public function testEmptyCrLf(): void
{
$this->check(["\r\n"], [""]);
}

public function testMultiLineSlow(): void
{
$this->check(["a", "bc", "\r", "\n\r\nef\r", "\n"], ["abc", "", "ef"]);
}

private function check(array $chunks, array $expectedLines): void
{
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);
}));
}
}

0 comments on commit a0a418e

Please sign in to comment.