UNPKG

spica

Version:

Supervisor, Coroutine, Channel, select, AtomicPromise, Cancellation, Cache, List, Queue, Stack, and some utils.

252 lines (249 loc) 6.83 kB
import { max, min, isArray } from '../alias'; import { Index } from '../index'; // Circular Indexed List export class List<T> { constructor( public capacity: number = 0, private readonly Container: ArrayConstructor | Uint32ArrayConstructor = Array, ) { if (capacity <= 0) { capacity = -capacity || 4; this.auto = true; } assert(this.capacity > 0); if (capacity >= 2 ** 32) throw new Error(`Too large capacity`); this.capacity = capacity; this.values = new this.Container(this.capacity) as T[]; this.nexts = new Uint32Array(this.capacity); this.prevs = new Uint32Array(this.capacity); } private auto = false; private values: T[]; private nexts: Uint32Array; private prevs: Uint32Array; private readonly ix = new Index(); private $length = 0; public get length() { return this.$length; } public head = 0; public get tail(): number { return this.nexts[this.head]; } public get last(): number { return this.prevs[this.head]; } public next(index: number): number { return this.nexts[index]; } public prev(index: number): number { return this.prevs[index]; } public at(offset: number): T { return this.values[this.index(offset)]; } public index(offset: number, index = this.head): number { switch (offset) { case 0: return index; case 1: return this.nexts[index]; case -1: return this.prevs[index]; } if (offset > 0) { const pointers = this.nexts; while (offset-- !== 0) { index = pointers[index]; } } else { const pointers = this.prevs; while (offset++ !== 0) { index = pointers[index]; } } return index; } public resize(capacity: number): void { if (capacity <= 0) { capacity = -capacity || 4; this.auto = true; } assert(this.capacity > 0); if (capacity > 2 ** 32) throw new Error(`Too large capacity`); if (capacity > this.nexts.length) { const size = max(capacity, min(this.capacity * 2, 2 ** 32)); if (isArray(this.values)) { this.values.length = size; } else { const values = new this.Container(size) as Uint32Array; values.set(this.values); this.values = values as any; } const nexts = new Uint32Array(size); nexts.set(this.nexts); this.nexts = nexts; const prevs = new Uint32Array(size); prevs.set(this.prevs); this.prevs = prevs; } this.capacity = capacity; while (this.$length > capacity) { this.del(this.last); } } public clear(): void { this.values = new this.Container(this.capacity) as T[]; this.ix.clear(); this.head = 0; this.$length = 0; } public add(value: T): number { const head = this.head; if (this.$length === 0) { assert(this.length === 0); const index = this.head = this.ix.pop(); ++this.$length; this.values[index] = value; this.nexts[index] = index; this.prevs[index] = index; return index; } if (this.$length !== this.capacity) { assert(this.length < this.capacity); const index = this.head = this.ix.pop(); ++this.$length; const last = this.prevs[head]; this.prevs[head] = this.nexts[last] = index; this.values[index] = value; this.nexts[index] = head; this.prevs[index] = last; return index; } else if (!this.auto) { assert(this.length === this.capacity); assert(this.ix.length === this.capacity); const index = this.head = this.prevs[head]; this.values[index] = value; return index; } else { this.resize(max(min(this.capacity * 2, 2 ** 32), this.capacity + 1)); return this.add(value); } } public del(index: number): void { assert(this.length > 0); const next = this.nexts[index]; const prev = this.prevs[index]; this.ix.push(index); this.values[index] = undefined as any; if (--this.$length !== 0) { this.nexts[prev] = next; this.prevs[next] = prev; } if (index === this.head) { this.head = next; } } public get(index: number): T { return this.values[index]; } public set(index: number, value: T): void { this.values[index] = value; } public insert(value: T, before: number): number { const head = this.head; this.head = before; const index = this.add(value); this.head = head; return index; } public unshift(value: T): number { return this.add(value); } public unshiftRotationally(value: T): number { if (this.$length === 0) return this.add(value); const index = this.last; this.values[index] = value; this.head = index; return index; } public push(value: T): number { return this.insert(value, this.head); } public pushRotationally(value: T): number { if (this.$length === 0) return this.add(value); const index = this.head; this.values[index] = value; this.head = this.nexts[index]; return index; } public shift(): T | undefined { if (this.$length === 0) return; const index = this.head; const value = this.values[index]; this.del(index); return value; } public pop(): T | undefined { if (this.$length === 0) return; const index = this.last; const value = this.values[index]; this.del(index); return value; } public move(index: number, before: number): boolean { if (index === before) return false; const a1 = index; const b1 = before; assert(a1 !== b1); const { nexts, prevs } = this; const a2 = nexts[a1]; if (a2 === b1) return false; const b0 = prevs[b1]; const a0 = prevs[a1]; nexts[b0] = a1; nexts[a1] = b1; prevs[b1] = a1; prevs[a1] = b0; nexts[a0] = a2; prevs[a2] = a0; assert(this.length > 10 || [...this].length === this.length); return true; } public moveToHead(index: number): void { this.move(index, this.head); this.head = index; } public moveToLast(index: number): void { this.move(index, this.head); this.head = index === this.head ? this.tail : this.head; } public swap(index1: number, index2: number): boolean { if (index1 === index2) return false; const index3 = this.nexts[index2]; this.move(index2, index1); this.move(index1, index3); switch (this.head) { case index1: this.head = index2; break; case index2: this.head = index1; break; } return true; } public *[Symbol.iterator](): Iterator<T, undefined, undefined> { for (let index = this.head; this.$length !== 0;) { yield this.values[index]; index = this.nexts[index]; if (index === this.head) break; } } }