index.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. /**
  2. * @file generational-cache.js
  3. * A generational pseudo-LRU cache with strict maximum size limits.
  4. */
  5. /**
  6. * @template K, V
  7. */
  8. export class GenerationalCache {
  9. #max;
  10. #boundary;
  11. #current = new Map();
  12. #old = new Map();
  13. /**
  14. * Initializes a new instance of the GenerationalCache class.
  15. * @param {number} max - The maximum number of items the cache can hold.
  16. */
  17. constructor(max) {
  18. this.max = max;
  19. }
  20. /**
  21. * Returns the total number of `entries` currently in the cache.
  22. * @note To optimize for write speed, this library allows temporary key
  23. * duplication between generations. Therefore, this value may not always
  24. * reflect the exact count of unique `keys`.
  25. * @returns {number} The total entry count.
  26. */
  27. get size() {
  28. return this.#current.size + this.#old.size;
  29. }
  30. /**
  31. * Returns the maximum capacity of the cache.
  32. * @returns {number} The maximum size limit.
  33. */
  34. get max() {
  35. return this.#max;
  36. }
  37. /**
  38. * Sets the maximum capacity of the cache and recalculates the boundary.
  39. * Clears the cache when updated.
  40. * @param {number} value - The new maximum capacity to set.
  41. */
  42. set max(value) {
  43. if (Number.isFinite(value) && value > 4) {
  44. this.#max = value;
  45. this.#boundary = Math.ceil(value / 2);
  46. } else {
  47. this.#max = 4;
  48. this.#boundary = 2;
  49. }
  50. this.clear();
  51. }
  52. /**
  53. * Retrieves an item from the cache.
  54. * If the item is in the older generation, it gets promoted to the current
  55. * generation.
  56. * @param {K} key - The key of the element to return.
  57. * @returns {V | undefined} The element associated with the specified key, or
  58. * undefined if the key cannot be found.
  59. */
  60. get(key) {
  61. let value = this.#current.get(key);
  62. if (value !== undefined) {
  63. return value;
  64. }
  65. value = this.#old.get(key);
  66. if (value !== undefined) {
  67. this.set(key, value);
  68. return value;
  69. }
  70. return undefined;
  71. }
  72. /**
  73. * Adds or updates an element with a specified key and a value to the cache.
  74. * @param {K} key - The key of the element to add.
  75. * @param {V} value - The value of the element to add.
  76. * @returns {GenerationalCache} The cache object itself.
  77. */
  78. set(key, value) {
  79. this.#current.set(key, value);
  80. // Swap generations if the current map reaches the boundary
  81. if (this.#current.size >= this.#boundary) {
  82. this.#old = this.#current;
  83. this.#current = new Map();
  84. }
  85. return this;
  86. }
  87. /**
  88. * Returns a boolean indicating whether an element with the specified key
  89. * exists or not.
  90. * @param {K} key - The key of the element to test for presence.
  91. * @returns {boolean} true if an element with the specified key exists in the
  92. * cache; otherwise false.
  93. */
  94. has(key) {
  95. return this.#current.has(key) || this.#old.has(key);
  96. }
  97. /**
  98. * Removes the specified element from the cache.
  99. * @param {K} key - The key of the element to remove.
  100. * @returns {boolean} true if an element in the cache existed and has been
  101. * removed, or false if the element does not exist.
  102. */
  103. delete(key) {
  104. const deletedFromCurrent = this.#current.delete(key);
  105. const deletedFromOld = this.#old.delete(key);
  106. return deletedFromCurrent || deletedFromOld;
  107. }
  108. /**
  109. * Removes all elements from the cache.
  110. */
  111. clear() {
  112. this.#current.clear();
  113. this.#old.clear();
  114. }
  115. }