index.js 8.7 KB

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