Skip to content
Merged
92 changes: 68 additions & 24 deletions src/lualib/Map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ Map = class Map<K, V> {
public static [Symbol.species] = Map;
public [Symbol.toStringTag] = "Map";

// Type of key is actually K
private items: { [key: string]: V } = {};
private items = new LuaTable<K, V>();
public size = 0;

// Key-order variables
private firstKey: K | undefined;
private lastKey: K | undefined;
private nextKey = new LuaTable<K, K>();
private previousKey = new LuaTable<K, K>();

constructor(entries?: Iterable<readonly [K, V]> | Array<readonly [K, V]>) {
if (entries === undefined) return;

Expand All @@ -23,15 +28,18 @@ Map = class Map<K, V> {
}
} else {
const array = entries as Array<[K, V]>;
this.size = array.length;
for (const kvp of array) {
this.items[kvp[0] as any] = kvp[1];
this.set(kvp[0], kvp[1]);
}
}
}

public clear(): void {
this.items = {};
this.items = new LuaTable();
this.nextKey = new LuaTable();
this.previousKey = new LuaTable();
this.firstKey = undefined;
this.lastKey = undefined;
this.size = 0;
return;
}
Expand All @@ -40,31 +48,64 @@ Map = class Map<K, V> {
const contains = this.has(key);
if (contains) {
this.size--;

// Do order bookkeeping
const next = this.nextKey.get(key);
const previous = this.previousKey.get(key);
if (next && previous) {
this.nextKey.set(previous, next);
this.previousKey.set(next, previous);
} else if (next) {
this.firstKey = next;
this.previousKey.set(next, undefined);
} else if (previous) {
this.lastKey = previous;
this.nextKey.set(previous, undefined);
} else {
this.firstKey = undefined;
this.lastKey = undefined;
}

this.nextKey.set(key, undefined);
this.previousKey.set(key, undefined);
}
this.items[key as any] = undefined;
this.items.set(key, undefined);

return contains;
}

