index.mjs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. const createLRU = (options) => {
  2. let { max, onEviction } = options;
  3. if (!(Number.isInteger(max) && max > 0))
  4. throw new TypeError("`max` must be a positive integer");
  5. let size = 0;
  6. let head = 0;
  7. let tail = 0;
  8. let free = [];
  9. const keyMap = /* @__PURE__ */ new Map();
  10. const keyList = new Array(max).fill(void 0);
  11. const valList = new Array(max).fill(void 0);
  12. const next = new Array(max).fill(0);
  13. const prev = new Array(max).fill(0);
  14. const setTail = (index, type) => {
  15. if (index === tail) return;
  16. const nextIndex = next[index];
  17. const prevIndex = prev[index];
  18. if (index === head) head = nextIndex;
  19. else if (type === "get" || prevIndex !== 0) next[prevIndex] = nextIndex;
  20. if (nextIndex !== 0) prev[nextIndex] = prevIndex;
  21. next[tail] = index;
  22. prev[index] = tail;
  23. next[index] = 0;
  24. tail = index;
  25. };
  26. const _evict = () => {
  27. const evictHead = head;
  28. const key = keyList[evictHead];
  29. onEviction == null ? void 0 : onEviction(key, valList[evictHead]);
  30. keyMap.delete(key);
  31. keyList[evictHead] = void 0;
  32. valList[evictHead] = void 0;
  33. head = next[evictHead];
  34. if (head !== 0) prev[head] = 0;
  35. size--;
  36. if (size === 0) head = tail = 0;
  37. free.push(evictHead);
  38. return evictHead;
  39. };
  40. return {
  41. /** Adds a key-value pair to the cache. Updates the value if the key already exists. */
  42. set(key, value) {
  43. if (key === void 0) return;
  44. let index = keyMap.get(key);
  45. if (index === void 0) {
  46. index = size === max ? _evict() : free.length > 0 ? free.pop() : size;
  47. keyMap.set(key, index);
  48. keyList[index] = key;
  49. size++;
  50. } else onEviction == null ? void 0 : onEviction(key, valList[index]);
  51. valList[index] = value;
  52. if (size === 1) head = tail = index;
  53. else setTail(index, "set");
  54. },
  55. /** Retrieves the value for a given key and moves the key to the most recent position. */
  56. get(key) {
  57. const index = keyMap.get(key);
  58. if (index === void 0) return;
  59. if (index !== tail) setTail(index, "get");
  60. return valList[index];
  61. },
  62. /** Retrieves the value for a given key without changing its position. */
  63. peek: (key) => {
  64. const index = keyMap.get(key);
  65. return index !== void 0 ? valList[index] : void 0;
  66. },
  67. /** Checks if a key exists in the cache. */
  68. has: (key) => keyMap.has(key),
  69. /** Iterates over all keys in the cache, from most recent to least recent. */
  70. *keys() {
  71. let current = tail;
  72. for (let i = 0; i < size; i++) {
  73. yield keyList[current];
  74. current = prev[current];
  75. }
  76. },
  77. /** Iterates over all values in the cache, from most recent to least recent. */
  78. *values() {
  79. let current = tail;
  80. for (let i = 0; i < size; i++) {
  81. yield valList[current];
  82. current = prev[current];
  83. }
  84. },
  85. /** Iterates over `[key, value]` pairs in the cache, from most recent to least recent. */
  86. *entries() {
  87. let current = tail;
  88. for (let i = 0; i < size; i++) {
  89. yield [keyList[current], valList[current]];
  90. current = prev[current];
  91. }
  92. },
  93. /** Iterates over each value-key pair in the cache, from most recent to least recent. */
  94. forEach: (callback) => {
  95. let current = tail;
  96. for (let i = 0; i < size; i++) {
  97. const key = keyList[current];
  98. const value = valList[current];
  99. callback(value, key);
  100. current = prev[current];
  101. }
  102. },
  103. /** Deletes a key-value pair from the cache. */
  104. delete(key) {
  105. const index = keyMap.get(key);
  106. if (index === void 0) return false;
  107. onEviction == null ? void 0 : onEviction(key, valList[index]);
  108. keyMap.delete(key);
  109. free.push(index);
  110. keyList[index] = void 0;
  111. valList[index] = void 0;
  112. const prevIndex = prev[index];
  113. const nextIndex = next[index];
  114. if (prevIndex !== 0) next[prevIndex] = nextIndex;
  115. if (nextIndex !== 0) prev[nextIndex] = prevIndex;
  116. if (index === head) head = nextIndex;
  117. if (index === tail) tail = prevIndex;
  118. size--;
  119. return true;
  120. },
  121. /** Evicts the oldest item or the specified number of the oldest items from the cache. */
  122. evict: (number) => {
  123. let toPrune = Math.min(number, size);
  124. while (toPrune > 0) {
  125. _evict();
  126. toPrune--;
  127. }
  128. },
  129. /** Clears all key-value pairs from the cache. */
  130. clear() {
  131. if (typeof onEviction === "function") {
  132. const iterator = keyMap.values();
  133. for (let result = iterator.next(); !result.done; result = iterator.next())
  134. onEviction(keyList[result.value], valList[result.value]);
  135. }
  136. keyMap.clear();
  137. keyList.fill(void 0);
  138. valList.fill(void 0);
  139. free = [];
  140. size = 0;
  141. head = tail = 0;
  142. },
  143. /** Resizes the cache to a new maximum size, evicting items if necessary. */
  144. resize: (newMax) => {
  145. if (!(Number.isInteger(newMax) && newMax > 0))
  146. throw new TypeError("`max` must be a positive integer");
  147. if (newMax === max) return;
  148. if (newMax < max) {
  149. let current = tail;
  150. const preserve = Math.min(size, newMax);
  151. const remove = size - preserve;
  152. const newKeyList = new Array(newMax);
  153. const newValList = new Array(newMax);
  154. const newNext = new Array(newMax);
  155. const newPrev = new Array(newMax);
  156. for (let i = 1; i <= remove; i++)
  157. onEviction == null ? void 0 : onEviction(keyList[i], valList[i]);
  158. for (let i = preserve - 1; i >= 0; i--) {
  159. newKeyList[i] = keyList[current];
  160. newValList[i] = valList[current];
  161. newNext[i] = i + 1;
  162. newPrev[i] = i - 1;
  163. keyMap.set(newKeyList[i], i);
  164. current = prev[current];
  165. }
  166. head = 0;
  167. tail = preserve - 1;
  168. size = preserve;
  169. keyList.length = newMax;
  170. valList.length = newMax;
  171. next.length = newMax;
  172. prev.length = newMax;
  173. for (let i = 0; i < preserve; i++) {
  174. keyList[i] = newKeyList[i];
  175. valList[i] = newValList[i];
  176. next[i] = newNext[i];
  177. prev[i] = newPrev[i];
  178. }
  179. free = [];
  180. for (let i = preserve; i < newMax; i++) free.push(i);
  181. } else {
  182. const fill = newMax - max;
  183. keyList.push(...new Array(fill).fill(void 0));
  184. valList.push(...new Array(fill).fill(void 0));
  185. next.push(...new Array(fill).fill(0));
  186. prev.push(...new Array(fill).fill(0));
  187. }
  188. max = newMax;
  189. },
  190. /** Returns the maximum number of items that can be stored in the cache. */
  191. get max() {
  192. return max;
  193. },
  194. /** Returns the number of items currently stored in the cache. */
  195. get size() {
  196. return size;
  197. },
  198. /** Returns the number of currently available slots in the cache before reaching the maximum size. */
  199. get available() {
  200. return max - size;
  201. }
  202. };
  203. };
  204. export {
  205. createLRU
  206. };