LazySet.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const makeSerializable = require("./makeSerializable.js");
  7. /**
  8. * @template T
  9. * @param {Set<T>} targetSet set where items should be added
  10. * @param {Set<Iterable<T>>} toMerge iterables to be merged
  11. * @returns {void}
  12. */
  13. const merge = (targetSet, toMerge) => {
  14. for (const set of toMerge) {
  15. for (const item of set) {
  16. targetSet.add(item);
  17. }
  18. }
  19. };
  20. /**
  21. * @template T
  22. * @param {Set<Iterable<T>>} targetSet set where iterables should be added
  23. * @param {Array<LazySet<T>>} toDeepMerge lazy sets to be flattened
  24. * @returns {void}
  25. */
  26. const flatten = (targetSet, toDeepMerge) => {
  27. for (const set of toDeepMerge) {
  28. if (set._set.size > 0) targetSet.add(set._set);
  29. if (set._needMerge) {
  30. for (const mergedSet of set._toMerge) {
  31. targetSet.add(mergedSet);
  32. }
  33. flatten(targetSet, set._toDeepMerge);
  34. }
  35. }
  36. };
  37. /**
  38. * Like Set but with an addAll method to eventually add items from another iterable.
  39. * Access methods make sure that all delayed operations are executed.
  40. * Iteration methods deopts to normal Set performance until clear is called again (because of the chance of modifications during iteration).
  41. * @template T
  42. */
  43. class LazySet {
  44. /**
  45. * @param {Iterable<T>=} iterable init iterable
  46. */
  47. constructor(iterable) {
  48. /** @type {Set<T>} */
  49. this._set = new Set(iterable);
  50. /** @type {Set<Iterable<T>>} */
  51. this._toMerge = new Set();
  52. /** @type {Array<LazySet<T>>} */
  53. this._toDeepMerge = [];
  54. this._needMerge = false;
  55. this._deopt = false;
  56. }
  57. _flatten() {
  58. flatten(this._toMerge, this._toDeepMerge);
  59. this._toDeepMerge.length = 0;
  60. }
  61. _merge() {
  62. this._flatten();
  63. merge(this._set, this._toMerge);
  64. this._toMerge.clear();
  65. this._needMerge = false;
  66. }
  67. _isEmpty() {
  68. return (
  69. this._set.size === 0 &&
  70. this._toMerge.size === 0 &&
  71. this._toDeepMerge.length === 0
  72. );
  73. }
  74. get size() {
  75. if (this._needMerge) this._merge();
  76. return this._set.size;
  77. }
  78. /**
  79. * @param {T} item an item
  80. * @returns {this} itself
  81. */
  82. add(item) {
  83. this._set.add(item);
  84. return this;
  85. }
  86. /**
  87. * @param {Iterable<T> | LazySet<T>} iterable a immutable iterable or another immutable LazySet which will eventually be merged into the Set
  88. * @returns {this} itself
  89. */
  90. addAll(iterable) {
  91. if (this._deopt) {
  92. const _set = this._set;
  93. for (const item of iterable) {
  94. _set.add(item);
  95. }
  96. } else {
  97. if (iterable instanceof LazySet) {
  98. if (iterable._isEmpty()) return this;
  99. this._toDeepMerge.push(iterable);
  100. this._needMerge = true;
  101. if (this._toDeepMerge.length > 100000) {
  102. this._flatten();
  103. }
  104. } else {
  105. this._toMerge.add(iterable);
  106. this._needMerge = true;
  107. }
  108. if (this._toMerge.size > 100000) this._merge();
  109. }
  110. return this;
  111. }
  112. clear() {
  113. this._set.clear();
  114. this._toMerge.clear();
  115. this._toDeepMerge.length = 0;
  116. this._needMerge = false;
  117. this._deopt = false;
  118. }
  119. /**
  120. * @param {T} value an item
  121. * @returns {boolean} true, if the value was in the Set before
  122. */
  123. delete(value) {
  124. if (this._needMerge) this._merge();
  125. return this._set.delete(value);
  126. }
  127. entries() {
  128. this._deopt = true;
  129. if (this._needMerge) this._merge();
  130. return this._set.entries();
  131. }
  132. /**
  133. * @param {function(T, T, Set<T>): void} callbackFn function called for each entry
  134. * @param {any} thisArg this argument for the callbackFn
  135. * @returns {void}
  136. */
  137. forEach(callbackFn, thisArg) {
  138. this._deopt = true;
  139. if (this._needMerge) this._merge();
  140. this._set.forEach(callbackFn, thisArg);
  141. }
  142. /**
  143. * @param {T} item an item
  144. * @returns {boolean} true, when the item is in the Set
  145. */
  146. has(item) {
  147. if (this._needMerge) this._merge();
  148. return this._set.has(item);
  149. }
  150. keys() {
  151. this._deopt = true;
  152. if (this._needMerge) this._merge();
  153. return this._set.keys();
  154. }
  155. values() {
  156. this._deopt = true;
  157. if (this._needMerge) this._merge();
  158. return this._set.values();
  159. }
  160. [Symbol.iterator]() {
  161. this._deopt = true;
  162. if (this._needMerge) this._merge();
  163. return this._set[Symbol.iterator]();
  164. }
  165. /* istanbul ignore next */
  166. get [Symbol.toStringTag]() {
  167. return "LazySet";
  168. }
  169. serialize({ write }) {
  170. if (this._needMerge) this._merge();
  171. write(this._set.size);
  172. for (const item of this._set) write(item);
  173. }
  174. static deserialize({ read }) {
  175. const count = read();
  176. const items = [];
  177. for (let i = 0; i < count; i++) {
  178. items.push(read());
  179. }
  180. return new LazySet(items);
  181. }
  182. }
  183. makeSerializable(LazySet, "webpack/lib/util/LazySet");
  184. module.exports = LazySet;