StackedCacheMap.js 3.3 KB

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