Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

io: change Reader interface #2591

Merged
merged 8 commits into from Jul 6, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 14 additions & 15 deletions js/buffer.ts
Expand Up @@ -4,8 +4,7 @@
// Copyright 2009 The Go Authors. All rights reserved. BSD license.
// https://github.com/golang/go/blob/master/LICENSE

//import * as io from "./io";
import { Reader, Writer, ReadResult, SyncReader, SyncWriter } from "./io";
import { Reader, Writer, EOF, SyncReader, SyncWriter } from "./io";
import { assert } from "./util";
import { TextDecoder } from "./text_encoding";
import { DenoError, ErrorKind } from "./errors";
Expand Down Expand Up @@ -131,22 +130,22 @@ export class Buffer implements Reader, SyncReader, Writer, SyncWriter {
* is drained. The return value n is the number of bytes read. If the
* buffer has no data to return, eof in the response will be true.
*/
readSync(p: Uint8Array): ReadResult {
readSync(p: Uint8Array): number | EOF {
if (this.empty()) {
// Buffer is empty, reset to recover space.
this.reset();
if (p.byteLength === 0) {
// this edge case is tested in 'bufferReadEmptyAtEOF' test
return { nread: 0, eof: false };
return 0;
}
return { nread: 0, eof: true };
return EOF;
}
const nread = copyBytes(p, this.buf.subarray(this.off));
this.off += nread;
return { nread, eof: false };
return nread;
}

async read(p: Uint8Array): Promise<ReadResult> {
async read(p: Uint8Array): Promise<number | EOF> {
const rr = this.readSync(p);
return Promise.resolve(rr);
}
Expand Down Expand Up @@ -226,12 +225,12 @@ export class Buffer implements Reader, SyncReader, Writer, SyncWriter {
const i = this._grow(MIN_READ);
this._reslice(i);
const fub = new Uint8Array(this.buf.buffer, i);
const { nread, eof } = await r.read(fub);
this._reslice(i + nread);
n += nread;
if (eof) {
const nread = await r.read(fub);
if (nread === EOF) {
return n;
}
this._reslice(i + nread);
n += nread;
} catch (e) {
return n;
}
Expand All @@ -247,12 +246,12 @@ export class Buffer implements Reader, SyncReader, Writer, SyncWriter {
const i = this._grow(MIN_READ);
this._reslice(i);
const fub = new Uint8Array(this.buf.buffer, i);
const { nread, eof } = r.readSync(fub);
this._reslice(i + nread);
n += nread;
if (eof) {
const nread = r.readSync(fub);
if (nread === EOF) {
return n;
}
this._reslice(i + nread);
n += nread;
} catch (e) {
return n;
}
Expand Down
10 changes: 5 additions & 5 deletions js/buffer_test.ts
Expand Up @@ -60,10 +60,10 @@ async function empty(buf: Buffer, s: string, fub: Uint8Array): Promise<void> {
check(buf, s);
while (true) {
const r = await buf.read(fub);
if (r.nread == 0) {
if (r === Deno.EOF) {
break;
}
s = s.slice(r.nread);
s = s.slice(r);
check(buf, s);
}
check(buf, "");
Expand Down Expand Up @@ -126,8 +126,7 @@ test(async function bufferReadEmptyAtEOF(): Promise<void> {
let buf = new Buffer();
const zeroLengthTmp = new Uint8Array(0);
let result = await buf.read(zeroLengthTmp);
assertEquals(result.nread, 0);
assertEquals(result.eof, false);
assertEquals(result, 0);
});

test(async function bufferLargeByteWrites(): Promise<void> {
Expand Down Expand Up @@ -217,7 +216,8 @@ test(async function bufferTestGrow(): Promise<void> {
for (let growLen of [0, 100, 1000, 10000, 100000]) {
const buf = new Buffer(xBytes.buffer as ArrayBuffer);
// If we read, this affects buf.off, which is good to test.
const { nread } = await buf.read(tmp);
const result = await buf.read(tmp);
const nread = result === Deno.EOF ? 0 : result;
buf.grow(growLen);
const yBytes = repeat("y", growLen);
await buf.write(yBytes);
Expand Down
2 changes: 1 addition & 1 deletion js/deno.ts
Expand Up @@ -20,9 +20,9 @@ export {
OpenMode
} from "./files";
export {
EOF,
copy,
toAsyncIterator,
ReadResult,
SeekMode,
Reader,
SyncReader,
Expand Down
2 changes: 1 addition & 1 deletion js/fetch.ts
Expand Up @@ -218,7 +218,7 @@ class Body implements domTypes.Body, domTypes.ReadableStream, io.ReadCloser {
return decoder.decode(ab);
}

read(p: Uint8Array): Promise<io.ReadResult> {
read(p: Uint8Array): Promise<number | io.EOF> {
return read(this.rid, p);
}

Expand Down
29 changes: 16 additions & 13 deletions js/files.ts
@@ -1,10 +1,10 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import {
EOF,
Reader,
Writer,
Seeker,
Closer,
ReadResult,
SeekMode,
SyncReader,
SyncWriter,
Expand Down Expand Up @@ -71,47 +71,50 @@ function reqRead(
return [builder, msg.Any.Read, inner, p];
}

function resRead(baseRes: null | msg.Base): ReadResult {
function resRead(baseRes: null | msg.Base): number | EOF {
assert(baseRes != null);
assert(msg.Any.ReadRes === baseRes!.innerType());
const res = new msg.ReadRes();
assert(baseRes!.inner(res) != null);
return { nread: res.nread(), eof: res.eof() };
if (res.eof()) {
return EOF;
}
return res.nread();
}

/** Read synchronously from a file ID into an array buffer.
*
* Return `ReadResult` for the operation.
* Return `number | EOF` for the operation.
*
* const file = Deno.openSync("/foo/bar.txt");
* const buf = new Uint8Array(100);
* const { nread, eof } = Deno.readSync(file.rid, buf);
* const nread = Deno.readSync(file.rid, buf);
* const text = new TextDecoder().decode(buf);
*
*/
export function readSync(rid: number, p: Uint8Array): ReadResult {
export function readSync(rid: number, p: Uint8Array): number | EOF {
return resRead(dispatch.sendSync(...reqRead(rid, p)));
}

/** Read from a file ID into an array buffer.
*
* Resolves with the `ReadResult` for the operation.
* Resolves with the `number | EOF` for the operation.
*
* (async () => {
* const file = await Deno.open("/foo/bar.txt");
* const buf = new Uint8Array(100);
* const { nread, eof } = await Deno.read(file.rid, buf);
* const nread = await Deno.read(file.rid, buf);
* const text = new TextDecoder().decode(buf);
* })();
*/
export async function read(rid: number, p: Uint8Array): Promise<ReadResult> {
export async function read(rid: number, p: Uint8Array): Promise<number | EOF> {
const nread = await sendAsyncMinimal(OP_READ, rid, p);
if (nread < 0) {
throw new Error("read error");
} else if (nread == 0) {
return { nread, eof: true };
return EOF;
} else {
return { nread, eof: false };
return nread;
}
}

Expand Down Expand Up @@ -227,11 +230,11 @@ export class File
return writeSync(this.rid, p);
}

read(p: Uint8Array): Promise<ReadResult> {
read(p: Uint8Array): Promise<number | EOF> {
return read(this.rid, p);
}

readSync(p: Uint8Array): ReadResult {
readSync(p: Uint8Array): number | EOF {
return readSync(this.rid, p);
}

Expand Down
13 changes: 7 additions & 6 deletions js/files_test.ts
Expand Up @@ -39,15 +39,16 @@ test(async function readerToAsyncIterator(): Promise<void> {

constructor(private readonly s: string) {}

async read(p: Uint8Array): Promise<Deno.ReadResult> {
async read(p: Uint8Array): Promise<number | Deno.EOF> {
const n = Math.min(p.byteLength, this.buf.byteLength - this.offset);
p.set(this.buf.slice(this.offset, this.offset + n));
this.offset += n;

return {
nread: n,
eof: this.offset === this.buf.byteLength
};
if (n === 0) {
return Deno.EOF;
}

return n;
}
}

Expand Down Expand Up @@ -228,7 +229,7 @@ testPerm(
const buf = new Uint8Array(20);
await file.seek(0, Deno.SeekMode.SEEK_START);
const result = await file.read(buf);
assertEquals(result.nread, 13);
assertEquals(result, 13);
file.close();

await Deno.remove(tempDir, { recursive: true });
Expand Down
46 changes: 19 additions & 27 deletions js/io.ts
Expand Up @@ -3,11 +3,8 @@
// Documentation liberally lifted from them too.
// Thank you! We love Go!

// The bytes read during an I/O call and a boolean indicating EOF.
export interface ReadResult {
nread: number;
eof: boolean;
}
export const EOF: unique symbol = Symbol("EOF");
export type EOF = typeof EOF;

// Seek whence values.
// https://golang.org/pkg/io/#pkg-constants
Expand All @@ -21,36 +18,27 @@ export enum SeekMode {
// https://golang.org/pkg/io/#Reader
export interface Reader {
/** Reads up to p.byteLength bytes into `p`. It resolves to the number
* of bytes read (`0` <= `n` <= `p.byteLength`) and any error encountered.
* of bytes read (`0` < `n` <= `p.byteLength`) and rejects if any error encountered.
* Even if `read()` returns `n` < `p.byteLength`, it may use all of `p` as
* scratch space during the call. If some data is available but not
* `p.byteLength` bytes, `read()` conventionally returns what is available
* instead of waiting for more.
*
* When `read()` encounters an error or end-of-file condition after
* successfully reading `n` > `0` bytes, it returns the number of bytes read.
* It may return the (non-nil) error from the same call or return the error
* (and `n` == `0`) from a subsequent call. An instance of this general case
* is that a `Reader` returning a non-zero number of bytes at the end of the
* input stream may return either `err` == `EOF` or `err` == `null`. The next
* `read()` should return `0`, `EOF`.
* When `read()` encounters end-of-file condition, it returns EOF symbol.
*
* Callers should always process the `n` > `0` bytes returned before
* considering the `EOF`. Doing so correctly handles I/O errors that happen
* after reading some bytes and also both of the allowed `EOF` behaviors.
* When `read()` encounters an error, it rejects with an error.
*
* Implementations of `read()` are discouraged from returning a zero byte
* count with a `null` error, except when `p.byteLength` == `0`. Callers
* should treat a return of `0` and `null` as indicating that nothing
* happened; in particular it does not indicate `EOF`.
* Callers should always process the `n` > `0` bytes returned before
* considering the EOF. Doing so correctly handles I/O errors that happen
* after reading some bytes and also both of the allowed EOF behaviors.
*
* Implementations must not retain `p`.
*/
read(p: Uint8Array): Promise<ReadResult>;
read(p: Uint8Array): Promise<number | EOF>;
}

export interface SyncReader {
readSync(p: Uint8Array): ReadResult;
readSync(p: Uint8Array): number | EOF;
}

// Writer is the interface that wraps the basic write() method.
Expand Down Expand Up @@ -128,10 +116,11 @@ export async function copy(dst: Writer, src: Reader): Promise<number> {
let gotEOF = false;
while (gotEOF === false) {
const result = await src.read(b);
if (result.eof) {
if (result === EOF) {
gotEOF = true;
} else {
n += await dst.write(b.subarray(0, result));
}
n += await dst.write(b.subarray(0, result.nread));
}
return n;
}
Expand All @@ -146,7 +135,7 @@ export function toAsyncIterator(r: Reader): AsyncIterableIterator<Uint8Array> {
const b = new Uint8Array(1024);
// Keep track if end-of-file has been reached, then
// signal that iterator is done during subsequent next()
// call. This is required because `r` can return a `ReadResult`
// call. This is required because `r` can return a `number | EOF`
// with data read and EOF reached. But if iterator returns
// `done` then `value` is discarded.
//
Expand All @@ -164,10 +153,13 @@ export function toAsyncIterator(r: Reader): AsyncIterableIterator<Uint8Array> {
}

const result = await r.read(b);
sawEof = result.eof;
if (result === EOF) {
sawEof = true;
return { value: new Uint8Array(), done: true };
}

return {
value: b.subarray(0, result.nread),
value: b.subarray(0, result),
done: false
};
}
Expand Down
4 changes: 2 additions & 2 deletions js/net.ts
@@ -1,5 +1,5 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { ReadResult, Reader, Writer, Closer } from "./io";
import { EOF, Reader, Writer, Closer } from "./io";
import * as msg from "gen/cli/msg_generated";
import { assert, notImplemented } from "./util";
import * as dispatch from "./dispatch";
Expand Down Expand Up @@ -55,7 +55,7 @@ class ConnImpl implements Conn {
return write(this.rid, p);
}

read(p: Uint8Array): Promise<ReadResult> {
read(p: Uint8Array): Promise<number | EOF> {
return read(this.rid, p);
}

Expand Down