summaryrefslogtreecommitdiff
path: root/std/util
diff options
context:
space:
mode:
Diffstat (limited to 'std/util')
-rw-r--r--std/util/async.ts117
-rw-r--r--std/util/async_test.ts73
-rw-r--r--std/util/deep_assign.ts31
-rw-r--r--std/util/deep_assign_test.ts24
-rw-r--r--std/util/has_own_property.ts30
5 files changed, 275 insertions, 0 deletions
diff --git a/std/util/async.ts b/std/util/async.ts
new file mode 100644
index 000000000..6e2db69dc
--- /dev/null
+++ b/std/util/async.ts
@@ -0,0 +1,117 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+
+// TODO(ry) It'd be better to make Deferred a class that inherits from
+// Promise, rather than an interface. This is possible in ES2016, however
+// typescript produces broken code when targeting ES5 code.
+// See https://github.com/Microsoft/TypeScript/issues/15202
+// At the time of writing, the github issue is closed but the problem remains.
+export interface Deferred<T> extends Promise<T> {
+ resolve: (value?: T | PromiseLike<T>) => void;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ reject: (reason?: any) => void;
+}
+
+/** Creates a Promise with the `reject` and `resolve` functions
+ * placed as methods on the promise object itself. It allows you to do:
+ *
+ * const p = deferred<number>();
+ * // ...
+ * p.resolve(42);
+ */
+export function deferred<T>(): Deferred<T> {
+ let methods;
+ const promise = new Promise<T>((resolve, reject): void => {
+ methods = { resolve, reject };
+ });
+ return Object.assign(promise, methods)! as Deferred<T>;
+}
+
+interface TaggedYieldedValue<T> {
+ iterator: AsyncIterableIterator<T>;
+ value: T;
+}
+
+/** The MuxAsyncIterator class multiplexes multiple async iterators into a
+ * single stream. It currently makes a few assumptions:
+ * - The iterators do not throw.
+ * - The final result (the value returned and not yielded from the iterator)
+ * does not matter; if there is any, it is discarded.
+ */
+export class MuxAsyncIterator<T> implements AsyncIterable<T> {
+ private iteratorCount = 0;
+ private yields: Array<TaggedYieldedValue<T>> = [];
+ private signal: Deferred<void> = deferred();
+
+ add(iterator: AsyncIterableIterator<T>): void {
+ ++this.iteratorCount;
+ this.callIteratorNext(iterator);
+ }
+
+ private async callIteratorNext(
+ iterator: AsyncIterableIterator<T>
+ ): Promise<void> {
+ const { value, done } = await iterator.next();
+ if (done) {
+ --this.iteratorCount;
+ } else {
+ this.yields.push({ iterator, value });
+ }
+ this.signal.resolve();
+ }
+
+ async *iterate(): AsyncIterableIterator<T> {
+ while (this.iteratorCount > 0) {
+ // Sleep until any of the wrapped iterators yields.
+ await this.signal;
+
+ // Note that while we're looping over `yields`, new items may be added.
+ for (let i = 0; i < this.yields.length; i++) {
+ const { iterator, value } = this.yields[i];
+ yield value;
+ this.callIteratorNext(iterator);
+ }
+
+ // Clear the `yields` list and reset the `signal` promise.
+ this.yields.length = 0;
+ this.signal = deferred();
+ }
+ }
+
+ [Symbol.asyncIterator](): AsyncIterableIterator<T> {
+ return this.iterate();
+ }
+}
+
+/** Collects all Uint8Arrays from an AsyncIterable and retuns a single
+ * Uint8Array with the concatenated contents of all the collected arrays.
+ */
+export async function collectUint8Arrays(
+ it: AsyncIterable<Uint8Array>
+): Promise<Uint8Array> {
+ const chunks = [];
+ let length = 0;
+ for await (const chunk of it) {
+ chunks.push(chunk);
+ length += chunk.length;
+ }
+ if (chunks.length === 1) {
+ // No need to copy.
+ return chunks[0];
+ }
+ const collected = new Uint8Array(length);
+ let offset = 0;
+ for (const chunk of chunks) {
+ collected.set(chunk, offset);
+ offset += chunk.length;
+ }
+ return collected;
+}
+
+// Delays the given milliseconds and resolves.
+export function delay(ms: number): Promise<void> {
+ return new Promise((res): number =>
+ setTimeout((): void => {
+ res();
+ }, ms)
+ );
+}
diff --git a/std/util/async_test.ts b/std/util/async_test.ts
new file mode 100644
index 000000000..adaac1e22
--- /dev/null
+++ b/std/util/async_test.ts
@@ -0,0 +1,73 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+import { test, runIfMain } from "../testing/mod.ts";
+import { assert, assertEquals, assertStrictEq } from "../testing/asserts.ts";
+import { collectUint8Arrays, deferred, MuxAsyncIterator } from "./async.ts";
+
+test(async function asyncDeferred(): Promise<void> {
+ const d = deferred<number>();
+ d.resolve(12);
+});
+
+async function* gen123(): AsyncIterableIterator<number> {
+ yield 1;
+ yield 2;
+ yield 3;
+}
+
+async function* gen456(): AsyncIterableIterator<number> {
+ yield 4;
+ yield 5;
+ yield 6;
+}
+
+test(async function asyncMuxAsyncIterator(): Promise<void> {
+ const mux = new MuxAsyncIterator<number>();
+ mux.add(gen123());
+ mux.add(gen456());
+ const results = new Set();
+ for await (const value of mux) {
+ results.add(value);
+ }
+ assertEquals(results.size, 6);
+});
+
+test(async function collectUint8Arrays0(): Promise<void> {
+ async function* gen(): AsyncIterableIterator<Uint8Array> {}
+ const result = await collectUint8Arrays(gen());
+ assert(result instanceof Uint8Array);
+ assertEquals(result.length, 0);
+});
+
+test(async function collectUint8Arrays0(): Promise<void> {
+ async function* gen(): AsyncIterableIterator<Uint8Array> {}
+ const result = await collectUint8Arrays(gen());
+ assert(result instanceof Uint8Array);
+ assertStrictEq(result.length, 0);
+});
+
+test(async function collectUint8Arrays1(): Promise<void> {
+ const buf = new Uint8Array([1, 2, 3]);
+ async function* gen(): AsyncIterableIterator<Uint8Array> {
+ yield buf;
+ }
+ const result = await collectUint8Arrays(gen());
+ assertStrictEq(result, buf);
+ assertStrictEq(result.length, 3);
+});
+
+test(async function collectUint8Arrays4(): Promise<void> {
+ async function* gen(): AsyncIterableIterator<Uint8Array> {
+ yield new Uint8Array([1, 2, 3]);
+ yield new Uint8Array([]);
+ yield new Uint8Array([4, 5]);
+ yield new Uint8Array([6]);
+ }
+ const result = await collectUint8Arrays(gen());
+ assert(result instanceof Uint8Array);
+ assertStrictEq(result.length, 6);
+ for (let i = 0; i < 6; i++) {
+ assertStrictEq(result[i], i + 1);
+ }
+});
+
+runIfMain(import.meta);
diff --git a/std/util/deep_assign.ts b/std/util/deep_assign.ts
new file mode 100644
index 000000000..b1c9f9ac9
--- /dev/null
+++ b/std/util/deep_assign.ts
@@ -0,0 +1,31 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+export function deepAssign(
+ target: Record<string, unknown>,
+ ...sources: object[]
+): object | undefined {
+ for (let i = 0; i < sources.length; i++) {
+ const source = sources[i];
+ if (!source || typeof source !== `object`) {
+ return;
+ }
+ Object.entries(source).forEach(([key, value]: [string, unknown]): void => {
+ if (value instanceof Date) {
+ target[key] = new Date(value);
+ return;
+ }
+ if (!value || typeof value !== `object`) {
+ target[key] = value;
+ return;
+ }
+ if (Array.isArray(value)) {
+ target[key] = [];
+ }
+ // value is an Object
+ if (typeof target[key] !== `object` || !target[key]) {
+ target[key] = {};
+ }
+ deepAssign(target[key] as Record<string, unknown>, value!);
+ });
+ }
+ return target;
+}
diff --git a/std/util/deep_assign_test.ts b/std/util/deep_assign_test.ts
new file mode 100644
index 000000000..c197344f7
--- /dev/null
+++ b/std/util/deep_assign_test.ts
@@ -0,0 +1,24 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+import { test } from "../testing/mod.ts";
+import { assertEquals, assert } from "../testing/asserts.ts";
+import { deepAssign } from "./deep_assign.ts";
+
+test(function deepAssignTest(): void {
+ const date = new Date("1979-05-27T07:32:00Z");
+ const reg = RegExp(/DENOWOWO/);
+ const obj1 = { deno: { bar: { deno: ["is", "not", "node"] } } };
+ const obj2 = { foo: { deno: date } };
+ const obj3 = { foo: { bar: "deno" }, reg: reg };
+ const actual = deepAssign(obj1, obj2, obj3);
+ const expected = {
+ foo: {
+ deno: new Date("1979-05-27T07:32:00Z"),
+ bar: "deno"
+ },
+ deno: { bar: { deno: ["is", "not", "node"] } },
+ reg: RegExp(/DENOWOWO/)
+ };
+ assert(date !== expected.foo.deno);
+ assert(reg !== expected.reg);
+ assertEquals(actual, expected);
+});
diff --git a/std/util/has_own_property.ts b/std/util/has_own_property.ts
new file mode 100644
index 000000000..707d951d3
--- /dev/null
+++ b/std/util/has_own_property.ts
@@ -0,0 +1,30 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+
+/**
+ * Determines whether an object has a property with the specified name.
+ * Avoid calling prototype builtin `hasOwnProperty` for two reasons:
+ *
+ * 1. `hasOwnProperty` is defined on the object as something else:
+ *
+ * const options = {
+ * ending: 'utf8',
+ * hasOwnProperty: 'foo'
+ * };
+ * options.hasOwnProperty('ending') // throws a TypeError
+ *
+ * 2. The object doesn't inherit from `Object.prototype`:
+ *
+ * const options = Object.create(null);
+ * options.ending = 'utf8';
+ * options.hasOwnProperty('ending'); // throws a TypeError
+ *
+ * @param obj A Object.
+ * @param v A property name.
+ * @see https://eslint.org/docs/rules/no-prototype-builtins
+ */
+export function hasOwnProperty<T>(obj: T, v: PropertyKey): boolean {
+ if (obj == null) {
+ return false;
+ }
+ return Object.prototype.hasOwnProperty.call(obj, v);
+}