diff --git a/archive/_common.ts b/archive/_common.ts index 069d1411b841..8fdf9ceadf56 100644 --- a/archive/_common.ts +++ b/archive/_common.ts @@ -1,6 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { PartialReadError } from "@std/io/buf-reader"; import type { Reader } from "@std/io/types"; /** @@ -173,6 +172,20 @@ export const USTAR_STRUCTURE = [ */ export type UstarFields = (typeof USTAR_STRUCTURE)[number]["field"]; +/** + * Thrown when a read from a stream fails to read the + * requested number of bytes. + */ +class PartialReadError extends Error { + partial: Uint8Array; + + constructor(partial: Uint8Array) { + super("Encountered UnexpectedEof, data only partially read"); + this.name = this.constructor.name; + this.partial = partial; + } +} + export async function readBlock( reader: Reader, p: Uint8Array, diff --git a/io/buf_reader.ts b/io/buf_reader.ts deleted file mode 100644 index a1edecc89517..000000000000 --- a/io/buf_reader.ts +++ /dev/null @@ -1,666 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. - -import { copy } from "@std/bytes/copy"; -import type { Reader } from "./types.ts"; - -const DEFAULT_BUF_SIZE = 4096; -const MIN_BUF_SIZE = 16; -const MAX_CONSECUTIVE_EMPTY_READS = 100; -const CR = "\r".charCodeAt(0); -const LF = "\n".charCodeAt(0); - -/** - * Thrown when a write operation is attempted on a full buffer. - * - * @example Usage - * ```ts - * import { BufWriter, BufferFullError, Writer } from "@std/io"; - * import { assert, assertEquals } from "@std/assert"; - * - * const writer: Writer = { - * write(p: Uint8Array): Promise { - * throw new BufferFullError(p); - * } - * }; - * const bufWriter = new BufWriter(writer); - * try { - * await bufWriter.write(new Uint8Array([1, 2, 3])); - * } catch (err) { - * assert(err instanceof BufferFullError); - * assertEquals(err.partial, new Uint8Array([3])); - * } - * ``` - * - * @deprecated Use - * {@linkcode https://jsr.io/@std/streams/doc/buffer/~/Buffer | Buffer} instead. - * This will be removed in 0.225.0. - */ -export class BufferFullError extends Error { - /** - * The partially read bytes - * - * @example Usage - * ```ts - * import { BufferFullError } from "@std/io"; - * import { assertEquals } from "@std/assert/equals"; - * - * const err = new BufferFullError(new Uint8Array(2)); - * assertEquals(err.partial, new Uint8Array(2)); - * ``` - */ - partial: Uint8Array; - - /** - * Construct a new instance. - * - * @param partial The bytes partially read - */ - constructor(partial: Uint8Array) { - super("Buffer full"); - this.name = this.constructor.name; - this.partial = partial; - } -} - -/** - * Thrown when a read from a stream fails to read the - * requested number of bytes. - * - * @example Usage - * ```ts - * import { PartialReadError } from "@std/io"; - * import { assertEquals } from "@std/assert/equals"; - * - * const err = new PartialReadError(new Uint8Array(2)); - * assertEquals(err.name, "PartialReadError"); - * - * ``` - * - * @deprecated Use - * {@linkcode https://jsr.io/@std/streams/doc/buffer/~/Buffer | Buffer} instead. - * This will be removed in 0.225.0. - */ -export class PartialReadError extends Error { - /** - * The partially read bytes - * - * @example Usage - * ```ts - * import { PartialReadError } from "@std/io"; - * import { assertEquals } from "@std/assert/equals"; - * - * const err = new PartialReadError(new Uint8Array(2)); - * assertEquals(err.partial, new Uint8Array(2)); - * ``` - */ - partial: Uint8Array; - - /** - * Construct a {@linkcode PartialReadError}. - * - * @param partial The bytes partially read - */ - constructor(partial: Uint8Array) { - super("Encountered UnexpectedEof, data only partially read"); - this.name = this.constructor.name; - this.partial = partial; - } -} - -/** - * Result type returned by of {@linkcode BufReader.readLine}. - * - * @deprecated Use - * {@linkcode https://jsr.io/@std/streams/doc/buffer/~/Buffer | Buffer} instead. - * This will be removed in 0.225.0. - */ -export interface ReadLineResult { - /** The line read */ - line: Uint8Array; - /** `true if the end of the line has not been reached, `false` otherwise. */ - more: boolean; -} - -/** - * Implements buffering for a {@linkcode Reader} object. - * - * @example Usage - * ```ts - * import { BufReader, Buffer } from "@std/io"; - * import { assertEquals } from "@std/assert/equals"; - * - * const encoder = new TextEncoder(); - * const decoder = new TextDecoder(); - * - * const reader = new BufReader(new Buffer(encoder.encode("hello world"))); - * const buf = new Uint8Array(11); - * await reader.read(buf); - * assertEquals(decoder.decode(buf), "hello world"); - * ``` - * - * @deprecated Use - * {@linkcode https://jsr.io/@std/streams/doc/buffer/~/Buffer | Buffer} instead. - * This will be removed in 0.225.0. - */ -export class BufReader implements Reader { - #buf!: Uint8Array; - #rd!: Reader; // Reader provided by caller. - #r = 0; // buf read position. - #w = 0; // buf write position. - #eof = false; - - /** - * Returns a new {@linkcode BufReader} if `r` is not already one. - * - * @example Usage - * ```ts - * import { BufReader, Buffer } from "@std/io"; - * import { assert } from "@std/assert/assert"; - * - * const reader = new Buffer(new TextEncoder().encode("hello world")); - * const bufReader = BufReader.create(reader); - * assert(bufReader instanceof BufReader); - * ``` - * - * @param r The reader to read from. - * @param size The size of the buffer. - * @returns A new {@linkcode BufReader} if `r` is not already one. - */ - static create(r: Reader, size: number = DEFAULT_BUF_SIZE): BufReader { - return r instanceof BufReader ? r : new BufReader(r, size); - } - - /** - * Constructs a new {@linkcode BufReader} for the given reader and buffer size. - * - * @param rd The reader to read from. - * @param size The size of the buffer. - */ - constructor(rd: Reader, size: number = DEFAULT_BUF_SIZE) { - if (size < MIN_BUF_SIZE) { - size = MIN_BUF_SIZE; - } - this.#reset(new Uint8Array(size), rd); - } - - /** - * Returns the size of the underlying buffer in bytes. - * - * @example Usage - * ```ts - * import { BufReader, Buffer } from "@std/io"; - * import { assertEquals } from "@std/assert/equals"; - * - * const reader = new Buffer(new TextEncoder().encode("hello world")); - * const bufReader = new BufReader(reader); - * - * assertEquals(bufReader.size(), 4096); - * ``` - * - * @returns The size of the underlying buffer in bytes. - */ - size(): number { - return this.#buf.byteLength; - } - - /** - * Returns the number of bytes that can be read from the current buffer. - * - * @example Usage - * ```ts - * import { BufReader, Buffer } from "@std/io"; - * import { assertEquals } from "@std/assert/equals"; - * - * const reader = new Buffer(new TextEncoder().encode("hello world")); - * const bufReader = new BufReader(reader); - * await bufReader.read(new Uint8Array(5)); - * assertEquals(bufReader.buffered(), 6); - * ``` - * - * @returns Number of bytes that can be read from the buffer - */ - buffered(): number { - return this.#w - this.#r; - } - - // Reads a new chunk into the buffer. - #fill = async () => { - // Slide existing data to beginning. - if (this.#r > 0) { - this.#buf.copyWithin(0, this.#r, this.#w); - this.#w -= this.#r; - this.#r = 0; - } - - if (this.#w >= this.#buf.byteLength) { - throw new Error("Buffer full while filling"); - } - - // Read new data: try a limited number of times. - for (let i = MAX_CONSECUTIVE_EMPTY_READS; i > 0; i--) { - const rr = await this.#rd.read(this.#buf.subarray(this.#w)); - if (rr === null) { - this.#eof = true; - return; - } - this.#w += rr; - if (rr > 0) { - return; - } - } - - throw new Error( - `No progress after ${MAX_CONSECUTIVE_EMPTY_READS} read() calls`, - ); - }; - - /** - * Discards any buffered data, resets all state, and switches - * the buffered reader to read from `r`. - * - * @example Usage - * ```ts - * import { BufReader, Buffer } from "@std/io"; - * import { assertEquals } from "@std/assert/equals"; - * - * const reader = new Buffer(new TextEncoder().encode("hello world")); - * const bufReader = new BufReader(reader); - * await bufReader.read(new Uint8Array(5)); - * bufReader.reset(reader); - * assertEquals(bufReader.buffered(), 6); - * ``` - * - * @param r The reader to read from. - */ - reset(r: Reader) { - this.#reset(this.#buf, r); - } - - #reset = (buf: Uint8Array, rd: Reader) => { - this.#buf = buf; - this.#rd = rd; - this.#eof = false; - // this.lastByte = -1; - // this.lastCharSize = -1; - }; - - /** - * Reads data into `p`. - * - * The bytes are taken from at most one `read()` on the underlying `Reader`, - * hence n may be less than `len(p)`. - * To read exactly `len(p)` bytes, use `io.ReadFull(b, p)`. - * - * @example Usage - * ```ts - * import { BufReader, Buffer } from "@std/io"; - * import { assertEquals } from "@std/assert/equals"; - * - * const reader = new Buffer(new TextEncoder().encode("hello world")); - * const bufReader = new BufReader(reader); - * const buf = new Uint8Array(5); - * await bufReader.read(buf); - * assertEquals(new TextDecoder().decode(buf), "hello"); - * ``` - * - * @param p The buffer to read data into. - * @returns The number of bytes read into `p`. - */ - async read(p: Uint8Array): Promise { - let rr: number | null = p.byteLength; - if (p.byteLength === 0) return rr; - - if (this.#r === this.#w) { - if (p.byteLength >= this.#buf.byteLength) { - // Large read, empty buffer. - // Read directly into p to avoid copy. - const rr = await this.#rd.read(p); - // if (rr.nread > 0) { - // this.lastByte = p[rr.nread - 1]; - // this.lastCharSize = -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 === 0 || rr === null) return rr; - this.#w += rr; - } - - // copy as much as we can - const copied = copy(this.#buf.subarray(this.#r, this.#w), p, 0); - this.#r += copied; - // this.lastByte = this.buf[this.r - 1]; - // this.lastCharSize = -1; - return copied; - } - - /** - * Reads exactly `p.length` bytes into `p`. - * - * If successful, `p` is returned. - * - * If the end of the underlying stream has been reached, and there are no more - * bytes available in the buffer, `readFull()` returns `null` instead. - * - * An error is thrown if some bytes could be read, but not enough to fill `p` - * entirely before the underlying stream reported an error or EOF. Any error - * thrown will have a `partial` property that indicates the slice of the - * buffer that has been successfully filled with data. - * - * Ported from https://golang.org/pkg/io/#ReadFull - * - * @example Usage - * ```ts - * import { BufReader, Buffer } from "@std/io"; - * import { assertEquals } from "@std/assert/equals"; - * - * const reader = new Buffer(new TextEncoder().encode("hello world")); - * const bufReader = new BufReader(reader); - * const buf = new Uint8Array(5); - * await bufReader.readFull(buf); - * assertEquals(new TextDecoder().decode(buf), "hello"); - * ``` - * - * @param p The buffer to read data into. - * @returns The buffer `p` if the read is successful, `null` if the end of the - * underlying stream has been reached, and there are no more bytes available in the buffer. - */ - async readFull(p: Uint8Array): Promise { - let bytesRead = 0; - while (bytesRead < p.length) { - const rr = await this.read(p.subarray(bytesRead)); - if (rr === null) { - if (bytesRead === 0) { - return null; - } else { - throw new PartialReadError(p.subarray(0, bytesRead)); - } - } - bytesRead += rr; - } - return p; - } - - /** - * Returns the next byte ([0, 255]) or `null`. - * - * @example Usage - * ```ts - * import { BufReader, Buffer } from "@std/io"; - * import { assertEquals } from "@std/assert/equals"; - * - * const reader = new Buffer(new TextEncoder().encode("hello world")); - * const bufReader = new BufReader(reader); - * const byte = await bufReader.readByte(); - * assertEquals(byte, 104); - * ``` - * - * @returns The next byte ([0, 255]) or `null`. - */ - async readByte(): Promise { - while (this.#r === this.#w) { - if (this.#eof) return null; - await this.#fill(); // buffer is empty. - } - const c = this.#buf[this.#r]!; - this.#r++; - // this.lastByte = c; - return c; - } - - /** - * 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 `null`). - * ReadString returns err !== null if and only if the returned data does not end - * in delim. - * - * @example Usage - * ```ts - * import { BufReader, Buffer } from "@std/io"; - * import { assertEquals } from "@std/assert/equals"; - * - * const reader = new Buffer(new TextEncoder().encode("hello world")); - * const bufReader = new BufReader(reader); - * const str = await bufReader.readString(" "); - * assertEquals(str, "hello "); - * - * const str2 = await bufReader.readString(" "); - * assertEquals(str2, "world"); - * ``` - * - * @param delim The delimiter to read until. - * @returns The string containing the data up to and including the delimiter. - */ - async readString(delim: string): Promise { - if (delim.length !== 1) { - throw new Error("Delimiter should be a single character"); - } - const buffer = await this.readSlice(delim.charCodeAt(0)); - if (buffer === null) return null; - return new TextDecoder().decode(buffer); - } - - /** - * A low-level line-reading primitive. Most callers should use - * `readString('\n')` instead. - * - * `readLine()` tries to return a single line, not including the end-of-line - * bytes. If the line was too long for the buffer then `more` is set and the - * beginning of the line is returned. The rest of the line will be returned - * from future calls. `more` will be false when returning the last fragment - * of the line. The returned buffer is only valid until the next call to - * `readLine()`. - * - * The text returned from this method does not include the line end ("\r\n" or - * "\n"). - * - * When the end of the underlying stream is reached, the final bytes in the - * stream are returned. No indication or error is given if the input ends - * without a final line end. When there are no more trailing bytes to read, - * `readLine()` returns `null`. - * - * @example Usage - * ```ts - * import { BufReader, Buffer } from "@std/io"; - * import { assertEquals } from "@std/assert/equals"; - * - * const reader = new Buffer(new TextEncoder().encode("hello\nworld")); - * const bufReader = new BufReader(reader); - * const line1 = await bufReader.readLine(); - * assertEquals(new TextDecoder().decode(line1!.line), "hello"); - * const line2 = await bufReader.readLine(); - * assertEquals(new TextDecoder().decode(line2!.line), "world"); - * ``` - * - * @returns The line read. - */ - async readLine(): Promise { - let line: Uint8Array | null = null; - - try { - line = await this.readSlice(LF); - } catch (err) { - // Don't throw if `readSlice()` failed with `BufferFullError`, instead we - // just return whatever is available and set the `more` flag. - if (!(err instanceof BufferFullError)) { - throw err; - } - - let partial = err.partial; - - // Handle the case where "\r\n" straddles the buffer. - if ( - !this.#eof && partial && - partial.byteLength > 0 && - partial[partial.byteLength - 1] === CR - ) { - // Put the '\r' back on buf and drop it from line. - // Let the next call to ReadLine check for "\r\n". - if (this.#r <= 0) { - throw new Error("Tried to rewind past start of buffer"); - } - this.#r--; - partial = partial.subarray(0, partial.byteLength - 1); - } - - if (partial) { - return { line: partial, more: !this.#eof }; - } - } - - if (line === null) { - return null; - } - - if (line.byteLength === 0) { - return { line, more: false }; - } - - if (line[line.byteLength - 1] === LF) { - let drop = 1; - if (line.byteLength > 1 && line[line.byteLength - 2] === CR) { - drop = 2; - } - line = line.subarray(0, line.byteLength - drop); - } - return { line, more: false }; - } - - /** - * Reads until the first occurrence of `delim` in the input, - * returning a slice pointing at the bytes in the buffer. The bytes stop - * being valid at the next read. - * - * If `readSlice()` encounters an error before finding a delimiter, or the - * buffer fills without finding a delimiter, it throws an error with a - * `partial` property that contains the entire buffer. - * - * If `readSlice()` encounters the end of the underlying stream and there are - * any bytes left in the buffer, the rest of the buffer is returned. In other - * words, EOF is always treated as a delimiter. Once the buffer is empty, - * it returns `null`. - * - * Because the data returned from `readSlice()` will be overwritten by the - * next I/O operation, most clients should use `readString()` instead. - * - * @example Usage - * ```ts - * import { BufReader, Buffer } from "@std/io"; - * import { assertEquals } from "@std/assert/equals"; - * - * const reader = new Buffer(new TextEncoder().encode("hello world")); - * const bufReader = new BufReader(reader); - * const slice = await bufReader.readSlice(0x20); - * assertEquals(new TextDecoder().decode(slice!), "hello "); - * ``` - * - * @param delim The delimiter to read until. - * @returns A slice pointing at the bytes in the buffer. - */ - async readSlice(delim: number): Promise { - let s = 0; // search start index - let slice: Uint8Array | undefined; - - while (true) { - // Search buffer. - let i = this.#buf.subarray(this.#r + s, this.#w).indexOf(delim); - if (i >= 0) { - i += s; - slice = this.#buf.subarray(this.#r, this.#r + i + 1); - this.#r += i + 1; - break; - } - - // EOF? - if (this.#eof) { - if (this.#r === this.#w) { - return null; - } - slice = this.#buf.subarray(this.#r, this.#w); - this.#r = this.#w; - break; - } - - // Buffer full? - if (this.buffered() >= this.#buf.byteLength) { - this.#r = this.#w; - // #4521 The internal buffer should not be reused across reads because it causes corruption of data. - const oldbuf = this.#buf; - const newbuf = this.#buf.slice(0); - this.#buf = newbuf; - throw new BufferFullError(oldbuf); - } - - s = this.#w - this.#r; // do not rescan area we scanned before - - // Buffer is not full. - await this.#fill(); - } - - // Handle last byte, if any. - // const i = slice.byteLength - 1; - // if (i >= 0) { - // this.lastByte = slice[i]; - // this.lastCharSize = -1 - // } - - return slice; - } - - /** - * Returns the next `n` bytes without advancing the reader. The - * bytes stop being valid at the next read call. - * - * When the end of the underlying stream is reached, but there are unread - * bytes left in the buffer, those bytes are returned. If there are no bytes - * left in the buffer, it returns `null`. - * - * If an error is encountered before `n` bytes are available, `peek()` throws - * an error with the `partial` property set to a slice of the buffer that - * contains the bytes that were available before the error occurred. - * - * @example Usage - * ```ts - * import { BufReader, Buffer } from "@std/io"; - * import { assertEquals } from "@std/assert/equals"; - * - * const reader = new Buffer(new TextEncoder().encode("hello world")); - * const bufReader = new BufReader(reader); - * const peeked = await bufReader.peek(5); - * assertEquals(new TextDecoder().decode(peeked!), "hello"); - * ``` - * - * @param n The number of bytes to peek. - * @returns The next `n` bytes without advancing the reader. - */ - async peek(n: number): Promise { - if (n < 0) { - throw new Error("Peek count cannot be negative"); - } - - let avail = this.#w - this.#r; - while (avail < n && avail < this.#buf.byteLength && !this.#eof) { - await this.#fill(); - avail = this.#w - this.#r; - } - - if (avail === 0 && this.#eof) { - return null; - } else if (avail < n && this.#eof) { - return this.#buf.subarray(this.#r, this.#r + avail); - } else if (avail < n) { - throw new BufferFullError(this.#buf.subarray(this.#r, this.#w)); - } - - return this.#buf.subarray(this.#r, this.#r + n); - } -} diff --git a/io/buf_reader_test.ts b/io/buf_reader_test.ts deleted file mode 100644 index a56d69e9a649..000000000000 --- a/io/buf_reader_test.ts +++ /dev/null @@ -1,411 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -// 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 { assert, assertEquals, assertRejects, fail } from "@std/assert"; -import { BufferFullError, BufReader, PartialReadError } from "./buf_reader.ts"; -import { StringReader } from "./string_reader.ts"; -import { bufsizes, MIN_READ_BUFFER_SIZE } from "./_test_common.ts"; -import { Buffer } from "./buffer.ts"; -import type { Reader } from "./types.ts"; -import { copy } from "@std/bytes/copy"; - -/** OneByteReader returns a Reader that implements - * each non-empty Read by reading one byte from r. - */ -class OneByteReader implements Reader { - constructor(readonly r: Reader) {} - - read(p: Uint8Array): Promise { - if (p.byteLength === 0) { - return Promise.resolve(0); - } - if (!(p instanceof Uint8Array)) { - throw new Error("Expected Uint8Array"); - } - return Promise.resolve(this.r.read(p.subarray(0, 1))); - } -} - -/** HalfReader returns a Reader that implements Read - * by reading half as many requested bytes from r. - */ -class HalfReader implements Reader { - constructor(readonly r: Reader) {} - - read(p: Uint8Array): Promise { - if (!(p instanceof Uint8Array)) { - throw new Error("Expected Uint8Array"); - } - const half = Math.floor((p.byteLength + 1) / 2); - return Promise.resolve(this.r.read(p.subarray(0, half))); - } -} - -async function readBytes(buf: BufReader): Promise { - const b = new Uint8Array(1000); - let nb = 0; - while (true) { - const c = await buf.readByte(); - if (c === null) { - break; // EOF - } - b[nb] = c; - nb++; - } - const decoder = new TextDecoder(); - return decoder.decode(b.subarray(0, nb)); -} - -interface ReadMaker { - name: string; - fn: (r: Reader) => Reader; -} - -const readMakers: ReadMaker[] = [ - { name: "full", fn: (r): Reader => r }, - { - name: "byte", - fn: (r): OneByteReader => new OneByteReader(r), - }, - { name: "half", fn: (r): HalfReader => new HalfReader(r) }, - // TODO(bartlomieju): { name: "data+err", r => new DataErrReader(r) }, - // { name: "timeout", fn: r => new TimeoutReader(r) }, -]; - -// Call read to accumulate the text of a file -async function reads(buf: BufReader, m: number): Promise { - const b = new Uint8Array(1000); - let nb = 0; - while (true) { - const result = await buf.read(b.subarray(nb, nb + m)); - if (result === null) { - break; - } - nb += result; - } - const decoder = new TextDecoder(); - return decoder.decode(b.subarray(0, nb)); -} - -interface NamedBufReader { - name: string; - fn: (r: BufReader) => Promise; -} - -const bufreaders: NamedBufReader[] = [ - { name: "1", fn: (b: BufReader): Promise => reads(b, 1) }, - { name: "2", fn: (b: BufReader): Promise => reads(b, 2) }, - { name: "3", fn: (b: BufReader): Promise => reads(b, 3) }, - { name: "4", fn: (b: BufReader): Promise => reads(b, 4) }, - { name: "5", fn: (b: BufReader): Promise => reads(b, 5) }, - { name: "7", fn: (b: BufReader): Promise => reads(b, 7) }, - { name: "bytes", fn: readBytes }, - // { name: "lines", fn: readLines }, -]; - -Deno.test("BufReader.readBytes() reads string from buffer", async function () { - const data = "hello world"; - const b = new BufReader(new StringReader(data)); - const s = await readBytes(b); - assertEquals(s, data); -}); - -Deno.test("BufReader with different readers", async function () { - const texts = new Array(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 (const text of texts) { - for (const readmaker of readMakers) { - for (const bufreader of bufreaders) { - for (const bufsize of bufsizes) { - const read = readmaker.fn(new StringReader(text)); - const buf = new BufReader(read, bufsize); - const s = await bufreader.fn(buf); - const debugStr = `reader=${readmaker.name} ` + - `fn=${bufreader.name} bufsize=${bufsize} want=${text} got=${s}`; - assertEquals(s, text, debugStr); - } - } - } - } -}); - -Deno.test("BufReader.readSlice() throws when reading with full buffer", async function () { - 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(new StringReader(longString), MIN_READ_BUFFER_SIZE); - const decoder = new TextDecoder(); - - try { - await buf.readSlice("!".charCodeAt(0)); - fail("readSlice should throw"); - } catch (err) { - assert(err instanceof BufferFullError); - assert(err.partial instanceof Uint8Array); - assertEquals(decoder.decode(err.partial), "And now, hello, "); - } - - const line = await buf.readSlice("!".charCodeAt(0)); - assert(line !== null); - const actual = decoder.decode(line); - assertEquals(actual, "world!"); -}); - -Deno.test("BufReader.readString()", async function () { - const string = "And now, hello world!"; - const buf = new BufReader(new StringReader(string), MIN_READ_BUFFER_SIZE); - - const line = await buf.readString(","); - assert(line !== null); - assertEquals(line, "And now,"); - assertEquals(line.length, 8); - - const line2 = await buf.readString(","); - assert(line2 !== null); - assertEquals(line2, " hello world!"); - - assertEquals(await buf.readString(","), null); - - try { - await buf.readString("deno"); - - fail("should throw"); - } catch (err) { - assert(err instanceof Error); - assert(err.message, "Delimiter should be a single character"); - } -}); - -Deno.test("BufReader.readFull() throws if array length is too big", async function () { - const enc = new TextEncoder(); - const dec = new TextDecoder(); - const text = "Hello World"; - const data = new Buffer(enc.encode(text)); - const bufr = new BufReader(data, 3); - { - const buf = new Uint8Array(6); - const r = await bufr.readFull(buf); - assert(r !== null); - assertEquals(r, buf); - assertEquals(dec.decode(buf), "Hello "); - } - { - const buf = new Uint8Array(6); - const error = await assertRejects( - () => bufr.readFull(buf), - PartialReadError, - ); - assertEquals(error.partial.length, 5); - assertEquals(dec.decode(buf.subarray(0, 5)), "World"); - } -}); - -Deno.test("BufReader.peek()", async function () { - const decoder = new TextDecoder(); - const p = new Uint8Array(10); - // string is 16 (minReadBufferSize) long. - const buf = new BufReader( - new StringReader("abcdefghijklmnop"), - MIN_READ_BUFFER_SIZE, - ); - - let actual = await buf.peek(1); - assert(actual !== null); - assertEquals(decoder.decode(actual), "a"); - - actual = await buf.peek(4); - assert(actual !== null); - assertEquals(decoder.decode(actual), "abcd"); - - try { - await buf.peek(32); - fail("peek() should throw"); - } catch (err) { - assert(err instanceof BufferFullError); - assert(err.partial instanceof Uint8Array); - assertEquals(decoder.decode(err.partial), "abcdefghijklmnop"); - } - - await buf.read(p.subarray(0, 3)); - assertEquals(decoder.decode(p.subarray(0, 3)), "abc"); - - actual = await buf.peek(1); - assert(actual !== null); - assertEquals(decoder.decode(actual), "d"); - - actual = await buf.peek(1); - assert(actual !== null); - assertEquals(decoder.decode(actual), "d"); - - actual = await buf.peek(1); - assert(actual !== null); - assertEquals(decoder.decode(actual), "d"); - - actual = await buf.peek(2); - assert(actual !== null); - assertEquals(decoder.decode(actual), "de"); - - const res = await buf.read(p.subarray(0, 3)); - assertEquals(decoder.decode(p.subarray(0, 3)), "def"); - assert(res !== null); - - actual = await buf.peek(4); - assert(actual !== null); - assertEquals(decoder.decode(actual), "ghij"); - - await buf.read(p); - assertEquals(decoder.decode(p), "ghijklmnop"); - - actual = await buf.peek(0); - assert(actual !== null); - assertEquals(decoder.decode(actual), ""); - - const r = await buf.peek(1); - assert(r === null); - /* 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) - } - */ -}); - -const encoder = new TextEncoder(); - -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\n" + - "uvw\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 { - #data: Uint8Array; - #stride: number; - - constructor(data: Uint8Array, stride: number) { - this.#data = data; - this.#stride = stride; - } - - read(buf: Uint8Array): Promise { - let nread = this.#stride; - if (nread > this.#data.byteLength) { - nread = this.#data.byteLength; - } - if (nread > buf.byteLength) { - nread = buf.byteLength; - } - if (nread === 0) { - return Promise.resolve(null); - } - copy(this.#data, buf as Uint8Array); - this.#data = this.#data.subarray(nread); - return Promise.resolve(nread); - } -} - -async function testReadLine(input: Uint8Array) { - for (let stride = 1; stride < 2; stride++) { - let done = 0; - const reader = new TestReader(input, stride); - const l = new BufReader(reader, input.byteLength + 1); - while (true) { - const r = await l.readLine(); - if (r === null) { - break; - } - const { line, more } = r; - assertEquals(more, false); - const want = testOutput.subarray(done, done + line.byteLength); - assertEquals( - line, - want, - `Bad line at stride ${stride}: want: ${want} got: ${line}`, - ); - done += line.byteLength; - } - assertEquals( - done, - testOutput.byteLength, - `readLine didn't return everything: got: ${done}, ` + - `want: ${testOutput} (stride: ${stride})`, - ); - } -} - -Deno.test("BufReader.readLine()", async function () { - await testReadLine(testInput); - await testReadLine(testInputrn); -}); - -Deno.test("BufReader.readLine() throws with BadResource", async () => { - const file = await Deno.open("README.md"); - const bufReader = new BufReader(file); - file.close(); - await assertRejects(async () => { - await bufReader.readLine(); - }, Deno.errors.BadResource); -}); - -Deno.test("BufReader.readLine() fills a full buffer", async () => { - const input = "@".repeat(5000) + "\n"; - const bufReader = new BufReader(new StringReader(input)); - const r = await bufReader.readLine(); - - assert(r !== null); - - const { line, more } = r; - assertEquals(more, true); - assertEquals(line, encoder.encode("@".repeat(4096))); -}); - -/* TODO(kt3k): Enable this test -Deno.test( - "bufReaderShouldNotShareArrayBufferAcrossReads", - async function () { - const decoder = new TextDecoder(); - const data = "abcdefghijklmnopqrstuvwxyz"; - const bufSize = 25; - const b = new BufReader(new StringReader(data), bufSize); - - const r1 = (await b.readLine()) as ReadLineResult; - assert(r1 !== null); - assertEquals(decoder.decode(r1.line), "abcdefghijklmnopqrstuvwxy"); - - const r2 = (await b.readLine()) as ReadLineResult; - assert(r2 !== null); - assertEquals(decoder.decode(r2.line), "z"); - assert( - r1.line.buffer !== r2.line.buffer, - "array buffer should not be shared across reads", - ); - }, -); -*/ diff --git a/io/deno.json b/io/deno.json index 43e80b8a5b35..3de98a15e395 100644 --- a/io/deno.json +++ b/io/deno.json @@ -3,7 +3,6 @@ "version": "0.224.9", "exports": { ".": "./mod.ts", - "./buf-reader": "./buf_reader.ts", "./buf-writer": "./buf_writer.ts", "./buffer": "./buffer.ts", "./copy": "./copy.ts", diff --git a/io/mod.ts b/io/mod.ts index b33798a8cc39..583305801c97 100644 --- a/io/mod.ts +++ b/io/mod.ts @@ -16,7 +16,6 @@ * @module */ -export * from "./buf_reader.ts"; export * from "./buf_writer.ts"; export * from "./buffer.ts"; export * from "./copy.ts";