UNPKG

n-way-set-associative-cache

Version:

A TypeScript implementation of an N-way set-associative cache with configurable replacement policies, supporting operations like put, get, delete, and listAll. Optimized for performance and extensibility.

238 lines (228 loc) 6.34 kB
// src/abstract/CacheDataStorageManager.ts var CacheDataStorageManager = class { capacity; evictionManager; constructor(capacity, evictionManager) { this.capacity = capacity; this.evictionManager = evictionManager; } getCapacity() { return this.capacity; } getEvictionManager() { return this.evictionManager; } }; // src/storages/InMemoryStorage.ts var InMemoryStorage = class extends CacheDataStorageManager { storage = /* @__PURE__ */ new Map(); add(key, value) { if (this.storage.size >= this.capacity) { const evictionKey = this.evictionManager.selectEvictionKey(); if (evictionKey !== void 0) { this.storage.delete(evictionKey); } } this.storage.set(key, value); this.evictionManager.recordAccess(key); } get(key) { if (this.storage.has(key)) { this.evictionManager.recordAccess(key); } return this.storage.get(key); } has(key) { return this.storage.has(key); } delete(key) { if (this.storage.delete(key)) { this.evictionManager.removeKey(key); return true; } return false; } listAll() { return Array.from(this.storage.entries()).map(([key, value]) => ({ key, value })); } clear() { this.storage.clear(); } }; // src/registeries/StorageTypeRegistry.ts var StorageRegistry = class _StorageRegistry { static storageRegistry = /* @__PURE__ */ new Map(); static registerStorage(type, constructor) { this.storageRegistry.set(type, constructor); } static getStorage(type) { return this.storageRegistry.get(type); } static { _StorageRegistry.registerStorage( 0 /* InMemoryStorage */, InMemoryStorage ); } }; // src/factories/CacheDataStorageFactory.ts var CacheDataStorageFactory = class { create(capacity, storageType, evictionManager) { const StorageClass = StorageRegistry.getStorage(storageType); if (!StorageClass) { throw new Error(`Storage type ${storageType} is not registered.`); } return new StorageClass(capacity, evictionManager); } }; // src/evictionmanagers/LruEvictionManager.ts var LruEvictionManager = class { order = []; recordAccess(key) { const index = this.order.indexOf(key); if (index !== -1) { this.order.splice(index, 1); } this.order.push(key); } selectEvictionKey() { return this.order.shift(); } removeKey(key) { const index = this.order.indexOf(key); if (index !== -1) { this.order.splice(index, 1); } } }; // src/evictionmanagers/MruEvictionStrategy.ts var MruEvictionManager = class { order = []; recordAccess(key) { const index = this.order.indexOf(key); if (index !== -1) { this.order.splice(index, 1); } this.order.push(key); } selectEvictionKey() { return this.order.pop(); } removeKey(key) { const index = this.order.indexOf(key); if (index !== -1) { this.order.splice(index, 1); } } }; // src/types/ReplacementPolicy.ts var ReplacementPolicy = /* @__PURE__ */ ((ReplacementPolicy2) => { ReplacementPolicy2[ReplacementPolicy2["LRU"] = 0] = "LRU"; ReplacementPolicy2[ReplacementPolicy2["MRU"] = 1] = "MRU"; return ReplacementPolicy2; })(ReplacementPolicy || {}); // src/registeries/EvictionRegistry.ts var EvictionManagerRegistry = class _EvictionManagerRegistry { static registry = /* @__PURE__ */ new Map(); static register(policy, manager) { this.registry.set(policy, manager); } static get(policy) { return this.registry.get(policy); } static { _EvictionManagerRegistry.register(0 /* LRU */, LruEvictionManager); _EvictionManagerRegistry.register(1 /* MRU */, MruEvictionManager); } }; // src/factories/EvictionManagerFactory.ts var EvictionManagerFactory = class { create(policy) { const ManagerClass = EvictionManagerRegistry.get(policy); if (!ManagerClass) { throw new Error( `No eviction manager registered for policy: ${ReplacementPolicy[policy]}` ); } return new ManagerClass(); } }; // src/NWaySetAssociativeCache.ts var NWaySetAssociativeCache = class { cache; capacity; ways; policy; storageType; storageFactory; evictionManagerFactory; logger; constructor(capacity, ways, policy = 0 /* LRU */, storageType = 0 /* InMemoryStorage */, storageFactory = new CacheDataStorageFactory(), evictionManagerFactory = new EvictionManagerFactory(), logger) { if (capacity % ways !== 0) { throw new Error("Capacity must be a multiple of the number of ways."); } this.capacity = capacity; this.ways = ways; this.policy = policy; this.storageType = storageType; this.storageFactory = storageFactory; this.evictionManagerFactory = evictionManagerFactory; this.cache = /* @__PURE__ */ new Map(); this.logger = logger || console; } getSetIndex(key) { const numSets = Math.floor(this.capacity / this.ways); const hashedKey = typeof key === "number" ? key : this.hashKey(key); return Math.abs(hashedKey) % numSets; } hashKey(key) { let hash = 0; const strKey = JSON.stringify(key); for (let i = 0; i < strKey.length; i++) { hash = hash * 31 + strKey.charCodeAt(i) >>> 0; } return hash; } put(key, value) { const setIndex = this.getSetIndex(key); if (!this.cache.has(setIndex)) { const evictionManager = this.evictionManagerFactory.create( this.policy ); const cacheDataStorage = this.storageFactory.create( this.capacity, this.storageType, evictionManager ); this.cache.set(setIndex, cacheDataStorage); } this.cache.get(setIndex).add(key, value); } get(key) { const setIndex = this.getSetIndex(key); return this.cache.get(setIndex)?.get(key); } has(key) { const setIndex = this.getSetIndex(key); return this.cache.has(setIndex) && this.cache.get(setIndex).has(key); } delete(key) { const setIndex = this.getSetIndex(key); return this.cache.get(setIndex)?.delete(key) ?? false; } clear() { this.cache.clear(); } listAll() { const entries = []; this.cache.forEach((storageManager) => { storageManager.listAll().forEach((entry) => { entries.push(entry); }); }); return entries; } }; export { NWaySetAssociativeCache };