vtt-mapper/src/common.ts

389 lines
8.1 KiB
TypeScript

const DEFAULT_SIZE = 128;
export class FreeList<T>
{
#data: T[] = [];
#free: LinkedList<number> = new LinkedList<number>();
#length: number = 0;
constructor(arr?: T[])
{
if(arr && arr.length > 0)
this.#data = arr;
}
insert(element: T): number
{
if(this.#free.empty)
{
return this.#data.push(element) - 1;
}
else
{
const index = this.#free.pop()!;
this.#data[index] = element;
return index;
}
}
erase(n: number): void
{
delete this.#data[n];
this.#free.add(n);
}
clear(): void
{
this.#data.length = 0;
this.#free.clear();
}
get(index: number): T
{
return this.#data[index];
}
set(index: number, value: T): void
{
this.#data[index] = value;
}
get length(): number
{
return this.#length;
}
}
export class IntList
{
#data: number[];
#fields: number;
#capacity: number = DEFAULT_SIZE;
#length: number = 0;
#free: number = -1;
constructor(fields: number)
{
if(fields <= 0)
throw new Error("Invalid field count");
this.#data = new Array(this.#capacity * fields);
this.#fields = fields;
}
get length(): number
{
return this.#length;
}
get(index: number, field: number): number
{
if(index >= this.#length || index < 0)
throw new Error("Invalid index");
if(field >= this.#fields || field < 0)
throw new Error("Invalid field");
return this.#data[index*this.#fields + field];
}
set(index: number, field: number, value: number): void
{
if(index >= this.#length || index < 0)
throw new Error("Invalid index");
if(field >= this.#fields || field < 0)
throw new Error("Invalid field");
this.#data[index*this.#fields + field] = value;
}
clear(): void
{
//Never edit the array during clear, we'll keep every data since they *must* always be override before being read.
this.#length = 0;
this.#free = -1;
}
push(): number
{
const pos = (this.#length + 1) * this.#fields;
if(pos > this.#capacity)
{
this.#capacity *= 2;
this.#data.length = this.#capacity * this.#fields;
}
return this.#length++;
}
pop(): void
{
if(this.#length <= 0)
return;
--this.#length;
}
insert(): number
{
if(this.#free !== -1)
{
const index = this.#free;
this.#free = this.#data[index * this.#fields];
return index;
}
else
return this.push();
}
erase(index: number): void
{
if(index >= this.#length || index < 0)
throw new Error("Invalid index");
this.#data[index * this.#fields] = this.#free;
this.#free = index;
}
toArray(): number[]
{
return this.#data.slice(0, this.#length);
}
//DEBUG
printReadable(names: string[] = []): void
{
const size = this.#length * this.#fields;
for(let i = 0; i < size; i)
{
const obj: Record<string, number> = {};
for(let j = 0; j < this.#fields; ++i, ++j)
{
obj[names[j] ?? j] = this.#data[i];
}
console.log(obj);
}
}
}
export class Stack<T>
{
#arr: T[];
#pos: number; //Index of the last non empty value
#bucketSize: number;
#bucketCount: number = 1;
constructor(size?: number)
{
this.#bucketSize = size ?? DEFAULT_SIZE;
this.#arr = new Array(this.#bucketSize * this.#bucketCount);
this.#pos = 0;
}
push(item: T): void
{
if(this.#pos >= this.#arr.length)
this.#expand();
this.#arr[this.#pos] = item;
this.#pos++;
}
pop(): T
{
if(this.length === 0)
throw new Error("Empty queue.");
const item = this.#arr[this.#pos];
delete this.#arr[this.#pos];
this.#pos--;
return item;
}
peek(): T
{
return this.#arr[this.#pos];
}
clear(): void
{
this.#pos = 0;
}
get length(): number
{
return this.#pos;
}
#expand(): void
{
if(this.length >= this.#bucketSize * this.#bucketCount)
this.#bucketCount++;
this.#arr.length = this.#bucketSize * this.#bucketCount;
}
}
export class Queue<T>
{
#arr: T[];
#idx: number; //Index of the first non empty value
#pos: number; //Index of the last non empty value
#bucketSize: number;
#bucketCount: number = 1;
constructor(size?: number)
{
this.#bucketSize = size ?? DEFAULT_SIZE;
this.#arr = new Array(this.#bucketSize * this.#bucketCount);
this.#idx = 0;
this.#pos = 0;
}
push(item: T): void
{
if(this.#pos >= this.#arr.length)
this.#expand();
this.#arr[this.#pos] = item;
this.#pos++;
}
pull(): T
{
if(this.length === 0)
throw new Error("Empty queue.");
const item = this.#arr[this.#idx];
delete this.#arr[this.#idx];
this.#idx++;
return item;
}
peek(): T
{
return this.#arr[this.#idx];
}
clear(): void
{
this.#idx = 0;
this.#pos = 0;
}
get length(): number
{
return this.#pos - this.#idx;
}
#shrink(): void
{
this.#arr.splice(0, this.#idx);
this.#arr.length = this.#bucketSize * this.#bucketCount;
this.#idx = 0;
}
#expand(): void
{
if(this.#idx !== 0)
this.#shrink();
if(this.length >= this.#bucketSize * this.#bucketCount)
this.#bucketCount++;
this.#arr.length = this.#bucketSize * this.#bucketCount;
}
}
export class LinkedList<T>
{
#head: LinkedElmt<T> | null;
constructor()
{
this.#head = null;
}
get empty(): boolean
{
return this.#head === null;
}
add(item: T): void
{
const head = this.#head;
this.#head = { elmt: item, next: head };
}
pop(): T | null
{
if(this.#head === null)
return null;
const head = this.#head;
this.#head = head.next;
return head.elmt;
}
peek(): T | null
{
return this.#head?.elmt ?? null;
}
forEach(cb: (e: T, i: number) => void): void
{
let current = this.#head, i = 0;
while(current)
{
cb(current.elmt, i++);
current = current.next;
}
}
toArray(): T[]
{
const result: T[] = [];
this.forEach(e => result.push(e));
return result;
}
clear()
{
this.#head = null;
}
}
interface LinkedElmt<T>
{
elmt: T;
next: LinkedElmt<T> | null;
}
export function clamp(x: number, min: number, max: number): number
{
return x > max ? max : x < min ? min : x;
}
export function lerp(x: number, a: number, b: number): number
{
return (1-x)*a+x*b;
}
export class Random
{
#seed: number;
#hasher: () => number;
constructor(seed: number)
{
this.#seed = seed;
this.#hasher = mb32(hash(seed));
}
next(): number
{
return this.#hasher();
}
nextInt(min?: number, max?: number): number
{
return Math.floor(this.nextFloat(min, max));
}
nextFloat(min?: number, max?: number): number
{
if(min === undefined && max === undefined)
{
min = 0;
max = 1;
}
else if(max === undefined)
{
max = min;
min = 0;
}
else if(min === undefined)
{
min = 0;
}
return lerp(this.#hasher() / 2**32, min!, max!);
}
}
const hash = (n: number) => Math.imul(n,2654435761) >>> 0;
const mb32 = (a: number) => (t?: number) => (a = a + 1831565813|0, t = Math.imul(a^a>>>15,1|a), t = t + Math.imul(t^t>>>7,61|t)^t, (t^t>>>14)>>>0);