StackedCacheMap.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. /**
  7. * @template K
  8. * @template V
  9. *
  10. * The StackedCacheMap is a data structure designed as an alternative to a Map
  11. * in situations where you need to handle multiple item additions and
  12. * frequently access the largest map.
  13. *
  14. * It is particularly optimized for efficiently adding multiple items
  15. * at once, which can be achieved using the `addAll` method.
  16. *
  17. * It has a fallback Map that is used when the map to be added is mutable.
  18. *
  19. * Note: `delete` and `has` are not supported for performance reasons.
  20. *
  21. * @example
  22. * ```js
  23. * const map = new StackedCacheMap();
  24. * map.addAll(new Map([["a", 1], ["b", 2]]), true);
  25. * map.addAll(new Map([["c", 3], ["d", 4]]), true);
  26. * map.get("a"); // 1
  27. * map.get("d"); // 4
  28. * for (const [key, value] of map) {
  29. * console.log(key, value);
  30. * }
  31. * ```
  32. */
  33. class StackedCacheMap {
  34. constructor() {
  35. /** @type {Map<K, V>} */
  36. this.map = new Map();
  37. /** @type {ReadonlyMap<K, V>[]} */
  38. this.stack = [];
  39. }
  40. /**
  41. * If `immutable` is true, the map can be referenced by the StackedCacheMap
  42. * and should not be changed afterwards. If the map is mutable, all items
  43. * are copied into a fallback Map.
  44. * @param {ReadonlyMap<K, V>} map map to add
  45. * @param {boolean=} immutable if 'map' is immutable and StackedCacheMap can keep referencing it
  46. */
  47. addAll(map, immutable) {
  48. if (immutable) {
  49. this.stack.push(map);
  50. // largest map should go first
  51. for (let i = this.stack.length - 1; i > 0; i--) {
  52. const beforeLast = this.stack[i - 1];
  53. if (beforeLast.size >= map.size) break;
  54. this.stack[i] = beforeLast;
  55. this.stack[i - 1] = map;
  56. }
  57. } else {
  58. for (const [key, value] of map) {
  59. this.map.set(key, value);
  60. }
  61. }
  62. }
  63. /**
  64. * @param {K} item the key of the element to add
  65. * @param {V} value the value of the element to add
  66. * @returns {void}
  67. */
  68. set(item, value) {
  69. this.map.set(item, value);
  70. }
  71. /**
  72. * @param {K} item the item to delete
  73. * @returns {void}
  74. */
  75. delete(item) {
  76. throw new Error("Items can't be deleted from a StackedCacheMap");
  77. }
  78. /**
  79. * @param {K} item the item to test
  80. * @returns {boolean} true if the item exists in this set
  81. */
  82. has(item) {
  83. throw new Error(
  84. "Checking StackedCacheMap.has before reading is inefficient, use StackedCacheMap.get and check for undefined"
  85. );
  86. }
  87. /**
  88. * @param {K} item the key of the element to return
  89. * @returns {V} the value of the element
  90. */
  91. get(item) {
  92. for (const map of this.stack) {
  93. const value = map.get(item);
  94. if (value !== undefined) return value;
  95. }
  96. return this.map.get(item);
  97. }
  98. clear() {
  99. this.stack.length = 0;
  100. this.map.clear();
  101. }
  102. /**
  103. * @returns {number} size of the map
  104. */
  105. get size() {
  106. let size = this.map.size;
  107. for (const map of this.stack) {
  108. size += map.size;
  109. }
  110. return size;
  111. }
  112. /**
  113. * @returns {Iterator<[K, V]>} iterator
  114. */
  115. [Symbol.iterator]() {
  116. const iterators = this.stack.map(map => map[Symbol.iterator]());
  117. let current = this.map[Symbol.iterator]();
  118. return {
  119. next() {
  120. let result = current.next();
  121. while (result.done && iterators.length > 0) {
  122. current = iterators.pop();
  123. result = current.next();
  124. }
  125. return result;
  126. }
  127. };
  128. }
  129. }
  130. module.exports = StackedCacheMap;