diff options
Diffstat (limited to 'std/io')
-rw-r--r-- | std/io/README.md | 159 | ||||
-rw-r--r-- | std/io/_iotest.ts | 53 | ||||
-rw-r--r-- | std/io/bufio.ts | 718 | ||||
-rw-r--r-- | std/io/bufio_test.ts | 566 | ||||
-rw-r--r-- | std/io/ioutil.ts | 84 | ||||
-rw-r--r-- | std/io/ioutil_test.ts | 107 | ||||
-rw-r--r-- | std/io/mod.ts | 6 | ||||
-rw-r--r-- | std/io/readers.ts | 61 | ||||
-rw-r--r-- | std/io/readers_test.ts | 63 | ||||
-rw-r--r-- | std/io/streams.ts | 64 | ||||
-rw-r--r-- | std/io/streams_test.ts | 189 | ||||
-rw-r--r-- | std/io/test.ts | 2 | ||||
-rw-r--r-- | std/io/writers.ts | 42 | ||||
-rw-r--r-- | std/io/writers_test.ts | 23 |
14 files changed, 0 insertions, 2137 deletions
diff --git a/std/io/README.md b/std/io/README.md deleted file mode 100644 index 4f5d7454f..000000000 --- a/std/io/README.md +++ /dev/null @@ -1,159 +0,0 @@ -# std/io - -## Bufio - -**Uses:** - -### readLines - -Read reader[like file], line by line: - -```ts title="readLines" -import { readLines } from "https://deno.land/std@$STD_VERSION/io/mod.ts"; -import * as path from "https://deno.land/std@$STD_VERSION/path/mod.ts"; - -const filename = path.join(Deno.cwd(), "std/io/README.md"); -let fileReader = await Deno.open(filename); - -for await (let line of readLines(fileReader)) { - console.log(line); -} -``` - -**Output:** - -````text -# std/io - -## readLines - -```ts -import * as path from "https://deno.land/std@$STD_VERSION/path/mod.ts"; - -## Rest of the file -```` - -### readStringDelim - -Read reader`[like file]` chunk by chunk, splitting based on delimiter. - -```ts title="readStringDelim" -import { readStringDelim } from "https://deno.land/std@$STD_VERSION/io/mod.ts"; -import * as path from "https://deno.land/std@$STD_VERSION/path/mod.ts"; - -const filename = path.join(Deno.cwd(), "std/io/README.md"); -let fileReader = await Deno.open(filename); - -for await (let line of readStringDelim(fileReader, "\n")) { - console.log(line); -} -``` - -**Output:** - -````text -# std/io - -## readLines - -```ts -import * as path from "https://deno.land/std@$STD_VERSION/path/mod.ts"; - -## Rest of the file -```` - -## Reader - -### StringReader - -Create a `Reader` object for `string`. - -```ts -import { StringReader } from "https://deno.land/std@$STD_VERSION/io/mod.ts"; - -const data = new Uint8Array(6); -const r = new StringReader("abcdef"); -const res0 = await r.read(data); -const res1 = await r.read(new Uint8Array(6)); - -// Number of bytes read -console.log(res0); // 6 -console.log(res1); // null, no byte left to read. EOL - -// text - -console.log(new TextDecoder().decode(data)); // abcdef -``` - -**Output:** - -```text -6 -null -abcdef -``` - -## Writer - -### StringWriter - -Create a `Writer` object for `string`. - -```ts -import { - copyN, - StringReader, - StringWriter, -} from "https://deno.land/std@$STD_VERSION/io/mod.ts"; - -const w = new StringWriter("base"); -const r = new StringReader("0123456789"); -await copyN(r, w, 4); // copy 4 bytes - -// Number of bytes read -console.log(w.toString()); //base0123 - -await Deno.copy(r, w); // copy all -console.log(w.toString()); // base0123456789 -``` - -**Output:** - -```text -base0123 -base0123456789 -``` - -## Streams - -### readerFromStreamReader - -Creates a `Reader` from a `ReadableStreamDefaultReader`. - -```ts -import { readerFromStreamReader } from "https://deno.land/std@$STD_VERSION/io/mod.ts"; -const res = await fetch("https://deno.land"); -const file = await Deno.open("./deno.land.html", { create: true, write: true }); - -const reader = readerFromStreamReader(res.body!.getReader()); -await Deno.copy(reader, file); -file.close(); -``` - -### writerFromStreamWriter - -Creates a `Writer` from a `WritableStreamDefaultWriter`. - -```ts -import { writerFromStreamWriter } from "https://deno.land/std@$STD_VERSION/io/mod.ts"; -const file = await Deno.open("./deno.land.html", { read: true }); - -const writableStream = new WritableStream({ - write(chunk): void { - console.log(chunk); - }, -}); -const writer = writerFromStreamWriter(writableStream.getWriter()); -await Deno.copy(file, writer); -file.close(); -``` diff --git a/std/io/_iotest.ts b/std/io/_iotest.ts deleted file mode 100644 index 5905d2552..000000000 --- a/std/io/_iotest.ts +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.// 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. -type Reader = Deno.Reader; - -/** 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) {} - - read(p: Uint8Array): Promise<number | null> { - if (p.byteLength === 0) { - return Promise.resolve(0); - } - if (!(p instanceof Uint8Array)) { - throw 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. - */ -export class HalfReader implements Reader { - constructor(readonly r: Reader) {} - - read(p: Uint8Array): Promise<number | null> { - if (!(p instanceof Uint8Array)) { - throw Error("expected Uint8Array"); - } - const half = Math.floor((p.byteLength + 1) / 2); - return Promise.resolve(this.r.read(p.subarray(0, half))); - } -} - -/** TimeoutReader returns `Deno.errors.TimedOut` on the second read - * with no data. Subsequent calls to read succeed. - */ -export class TimeoutReader implements Reader { - count = 0; - constructor(readonly r: Reader) {} - - read(p: Uint8Array): Promise<number | null> { - this.count++; - if (this.count === 2) { - throw new Deno.errors.TimedOut(); - } - return Promise.resolve(this.r.read(p)); - } -} diff --git a/std/io/bufio.ts b/std/io/bufio.ts deleted file mode 100644 index f3b1893cb..000000000 --- a/std/io/bufio.ts +++ /dev/null @@ -1,718 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// Based on https://github.com/golang/go/blob/891682/src/bufio/bufio.go -// 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. - -type Reader = Deno.Reader; -type Writer = Deno.Writer; -type WriterSync = Deno.WriterSync; -import { copy } from "../bytes/mod.ts"; -import { assert } from "../_util/assert.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); - -export class BufferFullError extends Error { - name = "BufferFullError"; - constructor(public partial: Uint8Array) { - super("Buffer full"); - } -} - -export class PartialReadError extends Deno.errors.UnexpectedEof { - name = "PartialReadError"; - partial?: Uint8Array; - constructor() { - super("Encountered UnexpectedEof, data only partially read"); - } -} - -/** Result type returned by of BufReader.readLine(). */ -export interface ReadLineResult { - line: Uint8Array; - more: boolean; -} - -/** BufReader implements buffering for a Reader object. */ -export class BufReader implements Reader { - private buf!: Uint8Array; - private rd!: Reader; // Reader provided by caller. - private r = 0; // buf read position. - private w = 0; // buf write position. - private eof = false; - // private lastByte: number; - // private lastCharSize: number; - - /** return new BufReader unless r is BufReader */ - static create(r: Reader, size: number = DEFAULT_BUF_SIZE): BufReader { - return r instanceof BufReader ? r : new BufReader(r, size); - } - - 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. */ - size(): number { - return this.buf.byteLength; - } - - buffered(): number { - return this.w - this.r; - } - - // Reads a new chunk into the buffer. - private async _fill(): Promise<void> { - // 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 Error("bufio: tried to fill full buffer"); - } - - // 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; - } - assert(rr >= 0, "negative read"); - 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. - */ - reset(r: Reader): void { - this._reset(this.buf, r); - } - - private _reset(buf: Uint8Array, rd: Reader): void { - this.buf = buf; - this.rd = rd; - this.eof = false; - // this.lastByte = -1; - // 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). - * To read exactly len(p) bytes, use io.ReadFull(b, p). - */ - async read(p: Uint8Array): Promise<number | null> { - 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); - const nread = rr ?? 0; - assert(nread >= 0, "negative read"); - // 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; - assert(rr >= 0, "negative read"); - 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 - */ - async readFull(p: Uint8Array): Promise<Uint8Array | null> { - let bytesRead = 0; - while (bytesRead < p.length) { - try { - const rr = await this.read(p.subarray(bytesRead)); - if (rr === null) { - if (bytesRead === 0) { - return null; - } else { - throw new PartialReadError(); - } - } - bytesRead += rr; - } catch (err) { - err.partial = p.subarray(0, bytesRead); - throw err; - } - } - return p; - } - - /** Returns the next byte [0, 255] or `null`. */ - async readByte(): Promise<number | null> { - 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; - } - - /** 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 `null`). - * 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 | null> { - 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); - } - - /** `readLine()` is a low-level line-reading primitive. Most callers should - * use `readString('\n')` instead or use a Scanner. - * - * `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 ReadLine 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`. - * - * Calling `unreadByte()` after `readLine()` will always unread the last byte - * read (possibly a character belonging to the line end) even if that byte is - * not part of the line returned by `readLine()`. - */ - async readLine(): Promise<ReadLineResult | null> { - let line: Uint8Array | null; - - try { - line = await this.readSlice(LF); - } catch (err) { - let { partial } = err; - assert( - partial instanceof Uint8Array, - "bufio: caught error from `readSlice()` without `partial` property", - ); - - // 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; - } - - // Handle the case where "\r\n" straddles the buffer. - if ( - !this.eof && - 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". - assert(this.r > 0, "bufio: tried to rewind past start of buffer"); - this.r--; - partial = partial.subarray(0, partial.byteLength - 1); - } - - 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 }; - } - - /** `readSlice()` 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. - */ - async readSlice(delim: number): Promise<Uint8Array | null> { - 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. - try { - await this._fill(); - } catch (err) { - err.partial = slice; - throw err; - } - } - - // Handle last byte, if any. - // const i = slice.byteLength - 1; - // if (i >= 0) { - // this.lastByte = slice[i]; - // this.lastCharSize = -1 - // } - - return slice; - } - - /** `peek()` 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. - */ - async peek(n: number): Promise<Uint8Array | null> { - if (n < 0) { - throw Error("negative count"); - } - - let avail = this.w - this.r; - while (avail < n && avail < this.buf.byteLength && !this.eof) { - try { - await this._fill(); - } catch (err) { - err.partial = this.buf.subarray(this.r, this.w); - throw err; - } - 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); - } -} - -abstract class AbstractBufBase { - buf!: Uint8Array; - usedBufferBytes = 0; - err: Error | null = null; - - /** Size returns the size of the underlying buffer in bytes. */ - size(): number { - return this.buf.byteLength; - } - - /** Returns how many bytes are unused in the buffer. */ - available(): number { - return this.buf.byteLength - this.usedBufferBytes; - } - - /** buffered returns the number of bytes that have been written into the - * current buffer. - */ - buffered(): number { - return this.usedBufferBytes; - } -} - -/** BufWriter implements buffering for an deno.Writer object. - * If an error occurs writing to a Writer, no more data will be - * accepted and all subsequent writes, and flush(), will return the error. - * After all data has been written, the client should call the - * flush() method to guarantee all data has been forwarded to - * the underlying deno.Writer. - */ -export class BufWriter extends AbstractBufBase implements Writer { - /** return new BufWriter unless writer is BufWriter */ - static create(writer: Writer, size: number = DEFAULT_BUF_SIZE): BufWriter { - return writer instanceof BufWriter ? writer : new BufWriter(writer, size); - } - - constructor(private writer: Writer, size: number = DEFAULT_BUF_SIZE) { - super(); - if (size <= 0) { - size = DEFAULT_BUF_SIZE; - } - this.buf = new Uint8Array(size); - } - - /** Discards any unflushed buffered data, clears any error, and - * resets buffer to write its output to w. - */ - reset(w: Writer): void { - this.err = null; - this.usedBufferBytes = 0; - this.writer = w; - } - - /** Flush writes any buffered data to the underlying io.Writer. */ - async flush(): Promise<void> { - if (this.err !== null) throw this.err; - if (this.usedBufferBytes === 0) return; - - try { - await Deno.writeAll( - this.writer, - this.buf.subarray(0, this.usedBufferBytes), - ); - } catch (e) { - this.err = e; - throw e; - } - - this.buf = new Uint8Array(this.buf.length); - this.usedBufferBytes = 0; - } - - /** Writes the contents of `data` into the buffer. If the contents won't fully - * fit into the buffer, those bytes that can are copied into the buffer, the - * buffer is the flushed to the writer and the remaining bytes are copied into - * the now empty buffer. - * - * @return the number of bytes written to the buffer. - */ - async write(data: Uint8Array): Promise<number> { - if (this.err !== null) throw this.err; - if (data.length === 0) return 0; - - let totalBytesWritten = 0; - let numBytesWritten = 0; - while (data.byteLength > this.available()) { - if (this.buffered() === 0) { - // Large write, empty buffer. - // Write directly from data to avoid copy. - try { - numBytesWritten = await this.writer.write(data); - } catch (e) { - this.err = e; - throw e; - } - } else { - numBytesWritten = copy(data, this.buf, this.usedBufferBytes); - this.usedBufferBytes += numBytesWritten; - await this.flush(); - } - totalBytesWritten += numBytesWritten; - data = data.subarray(numBytesWritten); - } - - numBytesWritten = copy(data, this.buf, this.usedBufferBytes); - this.usedBufferBytes += numBytesWritten; - totalBytesWritten += numBytesWritten; - return totalBytesWritten; - } -} - -/** BufWriterSync implements buffering for a deno.WriterSync object. - * If an error occurs writing to a WriterSync, no more data will be - * accepted and all subsequent writes, and flush(), will return the error. - * After all data has been written, the client should call the - * flush() method to guarantee all data has been forwarded to - * the underlying deno.WriterSync. - */ -export class BufWriterSync extends AbstractBufBase implements WriterSync { - /** return new BufWriterSync unless writer is BufWriterSync */ - static create( - writer: WriterSync, - size: number = DEFAULT_BUF_SIZE, - ): BufWriterSync { - return writer instanceof BufWriterSync - ? writer - : new BufWriterSync(writer, size); - } - - constructor(private writer: WriterSync, size: number = DEFAULT_BUF_SIZE) { - super(); - if (size <= 0) { - size = DEFAULT_BUF_SIZE; - } - this.buf = new Uint8Array(size); - } - - /** Discards any unflushed buffered data, clears any error, and - * resets buffer to write its output to w. - */ - reset(w: WriterSync): void { - this.err = null; - this.usedBufferBytes = 0; - this.writer = w; - } - - /** Flush writes any buffered data to the underlying io.WriterSync. */ - flush(): void { - if (this.err !== null) throw this.err; - if (this.usedBufferBytes === 0) return; - - try { - Deno.writeAllSync( - this.writer, - this.buf.subarray(0, this.usedBufferBytes), - ); - } catch (e) { - this.err = e; - throw e; - } - - this.buf = new Uint8Array(this.buf.length); - this.usedBufferBytes = 0; - } - - /** Writes the contents of `data` into the buffer. If the contents won't fully - * fit into the buffer, those bytes that can are copied into the buffer, the - * buffer is the flushed to the writer and the remaining bytes are copied into - * the now empty buffer. - * - * @return the number of bytes written to the buffer. - */ - writeSync(data: Uint8Array): number { - if (this.err !== null) throw this.err; - if (data.length === 0) return 0; - - let totalBytesWritten = 0; - let numBytesWritten = 0; - while (data.byteLength > this.available()) { - if (this.buffered() === 0) { - // Large write, empty buffer. - // Write directly from data to avoid copy. - try { - numBytesWritten = this.writer.writeSync(data); - } catch (e) { - this.err = e; - throw e; - } - } else { - numBytesWritten = copy(data, this.buf, this.usedBufferBytes); - this.usedBufferBytes += numBytesWritten; - this.flush(); - } - totalBytesWritten += numBytesWritten; - data = data.subarray(numBytesWritten); - } - - numBytesWritten = copy(data, this.buf, this.usedBufferBytes); - this.usedBufferBytes += numBytesWritten; - totalBytesWritten += numBytesWritten; - return totalBytesWritten; - } -} - -/** Generate longest proper prefix which is also suffix array. */ -function createLPS(pat: Uint8Array): Uint8Array { - const lps = new Uint8Array(pat.length); - lps[0] = 0; - let prefixEnd = 0; - let i = 1; - while (i < lps.length) { - if (pat[i] == pat[prefixEnd]) { - prefixEnd++; - lps[i] = prefixEnd; - i++; - } else if (prefixEnd === 0) { - lps[i] = 0; - i++; - } else { - prefixEnd = pat[prefixEnd - 1]; - } - } - return lps; -} - -/** Read delimited bytes from a Reader. */ -export async function* readDelim( - reader: Reader, - delim: Uint8Array, -): AsyncIterableIterator<Uint8Array> { - // Avoid unicode problems - const delimLen = delim.length; - const delimLPS = createLPS(delim); - - let inputBuffer = new Deno.Buffer(); - const inspectArr = new Uint8Array(Math.max(1024, delimLen + 1)); - - // Modified KMP - let inspectIndex = 0; - let matchIndex = 0; - while (true) { - const result = await reader.read(inspectArr); - if (result === null) { - // Yield last chunk. - yield inputBuffer.bytes(); - return; - } - if ((result as number) < 0) { - // Discard all remaining and silently fail. - return; - } - const sliceRead = inspectArr.subarray(0, result as number); - await Deno.writeAll(inputBuffer, sliceRead); - - let sliceToProcess = inputBuffer.bytes(); - while (inspectIndex < sliceToProcess.length) { - if (sliceToProcess[inspectIndex] === delim[matchIndex]) { - inspectIndex++; - matchIndex++; - if (matchIndex === delimLen) { - // Full match - const matchEnd = inspectIndex - delimLen; - const readyBytes = sliceToProcess.subarray(0, matchEnd); - // Copy - const pendingBytes = sliceToProcess.slice(inspectIndex); - yield readyBytes; - // Reset match, different from KMP. - sliceToProcess = pendingBytes; - inspectIndex = 0; - matchIndex = 0; - } - } else { - if (matchIndex === 0) { - inspectIndex++; - } else { - matchIndex = delimLPS[matchIndex - 1]; - } - } - } - // Keep inspectIndex and matchIndex. - inputBuffer = new Deno.Buffer(sliceToProcess); - } -} - -/** Read delimited strings from a Reader. */ -export async function* readStringDelim( - reader: Reader, - delim: string, -): AsyncIterableIterator<string> { - const encoder = new TextEncoder(); - const decoder = new TextDecoder(); - for await (const chunk of readDelim(reader, encoder.encode(delim))) { - yield decoder.decode(chunk); - } -} - -/** Read strings line-by-line from a Reader. */ -export async function* readLines( - reader: Reader, -): AsyncIterableIterator<string> { - for await (let chunk of readStringDelim(reader, "\n")) { - // Finding a CR at the end of the line is evidence of a - // "\r\n" at the end of the line. The "\r" part should be - // removed too. - if (chunk.endsWith("\r")) { - chunk = chunk.slice(0, -1); - } - yield chunk; - } -} diff --git a/std/io/bufio_test.ts b/std/io/bufio_test.ts deleted file mode 100644 index d6a0856de..000000000 --- a/std/io/bufio_test.ts +++ /dev/null @@ -1,566 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// Based on https://github.com/golang/go/blob/891682/src/bufio/bufio_test.go -// 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 { assert, assertEquals, fail } from "../testing/asserts.ts"; -import { - BufferFullError, - BufReader, - BufWriter, - BufWriterSync, - PartialReadError, - ReadLineResult, - readLines, - readStringDelim, -} from "./bufio.ts"; -import * as iotest from "./_iotest.ts"; -import { StringReader } from "./readers.ts"; -import { StringWriter } from "./writers.ts"; -import { copy } from "../bytes/mod.ts"; - -const encoder = new TextEncoder(); - -async function readBytes(buf: BufReader): Promise<string> { - 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)); -} - -Deno.test("bufioReaderSimple", async function (): Promise<void> { - const data = "hello world"; - const b = new BufReader(new StringReader(data)); - const s = await readBytes(b); - assertEquals(s, data); -}); - -interface ReadMaker { - name: string; - fn: (r: Deno.Reader) => Deno.Reader; -} - -const readMakers: ReadMaker[] = [ - { name: "full", fn: (r): Deno.Reader => r }, - { - name: "byte", - fn: (r): iotest.OneByteReader => new iotest.OneByteReader(r), - }, - { name: "half", fn: (r): iotest.HalfReader => new iotest.HalfReader(r) }, - // TODO(bartlomieju): { name: "data+err", r => new iotest.DataErrReader(r) }, - // { name: "timeout", fn: r => new iotest.TimeoutReader(r) }, -]; - -// Call read to accumulate the text of a file -async function reads(buf: BufReader, m: number): Promise<string> { - 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<string>; -} - -const bufreaders: NamedBufReader[] = [ - { name: "1", fn: (b: BufReader): Promise<string> => reads(b, 1) }, - { name: "2", fn: (b: BufReader): Promise<string> => reads(b, 2) }, - { name: "3", fn: (b: BufReader): Promise<string> => reads(b, 3) }, - { name: "4", fn: (b: BufReader): Promise<string> => reads(b, 4) }, - { name: "5", fn: (b: BufReader): Promise<string> => reads(b, 5) }, - { name: "7", fn: (b: BufReader): Promise<string> => reads(b, 7) }, - { name: "bytes", fn: readBytes }, - // { name: "lines", fn: readLines }, -]; - -const MIN_READ_BUFFER_SIZE = 16; -const bufsizes: number[] = [ - 0, - MIN_READ_BUFFER_SIZE, - 23, - 32, - 46, - 64, - 93, - 128, - 1024, - 4096, -]; - -Deno.test("bufioBufReader", async function (): Promise<void> { - 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 (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("bufioBufferFull", async function (): Promise<void> { - 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("bufioReadString", async function (): Promise<void> { - 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.message, "Delimiter should be a single character"); - } -}); - -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 Deno.Reader { - constructor(private data: Uint8Array, private stride: number) {} - - read(buf: Uint8Array): Promise<number | null> { - 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): Promise<void> { - 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); - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands - 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("bufioReadLine", async function (): Promise<void> { - await testReadLine(testInput); - await testReadLine(testInputrn); -}); - -Deno.test("bufioPeek", async function (): Promise<void> { - 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) - } - */ -}); - -Deno.test("bufioWriter", async function (): Promise<void> { - const data = new Uint8Array(8192); - - for (let i = 0; i < data.byteLength; i++) { - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands - data[i] = " ".charCodeAt(0) + (i % ("~".charCodeAt(0) - " ".charCodeAt(0))); - } - - const w = new Deno.Buffer(); - for (const nwrite of bufsizes) { - for (const bs of bufsizes) { - // Write nwrite bytes using buffer size bs. - // Check that the right amount makes it out - // and that the data is correct. - - w.reset(); - const buf = new BufWriter(w, bs); - - const context = `nwrite=${nwrite} bufsize=${bs}`; - const n = await buf.write(data.subarray(0, nwrite)); - assertEquals(n, nwrite, context); - - await buf.flush(); - - const written = w.bytes(); - assertEquals(written.byteLength, nwrite); - - for (let l = 0; l < written.byteLength; l++) { - assertEquals(written[l], data[l]); - } - } - } -}); - -Deno.test("bufioWriterSync", function (): void { - const data = new Uint8Array(8192); - - for (let i = 0; i < data.byteLength; i++) { - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands - data[i] = " ".charCodeAt(0) + (i % ("~".charCodeAt(0) - " ".charCodeAt(0))); - } - - const w = new Deno.Buffer(); - for (const nwrite of bufsizes) { - for (const bs of bufsizes) { - // Write nwrite bytes using buffer size bs. - // Check that the right amount makes it out - // and that the data is correct. - - w.reset(); - const buf = new BufWriterSync(w, bs); - - const context = `nwrite=${nwrite} bufsize=${bs}`; - const n = buf.writeSync(data.subarray(0, nwrite)); - assertEquals(n, nwrite, context); - - buf.flush(); - - const written = w.bytes(); - assertEquals(written.byteLength, nwrite); - - for (let l = 0; l < written.byteLength; l++) { - assertEquals(written[l], data[l]); - } - } - } -}); - -Deno.test("bufReaderReadFull", async function (): Promise<void> { - const enc = new TextEncoder(); - const dec = new TextDecoder(); - const text = "Hello World"; - const data = new Deno.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); - try { - await bufr.readFull(buf); - fail("readFull() should throw PartialReadError"); - } catch (err) { - assert(err instanceof PartialReadError); - assert(err.partial instanceof Uint8Array); - assertEquals(err.partial.length, 5); - assertEquals(dec.decode(buf.subarray(0, 5)), "World"); - } - } -}); - -Deno.test("readStringDelimAndLines", async function (): Promise<void> { - const enc = new TextEncoder(); - const data = new Deno.Buffer( - enc.encode("Hello World\tHello World 2\tHello World 3"), - ); - const chunks_ = []; - - for await (const c of readStringDelim(data, "\t")) { - chunks_.push(c); - } - - assertEquals(chunks_.length, 3); - assertEquals(chunks_, ["Hello World", "Hello World 2", "Hello World 3"]); - - const linesData = new Deno.Buffer(enc.encode("0\n1\n2\n3\n4\n5\n6\n7\n8\n9")); - // consider data with windows newlines too - const linesDataWindows = new Deno.Buffer( - enc.encode("0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9"), - ); - const lines_ = []; - - for await (const l of readLines(linesData)) { - lines_.push(l); - } - - assertEquals(lines_.length, 10); - assertEquals(lines_, ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]); - - // Now test for "windows" lines - lines_.length = 0; - for await (const l of readLines(linesDataWindows)) { - lines_.push(l); - } - assertEquals(lines_.length, 10); - assertEquals(lines_, ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]); -}); - -Deno.test( - "bufReaderShouldNotShareArrayBufferAcrossReads", - async function (): Promise<void> { - 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", - ); - }, -); - -Deno.test({ - name: "Reset buffer after flush", - async fn(): Promise<void> { - const stringWriter = new StringWriter(); - const bufWriter = new BufWriter(stringWriter); - const encoder = new TextEncoder(); - await bufWriter.write(encoder.encode("hello\nworld\nhow\nare\nyou?\n\n")); - await bufWriter.flush(); - await bufWriter.write(encoder.encode("foobar\n\n")); - await bufWriter.flush(); - const actual = stringWriter.toString(); - assertEquals(actual, "hello\nworld\nhow\nare\nyou?\n\nfoobar\n\n"); - }, -}); - -Deno.test({ - name: "Reset buffer after flush sync", - fn(): void { - const stringWriter = new StringWriter(); - const bufWriter = new BufWriterSync(stringWriter); - const encoder = new TextEncoder(); - bufWriter.writeSync(encoder.encode("hello\nworld\nhow\nare\nyou?\n\n")); - bufWriter.flush(); - bufWriter.writeSync(encoder.encode("foobar\n\n")); - bufWriter.flush(); - const actual = stringWriter.toString(); - assertEquals(actual, "hello\nworld\nhow\nare\nyou?\n\nfoobar\n\n"); - }, -}); - -Deno.test({ - name: "BufWriter.flush should write all bytes", - async fn(): Promise<void> { - const bufSize = 16 * 1024; - const data = new Uint8Array(bufSize); - data.fill("a".charCodeAt(0)); - - const cache: Uint8Array[] = []; - const writer: Deno.Writer = { - write(p: Uint8Array): Promise<number> { - cache.push(p.subarray(0, 1)); - - // Writer that only writes 1 byte at a time - return Promise.resolve(1); - }, - }; - - const bufWriter = new BufWriter(writer); - await bufWriter.write(data); - - await bufWriter.flush(); - const buf = new Uint8Array(cache.length); - for (let i = 0; i < cache.length; i++) buf.set(cache[i], i); - - assertEquals(data, buf); - }, -}); - -Deno.test({ - name: "BufWriterSync.flush should write all bytes", - fn(): void { - const bufSize = 16 * 1024; - const data = new Uint8Array(bufSize); - data.fill("a".charCodeAt(0)); - - const cache: Uint8Array[] = []; - const writer: Deno.WriterSync = { - writeSync(p: Uint8Array): number { - cache.push(p.subarray(0, 1)); - // Writer that only writes 1 byte at a time - return 1; - }, - }; - - const bufWriter = new BufWriterSync(writer); - bufWriter.writeSync(data); - - bufWriter.flush(); - const buf = new Uint8Array(cache.length); - for (let i = 0; i < cache.length; i++) buf.set(cache[i], i); - - assertEquals(data, buf); - }, -}); diff --git a/std/io/ioutil.ts b/std/io/ioutil.ts deleted file mode 100644 index d213095d9..000000000 --- a/std/io/ioutil.ts +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import type { BufReader } from "./bufio.ts"; -type Reader = Deno.Reader; -type Writer = Deno.Writer; -import { assert } from "../_util/assert.ts"; - -const DEFAULT_BUFFER_SIZE = 32 * 1024; - -/** copy N size at the most. - * If read size is lesser than N, then returns nread - * */ -export async function copyN( - r: Reader, - dest: Writer, - size: number, -): Promise<number> { - let bytesRead = 0; - let buf = new Uint8Array(DEFAULT_BUFFER_SIZE); - while (bytesRead < size) { - if (size - bytesRead < DEFAULT_BUFFER_SIZE) { - buf = new Uint8Array(size - bytesRead); - } - const result = await r.read(buf); - const nread = result ?? 0; - bytesRead += nread; - if (nread > 0) { - let n = 0; - while (n < nread) { - n += await dest.write(buf.slice(n, nread)); - } - assert(n === nread, "could not write"); - } - if (result === null) { - break; - } - } - return bytesRead; -} - -/** Read big endian 16bit short from BufReader */ -export async function readShort(buf: BufReader): Promise<number | null> { - const high = await buf.readByte(); - if (high === null) return null; - const low = await buf.readByte(); - if (low === null) throw new Deno.errors.UnexpectedEof(); - return (high << 8) | low; -} - -/** Read big endian 32bit integer from BufReader */ -export async function readInt(buf: BufReader): Promise<number | null> { - const high = await readShort(buf); - if (high === null) return null; - const low = await readShort(buf); - if (low === null) throw new Deno.errors.UnexpectedEof(); - return (high << 16) | low; -} - -const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER); - -/** Read big endian 64bit long from BufReader */ -export async function readLong(buf: BufReader): Promise<number | null> { - const high = await readInt(buf); - if (high === null) return null; - const low = await readInt(buf); - if (low === null) throw new Deno.errors.UnexpectedEof(); - const big = (BigInt(high) << 32n) | BigInt(low); - // We probably should provide a similar API that returns BigInt values. - if (big > MAX_SAFE_INTEGER) { - throw new RangeError( - "Long value too big to be represented as a JavaScript number.", - ); - } - return Number(big); -} - -/** Slice number into 64bit big endian byte array */ -export function sliceLongToBytes(d: number, dest = new Array(8)): number[] { - let big = BigInt(d); - for (let i = 0; i < 8; i++) { - dest[7 - i] = Number(big & 0xffn); - big >>= 8n; - } - return dest; -} diff --git a/std/io/ioutil_test.ts b/std/io/ioutil_test.ts deleted file mode 100644 index ec94ccc5b..000000000 --- a/std/io/ioutil_test.ts +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { - copyN, - readInt, - readLong, - readShort, - sliceLongToBytes, -} from "./ioutil.ts"; -import { StringReader } from "./readers.ts"; -import { BufReader } from "./bufio.ts"; - -class BinaryReader implements Deno.Reader { - index = 0; - - constructor(private bytes: Uint8Array = new Uint8Array(0)) {} - - read(p: Uint8Array): Promise<number | null> { - p.set(this.bytes.subarray(this.index, p.byteLength)); - this.index += p.byteLength; - return Promise.resolve(p.byteLength); - } -} - -Deno.test("testReadShort", async function (): Promise<void> { - const r = new BinaryReader(new Uint8Array([0x12, 0x34])); - const short = await readShort(new BufReader(r)); - assertEquals(short, 0x1234); -}); - -Deno.test("testReadInt", async function (): Promise<void> { - const r = new BinaryReader(new Uint8Array([0x12, 0x34, 0x56, 0x78])); - const int = await readInt(new BufReader(r)); - assertEquals(int, 0x12345678); -}); - -Deno.test("testReadLong", async function (): Promise<void> { - const r = new BinaryReader( - new Uint8Array([0x00, 0x00, 0x00, 0x78, 0x12, 0x34, 0x56, 0x78]), - ); - const long = await readLong(new BufReader(r)); - assertEquals(long, 0x7812345678); -}); - -Deno.test("testReadLong2", async function (): Promise<void> { - const r = new BinaryReader( - new Uint8Array([0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78]), - ); - const long = await readLong(new BufReader(r)); - assertEquals(long, 0x12345678); -}); - -Deno.test("testSliceLongToBytes", function (): void { - const arr = sliceLongToBytes(0x1234567890abcdef); - const actual = readLong(new BufReader(new BinaryReader(new Uint8Array(arr)))); - const expected = readLong( - new BufReader( - new BinaryReader( - new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef]), - ), - ), - ); - assertEquals(actual, expected); -}); - -Deno.test("testSliceLongToBytes2", function (): void { - const arr = sliceLongToBytes(0x12345678); - assertEquals(arr, [0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78]); -}); - -Deno.test("testCopyN1", async function (): Promise<void> { - const w = new Deno.Buffer(); - const r = new StringReader("abcdefghij"); - const n = await copyN(r, w, 3); - assertEquals(n, 3); - assertEquals(new TextDecoder().decode(w.bytes()), "abc"); -}); - -Deno.test("testCopyN2", async function (): Promise<void> { - const w = new Deno.Buffer(); - const r = new StringReader("abcdefghij"); - const n = await copyN(r, w, 11); - assertEquals(n, 10); - assertEquals(new TextDecoder().decode(w.bytes()), "abcdefghij"); -}); - -Deno.test("copyNWriteAllData", async function (): Promise<void> { - const tmpDir = await Deno.makeTempDir(); - const filepath = `${tmpDir}/data`; - const file = await Deno.open(filepath, { create: true, write: true }); - - const size = 16 * 1024 + 1; - const data = "a".repeat(32 * 1024); - const r = new StringReader(data); - const n = await copyN(r, file, size); // Over max file possible buffer - file.close(); - await Deno.remove(filepath); - - assertEquals(n, size); -}); - -Deno.test("testStringReaderEof", async function (): Promise<void> { - const r = new StringReader("abc"); - assertEquals(await r.read(new Uint8Array()), 0); - assertEquals(await r.read(new Uint8Array(4)), 3); - assertEquals(await r.read(new Uint8Array(1)), null); -}); diff --git a/std/io/mod.ts b/std/io/mod.ts deleted file mode 100644 index 257d3be9e..000000000 --- a/std/io/mod.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -export * from "./bufio.ts"; -export * from "./ioutil.ts"; -export * from "./readers.ts"; -export * from "./writers.ts"; -export * from "./streams.ts"; diff --git a/std/io/readers.ts b/std/io/readers.ts deleted file mode 100644 index c3ee2ad14..000000000 --- a/std/io/readers.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// Based on https://github.com/golang/go/blob/0452f9460f50f0f0aba18df43dc2b31906fb66cc/src/io/io.go -// 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 { encode } from "../encoding/utf8.ts"; - -/** Reader utility for strings */ -export class StringReader extends Deno.Buffer { - constructor(s: string) { - super(encode(s).buffer); - } -} - -/** Reader utility for combining multiple readers */ -export class MultiReader implements Deno.Reader { - private readonly readers: Deno.Reader[]; - private currentIndex = 0; - - constructor(...readers: Deno.Reader[]) { - this.readers = readers; - } - - async read(p: Uint8Array): Promise<number | null> { - const r = this.readers[this.currentIndex]; - if (!r) return null; - const result = await r.read(p); - if (result === null) { - this.currentIndex++; - return 0; - } - return result; - } -} - -/** - * A `LimitedReader` reads from `reader` but limits the amount of data returned to just `limit` bytes. - * Each call to `read` updates `limit` to reflect the new amount remaining. - * `read` returns `null` when `limit` <= `0` or - * when the underlying `reader` returns `null`. - */ -export class LimitedReader implements Deno.Reader { - constructor(public reader: Deno.Reader, public limit: number) {} - - async read(p: Uint8Array): Promise<number | null> { - if (this.limit <= 0) { - return null; - } - - if (p.length > this.limit) { - p = p.subarray(0, this.limit); - } - const n = await this.reader.read(p); - if (n == null) { - return null; - } - - this.limit -= n; - return n; - } -} diff --git a/std/io/readers_test.ts b/std/io/readers_test.ts deleted file mode 100644 index e29a984b8..000000000 --- a/std/io/readers_test.ts +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { LimitedReader, MultiReader, StringReader } from "./readers.ts"; -import { StringWriter } from "./writers.ts"; -import { copyN } from "./ioutil.ts"; -import { decode } from "../encoding/utf8.ts"; - -Deno.test("ioStringReader", async function (): Promise<void> { - const r = new StringReader("abcdef"); - const res0 = await r.read(new Uint8Array(6)); - assertEquals(res0, 6); - const res1 = await r.read(new Uint8Array(6)); - assertEquals(res1, null); -}); - -Deno.test("ioStringReader", async function (): Promise<void> { - const r = new StringReader("abcdef"); - const buf = new Uint8Array(3); - const res1 = await r.read(buf); - assertEquals(res1, 3); - assertEquals(decode(buf), "abc"); - const res2 = await r.read(buf); - assertEquals(res2, 3); - assertEquals(decode(buf), "def"); - const res3 = await r.read(buf); - assertEquals(res3, null); - assertEquals(decode(buf), "def"); -}); - -Deno.test("ioMultiReader", async function (): Promise<void> { - const r = new MultiReader(new StringReader("abc"), new StringReader("def")); - const w = new StringWriter(); - const n = await copyN(r, w, 4); - assertEquals(n, 4); - assertEquals(w.toString(), "abcd"); - await Deno.copy(r, w); - assertEquals(w.toString(), "abcdef"); -}); - -Deno.test("ioLimitedReader", async function (): Promise<void> { - let sr = new StringReader("abc"); - let r = new LimitedReader(sr, 2); - let buffer = await Deno.readAll(r); - assertEquals(decode(buffer), "ab"); - assertEquals(decode(await Deno.readAll(sr)), "c"); - sr = new StringReader("abc"); - r = new LimitedReader(sr, 3); - buffer = await Deno.readAll(r); - assertEquals(decode(buffer), "abc"); - assertEquals((await Deno.readAll(r)).length, 0); - sr = new StringReader("abc"); - r = new LimitedReader(sr, 4); - buffer = await Deno.readAll(r); - assertEquals(decode(buffer), "abc"); - assertEquals((await Deno.readAll(r)).length, 0); -}); - -Deno.test("ioLimitedReader", async function (): Promise<void> { - const rb = new StringReader("abc"); - const wb = new StringWriter(); - await Deno.copy(new LimitedReader(rb, -1), wb); - assertEquals(wb.toString(), ""); -}); diff --git a/std/io/streams.ts b/std/io/streams.ts deleted file mode 100644 index 8c726db43..000000000 --- a/std/io/streams.ts +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -/** Create a `Writer` from a `WritableStreamDefaultReader`. */ -export function writerFromStreamWriter( - streamWriter: WritableStreamDefaultWriter<Uint8Array>, -): Deno.Writer { - return { - async write(p: Uint8Array): Promise<number> { - await streamWriter.ready; - await streamWriter.write(p); - return p.length; - }, - }; -} - -/** Create a `Reader` from a `ReadableStreamDefaultReader`. */ -export function readerFromStreamReader( - streamReader: ReadableStreamDefaultReader<Uint8Array>, -): Deno.Reader { - const buffer = new Deno.Buffer(); - - return { - async read(p: Uint8Array): Promise<number | null> { - if (buffer.empty()) { - const res = await streamReader.read(); - if (res.done) { - return null; // EOF - } - - await Deno.writeAll(buffer, res.value); - } - - return buffer.read(p); - }, - }; -} - -/** Create a `WritableStream` from a `Writer`. */ -export function writableStreamFromWriter( - writer: Deno.Writer, -): WritableStream<Uint8Array> { - return new WritableStream({ - async write(chunk) { - await Deno.writeAll(writer, chunk); - }, - }); -} - -/** Create a `ReadableStream` from an `AsyncIterator`. */ -export function readableStreamFromAsyncIterator<T>( - iterator: AsyncIterableIterator<T>, -): ReadableStream<T> { - return new ReadableStream({ - async pull(controller) { - const { value, done } = await iterator.next(); - - if (done) { - controller.close(); - } else { - controller.enqueue(value); - } - }, - }); -} diff --git a/std/io/streams_test.ts b/std/io/streams_test.ts deleted file mode 100644 index 594fc2357..000000000 --- a/std/io/streams_test.ts +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { assert, assertEquals } from "../testing/asserts.ts"; -import { - readableStreamFromAsyncIterator, - readerFromStreamReader, - writableStreamFromWriter, - writerFromStreamWriter, -} from "./streams.ts"; - -function repeat(c: string, bytes: number): Uint8Array { - assertEquals(c.length, 1); - const ui8 = new Uint8Array(bytes); - ui8.fill(c.charCodeAt(0)); - return ui8; -} - -Deno.test("toWriterCheck", async function (): Promise<void> { - const written: string[] = []; - const chunks: string[] = ["hello", "deno", "land"]; - const writableStream = new WritableStream({ - write(chunk): void { - const decoder = new TextDecoder(); - written.push(decoder.decode(chunk)); - }, - }); - - const encoder = new TextEncoder(); - const writer = writerFromStreamWriter(writableStream.getWriter()); - - for (const chunk of chunks) { - const n = await writer.write(encoder.encode(chunk)); - // stream writers always write all the bytes - assertEquals(n, chunk.length); - } - - assertEquals(written, chunks); -}); - -Deno.test("toReaderCheck", async function (): Promise<void> { - const chunks: string[] = ["hello", "deno", "land"]; - const expected = chunks.slice(); - const readChunks: Uint8Array[] = []; - const readableStream = new ReadableStream({ - pull(controller): void { - const encoder = new TextEncoder(); - const chunk = chunks.shift(); - if (!chunk) return controller.close(); - controller.enqueue(encoder.encode(chunk)); - }, - }); - - const decoder = new TextDecoder(); - const reader = readerFromStreamReader(readableStream.getReader()); - - let i = 0; - - while (true) { - const b = new Uint8Array(1024); - const n = await reader.read(b); - - if (n === null) break; - - readChunks.push(b.subarray(0, n)); - assert(i < expected.length); - - i++; - } - - assertEquals( - expected, - readChunks.map((chunk) => decoder.decode(chunk)), - ); -}); - -Deno.test("toReaderBigChunksCheck", async function (): Promise<void> { - const bufSize = 1024; - const chunkSize = 3 * bufSize; - const writer = new Deno.Buffer(); - - // A readable stream can enqueue chunks bigger than Copy bufSize - // Reader returned by toReader should enqueue exceeding bytes - const chunks: string[] = [ - "a".repeat(chunkSize), - "b".repeat(chunkSize), - "c".repeat(chunkSize), - ]; - const expected = chunks.slice(); - const readableStream = new ReadableStream({ - pull(controller): void { - const encoder = new TextEncoder(); - const chunk = chunks.shift(); - if (!chunk) return controller.close(); - - controller.enqueue(encoder.encode(chunk)); - }, - }); - - const reader = readerFromStreamReader(readableStream.getReader()); - const n = await Deno.copy(reader, writer, { bufSize }); - - const expectedWritten = chunkSize * expected.length; - assertEquals(n, chunkSize * expected.length); - assertEquals(writer.length, expectedWritten); -}); - -Deno.test("toReaderBigIrregularChunksCheck", async function (): Promise<void> { - const bufSize = 1024; - const chunkSize = 3 * bufSize; - const writer = new Deno.Buffer(); - - // A readable stream can enqueue chunks bigger than Copy bufSize - // Reader returned by toReader should enqueue exceeding bytes - const chunks: Uint8Array[] = [ - repeat("a", chunkSize), - repeat("b", chunkSize + 253), - repeat("c", chunkSize + 8), - ]; - const expected = new Uint8Array( - chunks - .slice() - .map((chunk) => [...chunk]) - .flat(), - ); - const readableStream = new ReadableStream({ - pull(controller): void { - const chunk = chunks.shift(); - if (!chunk) return controller.close(); - - controller.enqueue(chunk); - }, - }); - - const reader = readerFromStreamReader(readableStream.getReader()); - - const n = await Deno.copy(reader, writer, { bufSize }); - assertEquals(n, expected.length); - assertEquals(expected, writer.bytes()); -}); - -Deno.test("toWritableCheck", async function (): Promise<void> { - const written: string[] = []; - const chunks: string[] = ["hello", "deno", "land"]; - const decoder = new TextDecoder(); - - // deno-lint-ignore require-await - async function write(p: Uint8Array): Promise<number> { - written.push(decoder.decode(p)); - return p.length; - } - - const writableStream = writableStreamFromWriter({ write }); - - const encoder = new TextEncoder(); - const streamWriter = writableStream.getWriter(); - for (const chunk of chunks) { - await streamWriter.write(encoder.encode(chunk)); - } - - assertEquals(written, chunks); -}); - -Deno.test("toReadableCheck", async function (): Promise<void> { - const chunks: string[] = ["hello", "deno", "land"]; - const expected = chunks.slice(); - const readChunks: string[] = []; - const encoder = new TextEncoder(); - - // deno-lint-ignore require-await - async function read(p: Uint8Array): Promise<number | null> { - const chunk = chunks.shift(); - if (chunk === undefined) { - return null; - } else { - const encoded = encoder.encode(chunk); - p.set(encoded); - return encoded.length; - } - } - const iter = Deno.iter({ read }); - const writableStream = readableStreamFromAsyncIterator(iter); - - const decoder = new TextDecoder(); - for await (const chunk of writableStream) { - readChunks.push(decoder.decode(chunk)); - } - - assertEquals(expected, readChunks); -}); diff --git a/std/io/test.ts b/std/io/test.ts deleted file mode 100644 index 590417055..000000000 --- a/std/io/test.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import "./mod.ts"; diff --git a/std/io/writers.ts b/std/io/writers.ts deleted file mode 100644 index a9ce82180..000000000 --- a/std/io/writers.ts +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -type Writer = Deno.Writer; -type WriterSync = Deno.WriterSync; -import { decode, encode } from "../encoding/utf8.ts"; - -/** Writer utility for buffering string chunks */ -export class StringWriter implements Writer, WriterSync { - private chunks: Uint8Array[] = []; - private byteLength = 0; - private cache: string | undefined; - - constructor(private base: string = "") { - const c = encode(base); - this.chunks.push(c); - this.byteLength += c.byteLength; - } - - write(p: Uint8Array): Promise<number> { - return Promise.resolve(this.writeSync(p)); - } - - writeSync(p: Uint8Array): number { - this.chunks.push(p); - this.byteLength += p.byteLength; - this.cache = undefined; - return p.byteLength; - } - - toString(): string { - if (this.cache) { - return this.cache; - } - const buf = new Uint8Array(this.byteLength); - let offs = 0; - for (const chunk of this.chunks) { - buf.set(chunk, offs); - offs += chunk.byteLength; - } - this.cache = decode(buf); - return this.cache; - } -} diff --git a/std/io/writers_test.ts b/std/io/writers_test.ts deleted file mode 100644 index 7135bfff3..000000000 --- a/std/io/writers_test.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { StringWriter } from "./writers.ts"; -import { StringReader } from "./readers.ts"; -import { copyN } from "./ioutil.ts"; - -Deno.test("ioStringWriter", async function (): Promise<void> { - const w = new StringWriter("base"); - const r = new StringReader("0123456789"); - await copyN(r, w, 4); - assertEquals(w.toString(), "base0123"); - await Deno.copy(r, w); - assertEquals(w.toString(), "base0123456789"); -}); - -Deno.test("ioStringWriterSync", function (): void { - const encoder = new TextEncoder(); - const w = new StringWriter(""); - w.writeSync(encoder.encode("deno")); - assertEquals(w.toString(), "deno"); - w.writeSync(encoder.encode("\nland")); - assertEquals(w.toString(), "deno\nland"); -}); |