index.mjs 6.8 KB

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