UNPKG

bigbigmapset

Version:

Extension classes of Map and Set that don't have size limits.

275 lines (274 loc) 7.78 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BigSet = exports.BigMap = void 0; /** * Extension of {@link Map} with no size limit. */ class BigMap extends Map { /** Where the entries are actually stored. The base {@link Map} this {@link BigMap} extends is left empty. */ chunks = []; get [Symbol.toStringTag]() { return "BigMap"; } clear() { this.chunks.length = 0; } constructor(entries) { super(); if (entries instanceof BigMap) { // clone for (const chunk of entries.chunks) { this.chunks.push(new Map(chunk)); } } else if (entries instanceof Map) { // clone this.chunks.push(new Map(entries)); } else if (entries !== undefined) { for (const [key, value] of entries) { this.set(key, value); // TODO check performance and optimize? } } } get size() { // TODO? cache let result = 0; for (const chunk of this.chunks) { result += chunk.size; } return result; } get(key) { // TODO check performance difference between has and !== undefined for (const chunk of this.chunks) { if (chunk.has(key)) { return chunk.get(key); } } return undefined; } has(key) { for (const chunk of this.chunks) { if (chunk.has(key)) { return true; } } return false; } delete(key) { for (let i = 0; i < this.chunks.length; i++) { const chunk = this.chunks[i]; if (chunk.delete(key)) { // delete chunk if empty if (chunk.size === 0) { this.chunks.splice(i, 1); // deleting from chunks messes up iteration // but it's fine because the function is exiting now anyway. } return true; } } return false; } set(key, value) { if (this.chunks.length > 0) { const finalIndex = this.chunks.length - 1; // replace entry if found in any chunk other than the final one for (let i = 0; i < finalIndex; i++) { const chunk = this.chunks[i]; if (chunk.has(key)) { chunk.set(key, value); return this; } } // try to add the entry to the final chunk const finalChunk = this.chunks[finalIndex]; try { finalChunk.set(key, value); return this; } catch (e) { if (e instanceof RangeError) { // final chunk is out of space } else throw e; } } // out of space. make new chunk const newChunk = new Map(); newChunk.set(key, value); this.chunks.push(newChunk); return this; } *entries() { for (const chunk of this.chunks) { yield* chunk.entries(); } return undefined; } *keys() { for (const chunk of this.chunks) { yield* chunk.keys(); } return undefined; } *values() { for (const chunk of this.chunks) { yield* chunk.values(); } return undefined; } forEach(callbackfn, thisArg) { if (arguments.length > 1) { for (const chunk of this.chunks) { chunk.forEach(callbackfn, thisArg); } } else { for (const chunk of this.chunks) { chunk.forEach(callbackfn); } } } *[Symbol.iterator]() { for (const chunk of this.chunks) { yield* chunk; } return undefined; } } exports.BigMap = BigMap; /** * Extension of {@link Set} with no size limit. */ class BigSet extends Set { /** Where the values are actually stored. The base {@link Set} this {@link BigSet} extends is left empty. */ chunks = []; get [Symbol.toStringTag]() { return "BigSet"; } clear() { this.chunks.length = 0; } constructor(values) { super(); if (values instanceof BigSet) { // clone for (const chunk of values.chunks) { this.chunks.push(new Set(chunk)); } } else if (values instanceof Set) { // clone this.chunks.push(new Set(values)); } else if (values !== undefined) { for (const value of values) { this.add(value); // TODO check performance and optimize? } } } get size() { // TODO? cache let result = 0; for (const chunk of this.chunks) { result += chunk.size; } return result; } has(value) { for (const chunk of this.chunks) { if (chunk.has(value)) { return true; } } return false; } delete(value) { for (let i = 0; i < this.chunks.length; i++) { const chunk = this.chunks[i]; if (chunk.delete(value)) { // delete chunk if empty if (chunk.size === 0) { this.chunks.splice(i, 1); // deleting from chunks messes up iteration // but it's fine because the function is exiting now anyway. } return true; } } return false; } add(value) { if (this.chunks.length > 0) { const finalIndex = this.chunks.length - 1; // check if found in any chunk other than the final one for (let i = 0; i < finalIndex; i++) { const chunk = this.chunks[i]; if (chunk.has(value)) { chunk.add(value); // just in case return this; } } // try to add the value to the final chunk const finalChunk = this.chunks[finalIndex]; try { finalChunk.add(value); return this; } catch (e) { if (e instanceof RangeError) { // final chunk is out of space } else throw e; } } // out of space. make new chunk const newChunk = new Set(); newChunk.add(value); this.chunks.push(newChunk); return this; } *entries() { for (const chunk of this.chunks) { yield* chunk.entries(); } return undefined; } *keys() { for (const chunk of this.chunks) { yield* chunk.keys(); } return undefined; } *values() { for (const chunk of this.chunks) { yield* chunk.values(); } return undefined; } forEach(callbackfn, thisArg) { if (arguments.length > 1) { for (const chunk of this.chunks) { chunk.forEach(callbackfn, thisArg); } } else { for (const chunk of this.chunks) { chunk.forEach(callbackfn); } } } *[Symbol.iterator]() { for (const chunk of this.chunks) { yield* chunk; } return undefined; } } exports.BigSet = BigSet;