First pass at bufio.read tests.

This commit is contained in:
Ryan Dahl 2018-11-07 14:17:36 -05:00
parent c5cc695970
commit 1eb57aa394
7 changed files with 170 additions and 28 deletions

View File

@ -4,7 +4,7 @@
//import * as io from "./io";
import { Reader, Writer, ReadResult } from "deno";
import { assert } from "./util.ts";
import { assert, copyBytes } from "./util.ts";
// MIN_READ is the minimum ArrayBuffer size passed to a read call by
// buffer.ReadFrom. As long as the Buffer has at least MIN_READ bytes beyond
@ -13,18 +13,6 @@ import { assert } from "./util.ts";
const MIN_READ = 512;
const MAX_SIZE = 2 ** 32 - 2;
// `off` is the offset into `dst` where it will at which to begin writing values
// from `src`.
// Returns the number of bytes copied.
function copyBytes(dst: Uint8Array, src: Uint8Array, off = 0): number {
const r = dst.byteLength - off;
if (src.byteLength > r) {
src = src.subarray(0, r);
}
dst.set(src, off);
return src.byteLength;
}
/** A Buffer is a variable-sized buffer of bytes with read() and write()
* methods. Based on https://golang.org/pkg/bytes/#Buffer
*/

View File

@ -1,8 +1,12 @@
// This code has been ported almost directly from Go's src/bytes/buffer_test.go
// Copyright 2009 The Go Authors. All rights reserved. BSD license.
// https://github.com/golang/go/blob/master/LICENSE
import { test, assert, assertEqual } from "./test_util.ts";
import { Buffer } from "deno";
import {
test,
assert,
assertEqual
} from "http://deno.land/x/testing/testing.ts";
import { Buffer } from "./buffer.ts";
// N controls how many iterations of certain checks are performed.
const N = 100;
@ -13,7 +17,7 @@ function init() {
if (testBytes == null) {
testBytes = new Uint8Array(N);
for (let i = 0; i < N; i++) {
testBytes[i] = "a".charCodeAt(0) + (i % 26);
testBytes[i] = "a".charCodeAt(0) + i % 26;
}
const decoder = new TextDecoder();
testString = decoder.decode(testBytes);

View File

@ -1,9 +1,22 @@
// 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 * as deno from "deno";
import { assert, copyBytes } from "./util.ts";
const DEFAULT_BUF_SIZE = 4096;
const MIN_BUF_SIZE = 16;
const MAX_CONSECUTIVE_EMPTY_READS = 100;
export class ErrNegativeRead extends Error {
constructor() {
super("bufio: reader returned negative count from Read");
this.name = "ErrNegativeRead";
}
}
export class Reader implements deno.Reader {
private buf: Uint8Array;
private rd: deno.Reader; // Reader provided by caller.
@ -16,7 +29,7 @@ export class Reader implements deno.Reader {
if (size < MIN_BUF_SIZE) {
size = MIN_BUF_SIZE;
}
this._reset(new Uint8Array(size), rd)
this._reset(new Uint8Array(size), rd);
}
/** Returns the size of the underlying buffer in bytes. */
@ -42,7 +55,7 @@ export class Reader implements deno.Reader {
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) {
throw Error("negative read");
throw new ErrNegativeRead();
}
this.w += nread;
if (eof) {
@ -69,9 +82,58 @@ export class Reader implements deno.Reader {
this.lastCharSize = -1;
}
/** reads data into p.
* It returns the number of bytes read into p.
* The bytes are taken from at most one Read on the underlying Reader,
* hence n may be less than len(p).
* At EOF, the count will be zero and err will be io.EOF.
* To read exactly len(p) bytes, use io.ReadFull(b, p).
*/
async read(p: ArrayBufferView): Promise<deno.ReadResult> {
throw Error("not implemented");
return { nread: 0, eof: false };
let rr: deno.ReadResult = { nread: p.byteLength, eof: false };
if (rr.nread === 0) {
return rr;
}
if (this.r === this.w) {
/*
if (this.err != null) {
throw this.readErr();
}
*/
if (p.byteLength >= this.buf.byteLength) {
// Large read, empty buffer.
// Read directly into p to avoid copy.
rr = await this.rd.read(p);
if (rr.nread < 0) {
throw new ErrNegativeRead();
}
if (rr.nread > 0) {
this.lastByte = p[rr.nread - 1];
// this.lastRuneSize = -1;
}
return rr;
}
// One read.
// Do not use this.fill, which will loop.
this.r = 0;
this.w = 0;
rr = await this.rd.read(this.buf);
if (rr.nread < 0) {
throw new ErrNegativeRead();
}
if (rr.nread === 0) {
return rr;
}
this.w += rr.nread;
}
// copy as much as we can
rr.nread = copyBytes(p as Uint8Array, this.buf.subarray(this.r, this.w), 0);
this.r += rr.nread;
this.lastByte = this.buf[this.r - 1];
// this.lastRuneSize = -1;
return rr;
}
/** Returns the next byte [0, 255] or -1 if EOF. */
@ -88,5 +150,3 @@ export class Reader implements deno.Reader {
return c;
}
}

View File

@ -1,3 +1,8 @@
// 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 * as deno from "deno";
import { test, assertEqual } from "http://deno.land/x/testing/testing.ts";
import * as bufio from "./bufio.ts";
@ -30,3 +35,78 @@ test(async function bufioReaderSimple() {
const s = await readBytes(b);
assertEqual(s, data);
});
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) },
*/
];
// Call read to accumulate the text of a file
async function reads(buf: bufio.Reader, m: number): Promise<string> {
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));
}
type BufReader = { name: string; fn: (r: bufio.Reader) => Promise<string> };
const bufreaders: BufReader[] = [
{ name: "1", fn: (b: bufio.Reader) => reads(b, 1) }
];
const MIN_READ_BUFFER_SIZE = 16;
const bufsizes: number[] = [
0,
MIN_READ_BUFFER_SIZE,
23,
32,
46,
64,
93,
128,
1024,
4096
];
test(async function bufioReader() {
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));
const buf = new bufio.Reader(read, bufsize);
const s = await bufreader.fn(buf);
const debugStr =
`reader=${readmaker.name} ` +
`fn=${bufreader.name} bufsize=${bufsize} want=${text} got=${s}`;
assertEqual(s, text, debugStr);
}
}
}
}
});

