-
Notifications
You must be signed in to change notification settings - Fork 0
/
SeekableIterator.ts
68 lines (58 loc) · 2.13 KB
/
SeekableIterator.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import type { Tuple } from '../types';
/**
* Wraps `iterator` to allow for seeking backwards and forwards. An internal cache of length `maxLength` is kept and
* progressively added to when iterating forwards.
*/
export class SeekableIterator<T> implements IterableIterator<T> {
protected cache: T[] = [];
protected i = 0;
protected iteratorDone = false;
constructor(
protected iterator: Iterator<T>,
protected maxLength = Infinity,
) {}
get elements(): readonly T[] {
return this.cache;
}
get done(): boolean {
// If the iterator is done, then determine if `i` is at the end of the cache.
return this.iteratorDone ? !this.cache.length || this.i >= this.cache.length : false;
}
[Symbol.iterator](): IterableIterator<T> {
return this;
}
public next(...args: any[]): IteratorResult<T> {
if (this.done) return { done: true, value: undefined };
const cachedValue = this.cache[this.i++];
if (cachedValue !== undefined) return { done: false, value: cachedValue };
const next = this.iterator.next(...(args as any));
if (next.done) this.iteratorDone = true;
else this.add(next.value);
return next;
}
/**
* Seeks forward/backwards to the index `i`. `i` may be any positive or negative number. Negative numbers seek
* starting from the end of the internal cache (e.g. -1 is the last element).
*/
public seek(i: number): void {
if (i < 0) i = this.cache.length + i;
if (i > this.i) while (this.i < i && !this.done) this.next();
else if (i < this.i) this.i = i;
}
/**
* Peek ahead of where the current iteration is. This doesn't consume any values of the iterator.
* @param ahead optional, the number of elements to peek ahead.
*/
public peek<N extends number = 1>(ahead: N = 1 as N): Tuple<T, N> {
const result: IteratorResult<T>[] = [];
for (let i = 0; i < ahead; i++) result.push(this.next().value);
this.i -= ahead;
return result as Tuple<T, N>;
}
/** Add `value` to the `cache`. */
private add(value: T) {
this.cache.push(value);
if (this.cache.length > this.maxLength) this.cache.shift();
}
}
export default SeekableIterator;