Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion archive/tar_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Tar } from "./tar.ts";
import { Untar } from "./untar.ts";
import { Buffer } from "../io/buffer.ts";
import { copy } from "../streams/copy.ts";
import { readAll } from "../streams/read_all.ts";
import { readAll } from "../io/read_all.ts";
import { filePath, testdataDir } from "./_test_common.ts";

Deno.test("createTarArchive", async function () {
Expand Down
2 changes: 1 addition & 1 deletion archive/untar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
UstarFields,
ustarStructure,
} from "./_common.ts";
import { readAll } from "../streams/read_all.ts";
import { readAll } from "../io/read_all.ts";
import type { Reader } from "../io/types.ts";

/**
Expand Down
2 changes: 1 addition & 1 deletion archive/untar_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from "./untar.ts";
import { Buffer } from "../io/buffer.ts";
import { copy } from "../streams/copy.ts";
import { readAll } from "../streams/read_all.ts";
import { readAll } from "../io/read_all.ts";
import { filePath, testdataDir } from "./_test_common.ts";

interface TestEntry {
Expand Down
4 changes: 2 additions & 2 deletions http/server_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import { ConnInfo, serve, serveListener, Server, serveTls } from "./server.ts";
import { mockConn as createMockConn } from "./_mock_conn.ts";
import { dirname, fromFileUrl, join, resolve } from "../path/mod.ts";
import { writeAll } from "../streams/write_all.ts";
import { readAll } from "../streams/read_all.ts";
import { writeAll } from "../io/write_all.ts";
import { readAll } from "../io/read_all.ts";
import { delay } from "../async/mod.ts";
import {
assert,
Expand Down
11 changes: 11 additions & 0 deletions io/_common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.

import type { Closer } from "./types.ts";

export function isCloser(value: unknown): value is Closer {
return typeof value === "object" && value !== null && value !== undefined &&
"close" in value &&
// deno-lint-ignore no-explicit-any
typeof (value as Record<string, any>)["close"] === "function";
}
5 changes: 5 additions & 0 deletions io/_constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.

export const DEFAULT_CHUNK_SIZE = 16_640;
export const DEFAULT_BUFFER_SIZE = 32 * 1024;
11 changes: 11 additions & 0 deletions io/_test_common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,14 @@ export class BinaryReader implements Reader {
return Promise.resolve(p.byteLength);
}
}

// N controls how many iterations of certain checks are performed.
const N = 100;

export function init(): Uint8Array {
const testBytes = new Uint8Array(N);
for (let i = 0; i < N; i++) {
testBytes[i] = "a".charCodeAt(0) + (i % 26);
}
return testBytes;
}
2 changes: 1 addition & 1 deletion io/buffer_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
assertThrows,
} from "../assert/mod.ts";
import { Buffer } from "./buffer.ts";
import { writeAllSync } from "../streams/write_all.ts";
import { writeAllSync } from "./write_all.ts";

const MAX_SIZE = 2 ** 32 - 2;
// N controls how many iterations of certain checks are performed.
Expand Down
50 changes: 50 additions & 0 deletions io/copy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.

import { DEFAULT_BUFFER_SIZE } from "./_constants.ts";
import type { Reader, Writer } from "./types.ts";

/**
* 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.
*
* @example
* ```ts
* import { copy } from "https://deno.land/std@$STD_VERSION/io/copy.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: Reader,
dst: Writer,
options?: {
bufSize?: number;
},
): Promise<number> {
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;
}
22 changes: 22 additions & 0 deletions io/copy_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { copy } from "./copy.ts";
import { assertEquals } from "../assert/assert_equals.ts";

const SRC_PATH = "./io/testdata/copy-src.txt";
const DST_PATH = "./io/testdata/copy-dst.txt";

Deno.test("copy()", async () => {
try {
using srcFile = await Deno.open(SRC_PATH);
using dstFile = await Deno.open(DST_PATH, {
create: true,
write: true,
});
await copy(srcFile, dstFile);
const srcOutput = await Deno.readFile(SRC_PATH);
const dstOutput = await Deno.readFile(DST_PATH);
assertEquals(srcOutput, dstOutput);
} finally {
await Deno.remove(DST_PATH);
}
});
2 changes: 1 addition & 1 deletion io/limited_reader_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { assertEquals } from "../assert/mod.ts";
import { LimitedReader } from "./limited_reader.ts";
import { StringWriter } from "./string_writer.ts";
import { copy } from "../streams/copy.ts";
import { readAll } from "../streams/read_all.ts";
import { readAll } from "./read_all.ts";
import { StringReader } from "./string_reader.ts";

Deno.test("ioLimitedReader", async function () {
Expand Down
7 changes: 6 additions & 1 deletion io/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
* utilities are also deprecated. Consider using web streams instead.
*
* @module
* @deprecated (will be removed after 1.0.0) Use the [Web Streams API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Streams_API} instead.
*/

export * from "./buf_reader.ts";
export * from "./buf_writer.ts";
export * from "./buffer.ts";
export * from "./copy.ts";
export * from "./copy_n.ts";
export * from "./limited_reader.ts";
export * from "./multi_reader.ts";
export * from "./read_all.ts";
export * from "./read_delim.ts";
export * from "./read_int.ts";
export * from "./read_lines.ts";
Expand All @@ -26,3 +27,7 @@ export * from "./read_string_delim.ts";
export * from "./slice_long_to_bytes.ts";
export * from "./string_reader.ts";
export * from "./string_writer.ts";
export * from "./to_readable_stream.ts";
export * from "./to_writable_stream.ts";
export * from "./types.ts";
export * from "./write_all.ts";
73 changes: 73 additions & 0 deletions io/read_all.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.

import { concat } from "../bytes/concat.ts";
import { DEFAULT_CHUNK_SIZE } from "./_constants.ts";
import type { Reader, ReaderSync } from "./types.ts";

/**
* Read {@linkcode Reader} `r` until EOF (`null`) and resolve to the content as
* {@linkcode Uint8Array}.
*
* @example
* ```ts
* import { readAll } from "https://deno.land/std@$STD_VERSION/io/read_all.ts";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: Buffer() is not imported here as it's deprecated.

*
* // 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);
* file.close();
* ```
*/
export async function readAll(reader: Reader): Promise<Uint8Array> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function differs from readAll() from std/streams in that it doesn't depend on Buffer() from std/io. It's simpler, only relying on concat(). This should be a non-breaking change.

const chunks: Uint8Array[] = [];
while (true) {
let chunk = new Uint8Array(DEFAULT_CHUNK_SIZE);
const n = await reader.read(chunk);
if (n === null) {
break;
}
if (n < DEFAULT_CHUNK_SIZE) {
chunk = chunk.subarray(0, n);
}
chunks.push(chunk);
}
return concat(chunks);
}