View File

@ -4,12 +4,12 @@ import { open, cwd } from "deno";
const addr = "0.0.0.0:4500";
const d = cwd();
listenAndServe(addr, async (req) => {
listenAndServe(addr, async req => {
const filename = d + "/" + req.url;
let res;
try {
res = { status: 200, body: open(filename) };
} catch(e) {
} catch (e) {
res = { status: 500, body: "bad" };
}
req.respond(res);

View File

@ -55,12 +55,12 @@ function readRequest(b: bufio.Reader): ServerRequest {
// First line: GET /index.html HTTP/1.0
const s = await tp.readLine();
const [ method, url, proto ] = parseRequestLine(s);
const [method, url, proto] = parseRequestLine(s);
console.log("readRequest", method, url);
}
// Returns [method, url, proto]
function parseRequestLine(line: string): [ string, string, string ] {
function parseRequestLine(line: string): [string, string, string] {
return line.split(" ", 3);
}
@ -69,4 +69,3 @@ export function listen(addr: string): Server {
const s = new Server(listener);
return s;
}

13
util.ts
View File

@ -1,6 +1,17 @@
export function assert(cond: boolean, msg = "assert") {
if (!cond) {
throw Error(msg);
}
}
// `off` is the offset into `dst` where it will at which to begin writing values
// from `src`.
// Returns the number of bytes copied.
export function copyBytes(dst: Uint8Array, src: Uint8Array, off = 0): number {
const r = dst.byteLength - off;
if (src.byteLength > r) {
src = src.subarray(0, r);
}
dst.set(src, off);
return src.byteLength;
}