summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bufio.ts55
-rw-r--r--bufio_test.ts34
-rw-r--r--iotest.ts61
3 files changed, 132 insertions, 18 deletions
diff --git a/bufio.ts b/bufio.ts
index 805a8136e..c1fcf9c47 100644
--- a/bufio.ts
+++ b/bufio.ts
@@ -24,6 +24,7 @@ export class Reader implements deno.Reader {
private w = 0; // buf write position.
private lastByte: number;
private lastCharSize: number;
+ private err: null | Error;
constructor(rd: deno.Reader, size = DEFAULT_BUF_SIZE) {
if (size < MIN_BUF_SIZE) {
@@ -37,9 +38,15 @@ export class Reader implements deno.Reader {
return this.buf.byteLength;
}
+ private _readErr(): Error {
+ const err = this.err;
+ this.err = null;
+ return err;
+ }
+
// Reads a new chunk into the buffer.
// Returns true if EOF, false on successful read.
- async _fill(): Promise<boolean> {
+ private async _fill(): Promise<boolean> {
// Slide existing data to beginning.
if (this.r > 0) {
this.buf.copyWithin(0, this.r, this.w);
@@ -53,15 +60,21 @@ export class Reader implements deno.Reader {
// Read new data: try a limited number of times.
for (let i = MAX_CONSECUTIVE_EMPTY_READS; i > 0; i--) {
- const { nread, eof } = await this.rd.read(this.buf.subarray(this.w));
- if (nread < 0) {
+ let rr: deno.ReadResult;
+ try {
+ rr = await this.rd.read(this.buf.subarray(this.w));
+ } catch (e) {
+ this.err = e;
+ return false;
+ }
+ if (rr.nread < 0) {
throw new ErrNegativeRead();
}
- this.w += nread;
- if (eof) {
+ this.w += rr.nread;
+ if (rr.eof) {
return true;
}
- if (nread > 0) {
+ if (rr.nread > 0) {
return false;
}
}
@@ -92,15 +105,16 @@ export class Reader implements deno.Reader {
async read(p: ArrayBufferView): Promise<deno.ReadResult> {
let rr: deno.ReadResult = { nread: p.byteLength, eof: false };
if (rr.nread === 0) {
+ if (this.err) {
+ throw this._readErr();
+ }
return rr;
}
if (this.r === this.w) {
- /*
- if (this.err != null) {
- throw this.readErr();
+ if (this.err) {
+ throw this._readErr();
}
- */
if (p.byteLength >= this.buf.byteLength) {
// Large read, empty buffer.
// Read directly into p to avoid copy.
@@ -112,6 +126,9 @@ export class Reader implements deno.Reader {
this.lastByte = p[rr.nread - 1];
// this.lastRuneSize = -1;
}
+ if (this.err) {
+ throw this._readErr();
+ }
return rr;
}
// One read.
@@ -123,6 +140,9 @@ export class Reader implements deno.Reader {
throw new ErrNegativeRead();
}
if (rr.nread === 0) {
+ if (this.err) {
+ throw this._readErr();
+ }
return rr;
}
this.w += rr.nread;
@@ -140,6 +160,9 @@ export class Reader implements deno.Reader {
async readByte(): Promise<number> {
while (this.r === this.w) {
const eof = await this._fill(); // buffer is empty.
+ if (this.err != null) {
+ throw this._readErr();
+ }
if (eof) {
return -1;
}
@@ -149,4 +172,16 @@ export class Reader implements deno.Reader {
this.lastByte = c;
return c;
}
+
+ /** readString() reads until the first occurrence of delim in the input,
+ * returning a string containing the data up to and including the delimiter.
+ * If ReadString encounters an error before finding a delimiter,
+ * it returns the data read before the error and the error itself (often io.EOF).
+ * ReadString returns err != nil if and only if the returned data does not end in
+ * delim.
+ * For simple uses, a Scanner may be more convenient.
+ */
+ async readString(delim: string): Promise<string> {
+ throw new Error("Not implemented");
+ }
}
diff --git a/bufio_test.ts b/bufio_test.ts
index 1600852af..dc16f5e69 100644
--- a/bufio_test.ts
+++ b/bufio_test.ts
@@ -7,6 +7,7 @@ import * as deno from "deno";
import { test, assertEqual } from "http://deno.land/x/testing/testing.ts";
import * as bufio from "./bufio.ts";
import { Buffer } from "./buffer.ts";
+import * as iotest from "./iotest.ts";
async function readBytes(buf: bufio.Reader): Promise<string> {
const b = new Uint8Array(1000);
@@ -39,15 +40,25 @@ test(async function bufioReaderSimple() {
type ReadMaker = { name: string; fn: (r: deno.Reader) => deno.Reader };
const readMakers: ReadMaker[] = [
- { name: "full", fn: r => r }
- /*
- { name: "byte", fn(r) => new iotest.OneByteReader(r) },
- { name: "half", fn(r) => new iotest.HalfReader(r) },
- { name: "data+err", r => new iotest.DataErrReader(r) },
- { name: "timeout", r => new iotest.TimeoutReader(r) },
- */
+ { name: "full", fn: r => r },
+ { name: "byte", fn: r => new iotest.OneByteReader(r) },
+ { name: "half", fn: r => new iotest.HalfReader(r) }
+ // TODO { name: "data+err", r => new iotest.DataErrReader(r) },
+ // { name: "timeout", fn: r => new iotest.TimeoutReader(r) },
];
+function readLines(b: bufio.Reader): string {
+ let s = "";
+ while (true) {
+ let s1 = b.readString("\n");
+ if (s1 == null) {
+ break; // EOF
+ }
+ s += s1;
+ }
+ return s;
+}
+
// Call read to accumulate the text of a file
async function reads(buf: bufio.Reader, m: number): Promise<string> {
const b = new Uint8Array(1000);
@@ -66,7 +77,14 @@ async function reads(buf: bufio.Reader, m: number): Promise<string> {
type BufReader = { name: string; fn: (r: bufio.Reader) => Promise<string> };
const bufreaders: BufReader[] = [
- { name: "1", fn: (b: bufio.Reader) => reads(b, 1) }
+ { name: "1", fn: (b: bufio.Reader) => reads(b, 1) },
+ { name: "2", fn: (b: bufio.Reader) => reads(b, 2) },
+ { name: "3", fn: (b: bufio.Reader) => reads(b, 3) },
+ { name: "4", fn: (b: bufio.Reader) => reads(b, 4) },
+ { name: "5", fn: (b: bufio.Reader) => reads(b, 5) },
+ { name: "7", fn: (b: bufio.Reader) => reads(b, 7) },
+ { name: "bytes", fn: readBytes }
+ // { name: "lines", fn: readLines },
];
const MIN_READ_BUFFER_SIZE = 16;
diff --git a/iotest.ts b/iotest.ts
new file mode 100644
index 000000000..e2498e155
--- /dev/null
+++ b/iotest.ts
@@ -0,0 +1,61 @@
+// 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.
+
+import { Reader, ReadResult } from "deno";
+
+/** OneByteReader returns a Reader that implements
+ * each non-empty Read by reading one byte from r.
+ */
+export class OneByteReader implements Reader {
+ constructor(readonly r: Reader) {}
+
+ async read(p: ArrayBufferView): Promise<ReadResult> {
+ if (p.byteLength === 0) {
+ return { nread: 0, eof: false };
+ }
+ if (!(p instanceof Uint8Array)) {
+ throw Error("expected Uint8Array");
+ }
+ return this.r.read(p.subarray(0, 1));
+ }
+}
+
+/** HalfReader returns a Reader that implements Read
+ * by reading half as many requested bytes from r.
+ */
+export class HalfReader implements Reader {
+ constructor(readonly r: Reader) {}
+
+ async read(p: ArrayBufferView): Promise<ReadResult> {
+ if (!(p instanceof Uint8Array)) {
+ throw Error("expected Uint8Array");
+ }
+ const half = Math.floor((p.byteLength + 1) / 2);
+ return this.r.read(p.subarray(0, half));
+ }
+}
+
+export class ErrTimeout extends Error {
+ constructor() {
+ super("timeout");
+ this.name = "ErrTimeout";
+ }
+}
+
+/** TimeoutReader returns ErrTimeout on the second read
+ * with no data. Subsequent calls to read succeed.
+ */
+export class TimeoutReader implements Reader {
+ count = 0;
+ constructor(readonly r: Reader) {}
+
+ async read(p: ArrayBufferView): Promise<ReadResult> {
+ this.count++;
+ if (this.count === 2) {
+ throw new ErrTimeout();
+ }
+ return this.r.read(p);
+ }
+}