2020-05-25 21:30:55 -05:00

98 lines
2.7 KiB
TypeScript

/**
* 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<T> {
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);
}
}