123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- const createLRU = (options) => {
- let { max, onEviction } = options;
- if (!(Number.isInteger(max) && max > 0))
- throw new TypeError("`max` must be a positive integer");
- let size = 0;
- let head = 0;
- let tail = 0;
- let free = [];
- const keyMap = /* @__PURE__ */ new Map();
- const keyList = new Array(max).fill(void 0);
- const valList = new Array(max).fill(void 0);
- const next = new Array(max).fill(0);
- const prev = new Array(max).fill(0);
- const setTail = (index, type) => {
- if (index === tail) return;
- const nextIndex = next[index];
- const prevIndex = prev[index];
- if (index === head) head = nextIndex;
- else if (type === "get" || prevIndex !== 0) next[prevIndex] = nextIndex;
- if (nextIndex !== 0) prev[nextIndex] = prevIndex;
- next[tail] = index;
- prev[index] = tail;
- next[index] = 0;
- tail = index;
- };
- const _evict = () => {
- const evictHead = head;
- const key = keyList[evictHead];
- onEviction == null ? void 0 : onEviction(key, valList[evictHead]);
- keyMap.delete(key);
- keyList[evictHead] = void 0;
- valList[evictHead] = void 0;
- head = next[evictHead];
- if (head !== 0) prev[head] = 0;
- size--;
- if (size === 0) head = tail = 0;
- free.push(evictHead);
- return evictHead;
- };
- return {
- /** Adds a key-value pair to the cache. Updates the value if the key already exists. */
- set(key, value) {
- if (key === void 0) return;
- let index = keyMap.get(key);
- if (index === void 0) {
- index = size === max ? _evict() : free.length > 0 ? free.pop() : size;
- keyMap.set(key, index);
- keyList[index] = key;
- size++;
- } else onEviction == null ? void 0 : onEviction(key, valList[index]);
- valList[index] = value;
- if (size === 1) head = tail = index;
- else setTail(index, "set");
- },
- /** Retrieves the value for a given key and moves the key to the most recent position. */
- get(key) {
- const index = keyMap.get(key);
- if (index === void 0) return;
- if (index !== tail) setTail(index, "get");
- return valList[index];
- },
- /** Retrieves the value for a given key without changing its position. */
- peek: (key) => {
- const index = keyMap.get(key);
- return index !== void 0 ? valList[index] : void 0;
- },
- /** Checks if a key exists in the cache. */
- has: (key) => keyMap.has(key),
- /** Iterates over all keys in the cache, from most recent to least recent. */
- *keys() {
- let current = tail;
- for (let i = 0; i < size; i++) {
- yield keyList[current];
- current = prev[current];
- }
- },
- /** Iterates over all values in the cache, from most recent to least recent. */
- *values() {
- let current = tail;
- for (let i = 0; i < size; i++) {
- yield valList[current];
- current = prev[current];
- }
- },
- /** Iterates over `[key, value]` pairs in the cache, from most recent to least recent. */
- *entries() {
- let current = tail;
- for (let i = 0; i < size; i++) {
- yield [keyList[current], valList[current]];
- current = prev[current];
- }
- },
- /** Iterates over each value-key pair in the cache, from most recent to least recent. */
- forEach: (callback) => {
- let current = tail;
- for (let i = 0; i < size; i++) {
- const key = keyList[current];
- const value = valList[current];
- callback(value, key);
- current = prev[current];
- }
- },
- /** Deletes a key-value pair from the cache. */
- delete(key) {
- const index = keyMap.get(key);
- if (index === void 0) return false;
- onEviction == null ? void 0 : onEviction(key, valList[index]);
- keyMap.delete(key);
- free.push(index);
- keyList[index] = void 0;
- valList[index] = void 0;
- const prevIndex = prev[index];
- const nextIndex = next[index];
- if (prevIndex !== 0) next[prevIndex] = nextIndex;
- if (nextIndex !== 0) prev[nextIndex] = prevIndex;
- if (index === head) head = nextIndex;
- if (index === tail) tail = prevIndex;
- size--;
- return true;
- },
- /** Evicts the oldest item or the specified number of the oldest items from the cache. */
- evict: (number) => {
- let toPrune = Math.min(number, size);
- while (toPrune > 0) {
- _evict();
- toPrune--;
- }
- },
- /** Clears all key-value pairs from the cache. */
- clear() {
- if (typeof onEviction === "function") {
- const iterator = keyMap.values();
- for (let result = iterator.next(); !result.done; result = iterator.next())
- onEviction(keyList[result.value], valList[result.value]);
- }
- keyMap.clear();
- keyList.fill(void 0);
- valList.fill(void 0);
- free = [];
- size = 0;
- head = tail = 0;
- },
- /** Resizes the cache to a new maximum size, evicting items if necessary. */
- resize: (newMax) => {
- if (!(Number.isInteger(newMax) && newMax > 0))
- throw new TypeError("`max` must be a positive integer");
- if (newMax === max) return;
- if (newMax < max) {
- let current = tail;
- const preserve = Math.min(size, newMax);
- const remove = size - preserve;
- const newKeyList = new Array(newMax);
- const newValList = new Array(newMax);
- const newNext = new Array(newMax);
- const newPrev = new Array(newMax);
- for (let i = 1; i <= remove; i++)
- onEviction == null ? void 0 : onEviction(keyList[i], valList[i]);
- for (let i = preserve - 1; i >= 0; i--) {
- newKeyList[i] = keyList[current];
- newValList[i] = valList[current];
- newNext[i] = i + 1;
- newPrev[i] = i - 1;
- keyMap.set(newKeyList[i], i);
- current = prev[current];
- }
- head = 0;
- tail = preserve - 1;
- size = preserve;
- keyList.length = newMax;
- valList.length = newMax;
- next.length = newMax;
- prev.length = newMax;
- for (let i = 0; i < preserve; i++) {
- keyList[i] = newKeyList[i];
- valList[i] = newValList[i];
- next[i] = newNext[i];
- prev[i] = newPrev[i];
- }
- free = [];
- for (let i = preserve; i < newMax; i++) free.push(i);
- } else {
- const fill = newMax - max;
- keyList.push(...new Array(fill).fill(void 0));
- valList.push(...new Array(fill).fill(void 0));
- next.push(...new Array(fill).fill(0));
- prev.push(...new Array(fill).fill(0));
- }
- max = newMax;
- },
- /** Returns the maximum number of items that can be stored in the cache. */
- get max() {
- return max;
- },
- /** Returns the number of items currently stored in the cache. */
- get size() {
- return size;
- },
- /** Returns the number of currently available slots in the cache before reaching the maximum size. */
- get available() {
- return max - size;
- }
- };
- };
- export {
- createLRU
- };
|