2018-11-07 14:17:36 -05:00
|
|
|
// Ported to Deno from:
|
|
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2018-11-08 02:05:06 -05:00
|
|
|
import { Reader, ReadResult } from "deno";
|
|
|
|
import {
|
|
|
|
test,
|
|
|
|
assert,
|
|
|
|
assertEqual
|
|
|
|
} from "https://deno.land/x/testing/testing.ts";
|
2018-11-07 23:19:08 -05:00
|
|
|
import { BufReader, BufState } from "./bufio.ts";
|
2018-11-07 13:16:07 -05:00
|
|
|
import { Buffer } from "./buffer.ts";
|
2018-11-07 20:28:01 -05:00
|
|
|
import * as iotest from "./iotest.ts";
|
2018-11-08 02:05:06 -05:00
|
|
|
import { charCode, copyBytes } from "./util.ts";
|
|
|
|
|
|
|
|
const encoder = new TextEncoder();
|
2018-11-07 13:16:07 -05:00
|
|
|
|
2018-11-07 21:01:32 -05:00
|
|
|
async function readBytes(buf: BufReader): Promise<string> {
|
2018-11-07 13:16:07 -05:00
|
|
|
const b = new Uint8Array(1000);
|
|
|
|
let nb = 0;
|
|
|
|
while (true) {
|
|
|
|
let c = await buf.readByte();
|
|
|
|
if (c < 0) {
|
|
|
|
break; // EOF
|
|
|
|
}
|
|
|
|
b[nb] = c;
|
|
|
|
nb++;
|
|
|
|
}
|
|
|
|
const decoder = new TextDecoder();
|
|
|
|
return decoder.decode(b.subarray(0, nb));
|
|
|
|
}
|
|
|
|
|
2018-11-08 02:05:06 -05:00
|
|
|
function stringsReader(s: string): Reader {
|
2018-11-07 13:16:07 -05:00
|
|
|
const ui8 = encoder.encode(s);
|
|
|
|
return new Buffer(ui8.buffer as ArrayBuffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
test(async function bufioReaderSimple() {
|
|
|
|
const data = "hello world";
|
2018-11-07 21:01:32 -05:00
|
|
|
const b = new BufReader(stringsReader(data));
|
2018-11-07 13:16:07 -05:00
|
|
|
const s = await readBytes(b);
|
|
|
|
assertEqual(s, data);
|
|
|
|
});
|
2018-11-07 14:17:36 -05:00
|
|
|
|
2018-11-08 02:05:06 -05:00
|
|
|
type ReadMaker = { name: string; fn: (r: Reader) => Reader };
|
2018-11-07 14:17:36 -05:00
|
|
|
|
|
|
|
const readMakers: ReadMaker[] = [
|
2018-11-07 20:28:01 -05:00
|
|
|
{ name: "full", fn: r => r },
|
|
|
|
{ name: "byte", fn: r => new iotest.OneByteReader(r) },
|
2018-11-07 23:19:08 -05:00
|
|
|
{ name: "half", fn: r => new iotest.HalfReader(r) }
|
2018-11-07 20:28:01 -05:00
|
|
|
// TODO { name: "data+err", r => new iotest.DataErrReader(r) },
|
|
|
|
// { name: "timeout", fn: r => new iotest.TimeoutReader(r) },
|
2018-11-07 14:17:36 -05:00
|
|
|
];
|
|
|
|
|
2018-11-07 21:01:32 -05:00
|
|
|
function readLines(b: BufReader): string {
|
2018-11-07 20:28:01 -05:00
|
|
|
let s = "";
|
|
|
|
while (true) {
|
2018-11-07 23:19:08 -05:00
|
|
|
let s1 = b.readString("\n");
|
2018-11-07 20:28:01 -05:00
|
|
|
if (s1 == null) {
|
|
|
|
break; // EOF
|
|
|
|
}
|
|
|
|
s += s1;
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2018-11-07 14:17:36 -05:00
|
|
|
// Call read to accumulate the text of a file
|
2018-11-07 21:01:32 -05:00
|
|
|
async function reads(buf: BufReader, m: number): Promise<string> {
|
2018-11-07 14:17:36 -05:00
|
|
|
const b = new Uint8Array(1000);
|
|
|
|
let nb = 0;
|
|
|
|
while (true) {
|
|
|
|
const { nread, eof } = await buf.read(b.subarray(nb, nb + m));
|
|
|
|
nb += nread;
|
|
|
|
if (eof) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const decoder = new TextDecoder();
|
|
|
|
return decoder.decode(b.subarray(0, nb));
|
|
|
|
}
|
|
|
|
|
2018-11-07 21:01:32 -05:00
|
|
|
type NamedBufReader = { name: string; fn: (r: BufReader) => Promise<string> };
|
2018-11-07 14:17:36 -05:00
|
|
|
|
2018-11-07 21:01:32 -05:00
|
|
|
const bufreaders: NamedBufReader[] = [
|
|
|
|
{ name: "1", fn: (b: BufReader) => reads(b, 1) },
|
|
|
|
{ name: "2", fn: (b: BufReader) => reads(b, 2) },
|
|
|
|
{ name: "3", fn: (b: BufReader) => reads(b, 3) },
|
|
|
|
{ name: "4", fn: (b: BufReader) => reads(b, 4) },
|
|
|
|
{ name: "5", fn: (b: BufReader) => reads(b, 5) },
|
|
|
|
{ name: "7", fn: (b: BufReader) => reads(b, 7) },
|
2018-11-07 23:19:08 -05:00
|
|
|
{ name: "bytes", fn: readBytes }
|
2018-11-07 20:28:01 -05:00
|
|
|
// { name: "lines", fn: readLines },
|
2018-11-07 14:17:36 -05:00
|
|
|
];
|
|
|
|
|
|
|
|
const MIN_READ_BUFFER_SIZE = 16;
|
|
|
|
const bufsizes: number[] = [
|
|
|
|
0,
|
|
|
|
MIN_READ_BUFFER_SIZE,
|
|
|
|
23,
|
|
|
|
32,
|
|
|
|
46,
|
|
|
|
64,
|
|
|
|
93,
|
|
|
|
128,
|
|
|
|
1024,
|
|
|
|
4096
|
|
|
|
];
|
|
|
|
|
2018-11-07 21:01:32 -05:00
|
|
|
test(async function bufioBufReader() {
|
2018-11-07 14:17:36 -05:00
|
|
|
const texts = new Array<string>(31);
|
|
|
|
let str = "";
|
|
|
|
let all = "";
|
|
|
|
for (let i = 0; i < texts.length - 1; i++) {
|
|
|
|
texts[i] = str + "\n";
|
|
|
|
all += texts[i];
|
|
|
|
str += String.fromCharCode(i % 26 + 97);
|
|
|
|
}
|
|
|
|
texts[texts.length - 1] = all;
|
|
|
|
|
|
|
|
for (let text of texts) {
|
|
|
|
for (let readmaker of readMakers) {
|
|
|
|
for (let bufreader of bufreaders) {
|
|
|
|
for (let bufsize of bufsizes) {
|
|
|
|
const read = readmaker.fn(stringsReader(text));
|
2018-11-07 21:01:32 -05:00
|
|
|
const buf = new BufReader(read, bufsize);
|
2018-11-07 14:17:36 -05:00
|
|
|
const s = await bufreader.fn(buf);
|
|
|
|
const debugStr =
|
|
|
|
`reader=${readmaker.name} ` +
|
|
|
|
`fn=${bufreader.name} bufsize=${bufsize} want=${text} got=${s}`;
|
|
|
|
assertEqual(s, text, debugStr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2018-11-07 23:19:08 -05:00
|
|
|
|
|
|
|
test(async function bufioBufferFull() {
|
|
|
|
const longString =
|
|
|
|
"And now, hello, world! It is the time for all good men to come to the aid of their party";
|
|
|
|
const buf = new BufReader(stringsReader(longString), MIN_READ_BUFFER_SIZE);
|
2018-11-08 02:05:06 -05:00
|
|
|
let [line, err] = await buf.readSlice(charCode("!"));
|
2018-11-07 23:19:08 -05:00
|
|
|
|
|
|
|
const decoder = new TextDecoder();
|
|
|
|
let actual = decoder.decode(line);
|
2018-11-08 02:05:06 -05:00
|
|
|
assertEqual(err, "BufferFull");
|
2018-11-07 23:19:08 -05:00
|
|
|
assertEqual(actual, "And now, hello, ");
|
|
|
|
|
2018-11-08 02:05:06 -05:00
|
|
|
[line, err] = await buf.readSlice(charCode("!"));
|
2018-11-07 23:19:08 -05:00
|
|
|
actual = decoder.decode(line);
|
|
|
|
assertEqual(actual, "world!");
|
2018-11-08 02:05:06 -05:00
|
|
|
assert(err == null);
|
|
|
|
});
|
|
|
|
|
|
|
|
const testInput = encoder.encode(
|
|
|
|
"012\n345\n678\n9ab\ncde\nfgh\nijk\nlmn\nopq\nrst\nuvw\nxy"
|
|
|
|
);
|
|
|
|
const testInputrn = encoder.encode(
|
|
|
|
"012\r\n345\r\n678\r\n9ab\r\ncde\r\nfgh\r\nijk\r\nlmn\r\nopq\r\nrst\r\nuvw\r\nxy\r\n\n\r\n"
|
|
|
|
);
|
|
|
|
const testOutput = encoder.encode("0123456789abcdefghijklmnopqrstuvwxy");
|
|
|
|
|
|
|
|
// TestReader wraps a Uint8Array and returns reads of a specific length.
|
|
|
|
class TestReader implements Reader {
|
|
|
|
constructor(private data: Uint8Array, private stride: number) {}
|
|
|
|
|
|
|
|
async read(buf: ArrayBufferView): Promise<ReadResult> {
|
|
|
|
let nread = this.stride;
|
|
|
|
if (nread > this.data.byteLength) {
|
|
|
|
nread = this.data.byteLength;
|
|
|
|
}
|
|
|
|
if (nread > buf.byteLength) {
|
|
|
|
nread = buf.byteLength;
|
|
|
|
}
|
|
|
|
copyBytes(buf as Uint8Array, this.data);
|
|
|
|
this.data = this.data.subarray(nread);
|
|
|
|
let eof = false;
|
|
|
|
if (this.data.byteLength == 0) {
|
|
|
|
eof = true;
|
|
|
|
}
|
|
|
|
return { nread, eof };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function testReadLine(input: Uint8Array): Promise<void> {
|
|
|
|
for (let stride = 1; stride < 2; stride++) {
|
|
|
|
let done = 0;
|
|
|
|
let reader = new TestReader(input, stride);
|
|
|
|
let l = new BufReader(reader, input.byteLength + 1);
|
|
|
|
while (true) {
|
|
|
|
let [line, isPrefix, err] = await l.readLine();
|
|
|
|
if (line.byteLength > 0 && err != null) {
|
|
|
|
throw Error("readLine returned both data and error");
|
|
|
|
}
|
|
|
|
assertEqual(isPrefix, false);
|
|
|
|
if (err == "EOF") {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
let want = testOutput.subarray(done, done + line.byteLength);
|
|
|
|
assertEqual(
|
|
|
|
line,
|
|
|
|
want,
|
|
|
|
`Bad line at stride ${stride}: want: ${want} got: ${line}`
|
|
|
|
);
|
|
|
|
done += line.byteLength;
|
|
|
|
}
|
|
|
|
assertEqual(
|
|
|
|
done,
|
|
|
|
testOutput.byteLength,
|
|
|
|
`readLine didn't return everything: got: ${done}, ` +
|
|
|
|
`want: ${testOutput} (stride: ${stride})`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
test(async function bufioReadLine() {
|
|
|
|
await testReadLine(testInput);
|
|
|
|
await testReadLine(testInputrn);
|
2018-11-07 23:19:08 -05:00
|
|
|
});
|
2018-11-08 04:01:20 -05:00
|
|
|
|
|
|
|
test(async function bufioPeek() {
|
|
|
|
const decoder = new TextDecoder();
|
|
|
|
let p = new Uint8Array(10);
|
|
|
|
// string is 16 (minReadBufferSize) long.
|
|
|
|
let buf = new BufReader(
|
|
|
|
stringsReader("abcdefghijklmnop"),
|
|
|
|
MIN_READ_BUFFER_SIZE
|
|
|
|
);
|
|
|
|
|
|
|
|
let [actual, err] = await buf.peek(1);
|
|
|
|
assertEqual(decoder.decode(actual), "a");
|
|
|
|
assert(err == null);
|
|
|
|
|
|
|
|
[actual, err] = await buf.peek(4);
|
|
|
|
assertEqual(decoder.decode(actual), "abcd");
|
|
|
|
assert(err == null);
|
|
|
|
|
|
|
|
[actual, err] = await buf.peek(32);
|
|
|
|
assertEqual(decoder.decode(actual), "abcdefghijklmnop");
|
|
|
|
assertEqual(err, "BufferFull");
|
|
|
|
|
|
|
|
await buf.read(p.subarray(0, 3));
|
|
|
|
assertEqual(decoder.decode(p.subarray(0, 3)), "abc");
|
|
|
|
|
|
|
|
[actual, err] = await buf.peek(1);
|
|
|
|
assertEqual(decoder.decode(actual), "d");
|
|
|
|
assert(err == null);
|
|
|
|
|
|
|
|
[actual, err] = await buf.peek(1);
|
|
|
|
assertEqual(decoder.decode(actual), "d");
|
|
|
|
assert(err == null);
|
|
|
|
|
|
|
|
[actual, err] = await buf.peek(1);
|
|
|
|
assertEqual(decoder.decode(actual), "d");
|
|
|
|
assert(err == null);
|
|
|
|
|
|
|
|
[actual, err] = await buf.peek(2);
|
|
|
|
assertEqual(decoder.decode(actual), "de");
|
|
|
|
assert(err == null);
|
|
|
|
|
|
|
|
let { eof } = await buf.read(p.subarray(0, 3));
|
|
|
|
assertEqual(decoder.decode(p.subarray(0, 3)), "def");
|
|
|
|
assert(!eof);
|
|
|
|
assert(err == null);
|
|
|
|
|
|
|
|
[actual, err] = await buf.peek(4);
|
|
|
|
assertEqual(decoder.decode(actual), "ghij");
|
|
|
|
assert(err == null);
|
|
|
|
|
|
|
|
await buf.read(p);
|
|
|
|
assertEqual(decoder.decode(p), "ghijklmnop");
|
|
|
|
|
|
|
|
[actual, err] = await buf.peek(0);
|
|
|
|
assertEqual(decoder.decode(actual), "");
|
|
|
|
assert(err == null);
|
|
|
|
|
|
|
|
[actual, err] = await buf.peek(1);
|
|
|
|
assertEqual(decoder.decode(actual), "");
|
|
|
|
assert(err == "EOF");
|
|
|
|
/* TODO
|
|
|
|
// Test for issue 3022, not exposing a reader's error on a successful Peek.
|
|
|
|
buf = NewReaderSize(dataAndEOFReader("abcd"), 32)
|
|
|
|
if s, err := buf.Peek(2); string(s) != "ab" || err != nil {
|
|
|
|
t.Errorf(`Peek(2) on "abcd", EOF = %q, %v; want "ab", nil`, string(s), err)
|
|
|
|
}
|
|
|
|
if s, err := buf.Peek(4); string(s) != "abcd" || err != nil {
|
|
|
|
t.Errorf(`Peek(4) on "abcd", EOF = %q, %v; want "abcd", nil`, string(s), err)
|
|
|
|
}
|
|
|
|
if n, err := buf.Read(p[0:5]); string(p[0:n]) != "abcd" || err != nil {
|
|
|
|
t.Fatalf("Read after peek = %q, %v; want abcd, EOF", p[0:n], err)
|
|
|
|
}
|
|
|
|
if n, err := buf.Read(p[0:1]); string(p[0:n]) != "" || err != io.EOF {
|
|
|
|
t.Fatalf(`second Read after peek = %q, %v; want "", EOF`, p[0:n], err)
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
});
|