更新
This commit is contained in:
+125
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* @file generational-cache.js
|
||||
* A generational pseudo-LRU cache with strict maximum size limits.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template K, V
|
||||
*/
|
||||
export class GenerationalCache {
|
||||
#max;
|
||||
#boundary;
|
||||
#current = new Map();
|
||||
#old = new Map();
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the GenerationalCache class.
|
||||
* @param {number} max - The maximum number of items the cache can hold.
|
||||
*/
|
||||
constructor(max) {
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of `entries` currently in the cache.
|
||||
* @note To optimize for write speed, this library allows temporary key
|
||||
* duplication between generations. Therefore, this value may not always
|
||||
* reflect the exact count of unique `keys`.
|
||||
* @returns {number} The total entry count.
|
||||
*/
|
||||
get size() {
|
||||
return this.#current.size + this.#old.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum capacity of the cache.
|
||||
* @returns {number} The maximum size limit.
|
||||
*/
|
||||
get max() {
|
||||
return this.#max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum capacity of the cache and recalculates the boundary.
|
||||
* Clears the cache when updated.
|
||||
* @param {number} value - The new maximum capacity to set.
|
||||
*/
|
||||
set max(value) {
|
||||
if (Number.isFinite(value) && value > 4) {
|
||||
this.#max = value;
|
||||
this.#boundary = Math.ceil(value / 2);
|
||||
} else {
|
||||
this.#max = 4;
|
||||
this.#boundary = 2;
|
||||
}
|
||||
this.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an item from the cache.
|
||||
* If the item is in the older generation, it gets promoted to the current
|
||||
* generation.
|
||||
* @param {K} key - The key of the element to return.
|
||||
* @returns {V | undefined} The element associated with the specified key, or
|
||||
* undefined if the key cannot be found.
|
||||
*/
|
||||
get(key) {
|
||||
let value = this.#current.get(key);
|
||||
if (value !== undefined) {
|
||||
return value;
|
||||
}
|
||||
value = this.#old.get(key);
|
||||
if (value !== undefined) {
|
||||
this.set(key, value);
|
||||
return value;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or updates an element with a specified key and a value to the cache.
|
||||
* @param {K} key - The key of the element to add.
|
||||
* @param {V} value - The value of the element to add.
|
||||
* @returns {GenerationalCache} The cache object itself.
|
||||
*/
|
||||
set(key, value) {
|
||||
this.#current.set(key, value);
|
||||
// Swap generations if the current map reaches the boundary
|
||||
if (this.#current.size >= this.#boundary) {
|
||||
this.#old = this.#current;
|
||||
this.#current = new Map();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean indicating whether an element with the specified key
|
||||
* exists or not.
|
||||
* @param {K} key - The key of the element to test for presence.
|
||||
* @returns {boolean} true if an element with the specified key exists in the
|
||||
* cache; otherwise false.
|
||||
*/
|
||||
has(key) {
|
||||
return this.#current.has(key) || this.#old.has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified element from the cache.
|
||||
* @param {K} key - The key of the element to remove.
|
||||
* @returns {boolean} true if an element in the cache existed and has been
|
||||
* removed, or false if the element does not exist.
|
||||
*/
|
||||
delete(key) {
|
||||
const deletedFromCurrent = this.#current.delete(key);
|
||||
const deletedFromOld = this.#old.delete(key);
|
||||
return deletedFromCurrent || deletedFromOld;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all elements from the cache.
|
||||
*/
|
||||
clear() {
|
||||
this.#current.clear();
|
||||
this.#old.clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user