UNPKG

@thi.ng/cache

Version:

In-memory cache implementations with ES6 Map-like API and different eviction strategies

142 lines (141 loc) 3.01 kB
import { DCons } from "@thi.ng/dcons/dcons"; class LRUCache { map; items; opts; _size; constructor(pairs, opts) { const _opts = { maxlen: Infinity, maxsize: Infinity, map: () => /* @__PURE__ */ new Map(), ksize: () => 0, vsize: () => 0, ...opts }; this.map = _opts.map(); this.items = new DCons(); this._size = 0; this.opts = _opts; if (pairs) { this.into(pairs); } } get length() { return this.items.length; } get size() { return this._size; } [Symbol.iterator]() { return this.entries(); } *entries() { for (let e of this.items) yield [e.k, e]; } *keys() { for (let e of this.items) yield e.k; } *values() { for (let e of this.items) yield e.v; } copy() { const c = this.empty(); c.items = this.items.copy(); let cell = c.items.head; while (cell) { c.map.set(cell.value.k, cell); cell = cell.next; } return c; } empty() { return new LRUCache(null, this.opts); } release() { this._size = 0; this.map.clear(); const release = this.opts.release; if (release) { let e; while (e = this.items.drop()) { release(e.k, e.v); } return true; } return this.items.release(); } has(key) { return this.map.has(key); } get(key, notFound) { const e = this.map.get(key); return e ? this.resetEntry(e) : notFound; } set(key, value) { const size = this.opts.ksize(key) + this.opts.vsize(value); const e = this.map.get(key); const additionalSize = Math.max(0, size - (e ? e.value.s : 0)); this._size += additionalSize; if (this.ensureSize()) { this.doSetEntry(e, key, value, size); } else { this._size -= additionalSize; } return value; } into(pairs) { for (let p of pairs) { this.set(p[0], p[1]); } return this; } async getSet(key, retrieve) { const e = this.map.get(key); return e ? this.resetEntry(e) : this.set(key, await retrieve()); } delete(key) { const e = this.map.get(key); if (e) { this.removeEntry(e); return true; } return false; } resetEntry(e) { this.items.asTail(e); return e.value.v; } ensureSize() { const { release, maxsize, maxlen } = this.opts; while (this._size > maxsize || this.length >= maxlen) { const e = this.items.drop(); if (!e) return false; this.map.delete(e.k); release?.(e.k, e.v); this._size -= e.s; } return true; } removeEntry(e) { const ee = e.value; this.map.delete(ee.k); this.items.remove(e); this.opts.release?.(ee.k, ee.v); this._size -= ee.s; } doSetEntry(e, k, v, s) { if (e) { this.opts.update?.(k, e.value.v, v); e.value.v = v; e.value.s = s; this.items.asTail(e); } else { this.items.push({ k, v, s }); this.map.set(k, this.items.tail); } } } export { LRUCache };