From 5abec6efc5028bb281c29f5df40c0d87a4b8ebeb Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Mon, 17 Dec 2018 22:40:42 -0500 Subject: [PATCH] Remove buffer.ts and use the one built in to deno. --- buffer.ts | 217 ---------------------------------------------- buffer_test.ts | 189 ---------------------------------------- bufio_test.ts | 5 +- http_test.ts | 2 +- test.ts | 1 - textproto_test.ts | 2 +- util.ts | 8 +- 7 files changed, 11 insertions(+), 413 deletions(-) delete mode 100644 buffer.ts delete mode 100644 buffer_test.ts diff --git a/buffer.ts b/buffer.ts deleted file mode 100644 index d177bc18dc..0000000000 --- a/buffer.ts +++ /dev/null @@ -1,217 +0,0 @@ -// This code has been ported almost directly from Go's src/bytes/buffer.go -// 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 } from "deno"; -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 -// what is required to hold the contents of r, readFrom() will not grow the -// underlying buffer. -const MIN_READ = 512; -const MAX_SIZE = 2 ** 32 - 2; - -const encoder = new TextEncoder(); -export function stringsReader(s: string): Reader { - const ui8 = encoder.encode(s); - return new Buffer(ui8.buffer as ArrayBuffer); -} - -/** A Buffer is a variable-sized buffer of bytes with read() and write() - * methods. Based on https://golang.org/pkg/bytes/#Buffer - */ -export class Buffer implements Reader, Writer { - private buf: Uint8Array; // contents are the bytes buf[off : len(buf)] - private off = 0; // read at buf[off], write at buf[buf.byteLength] - - constructor(ab?: ArrayBuffer) { - if (ab == null) { - this.buf = new Uint8Array(0); - } else { - this.buf = new Uint8Array(ab); - } - } - - /** bytes() returns a slice holding the unread portion of the buffer. - * The slice is valid for use only until the next buffer modification (that - * is, only until the next call to a method like read(), write(), reset(), or - * truncate()). The slice aliases the buffer content at least until the next - * buffer modification, so immediate changes to the slice will affect the - * result of future reads. - */ - bytes(): Uint8Array { - return this.buf.subarray(this.off); - } - - /** toString() returns the contents of the unread portion of the buffer - * as a string. Warning - if multibyte characters are present when data is - * flowing through the buffer, this method may result in incorrect strings - * due to a character being split. - */ - toString(): string { - const decoder = new TextDecoder(); - return decoder.decode(this.buf.subarray(this.off)); - } - - /** empty() returns whether the unread portion of the buffer is empty. */ - empty() { - return this.buf.byteLength <= this.off; - } - - /** length is a getter that returns the number of bytes of the unread - * portion of the buffer - */ - get length() { - return this.buf.byteLength - this.off; - } - - /** Returns the capacity of the buffer's underlying byte slice, that is, - * the total space allocated for the buffer's data. - */ - get capacity(): number { - return this.buf.buffer.byteLength; - } - - /** truncate() discards all but the first n unread bytes from the buffer but - * continues to use the same allocated storage. It throws if n is negative or - * greater than the length of the buffer. - */ - truncate(n: number): void { - if (n === 0) { - this.reset(); - return; - } - if (n < 0 || n > this.length) { - throw Error("bytes.Buffer: truncation out of range"); - } - this._reslice(this.off + n); - } - - /** reset() resets the buffer to be empty, but it retains the underlying - * storage for use by future writes. reset() is the same as truncate(0) - */ - reset(): void { - this._reslice(0); - this.off = 0; - } - - /** _tryGrowByReslice() is a version of grow for the fast-case - * where the internal buffer only needs to be resliced. It returns the index - * where bytes should be written and whether it succeeded. - * It returns -1 if a reslice was not needed. - */ - private _tryGrowByReslice(n: number): number { - const l = this.buf.byteLength; - if (n <= this.capacity - l) { - this._reslice(l + n); - return l; - } - return -1; - } - - private _reslice(len: number): void { - assert(len <= this.buf.buffer.byteLength); - this.buf = new Uint8Array(this.buf.buffer, 0, len); - } - - /** read() reads the next len(p) bytes from the buffer or until the buffer - * 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. - */ - async read(p: Uint8Array): Promise { - if (this.empty()) { - // Buffer is empty, reset to recover space. - this.reset(); - if (p.byteLength === 0) { - // TODO This edge case should be tested by porting TestReadEmptyAtEOF - // from the Go tests. - return { nread: 0, eof: false }; - } - return { nread: 0, eof: true }; - } - const nread = copyBytes(p, this.buf.subarray(this.off)); - this.off += nread; - return { nread, eof: false }; - } - - async write(p: Uint8Array): Promise { - const m = this._grow(p.byteLength); - return copyBytes(this.buf, p, m); - } - - /** _grow() grows the buffer to guarantee space for n more bytes. - * It returns the index where bytes should be written. - * If the buffer can't grow it will throw with ErrTooLarge. - */ - private _grow(n: number): number { - const m = this.length; - // If buffer is empty, reset to recover space. - if (m === 0 && this.off !== 0) { - this.reset(); - } - // Fast: Try to grow by means of a reslice. - const i = this._tryGrowByReslice(n); - if (i >= 0) { - return i; - } - const c = this.capacity; - if (n <= Math.floor(c / 2) - m) { - // We can slide things down instead of allocating a new - // ArrayBuffer. We only need m+n <= c to slide, but - // we instead let capacity get twice as large so we - // don't spend all our time copying. - copyBytes(this.buf, this.buf.subarray(this.off)); - } else if (c > MAX_SIZE - c - n) { - throw Error("ErrTooLarge"); // TODO DenoError(TooLarge) - } else { - // Not enough space anywhere, we need to allocate. - const buf = new Uint8Array(2 * c + n); - copyBytes(buf, this.buf.subarray(this.off)); - this.buf = buf; - } - // Restore this.off and len(this.buf). - this.off = 0; - this._reslice(m + n); - return m; - } - - /** grow() grows the buffer's capacity, if necessary, to guarantee space for - * another n bytes. After grow(n), at least n bytes can be written to the - * buffer without another allocation. If n is negative, grow() will panic. If - * the buffer can't grow it will throw ErrTooLarge. - * Based on https://golang.org/pkg/bytes/#Buffer.Grow - */ - grow(n: number): void { - if (n < 0) { - throw Error("Buffer.grow: negative count"); - } - const m = this._grow(n); - this._reslice(m); - } - - /** readFrom() reads data from r until EOF and appends it to the buffer, - * growing the buffer as needed. It returns the number of bytes read. If the - * buffer becomes too large, readFrom will panic with ErrTooLarge. - * Based on https://golang.org/pkg/bytes/#Buffer.ReadFrom - */ - async readFrom(r: Reader): Promise { - let n = 0; - while (true) { - try { - 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) { - return n; - } - } catch (e) { - return n; - } - } - } -} diff --git a/buffer_test.ts b/buffer_test.ts deleted file mode 100644 index 9a71e80a33..0000000000 --- a/buffer_test.ts +++ /dev/null @@ -1,189 +0,0 @@ -// 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 "https://deno.land/x/testing/testing.ts"; -import { Buffer } from "./buffer.ts"; - -// N controls how many iterations of certain checks are performed. -const N = 100; -let testBytes: Uint8Array | null; -let testString: string | null; - -function init() { - if (testBytes == null) { - testBytes = new Uint8Array(N); - for (let i = 0; i < N; i++) { - testBytes[i] = "a".charCodeAt(0) + i % 26; - } - const decoder = new TextDecoder(); - testString = decoder.decode(testBytes); - } -} - -function check(buf: Buffer, s: string) { - const bytes = buf.bytes(); - assertEqual(buf.length, bytes.byteLength); - const decoder = new TextDecoder(); - const bytesStr = decoder.decode(bytes); - assertEqual(bytesStr, s); - assertEqual(buf.length, buf.toString().length); - assertEqual(buf.length, s.length); -} - -// Fill buf through n writes of byte slice fub. -// The initial contents of buf corresponds to the string s; -// the result is the final contents of buf returned as a string. -async function fillBytes( - buf: Buffer, - s: string, - n: number, - fub: Uint8Array -): Promise { - check(buf, s); - for (; n > 0; n--) { - let m = await buf.write(fub); - assertEqual(m, fub.byteLength); - const decoder = new TextDecoder(); - s += decoder.decode(fub); - check(buf, s); - } - return s; -} - -// Empty buf through repeated reads into fub. -// The initial contents of buf corresponds to the string s. -async function empty(buf: Buffer, s: string, fub: Uint8Array): Promise { - check(buf, s); - while (true) { - const r = await buf.read(fub); - if (r.nread == 0) { - break; - } - s = s.slice(r.nread); - check(buf, s); - } - check(buf, ""); -} - -test(function bufferNewBuffer() { - init(); - const buf = new Buffer(testBytes.buffer as ArrayBuffer); - check(buf, testString); -}); - -test(async function bufferBasicOperations() { - init(); - let buf = new Buffer(); - for (let i = 0; i < 5; i++) { - check(buf, ""); - - buf.reset(); - check(buf, ""); - - buf.truncate(0); - check(buf, ""); - - let n = await buf.write(testBytes.subarray(0, 1)); - assertEqual(n, 1); - check(buf, "a"); - - n = await buf.write(testBytes.subarray(1, 2)); - assertEqual(n, 1); - check(buf, "ab"); - - n = await buf.write(testBytes.subarray(2, 26)); - assertEqual(n, 24); - check(buf, testString.slice(0, 26)); - - buf.truncate(26); - check(buf, testString.slice(0, 26)); - - buf.truncate(20); - check(buf, testString.slice(0, 20)); - - await empty(buf, testString.slice(0, 20), new Uint8Array(5)); - await empty(buf, "", new Uint8Array(100)); - - // TODO buf.writeByte() - // TODO buf.readByte() - } -}); - -test(async function bufferLargeByteWrites() { - init(); - const buf = new Buffer(); - const limit = 9; - for (let i = 3; i < limit; i += 3) { - const s = await fillBytes(buf, "", 5, testBytes); - await empty(buf, s, new Uint8Array(Math.floor(testString.length / i))); - } - check(buf, ""); -}); - -test(async function bufferLargeByteReads() { - init(); - const buf = new Buffer(); - for (let i = 3; i < 30; i += 3) { - const n = Math.floor(testBytes.byteLength / i); - const s = await fillBytes(buf, "", 5, testBytes.subarray(0, n)); - await empty(buf, s, new Uint8Array(testString.length)); - } - check(buf, ""); -}); - -test(function bufferCapWithPreallocatedSlice() { - const buf = new Buffer(new ArrayBuffer(10)); - assertEqual(buf.capacity, 10); -}); - -test(async function bufferReadFrom() { - init(); - const buf = new Buffer(); - for (let i = 3; i < 30; i += 3) { - const s = await fillBytes( - buf, - "", - 5, - testBytes.subarray(0, Math.floor(testBytes.byteLength / i)) - ); - const b = new Buffer(); - await b.readFrom(buf); - const fub = new Uint8Array(testString.length); - await empty(b, s, fub); - } -}); - -function repeat(c: string, bytes: number): Uint8Array { - assertEqual(c.length, 1); - const ui8 = new Uint8Array(bytes); - ui8.fill(c.charCodeAt(0)); - return ui8; -} - -test(async function bufferTestGrow() { - const tmp = new Uint8Array(72); - for (let startLen of [0, 100, 1000, 10000, 100000]) { - const xBytes = repeat("x", startLen); - 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, eof } = await buf.read(tmp); - buf.grow(growLen); - const yBytes = repeat("y", growLen); - await buf.write(yBytes); - // Check that buffer has correct data. - assertEqual( - buf.bytes().subarray(0, startLen - nread), - xBytes.subarray(nread) - ); - assertEqual( - buf.bytes().subarray(startLen - nread, startLen - nread + growLen), - yBytes - ); - } - } -}); diff --git a/bufio_test.ts b/bufio_test.ts index 5f32500a7a..839e613882 100644 --- a/bufio_test.ts +++ b/bufio_test.ts @@ -3,16 +3,15 @@ // 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"; +import { Buffer, Reader, ReadResult } from "deno"; import { test, assert, assertEqual } from "https://deno.land/x/testing/testing.ts"; import { BufReader, BufState, BufWriter } from "./bufio.ts"; -import { Buffer, stringsReader } from "./buffer.ts"; import * as iotest from "./iotest.ts"; -import { charCode, copyBytes } from "./util.ts"; +import { charCode, copyBytes, stringsReader } from "./util.ts"; const encoder = new TextEncoder(); diff --git a/http_test.ts b/http_test.ts index 879afbf532..cdb7f8303e 100644 --- a/http_test.ts +++ b/http_test.ts @@ -17,7 +17,7 @@ import { setContentLength, Response } from "./http"; -import { Buffer } from "./buffer"; +import { Buffer } from "deno"; import { BufWriter } from "./bufio"; interface ResponseTest { diff --git a/test.ts b/test.ts index 8cb0a2ca2e..c298b7aab1 100644 --- a/test.ts +++ b/test.ts @@ -1,6 +1,5 @@ import { run } from "deno"; -import "./buffer_test.ts"; import "./bufio_test.ts"; import "./http_test.ts"; import "./textproto_test.ts"; diff --git a/textproto_test.ts b/textproto_test.ts index 32311a4688..57ee99459f 100644 --- a/textproto_test.ts +++ b/textproto_test.ts @@ -5,7 +5,7 @@ import { BufReader } from "./bufio.ts"; import { TextProtoReader } from "./textproto.ts"; -import { stringsReader } from "./buffer.ts"; +import { stringsReader } from "./util.ts"; import { test, assert, diff --git a/util.ts b/util.ts index 6a230ef3b1..811940b4d7 100644 --- a/util.ts +++ b/util.ts @@ -1,4 +1,4 @@ -import { Reader } from "deno"; +import { Buffer, Reader } from "deno"; export function assert(cond: boolean, msg = "assert") { if (!cond) { @@ -21,3 +21,9 @@ export function copyBytes(dst: Uint8Array, src: Uint8Array, off = 0): number { export function charCode(s: string): number { return s.charCodeAt(0); } + +const encoder = new TextEncoder(); +export function stringsReader(s: string): Reader { + const ui8 = encoder.encode(s); + return new Buffer(ui8.buffer as ArrayBuffer); +}