UNPKG

memoru

Version:

A hash-based LRU cache that evicts entries based on memory usage rather than time or item count.

142 lines (141 loc) 4.46 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Memoru = void 0; const memory_stats_1 = require("./memory-stats"); /** * Memoru is a high-performance LRU cache using a two-map hashlru algorithm. * Supports any key type and can be rotated by size or memory threshold. * @public */ class Memoru { /** * Create a new Memoru instance. * @param options - Configuration options for the cache * @public */ constructor(options) { if (options.max !== undefined && (typeof options.max !== 'number' || options.max <= 0)) { throw new Error('Memoru max value, if provided, must be a number greater than 0'); } this.max = options.max; this.size = 0; this.cache = new Map(); this._cache = new Map(); if (options.memoryStats) { // If respectGC is enabled, ensure monitorGC is also enabled if (options.respectGC && !options.memoryStats.monitorGC) { options.memoryStats.monitorGC = true; } this.memoryMonitor = new memory_stats_1.MemoryStatsMonitor(options.memoryStats); this.memoryMonitor.on('threshold', (event) => { // Skip rotation if GC is active and respectGC option is enabled if (options.respectGC && event.gcActive) { return; } this.rotate(); }); // Listen for GC events if enabled if (options.respectGC) { this.memoryMonitor.on('gc:start', () => { // Do nothing when GC starts - threshold events will be skipped }); this.memoryMonitor.on('gc:end', () => { // Optional: could trigger a rotation here after GC completes // if that becomes a desirable feature }); } } } /** * Rotate the cache, moving current cache to shadow cache and clearing the main cache. * Called internally on size or memory threshold. * @internal */ rotate() { var _a; // Don't rotate if GC is in progress and we're configured to respect it if ((_a = this.memoryMonitor) === null || _a === void 0 ? void 0 : _a.isGCActive()) { return; } this.size = 0; this._cache = this.cache; this.cache = new Map(); } /** * Internal update method for inserting a new item and handling rotation. * @param key - The key to insert * @param value - The value to insert * @internal */ update(key, value) { var _a; this.cache.set(key, value); this.size++; // Check if size-based rotation is needed and no GC is in progress if (this.max !== undefined && this.size >= this.max && !((_a = this.memoryMonitor) === null || _a === void 0 ? void 0 : _a.isGCActive())) { this.rotate(); } } /** * Check if the cache contains a key. * @param key - The key to check * @public */ has(key) { return this.cache.has(key) || this._cache.has(key); } /** * Remove a key from the cache and shadow cache. * @param key - The key to remove * @public */ remove(key) { this.cache.delete(key); this._cache.delete(key); } /** * Get a value from the cache. If found in the shadow cache, it is promoted to the main cache. * @param key - The key to retrieve * @public */ get(key) { if (this.cache.has(key)) { return this.cache.get(key); } if (this._cache.has(key)) { const v = this._cache.get(key); if (v !== undefined) { this.update(key, v); return v; } } return undefined; } /** * Set a value in the cache. * @param key - The key to set * @param value - The value to set * @public */ set(key, value) { if (this.cache.has(key)) { this.cache.set(key, value); } else { this.update(key, value); } } /** * Clear the cache and shadow cache. * @public */ clear() { this.cache = new Map(); this._cache = new Map(); this.size = 0; } } exports.Memoru = Memoru;