@rimbu/stream
Version:
Efficient structure representing a sequence of elements, with powerful operations for TypeScript
916 lines (733 loc) • 23 kB
text/typescript
import { Token } from '@rimbu/base';
import {
AsyncOptLazy,
CollectFun,
TraverseState,
type ArrayNonEmpty,
type AsyncCollectFun,
type MaybePromise,
} from '@rimbu/common';
import {
AsyncTransformer,
type AsyncFastIterator,
type AsyncReducer,
type AsyncStream,
type AsyncStreamSource,
} from '@rimbu/stream/async';
import {
closeIters,
fromAsyncStreamSource,
type AsyncStreamSourceHelpers,
} from '@rimbu/stream/async-custom';
export const fixedDoneAsyncIteratorResult = Object.freeze(
Promise.resolve(
Object.freeze({
done: true,
value: undefined,
}) as IteratorResult<any>
)
);
export function isAsyncFastIterator<T>(
iterator: AsyncIterator<T>
): iterator is AsyncFastIterator<T> {
return `fastNext` in iterator;
}
export const emptyAsyncFastIterator: AsyncFastIterator<any> = Object.freeze({
fastNext<O>(otherwise?: AsyncOptLazy<O>): MaybePromise<O> {
return AsyncOptLazy.toMaybePromise(otherwise!);
},
next(): Promise<IteratorResult<any>> {
return fixedDoneAsyncIteratorResult;
},
});
export abstract class AsyncFastIteratorBase<T> implements AsyncFastIterator<T> {
abstract fastNext<O>(otherwise?: AsyncOptLazy<O>): MaybePromise<T | O>;
return?: () => Promise<any>;
async next(): Promise<IteratorResult<T>> {
const done = Symbol('Done');
const value = await this.fastNext(done);
if (done === value) return fixedDoneAsyncIteratorResult;
return { value, done: false };
}
}
export class AsyncOfIterator<T> extends AsyncFastIteratorBase<T> {
constructor(readonly values: ArrayNonEmpty<AsyncOptLazy<T>>) {
super();
}
index = 0;
fastNext<O>(otherwise?: AsyncOptLazy<O>): MaybePromise<T | O> {
const index = this.index;
const values = this.values;
if (index >= values.length) return AsyncOptLazy.toMaybePromise(otherwise!);
return AsyncOptLazy.toMaybePromise(values[this.index++]);
}
}
export class FromResourceIterator<T, R> extends AsyncFastIteratorBase<T> {
constructor(
readonly open: () => MaybePromise<R>,
readonly createSource: (resource: R) => MaybePromise<AsyncStreamSource<T>>,
readonly close: ((resource: R) => MaybePromise<void>) | undefined,
readonly asyncStreamSourceHelpers: AsyncStreamSourceHelpers
) {
super();
this.return = async (): Promise<void> => {
if (close && this.resource) {
await close(this.resource);
this.resource = undefined;
}
await this.iterator?.return?.();
(this.return as any) = undefined;
};
}
resource: R | undefined;
iterator: AsyncFastIterator<T> | undefined;
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
if (undefined === this.iterator) {
const resource = await this.open();
this.resource = resource;
const source = await this.createSource(resource);
this.iterator = this.asyncStreamSourceHelpers
.fromAsyncStreamSource(source)
[Symbol.asyncIterator]();
}
try {
return await this.iterator.fastNext(async () => {
if (undefined !== this.return) {
await this.return();
}
return AsyncOptLazy.toMaybePromise(otherwise!);
});
} catch (err) {
if (undefined !== this.return) {
await this.return();
}
throw err;
}
}
}
export class AsyncUnfoldIterator<T> extends AsyncFastIteratorBase<T> {
constructor(
init: T,
readonly getNext: (
current: T,
index: number,
stop: Token
) => MaybePromise<T | Token>
) {
super();
this.current = init;
}
current: T | Token;
index = 0;
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
const current = this.current;
if (Token === current) return AsyncOptLazy.toMaybePromise(otherwise!);
if (this.index === 0) {
this.index++;
return current;
}
const next = await this.getNext(current, this.index++, Token);
this.current = next;
if (Token === next) return AsyncOptLazy.toMaybePromise(otherwise!);
return next;
}
}
export class AsyncZipWithIterator<
I extends readonly unknown[],
R,
> extends AsyncFastIteratorBase<R> {
constructor(
readonly iterables: { [K in keyof I]: AsyncStreamSource<I[K]> },
readonly zipFun: (...values: I) => MaybePromise<R>,
readonly asyncStreamSourceHelpers: AsyncStreamSourceHelpers
) {
super();
this.sources = iterables.map(
(source): AsyncFastIterator<any> =>
this.asyncStreamSourceHelpers
.fromAsyncStreamSource(source)
[Symbol.asyncIterator]()
);
this.sourcesToClose = new Set(this.sources);
this.return = (): Promise<void> => closeIters(...this.sourcesToClose);
}
readonly sources: AsyncFastIterator<any>[];
readonly sourcesToClose: Set<AsyncFastIterator<any>>;
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<R | O> {
const sources = this.sources;
const done = Symbol('Done');
const result = await Promise.all(
sources.map((source) =>
source.fastNext(() => {
this.sourcesToClose.delete(source);
return done;
})
)
);
if (this.sourcesToClose.size !== sources.length) {
await closeIters(this);
return AsyncOptLazy.toMaybePromise(otherwise!);
}
return (this.zipFun as any)(...result);
}
}
export class AsyncZipAllWithItererator<
I extends readonly unknown[],
F,
R,
> extends AsyncFastIteratorBase<R> {
constructor(
readonly fillValue: AsyncOptLazy<F>,
readonly iters: { [K in keyof I]: AsyncStreamSource<I[K]> },
readonly zipFun: (
...values: { [K in keyof I]: I[K] | F }
) => MaybePromise<R>,
readonly asyncStreamSourceHelpers: AsyncStreamSourceHelpers
) {
super();
this.sources = iters.map(
(o): AsyncFastIterator<any> =>
this.asyncStreamSourceHelpers
.fromAsyncStreamSource(o)
[Symbol.asyncIterator]()
);
this.sourcesToClose = new Set(this.sources);
this.return = (): Promise<void> => closeIters(...this.sourcesToClose);
}
readonly sources: AsyncFastIterator<any>[];
readonly sourcesToClose: Set<AsyncFastIterator<any>>;
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<R | O> {
if (this.sourcesToClose.size === 0) {
return AsyncOptLazy.toMaybePromise(otherwise!);
}
const sources = this.sources;
const fillValue = this.fillValue;
const result = await Promise.all(
sources.map((source) => {
if (this.sourcesToClose.has(source)) {
return source.fastNext(() => {
this.sourcesToClose.delete(source);
return AsyncOptLazy.toMaybePromise(fillValue);
});
}
return AsyncOptLazy.toMaybePromise(fillValue);
})
);
if (this.sourcesToClose.size === 0) {
return AsyncOptLazy.toMaybePromise(otherwise!);
}
return (this.zipFun as any)(...result);
}
}
export class FromAsyncIterator<T> implements AsyncFastIterator<T> {
constructor(
readonly source: AsyncIterator<T>,
close?: () => MaybePromise<void>
) {
if (source.return && close) {
this.return = (): Promise<any> =>
Promise.all([source.return?.(), close?.()]);
} else if (source.return) {
this.return = (): Promise<any> | undefined => source.return?.();
} else if (close) {
this.return = close;
}
}
return?: () => MaybePromise<any>;
next(): Promise<IteratorResult<T>> {
return this.source.next();
}
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
const result = await this.source.next();
if (result.done) {
await closeIters(this);
return AsyncOptLazy.toMaybePromise(otherwise!);
}
return result.value;
}
}
export class FromIterator<T> extends AsyncFastIteratorBase<T> {
constructor(
readonly iterator: Iterator<T>,
close?: () => MaybePromise<void>
) {
super();
if (close !== undefined) {
this.return = close;
}
}
return?: () => MaybePromise<any>;
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
const result = this.iterator.next();
if (result.done) {
await closeIters(this);
return AsyncOptLazy.toMaybePromise(otherwise!);
}
return result.value;
}
}
export class FromPromise<T> extends AsyncFastIteratorBase<T> {
constructor(
readonly promise: () => Promise<AsyncStreamSource<T>>,
readonly asyncStreamSourceHelpers: AsyncStreamSourceHelpers,
close?: () => MaybePromise<void>
) {
super();
this.return = async (): Promise<void> => {
if (close) {
await close();
}
if (this.iterator) {
await this.iterator.return?.();
}
};
}
iterator: AsyncFastIterator<T> | undefined;
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
if (this.iterator === undefined) {
const source = await this.promise();
this.iterator = this.asyncStreamSourceHelpers
.fromAsyncStreamSource(source)
[Symbol.asyncIterator]();
}
return this.iterator.fastNext(otherwise!);
}
}
export class AsyncPrependIterator<T> extends AsyncFastIteratorBase<T> {
constructor(
readonly source: AsyncFastIterator<T>,
readonly item: AsyncOptLazy<T>
) {
super();
this.return = async (): Promise<void> => {
if (this.prependDone) return closeIters(this.source);
};
}
prependDone = false;
fastNext<O>(otherwise?: AsyncOptLazy<O>): MaybePromise<T | O> {
if (this.prependDone) {
return this.source.fastNext(otherwise!);
}
this.prependDone = true;
return AsyncOptLazy.toMaybePromise(this.item);
}
}
export class AsyncAppendIterator<T> extends AsyncFastIteratorBase<T> {
constructor(
readonly source: AsyncFastIterator<T>,
readonly item: AsyncOptLazy<T>
) {
super();
this.return = async (): Promise<void> => {
if (!this.appendDone) return closeIters(source);
};
}
appendDone = false;
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
if (this.appendDone) return AsyncOptLazy.toMaybePromise(otherwise!);
const done = Symbol('Done');
const value = await this.source.fastNext(done);
if (done !== value) return value;
this.appendDone = true;
return AsyncOptLazy.toMaybePromise(this.item);
}
}
export class AsyncIndexedIterator<T> extends AsyncFastIteratorBase<
[number, T]
> {
index: number;
constructor(
readonly source: AsyncFastIterator<T>,
readonly startIndex = 0
) {
super();
this.index = startIndex;
this.return = (): Promise<void> => closeIters(source);
}
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<[number, T] | O> {
const done = Symbol('Done');
const value = await this.source.fastNext(done);
if (done === value) {
return AsyncOptLazy.toMaybePromise(otherwise!);
}
return [this.index++, value];
}
}
export class AsyncMapIterator<T, T2> extends AsyncFastIteratorBase<T2> {
constructor(
readonly source: AsyncFastIterator<T>,
readonly mapFun: (value: T, index: number) => MaybePromise<T2>
) {
super();
this.return = (): Promise<void> => closeIters(source);
}
readonly state = TraverseState();
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T2 | O> {
const state = this.state;
const done = Symbol('Done');
const next = await this.source.fastNext(done);
if (done === next) {
return AsyncOptLazy.toMaybePromise(otherwise!);
}
return this.mapFun(next, state.nextIndex());
}
}
export class AsyncMapPureIterator<
T,
A extends readonly unknown[],
T2,
> extends AsyncFastIteratorBase<T2> {
constructor(
readonly source: AsyncFastIterator<T>,
readonly mapFun: (value: T, ...args: A) => MaybePromise<T2>,
readonly args: A
) {
super();
this.return = (): Promise<void> => closeIters(source);
}
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T2 | O> {
const done = Symbol('Done');
const next = await this.source.fastNext(done);
if (done === next) return AsyncOptLazy.toMaybePromise(otherwise!);
return this.mapFun(next, ...this.args);
}
}
export class AsyncConcatIterator<T> extends AsyncFastIteratorBase<T> {
iterator: AsyncFastIterator<T>;
constructor(
readonly source: AsyncStream<T>,
readonly otherSources: AsyncStreamSource<T>[],
readonly asyncStreamSourceHelpers: AsyncStreamSourceHelpers
) {
super();
this.iterator = source[Symbol.asyncIterator]();
this.return = (): Promise<void> => closeIters(this.iterator);
}
sourceIndex = 0;
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
const done = Symbol('Done');
let value: T | typeof done;
const length = this.otherSources.length;
const { asyncStreamSourceHelpers } = this;
while (done === (value = await this.iterator.fastNext(done))) {
if (this.sourceIndex >= length) {
return AsyncOptLazy.toMaybePromise(otherwise!);
}
let nextSource: AsyncStreamSource<T> =
this.otherSources[this.sourceIndex++];
while (
asyncStreamSourceHelpers.isEmptyAsyncStreamSourceInstance(nextSource)
) {
if (this.sourceIndex >= length) {
return AsyncOptLazy.toMaybePromise(otherwise!);
}
nextSource = this.otherSources[this.sourceIndex++];
}
this.iterator = asyncStreamSourceHelpers
.fromAsyncStreamSource(nextSource)
[Symbol.asyncIterator]();
}
return value;
}
}
export class AsyncFilterIterator<T> extends AsyncFastIteratorBase<T> {
constructor(
readonly source: AsyncFastIterator<T>,
readonly pred: (
value: T,
index: number,
halt: () => void
) => MaybePromise<boolean>,
readonly negate: boolean
) {
super();
this.return = (): Promise<void> => closeIters(source);
}
readonly state = TraverseState();
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
const state = this.state;
if (state.halted) {
return AsyncOptLazy.toMaybePromise(otherwise!);
}
const done = Symbol('Done');
let value: T | typeof done;
const source = this.source;
const pred = this.pred;
const halt = state.halt;
const negate = this.negate;
while (!state.halted && done !== (value = await source.fastNext(done))) {
const cond = await pred(value, state.nextIndex(), halt);
if (cond !== negate) return value;
}
if (state.halted && done !== value!) {
await closeIters(this);
}
return AsyncOptLazy.toMaybePromise(otherwise!);
}
}
export class AsyncFilterPureIterator<
T,
A extends readonly unknown[],
> extends AsyncFastIteratorBase<T> {
constructor(
readonly source: AsyncFastIterator<T>,
readonly pred: (value: T, ...args: A) => MaybePromise<boolean>,
readonly args: A,
readonly negate: boolean
) {
super();
this.return = (): Promise<void> => closeIters(source);
}
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
const done = Symbol('Done');
let value: T | typeof done;
const source = this.source;
const pred = this.pred;
const args = this.args;
const negate = this.negate;
while (done !== (value = await source.fastNext(done))) {
const cond = await pred(value, ...args);
if (cond !== negate) return value;
}
return AsyncOptLazy.toMaybePromise(otherwise!);
}
}
export class AsyncCollectIterator<T, R> extends AsyncFastIteratorBase<R> {
constructor(
readonly source: AsyncFastIterator<T>,
readonly collectFun: AsyncCollectFun<T, R>
) {
super();
this.return = (): Promise<void> => closeIters(source);
}
readonly state = TraverseState();
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<R | O> {
const state = this.state;
if (state.halted) {
return AsyncOptLazy.toMaybePromise(otherwise!);
}
const { halt } = state;
const done = Symbol('Done');
let value: T | typeof done;
const source = this.source;
const collectFun = this.collectFun;
try {
while (!state.halted && done !== (value = await source.fastNext(done))) {
const result = await collectFun(
value,
state.nextIndex(),
CollectFun.Skip,
halt
);
if (CollectFun.Skip === result) continue;
return result as R;
}
return AsyncOptLazy.toMaybePromise(otherwise!);
} finally {
if (state.halted && done !== value!) {
await closeIters(this);
}
}
}
}
export class AsyncDropWhileIterator<T> extends AsyncFastIteratorBase<T> {
constructor(
readonly source: AsyncFastIterator<T>,
readonly pred: (value: T, index: number) => MaybePromise<boolean>,
readonly negate: boolean
) {
super();
this.return = (): Promise<void> => closeIters(source);
}
pass = false;
index = 0;
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
const source = this.source;
if (this.pass) return source.fastNext(otherwise!);
const done = Symbol('Done');
let value: T | typeof done;
const negate = this.negate;
while (done !== (value = await source.fastNext(done))) {
this.pass = (await this.pred(value, this.index++)) === negate;
if (this.pass) return value;
}
return AsyncOptLazy.toMaybePromise(otherwise!);
}
}
export class AsyncTakeIterator<T> extends AsyncFastIteratorBase<T> {
constructor(
readonly source: AsyncFastIterator<T>,
readonly amount: number
) {
super();
this.return = (): Promise<void> => closeIters(source);
}
i = 0;
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
if (this.i++ >= this.amount) {
await closeIters(this);
(this.return as any) = undefined;
return AsyncOptLazy.toMaybePromise(otherwise!);
}
return this.source.fastNext(otherwise!);
}
}
export class AsyncDropIterator<T> extends AsyncFastIteratorBase<T> {
remain: number;
constructor(
readonly source: AsyncFastIterator<T>,
readonly amount: number
) {
super();
this.return = (): Promise<void> => closeIters(source);
this.remain = amount;
}
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
const source = this.source;
if (this.remain <= 0) return source.fastNext(otherwise!);
const done = Symbol('Done');
let value: T | typeof done;
while (done !== (value = await source.fastNext(done))) {
if (this.remain-- <= 0) {
return value;
}
}
return AsyncOptLazy.toMaybePromise(otherwise!);
}
}
export class AsyncRepeatIterator<T> extends AsyncFastIteratorBase<T> {
iterator: AsyncFastIterator<T>;
remain: number | undefined;
constructor(
readonly source: AsyncStream<T>,
readonly amount?: number
) {
super();
this.iterator = source[Symbol.asyncIterator]();
this.return = (): Promise<void> => closeIters(this.iterator);
this.remain = amount;
}
isEmpty = true;
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<T | O> {
const done = Symbol('Done');
const iterator = this.iterator;
let value = await iterator.fastNext(done);
if (done !== value) {
this.isEmpty = false;
return value;
}
if (this.isEmpty) {
return AsyncOptLazy.toMaybePromise(otherwise!) as O;
}
if (undefined !== this.remain) {
this.remain--;
if (this.remain <= 0) {
return AsyncOptLazy.toMaybePromise(otherwise!);
}
}
this.iterator = this.source[Symbol.asyncIterator]();
value = await this.iterator.fastNext(done);
if (done === value) {
return AsyncOptLazy.toMaybePromise(otherwise!);
}
return value;
}
}
export class AsyncReduceIterator<I, R> extends AsyncFastIteratorBase<R> {
constructor(
readonly sourceIterator: AsyncFastIterator<I>,
readonly reducer: AsyncReducer<I, R>
) {
super();
this.return = async (): Promise<void> => {
if (undefined !== this.#instance && !this.#instance.halted) {
await Promise.all([
closeIters(sourceIterator),
this.#instance.onClose(),
]);
} else {
await closeIters(sourceIterator);
}
};
}
#instance: AsyncReducer.Instance<I, R> | undefined;
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<R | O> {
if (undefined === this.#instance) {
this.#instance = await this.reducer.compile();
}
const reducerInstance = this.#instance;
try {
if (reducerInstance.halted) {
return (await AsyncOptLazy.toMaybePromise(otherwise)) as O;
}
const done = Symbol('Done');
const nextInput = await this.sourceIterator.fastNext(done);
if (done === nextInput) {
this.#instance.halt();
return AsyncOptLazy.toMaybePromise(otherwise!);
}
await reducerInstance.next(nextInput);
return reducerInstance.getOutput();
} finally {
if (reducerInstance.halted) {
this.return?.();
(this.return as any) = undefined;
}
}
}
}
export class AsyncTransformerFastIterator<
T,
R,
> extends AsyncFastIteratorBase<R> {
constructor(
readonly sourceIterator: AsyncFastIterator<T>,
readonly transformer: AsyncTransformer.Accept<T, R>
) {
super();
this.return = async (): Promise<void> => {
if (undefined !== this.#instance) {
await Promise.all([
closeIters(sourceIterator),
this.#instance.onClose(),
]);
} else {
await closeIters(sourceIterator);
}
};
}
#done = false;
#instance: AsyncReducer.Instance<T, AsyncStreamSource<R>> | undefined;
#currentValues: AsyncFastIterator<R> | undefined;
async fastNext<O>(otherwise?: AsyncOptLazy<O>): Promise<R | O> {
if (this.#done) {
return AsyncOptLazy.toPromise(otherwise) as O;
}
if (undefined === this.#instance) {
this.#instance = await AsyncTransformer.from(this.transformer).compile();
}
const transformerInstance = this.#instance;
const done = Symbol('done');
let nextValue: R | typeof done;
while (
undefined === this.#currentValues ||
done === (nextValue = await this.#currentValues.fastNext(done))
) {
const nextSource = await this.sourceIterator.fastNext(done);
if (done === nextSource) {
if (transformerInstance.halted) {
return AsyncOptLazy.toMaybePromise(otherwise!);
}
this.#done = true;
transformerInstance.halt();
(this.return as any) = undefined;
} else {
await transformerInstance.next(nextSource);
}
const nextValuesSource = await transformerInstance.getOutput();
this.#currentValues =
fromAsyncStreamSource(nextValuesSource)[Symbol.asyncIterator]();
}
return nextValue;
}
}