import { Buffer } from "./buffer.ts"; import { copy as copyBytes } from "../bytes/mod.ts"; import { assert } from "../testing/asserts.ts"; const DEFAULT_BUFFER_SIZE = 32 * 1024; /** Read Reader `r` until EOF (`null`) and resolve to the content as * Uint8Array`. * * ```ts * * // Example from stdin * const stdinContent = await readAll(Deno.stdin); * * // Example from file * const file = await Deno.open("my_file.txt", {read: true}); * const myFileContent = await readAll(file); * Deno.close(file.rid); * * // Example from buffer * const myData = new Uint8Array(100); * // ... fill myData array with data * const reader = new Buffer(myData.buffer); * const bufferContent = await readAll(reader); * ``` */ export async function readAll(r: Deno.Reader): Promise { const buf = new Buffer(); await buf.readFrom(r); return buf.bytes(); } /** Synchronously reads Reader `r` until EOF (`null`) and returns the content * as `Uint8Array`. * * ```ts * // Example from stdin * const stdinContent = readAllSync(Deno.stdin); * * // Example from file * const file = Deno.openSync("my_file.txt", {read: true}); * const myFileContent = readAllSync(file); * Deno.close(file.rid); * * // Example from buffer * const myData = new Uint8Array(100); * // ... fill myData array with data * const reader = new Buffer(myData.buffer); * const bufferContent = readAllSync(reader); * ``` */ export function readAllSync(r: Deno.ReaderSync): Uint8Array { const buf = new Buffer(); buf.readFromSync(r); return buf.bytes(); } export interface ByteRange { /** The 0 based index of the start byte for a range. */ start: number; /** The 0 based index of the end byte for a range, which is inclusive. */ end: number; } /** * Read a range of bytes from a file or other resource that is readable and * seekable. The range start and end are inclusive of the bytes within that * range. * * ```ts * // Read the first 10 bytes of a file * const file = await Deno.open("example.txt", { read: true }); * const bytes = await readRange(file, { start: 0, end: 9 }); * assert(bytes.length, 10); * ``` */ export async function readRange( r: Deno.Reader & Deno.Seeker, range: ByteRange, ): Promise { // byte ranges are inclusive, so we have to add one to the end let length = range.end - range.start + 1; assert(length > 0, "Invalid byte range was passed."); await r.seek(range.start, Deno.SeekMode.Start); const result = new Uint8Array(length); let off = 0; while (length) { const p = new Uint8Array(Math.min(length, DEFAULT_BUFFER_SIZE)); const nread = await r.read(p); assert(nread !== null, "Unexpected EOF reach while reading a range."); assert(nread > 0, "Unexpected read of 0 bytes while reading a range."); copyBytes(p, result, off); off += nread; length -= nread; assert(length >= 0, "Unexpected length remaining after reading range."); } return result; } /** * Read a range of bytes synchronously from a file or other resource that is * readable and seekable. The range start and end are inclusive of the bytes * within that range. * * ```ts * // Read the first 10 bytes of a file * const file = Deno.openSync("example.txt", { read: true }); * const bytes = readRangeSync(file, { start: 0, end: 9 }); * assert(bytes.length, 10); * ``` */ export function readRangeSync( r: Deno.ReaderSync & Deno.SeekerSync, range: ByteRange, ): Uint8Array { // byte ranges are inclusive, so we have to add one to the end let length = range.end - range.start + 1; assert(length > 0, "Invalid byte range was passed."); r.seekSync(range.start, Deno.SeekMode.Start); const result = new Uint8Array(length); let off = 0; while (length) { const p = new Uint8Array(Math.min(length, DEFAULT_BUFFER_SIZE)); const nread = r.readSync(p); assert(nread !== null, "Unexpected EOF reach while reading a range."); assert(nread > 0, "Unexpected read of 0 bytes while reading a range."); copyBytes(p, result, off); off += nread; length -= nread; assert(length >= 0, "Unexpected length remaining after reading range."); } return result; } /** Write all the content of the array buffer (`arr`) to the writer (`w`). * * ```ts * // Example writing to stdout * const contentBytes = new TextEncoder().encode("Hello World"); * await writeAll(Deno.stdout, contentBytes); * * // Example writing to file * const contentBytes = new TextEncoder().encode("Hello World"); * const file = await Deno.open('test.file', {write: true}); * await writeAll(file, contentBytes); * Deno.close(file.rid); * * // Example writing to buffer * const contentBytes = new TextEncoder().encode("Hello World"); * const writer = new Buffer(); * await writeAll(writer, contentBytes); * console.log(writer.bytes().length); // 11 * ``` */ export async function writeAll(w: Deno.Writer, arr: Uint8Array) { let nwritten = 0; while (nwritten < arr.length) { nwritten += await w.write(arr.subarray(nwritten)); } } /** Synchronously write all the content of the array buffer (`arr`) to the * writer (`w`). * * ```ts * // Example writing to stdout * const contentBytes = new TextEncoder().encode("Hello World"); * writeAllSync(Deno.stdout, contentBytes); * * // Example writing to file * const contentBytes = new TextEncoder().encode("Hello World"); * const file = Deno.openSync('test.file', {write: true}); * writeAllSync(file, contentBytes); * Deno.close(file.rid); * * // Example writing to buffer * const contentBytes = new TextEncoder().encode("Hello World"); * const writer = new Buffer(); * writeAllSync(writer, contentBytes); * console.log(writer.bytes().length); // 11 * ``` */ export function writeAllSync(w: Deno.WriterSync, arr: Uint8Array): void { let nwritten = 0; while (nwritten < arr.length) { nwritten += w.writeSync(arr.subarray(nwritten)); } } /** Turns a Reader, `r`, into an async iterator. * * ```ts * let f = await Deno.open("/etc/passwd"); * for await (const chunk of iter(f)) { * console.log(chunk); * } * f.close(); * ``` * * Second argument can be used to tune size of a buffer. * Default size of the buffer is 32kB. * * ```ts * let f = await Deno.open("/etc/passwd"); * const iter = iter(f, { * bufSize: 1024 * 1024 * }); * for await (const chunk of iter) { * console.log(chunk); * } * f.close(); * ``` * * Iterator uses an internal buffer of fixed size for efficiency; it returns * a view on that buffer on each iteration. It is therefore caller's * responsibility to copy contents of the buffer if needed; otherwise the * next iteration will overwrite contents of previously returned chunk. */ export async function* iter( r: Deno.Reader, options?: { bufSize?: number; }, ): AsyncIterableIterator { const bufSize = options?.bufSize ?? DEFAULT_BUFFER_SIZE; const b = new Uint8Array(bufSize); while (true) { const result = await r.read(b); if (result === null) { break; } yield b.subarray(0, result); } } /** Turns a ReaderSync, `r`, into an iterator. * * ```ts * let f = Deno.openSync("/etc/passwd"); * for (const chunk of iterSync(f)) { * console.log(chunk); * } * f.close(); * ``` * * Second argument can be used to tune size of a buffer. * Default size of the buffer is 32kB. * * ```ts * let f = await Deno.open("/etc/passwd"); * const iter = iterSync(f, { * bufSize: 1024 * 1024 * }); * for (const chunk of iter) { * console.log(chunk); * } * f.close(); * ``` * * Iterator uses an internal buffer of fixed size for efficiency; it returns * a view on that buffer on each iteration. It is therefore caller's * responsibility to copy contents of the buffer if needed; otherwise the * next iteration will overwrite contents of previously returned chunk. */ export function* iterSync( r: Deno.ReaderSync, options?: { bufSize?: number; }, ): IterableIterator { const bufSize = options?.bufSize ?? DEFAULT_BUFFER_SIZE; const b = new Uint8Array(bufSize); while (true) { const result = r.readSync(b); if (result === null) { break; } yield b.subarray(0, result); } } /** Copies from `src` to `dst` until either EOF (`null`) is read from `src` or * an error occurs. It resolves to the number of bytes copied or rejects with * the first error encountered while copying. * * ```ts * const source = await Deno.open("my_file.txt"); * const bytesCopied1 = await copy(source, Deno.stdout); * const destination = await Deno.create("my_file_2.txt"); * const bytesCopied2 = await copy(source, destination); * ``` * * @param src The source to copy from * @param dst The destination to copy to * @param options Can be used to tune size of the buffer. Default size is 32kB */ export async function copy( src: Deno.Reader, dst: Deno.Writer, options?: { bufSize?: number; }, ): Promise { let n = 0; const bufSize = options?.bufSize ?? DEFAULT_BUFFER_SIZE; const b = new Uint8Array(bufSize); let gotEOF = false; while (gotEOF === false) { const result = await src.read(b); if (result === null) { gotEOF = true; } else { let nwritten = 0; while (nwritten < result) { nwritten += await dst.write(b.subarray(nwritten, result)); } n += nwritten; } } return n; }