public forEach(callback: (value: V, key: K, map: Map<K, V>) => any): void {
for (const key in this.items) {
callback(this.items[key], key as any, this);
for (const key of this.keys()) {
callback(this.items.get(key), key, this);
}
return;
}

public get(key: K): V | undefined {
return this.items[key as any];
return this.items.get(key);
}

public has(key: K): boolean {
return this.items[key as any] !== undefined;
return this.nextKey.get(key) !== undefined || this.lastKey === key;
}

public set(key: K, value: V): this {
if (!this.has(key)) {
const isNewValue = !this.has(key);
if (isNewValue) {
this.size++;
}
this.items[key as any] = value;
this.items.set(key, value);

// Do order bookkeeping
if (this.firstKey === undefined) {
this.firstKey = key;
this.lastKey = key;
} else if (isNewValue) {
this.nextKey.set(this.lastKey, key);
this.previousKey.set(key, this.lastKey);
this.lastKey = key;
}

return this;
}

Expand All @@ -74,44 +115,47 @@ Map = class Map<K, V> {

public entries(): IterableIterator<[K, V]> {
const items = this.items;
let key: K;
let value: V;
const nextKey = this.nextKey;
let key = this.firstKey;
return {
[Symbol.iterator](): IterableIterator<[K, V]> {
return this;
},
next(): IteratorResult<[K, V]> {
[key, value] = next(items, key);
return { done: !key, value: [key, value] };
const result = { done: !key, value: [key, items.get(key)] as [K, V] };
key = nextKey.get(key);
return result;
},
};
}

public keys(): IterableIterator<K> {
const items = this.items;
let key: K;
const nextKey = this.nextKey;
let key = this.firstKey;
return {
[Symbol.iterator](): IterableIterator<K> {
return this;
},
next(): IteratorResult<K> {
[key] = next(items, key);
return { done: !key, value: key };
const result = { done: !key, value: key };
key = nextKey.get(key);
return result;
},
};
}

public values(): IterableIterator<V> {
const items = this.items;
let key: K;
let value: V;
const nextKey = this.nextKey;
let key = this.firstKey;
return {
[Symbol.iterator](): IterableIterator<V> {
return this;
},
next(): IteratorResult<V> {
[key, value] = next(items, key);
return { done: !key, value };
const result = { done: !key, value: items.get(key) };
key = nextKey.get(key);
return result;
},
};
}
Expand Down
85 changes: 62 additions & 23 deletions src/lualib/Set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ Set = class Set<T> {
public static [Symbol.species] = Set;
public [Symbol.toStringTag] = "Set";

// Key type is actually T
private items: { [key: string]: boolean } = {};
public size = 0;

private firstKey: T | undefined;
private lastKey: T | undefined;
private nextKey = new LuaTable<T, T>();
private previousKey = new LuaTable<T, T>();

constructor(values?: Iterable<T> | T[]) {
if (values === undefined) return;

Expand All @@ -22,23 +25,36 @@ Set = class Set<T> {
}
} else {
const array = values as T[];
this.size = array.length;
for (const value of array) {
this.items[value as any] = true;
this.add(value);
}
}
}

public add(value: T): Set<T> {
if (!this.has(value)) {
const isNewValue = !this.has(value);
if (isNewValue) {
this.size++;
}
this.items[value as any] = true;

// Do order bookkeeping
if (this.firstKey === undefined) {
this.firstKey = value;
this.lastKey = value;
} else if (isNewValue) {
this.nextKey.set(this.lastKey, value);
this.previousKey.set(value, this.lastKey);
this.lastKey = value;
}

return this;
}

public clear(): void {
this.items = {};
this.nextKey = new LuaTable();
this.previousKey = new LuaTable();
this.firstKey = undefined;
this.lastKey = undefined;
this.size = 0;
return;
}
Expand All @@ -47,63 +63,86 @@ Set = class Set<T> {
const contains = this.has(value);
if (contains) {
this.size--;

// Do order bookkeeping
const next = this.nextKey.get(value);
const previous = this.previousKey.get(value);
if (next && previous) {
this.nextKey.set(previous, next);
this.previousKey.set(next, previous);
} else if (next) {
this.firstKey = next;
this.previousKey.set(next, undefined);
} else if (previous) {
this.lastKey = previous;
this.nextKey.set(previous, undefined);
} else {
this.firstKey = undefined;
this.lastKey = undefined;
}

this.nextKey.set(value, undefined);
this.previousKey.set(value, undefined);
}
this.items[value as any] = undefined;

return contains;
}

public forEach(callback: (value: T, key: T, set: Set<T>) => any): void {
for (const key in this.items) {
callback(key as any, key as any, this);
for (const key of this.keys()) {
callback(key, key, this);
}
}

public has(value: T): boolean {
return this.items[value as any] === true;
return this.nextKey.get(value) !== undefined || this.lastKey === value;
}

public [Symbol.iterator](): IterableIterator<T> {
return this.values();
}

public entries(): IterableIterator<[T, T]> {
const items = this.items;
let key: T;
const nextKey = this.nextKey;
let key: T = this.firstKey;
return {
[Symbol.iterator](): IterableIterator<[T, T]> {
return this;
},
next(): IteratorResult<[T, T]> {
[key] = next(items, key);
return { done: !key, value: [key, key] };
const result = { done: !key, value: [key, key] as [T, T] };
key = nextKey.get(key);
return result;
},
};
}

public keys(): IterableIterator<T> {
const items = this.items;
let key: T;
const nextKey = this.nextKey;
let key: T = this.firstKey;
return {
[Symbol.iterator](): IterableIterator<T> {
return this;
},
next(): IteratorResult<T> {
[key] = next(items, key);
return { done: !key, value: key };
const result = { done: !key, value: key };
key = nextKey.get(key);
return result;
},
};
}

public values(): IterableIterator<T> {
const items = this.items;
let key: T;
const nextKey = this.nextKey;
let key: T = this.firstKey;
return {
[Symbol.iterator](): IterableIterator<T> {
return this;
},
next(): IteratorResult<T> {
[key] = next(items, key);
return { done: !key, value: key };
const result = { done: !key, value: key };
key = nextKey.get(key);
return result;
},
};
}
Expand Down
15 changes: 7 additions & 8 deletions src/lualib/WeakMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ WeakMap = class WeakMap<K extends object, V> {
public static [Symbol.species] = WeakMap;
public [Symbol.toStringTag] = "WeakMap";

// Type of key is actually K
private items: { [key: string]: V } = {};
private items = new LuaTable<K, V>();

constructor(entries?: Iterable<readonly [K, V]> | Array<readonly [K, V]>) {
setmetatable(this.items, { __mode: "k" });
Expand All @@ -19,31 +18,31 @@ WeakMap = class WeakMap<K extends object, V> {
break;
}
const value: [K, V] = result.value; // Ensures index is offset when tuple is accessed
this.set(value[0], value[1]);
this.items.set(value[0], value[1]);
}
} else {
for (const kvp of entries as Array<[K, V]>) {
this.items[kvp[0] as any] = kvp[1];
this.items.set(kvp[0], kvp[1]);
}
}
}

public delete(key: K): boolean {
const contains = this.has(key);
this.items[key as any] = undefined;
this.items.set(key, undefined);
return contains;
}

public get(key: K): V | undefined {
return this.items[key as any];
return this.items.get(key);
}

public has(key: K): boolean {
return this.items[key as any] !== undefined;
return this.items.get(key) !== undefined;
}

public set(key: K, value: V): this {
this.items[key as any] = value;
this.items.set(key, value);
return this;
}
};
Loading