@rimbu/stream
Version:
Efficient structure representing a sequence of elements, with powerful operations for TypeScript
795 lines (630 loc) • 18.1 kB
text/typescript
import { Token } from '@rimbu/base';
import { CollectFun, OptLazy, TraverseState } from '@rimbu/common';
import { type Reducer } from '@rimbu/stream';
import {
type FastIterator,
type Stream,
type StreamSource,
} from '@rimbu/stream';
import {
fromStreamSource,
type StreamSourceHelpers,
} from '@rimbu/stream/custom';
export const fixedDoneIteratorResult: IteratorResult<any> = Object.freeze({
done: true,
value: undefined,
});
export const emptyFastIterator: FastIterator<any> = Object.freeze({
fastNext<O>(otherwise?: OptLazy<O>): O {
return OptLazy(otherwise) as O;
},
next(): IteratorResult<any> {
return fixedDoneIteratorResult;
},
});
export function isFastIterator<T>(
iterator: Iterator<T>
): iterator is FastIterator<T> {
return `fastNext` in iterator;
}
/**
* A base class for `FastIterator` instances, that takes implements the default `next`
* function based on the abstract `fastNext` function.
*/
export abstract class FastIteratorBase<T> implements FastIterator<T> {
abstract fastNext<O>(otherwise?: OptLazy<O>): T | O;
next(): IteratorResult<T> {
const done = Symbol('Done');
const value = this.fastNext(done);
if (done === value) return fixedDoneIteratorResult;
return { value, done: false };
}
}
export class ReducerFastIterator<T, R> extends FastIteratorBase<R> {
constructor(
readonly sourceIterator: FastIterator<T>,
readonly reducerInstance: Reducer.Instance<T, R>
) {
super();
}
fastNext<O>(otherwise?: OptLazy<O>): R | O {
if (this.reducerInstance.halted) {
return OptLazy(otherwise) as O;
}
const done = Symbol('done');
const nextInput = this.sourceIterator.fastNext(done);
if (done === nextInput) {
this.reducerInstance.halt();
return OptLazy(otherwise) as O;
}
this.reducerInstance.next(nextInput);
return this.reducerInstance.getOutput();
}
}
export class TransformerFastIterator<T, R> extends FastIteratorBase<R> {
constructor(
readonly sourceIterator: FastIterator<T>,
readonly transformerInstance: Reducer.Instance<T, StreamSource<R>>
) {
super();
}
#done = false;
#currentValues: FastIterator<R> | undefined;
fastNext<O>(otherwise?: OptLazy<O>): R | O {
if (this.#done) {
return OptLazy(otherwise) as O;
}
const done = Symbol('done');
let nextValue: R | typeof done;
while (
undefined === this.#currentValues ||
done === (nextValue = this.#currentValues.fastNext(done))
) {
if (this.transformerInstance.halted) {
this.#done = true;
return OptLazy(otherwise) as O;
}
const nextSource = this.sourceIterator.fastNext(done);
if (done === nextSource) {
if (this.transformerInstance.halted) {
this.#done = true;
return OptLazy(otherwise) as O;
}
this.#done = true;
this.transformerInstance.halt();
} else {
this.transformerInstance.next(nextSource);
}
const nextValuesSource = this.transformerInstance.getOutput();
this.#currentValues =
fromStreamSource(nextValuesSource)[Symbol.iterator]();
}
return nextValue;
}
}
export class ConcatIterator<T> extends FastIteratorBase<T> {
iterator: FastIterator<T>;
constructor(
readonly source: Stream<T>,
readonly otherSources: StreamSource<T>[],
readonly streamSourceHelpers: StreamSourceHelpers
) {
super();
this.iterator = source[Symbol.iterator]();
}
sourceIndex = 0;
fastNext<O>(otherwise?: OptLazy<O>): T | O {
const done = Symbol('Done');
let value: T | typeof done;
const length = this.otherSources.length;
const { streamSourceHelpers } = this;
while (done === (value = this.iterator.fastNext(done))) {
if (this.sourceIndex >= length) return OptLazy(otherwise) as O;
let nextSource: StreamSource<T> = this.otherSources[this.sourceIndex++];
while (streamSourceHelpers.isEmptyStreamSourceInstance(nextSource)) {
if (this.sourceIndex >= length) {
return OptLazy(otherwise) as O;
}
nextSource = this.otherSources[this.sourceIndex++];
}
this.iterator = streamSourceHelpers
.fromStreamSource(nextSource)
[Symbol.iterator]();
}
return value;
}
}
export class IndexedIterator<T> extends FastIteratorBase<[number, T]> {
constructor(
readonly source: FastIterator<T>,
readonly startIndex: number
) {
super();
}
index = this.startIndex;
fastNext<O>(otherwise?: OptLazy<O> | undefined): [number, T] | O {
const done = Symbol('Done');
const value = this.source.fastNext(done);
if (done === value) {
return OptLazy(otherwise) as O;
}
return [this.index++, value];
}
}
export class FilterIterator<T> extends FastIteratorBase<T> {
constructor(
readonly source: FastIterator<T>,
readonly pred: (value: T, index: number, halt: () => void) => boolean,
readonly negate: boolean
) {
super();
}
readonly state = TraverseState();
fastNext<O>(otherwise?: OptLazy<O>): T | O {
const state = this.state;
if (state.halted) return OptLazy(otherwise) as O;
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 = source.fastNext(done))) {
if (pred(value, state.nextIndex(), halt) !== negate) return value;
}
return OptLazy(otherwise) as O;
}
}
export class FilterPureIterator<
T,
A extends readonly unknown[],
> extends FastIteratorBase<T> {
constructor(
readonly source: FastIterator<T>,
readonly pred: (value: T, ...args: A) => boolean,
readonly args: A,
readonly negate: boolean
) {
super();
}
fastNext<O>(otherwise?: OptLazy<O>): 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 = source.fastNext(done))) {
if (pred(value, ...args) !== negate) return value;
}
return OptLazy(otherwise) as O;
}
}
export class CollectIterator<T, R> extends FastIteratorBase<R> {
constructor(
readonly source: FastIterator<T>,
readonly collectFun: CollectFun<T, R>
) {
super();
}
readonly state = TraverseState();
fastNext<O>(otherwise?: OptLazy<O>): R | O {
const state = this.state;
if (state.halted) return OptLazy(otherwise) as O;
const { halt } = state;
const done = Symbol('Done');
let value: T | typeof done;
const source = this.source;
const collectFun = this.collectFun;
while (!state.halted && done !== (value = source.fastNext(done))) {
const result = collectFun(
value,
state.nextIndex(),
CollectFun.Skip,
halt
);
if (CollectFun.Skip === result) continue;
return result as R;
}
return OptLazy(otherwise as O);
}
}
export class DropWhileIterator<T> extends FastIteratorBase<T> {
constructor(
readonly source: FastIterator<T>,
readonly pred: (value: T, index: number) => boolean,
readonly negate: boolean
) {
super();
}
pass = false;
index = 0;
fastNext<O>(otherwise?: OptLazy<O>): 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 = source.fastNext(done))) {
this.pass = this.pred(value, this.index++) === negate;
if (this.pass) return value;
}
return OptLazy(otherwise) as O;
}
}
export class TakeIterator<T> extends FastIteratorBase<T> {
constructor(
readonly source: FastIterator<T>,
readonly amount: number
) {
super();
}
i = 0;
fastNext<O>(otherwise?: OptLazy<O>): T | O {
if (this.i++ >= this.amount) return OptLazy(otherwise) as O;
return this.source.fastNext(otherwise!);
}
}
export class DropIterator<T> extends FastIteratorBase<T> {
remain: number;
constructor(
readonly source: FastIterator<T>,
readonly amount: number
) {
super();
this.remain = amount;
}
fastNext<O>(otherwise?: OptLazy<O>): 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 = source.fastNext(done))) {
if (this.remain-- <= 0) return value;
}
return OptLazy(otherwise) as O;
}
}
export class RepeatIterator<T> extends FastIteratorBase<T> {
iterator: FastIterator<T>;
remain: number | undefined;
constructor(
readonly source: Stream<T>,
readonly amount?: number
) {
super();
this.iterator = source[Symbol.iterator]();
this.remain = amount;
}
isEmpty = true;
fastNext<O>(otherwise?: OptLazy<O>): T | O {
const done = Symbol('Done');
let value = this.iterator.fastNext(done);
if (done !== value) {
this.isEmpty = false;
return value;
}
if (this.isEmpty) return OptLazy(otherwise) as O;
if (undefined !== this.remain) {
this.remain--;
if (this.remain <= 0) return OptLazy(otherwise) as O;
}
this.iterator = this.source[Symbol.iterator]();
value = this.iterator.fastNext(done);
if (done === value) return OptLazy(otherwise) as O;
return value;
}
}
export class ArrayIterator<T> extends FastIteratorBase<T> {
i: number;
constructor(
readonly array: readonly T[],
readonly startIndex: number,
readonly endIndex: number
) {
super();
this.i = startIndex;
}
fastNext<O>(otherwise?: OptLazy<O>): T | O {
if (this.i > this.endIndex) return OptLazy(otherwise) as O;
return this.array[this.i++];
}
}
export class ArrayReverseIterator<T> extends FastIteratorBase<T> {
i: number;
constructor(
readonly array: readonly T[],
readonly startIndex: number,
endIndex: number
) {
super();
this.i = endIndex;
}
fastNext<O>(otherwise?: OptLazy<O>): T | O {
if (this.i < this.startIndex) return OptLazy(otherwise) as O;
return this.array[this.i--];
}
}
export class AlwaysIterator<T> extends FastIteratorBase<T> {
constructor(readonly value: T) {
super();
}
fastNext(): T {
return this.value;
}
}
export class MapApplyIterator<
T extends readonly unknown[],
A extends readonly unknown[],
R,
> extends FastIteratorBase<R> {
constructor(
source: StreamSource<T>,
readonly f: (...args: [...T, ...A]) => R,
readonly args: A,
streamSourceHelpers: StreamSourceHelpers
) {
super();
this.iter = streamSourceHelpers.fromStreamSource(source)[Symbol.iterator]();
}
iter: FastIterator<T>;
fastNext<O>(otherwise?: OptLazy<O>): R | O {
const done = Symbol();
const next = this.iter.fastNext(done);
const args = this.args;
if (done === next) return OptLazy(otherwise)!;
return this.f(...next, ...args);
}
}
export class FilterApplyIterator<
T extends readonly unknown[],
A extends readonly unknown[],
> extends FastIteratorBase<T> {
constructor(
source: StreamSource<T>,
readonly pred: (...args: [...T, ...A]) => boolean,
readonly args: A,
readonly negate: boolean,
streamSourceHelpers: StreamSourceHelpers
) {
super();
this.iter = streamSourceHelpers.fromStreamSource(source)[Symbol.iterator]();
}
iter: FastIterator<T>;
fastNext<O>(otherwise?: OptLazy<O>): T | O {
const done = Symbol();
let next: T | typeof done;
const pred = this.pred;
const iter = this.iter;
const args = this.args;
const negate = this.negate;
while (done !== (next = iter.fastNext(done))) {
if (pred(...next, ...args) !== negate) return next;
}
return OptLazy(otherwise)!;
}
}
export class RangeUpIterator extends FastIteratorBase<number> {
state: number;
constructor(
readonly start = 0,
readonly end: number | undefined,
readonly delta: number
) {
super();
this.state = start;
}
fastNext<O>(otherwise?: OptLazy<O>): number | O {
if (undefined !== this.end) {
if (this.state > this.end) {
return OptLazy(otherwise) as O;
}
}
const currentState = this.state;
this.state += this.delta;
return currentState;
}
}
export class RangeDownIterator extends FastIteratorBase<number> {
state: number;
constructor(
readonly start = 0,
readonly end: number | undefined,
readonly delta: number
) {
super();
this.state = start;
}
fastNext<O>(otherwise?: OptLazy<O>): number | O {
if (undefined !== this.end) {
if (this.state < this.end) {
return OptLazy(otherwise) as O;
}
}
const currentState = this.state;
this.state += this.delta;
return currentState;
}
}
export class RandomIterator extends FastIteratorBase<number> {
fastNext(): number {
return Math.random();
}
}
export class RandomIntIterator extends FastIteratorBase<number> {
readonly width: number;
constructor(
readonly min: number,
readonly max: number
) {
super();
this.width = max - min;
}
fastNext(): number {
return this.min + Math.round(Math.random() * this.width);
}
}
export class UnfoldIterator<T> extends FastIteratorBase<T> {
constructor(
init: T,
readonly getNext: (current: T, index: number, stop: Token) => T | Token
) {
super();
this.current = init;
}
current: T | Token;
index = 0;
fastNext<O>(otherwise?: OptLazy<O>): T | O {
const current = this.current;
if (Token === current) return OptLazy(otherwise) as O;
if (this.index === 0) {
this.index++;
return current;
}
const next = this.getNext(current, this.index++, Token);
this.current = next;
if (Token === next) return OptLazy(otherwise) as O;
return next;
}
}
export class ZipWithIterator<
I extends readonly unknown[],
R,
> extends FastIteratorBase<R> {
constructor(
readonly iterables: { [K in keyof I]: StreamSource<I[K]> },
readonly zipFun: (...values: I) => R,
streamSourceHelpers: StreamSourceHelpers
) {
super();
this.sources = iterables.map(
(source): FastIterator<any> =>
streamSourceHelpers.fromStreamSource(source)[Symbol.iterator]()
);
}
readonly sources: FastIterator<any>[];
fastNext<O>(otherwise?: OptLazy<O>): R | O {
const result = [];
let sourceIndex = -1;
const sources = this.sources;
const done = Symbol('Done');
while (++sourceIndex < sources.length) {
const value = sources[sourceIndex].fastNext(done);
if (done === value) return OptLazy(otherwise) as O;
result.push(value);
}
return (this.zipFun as any)(...result);
}
}
export class ZipAllWithItererator<
I extends readonly unknown[],
F,
R,
> extends FastIteratorBase<R> {
constructor(
readonly fillValue: OptLazy<F>,
readonly iters: { [K in keyof I]: StreamSource<I[K]> },
readonly zipFun: (...values: { [K in keyof I]: I[K] | F }) => R,
streamSourceHelpers: StreamSourceHelpers
) {
super();
this.sources = iters.map(
(o): FastIterator<any> =>
streamSourceHelpers.fromStreamSource(o)[Symbol.iterator]()
);
}
readonly sources: FastIterator<any>[];
allDone = false;
fastNext<O>(otherwise?: OptLazy<O>): R | O {
if (this.allDone) return OptLazy(otherwise) as O;
const result = [];
let sourceIndex = -1;
const sources = this.sources;
const done = Symbol('Done');
let anyNotDone = false;
const fillValue = this.fillValue;
while (++sourceIndex < sources.length) {
const value = sources[sourceIndex].fastNext(done);
if (done === value) {
result.push(OptLazy(fillValue));
} else {
anyNotDone = true;
result.push(value);
}
}
if (!anyNotDone) {
this.allDone = true;
return OptLazy(otherwise) as O;
}
return (this.zipFun as any)(...result);
}
}
export class PrependIterator<T> extends FastIteratorBase<T> {
constructor(
readonly source: FastIterator<T>,
readonly item: OptLazy<T>
) {
super();
}
prependDone = false;
fastNext<O>(otherwise?: OptLazy<O>): T | O {
if (this.prependDone) {
return this.source.fastNext(otherwise!);
}
this.prependDone = true;
return OptLazy(this.item);
}
}
export class AppendIterator<T> extends FastIteratorBase<T> {
constructor(
readonly source: FastIterator<T>,
readonly item: OptLazy<T>
) {
super();
}
appendDone = false;
fastNext<O>(otherwise?: OptLazy<O>): T | O {
if (this.appendDone) return OptLazy(otherwise) as O;
const done = Symbol('Done');
const value = this.source.fastNext(done);
if (done !== value) return value;
this.appendDone = true;
return OptLazy(this.item);
}
}
export class MapIterator<T, T2> extends FastIteratorBase<T2> {
constructor(
readonly source: FastIterator<T>,
readonly mapFun: (value: T, index: number) => T2
) {
super();
}
readonly state = TraverseState();
fastNext<O>(otherwise?: OptLazy<O>): T2 | O {
const state = this.state;
if (state.halted) return OptLazy(otherwise) as O;
const done = Symbol('Done');
const next = this.source.fastNext(done);
if (done === next) return OptLazy(otherwise) as O;
return this.mapFun(next, state.nextIndex());
}
}
export class MapPureIterator<
T,
A extends readonly unknown[],
T2,
> extends FastIteratorBase<T2> {
constructor(
readonly source: FastIterator<T>,
readonly mapFun: (value: T, ...args: A) => T2,
readonly args: A
) {
super();
}
fastNext<O>(otherwise?: OptLazy<O>): T2 | O {
const done = Symbol('Done');
const next = this.source.fastNext(done);
if (done === next) return OptLazy(otherwise) as O;
return this.mapFun(next, ...this.args);
}
}