/**
* Synchronously reads {@linkcode ReaderSync} `r` until EOF (`null`) and returns
* the content as {@linkcode Uint8Array}.
*
* @example
* ```ts
* import { readAllSync } from "https://deno.land/std@$STD_VERSION/io/read_all.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);
* file.close();
* ```
*/
export function readAllSync(reader: ReaderSync): Uint8Array {
const chunks: Uint8Array[] = [];
while (true) {
const chunk = new Uint8Array(DEFAULT_CHUNK_SIZE);
const n = reader.readSync(chunk);
if (n === null) {
break;
}
if (n < DEFAULT_CHUNK_SIZE) {
chunks.push(chunk.subarray(0, n));
break;
}
chunks.push(chunk);
}
return concat(chunks);
}
22 changes: 22 additions & 0 deletions io/read_all_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

import { assert, assertEquals } from "../assert/mod.ts";
import { readAll, readAllSync } from "./read_all.ts";
import { Buffer } from "./buffer.ts";
import { init } from "./_test_common.ts";

Deno.test("readAll()", async () => {
const testBytes = init();
assert(testBytes);
const reader = new Buffer(testBytes.buffer);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We rely on Buffer() here but should work on removing that dependency in the future (in a PR).

const actualBytes = await readAll(reader);
assertEquals(testBytes, actualBytes);
});

Deno.test("readAllSync()", () => {
const testBytes = init();
assert(testBytes);
const reader = new Buffer(testBytes.buffer);
const actualBytes = readAllSync(reader);
assertEquals(testBytes, actualBytes);
});
1 change: 1 addition & 0 deletions io/testdata/copy-src.txt

Large diffs are not rendered by default.

75 changes: 75 additions & 0 deletions io/to_readable_stream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.

import { DEFAULT_CHUNK_SIZE } from "./_constants.ts";
import { isCloser } from "./_common.ts";
import type { Closer, Reader } from "./types.ts";

/** Options for {@linkcode toReadableStream}. */
export interface ToReadableStreamOptions {
/** If the `reader` is also a `Closer`, automatically close the `reader`
* when `EOF` is encountered, or a read error occurs.
*
* @default {true}
*/
autoClose?: boolean;

/** The size of chunks to allocate to read, the default is ~16KiB, which is
* the maximum size that Deno operations can currently support. */
chunkSize?: number;

/** The queuing strategy to create the `ReadableStream` with. */
strategy?: QueuingStrategy<Uint8Array>;
}

/**
* Create a {@linkcode ReadableStream} of {@linkcode Uint8Array}s from a
* {@linkcode Reader}.
*
* When the pull algorithm is called on the stream, a chunk from the reader
* will be read. When `null` is returned from the reader, the stream will be
* closed along with the reader (if it is also a `Closer`).
*
* @example
* ```ts
* import { toReadableStream } from "https://deno.land/std@$STD_VERSION/io/to_readable_stream.ts";
*
* const file = await Deno.open("./file.txt", { read: true });
* const fileStream = toReadableStream(file);
* ```
*/
export function toReadableStream(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: renamed from readableStreamfFromReader().

reader: Reader | (Reader & Closer),
{
autoClose = true,
chunkSize = DEFAULT_CHUNK_SIZE,
strategy,
}: ToReadableStreamOptions = {},
): ReadableStream<Uint8Array> {
return new ReadableStream({
async pull(controller) {
const chunk = new Uint8Array(chunkSize);
try {
const read = await reader.read(chunk);
if (read === null) {
if (isCloser(reader) && autoClose) {
reader.close();
}
controller.close();
return;
}
controller.enqueue(chunk.subarray(0, read));
} catch (e) {
controller.error(e);
if (isCloser(reader)) {
reader.close();
}
}
},
cancel() {
if (isCloser(reader) && autoClose) {
reader.close();
}
},
}, strategy);
}
Loading