/** * Utility to cache array of objects associated to a key. * We use it to cache array of edge/vertex pairs (outE or inE) * Cache size is capped to a maximum. */ export class ArraysByKeyCache { private cache: { [key: string]: T[] }; private keyQueue: string[]; // Last touched key FIFO to purge cache if too big. private totalElements: number; private maxNbElements: number; public constructor(maxNbElements: number) { this.maxNbElements = maxNbElements; this.clear(); } public clear(): void { this.cache = {}; this.keyQueue = []; this.totalElements = 0; } /** * To simplify, the array of cached elements array for a given key is dense (there is no index at which an elemnt is missing). * Retrieving a page within the array is guaranteed to return a complete page. * @param key * @param newElt * @param index */ public insert(key: string, index: number, newElt: T): void { const elements: T[] = this.cache[key] || []; this.cache[key] = elements; if (index < 0) { console.error("Inserting with negative index is not allowed by ArraysByCache"); return; } // Check that previous index is populated, if not, ignore if (index > elements.length) { console.error("Inserting non-contiguous element is not allowed by ArraysByCache"); return; } // Update last updated this.markKeyAsTouched(key); if (this.totalElements + 1 > this.maxNbElements && key !== this.keyQueue[0]) { this.reduceCacheSize(); } elements[index] = newElt; this.totalElements++; } /** * Retrieve a page of elements. * Return array of elements or null. null means "complete page not found in cache". * @param key * @param startIndex * @param pageSize */ public retrieve(key: string, startIndex: number, pageSize: number): T[] { if (!this.cache.hasOwnProperty(key)) { return null; } const elements = this.cache[key]; if (startIndex + pageSize > elements.length) { return null; } return elements.slice(startIndex, startIndex + pageSize); } /** * Invalidate elements to keep the total number below the limit * TODO: instead of invalidating the entire array, remove only needed number of elements */ private reduceCacheSize(): void { // remove an key and its array const oldKey = this.keyQueue.shift(); this.totalElements -= this.cache[oldKey].length; delete this.cache[oldKey]; } /** * Bubble up this key as new. * @param key */ private markKeyAsTouched(key: string) { const n = this.keyQueue.indexOf(key); if (n > -1) { this.keyQueue.splice(n, 1); } this.keyQueue.push(key); } }