source: frontend/node_modules/webpack/lib/util/LazyBucketSortedSet.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 7.4 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const { first } = require("./SetHelpers");
9const SortableSet = require("./SortableSet");
10
11/**
12 * Callback that extracts the grouping key for an item at one bucket layer.
13 * @template T
14 * @template K
15 * @typedef {(item: T) => K} GetKey
16 */
17
18/**
19 * Comparison function used to order keys or leaf items.
20 * @template T
21 * @typedef {(a: T, n: T) => number} Comparator
22 */
23
24/**
25 * Internal bucket entry, either another nested bucket set or a sorted leaf set.
26 * @template T
27 * @template K
28 * @typedef {LazyBucketSortedSet<T, K> | SortableSet<T>} Entry
29 */
30
31/**
32 * Constructor argument accepted for nested bucket layers or the final leaf
33 * comparator.
34 * @template T
35 * @template K
36 * @typedef {GetKey<T, K> | Comparator<K> | Comparator<T>} Arg
37 */
38
39/**
40 * Multi layer bucket sorted set:
41 * Supports adding non-existing items (DO NOT ADD ITEM TWICE),
42 * Supports removing exiting items (DO NOT REMOVE ITEM NOT IN SET),
43 * Supports popping the first items according to defined order,
44 * Supports iterating all items without order,
45 * Supports updating an item in an efficient way,
46 * Supports size property, which is the number of items,
47 * Items are lazy partially sorted when needed
48 * @template T
49 * @template K
50 */
51class LazyBucketSortedSet {
52 /**
53 * Creates a lazily sorted, potentially multi-level bucket structure whose
54 * order is only fully resolved when items are popped.
55 * @param {GetKey<T, K>} getKey function to get key from item
56 * @param {Comparator<K>=} comparator comparator to sort keys
57 * @param {...Arg<T, K>} args more pairs of getKey and comparator plus optional final comparator for the last layer
58 */
59 constructor(getKey, comparator, ...args) {
60 this._getKey = getKey;
61 this._innerArgs = args;
62 this._leaf = args.length <= 1;
63 this._keys = new SortableSet(undefined, comparator);
64 /** @type {Map<K, Entry<T, K>>} */
65 this._map = new Map();
66 /** @type {Set<T>} */
67 this._unsortedItems = new Set();
68 this.size = 0;
69 }
70
71 /**
72 * Adds an item to the unsorted staging area so sorting can be deferred until
73 * an ordered pop is requested.
74 * @param {T} item an item
75 * @returns {void}
76 */
77 add(item) {
78 this.size++;
79 this._unsortedItems.add(item);
80 }
81
82 /**
83 * Inserts an item into the correct nested bucket, creating intermediate
84 * bucket structures on demand.
85 * @param {K} key key of item
86 * @param {T} item the item
87 * @returns {void}
88 */
89 _addInternal(key, item) {
90 let entry = this._map.get(key);
91 if (entry === undefined) {
92 entry = this._leaf
93 ? new SortableSet(
94 undefined,
95 /** @type {Comparator<T>} */
96 (this._innerArgs[0])
97 )
98 : new LazyBucketSortedSet(
99 .../** @type {[GetKey<T, K>, Comparator<K>]} */
100 (this._innerArgs)
101 );
102 this._keys.add(key);
103 this._map.set(key, entry);
104 }
105 entry.add(item);
106 }
107
108 /**
109 * Removes an item from either the unsorted staging area or its resolved
110 * bucket and prunes empty buckets as needed.
111 * @param {T} item an item
112 * @returns {void}
113 */
114 delete(item) {
115 this.size--;
116 if (this._unsortedItems.has(item)) {
117 this._unsortedItems.delete(item);
118 return;
119 }
120 const key = this._getKey(item);
121 const entry = /** @type {Entry<T, K>} */ (this._map.get(key));
122 entry.delete(item);
123 if (entry.size === 0) {
124 this._deleteKey(key);
125 }
126 }
127
128 /**
129 * Removes an empty bucket key and its corresponding nested entry.
130 * @param {K} key key to be removed
131 * @returns {void}
132 */
133 _deleteKey(key) {
134 this._keys.delete(key);
135 this._map.delete(key);
136 }
137
138 /**
139 * Removes and returns the smallest item according to the configured bucket
140 * order, sorting only the portions of the structure that are needed.
141 * @returns {T | undefined} an item
142 */
143 popFirst() {
144 if (this.size === 0) return;
145 this.size--;
146 if (this._unsortedItems.size > 0) {
147 for (const item of this._unsortedItems) {
148 const key = this._getKey(item);
149 this._addInternal(key, item);
150 }
151 this._unsortedItems.clear();
152 }
153 this._keys.sort();
154 const key = /** @type {K} */ (first(this._keys));
155 const entry = this._map.get(key);
156 if (this._leaf) {
157 const leafEntry = /** @type {SortableSet<T>} */ (entry);
158 leafEntry.sort();
159 const item = /** @type {T} */ (first(leafEntry));
160 leafEntry.delete(item);
161 if (leafEntry.size === 0) {
162 this._deleteKey(key);
163 }
164 return item;
165 }
166 const nodeEntry =
167 /** @type {LazyBucketSortedSet<T, K>} */
168 (entry);
169 const item = nodeEntry.popFirst();
170 if (nodeEntry.size === 0) {
171 this._deleteKey(key);
172 }
173 return item;
174 }
175
176 /**
177 * Begins an in-place update for an item and returns a completion callback
178 * that can either reinsert it under a new key or remove it entirely.
179 * @param {T} item to be updated item
180 * @returns {(remove?: true) => void} finish update
181 */
182 startUpdate(item) {
183 if (this._unsortedItems.has(item)) {
184 return (remove) => {
185 if (remove) {
186 this._unsortedItems.delete(item);
187 this.size--;
188 }
189 };
190 }
191 const key = this._getKey(item);
192 if (this._leaf) {
193 const oldEntry = /** @type {SortableSet<T>} */ (this._map.get(key));
194 return (remove) => {
195 if (remove) {
196 this.size--;
197 oldEntry.delete(item);
198 if (oldEntry.size === 0) {
199 this._deleteKey(key);
200 }
201 return;
202 }
203 const newKey = this._getKey(item);
204 if (key === newKey) {
205 // This flags the sortable set as unordered
206 oldEntry.add(item);
207 } else {
208 oldEntry.delete(item);
209 if (oldEntry.size === 0) {
210 this._deleteKey(key);
211 }
212 this._addInternal(newKey, item);
213 }
214 };
215 }
216 const oldEntry =
217 /** @type {LazyBucketSortedSet<T, K>} */
218 (this._map.get(key));
219 const finishUpdate = oldEntry.startUpdate(item);
220 return (remove) => {
221 if (remove) {
222 this.size--;
223 finishUpdate(true);
224 if (oldEntry.size === 0) {
225 this._deleteKey(key);
226 }
227 return;
228 }
229 const newKey = this._getKey(item);
230 if (key === newKey) {
231 finishUpdate();
232 } else {
233 finishUpdate(true);
234 if (oldEntry.size === 0) {
235 this._deleteKey(key);
236 }
237 this._addInternal(newKey, item);
238 }
239 };
240 }
241
242 /**
243 * Appends iterators for every stored bucket and leaf to support unordered
244 * traversal across the entire structure.
245 * @param {Iterator<T>[]} iterators list of iterators to append to
246 * @returns {void}
247 */
248 _appendIterators(iterators) {
249 if (this._unsortedItems.size > 0) {
250 iterators.push(this._unsortedItems[Symbol.iterator]());
251 }
252 for (const key of this._keys) {
253 const entry = this._map.get(key);
254 if (this._leaf) {
255 const leafEntry = /** @type {SortableSet<T>} */ (entry);
256 const iterator = leafEntry[Symbol.iterator]();
257 iterators.push(iterator);
258 } else {
259 const nodeEntry =
260 /** @type {LazyBucketSortedSet<T, K>} */
261 (entry);
262 nodeEntry._appendIterators(iterators);
263 }
264 }
265 }
266
267 /**
268 * Iterates over all stored items without imposing bucket sort order.
269 * @returns {Iterator<T>} the iterator
270 */
271 [Symbol.iterator]() {
272 /** @type {Iterator<T>[]} */
273 const iterators = [];
274 this._appendIterators(iterators);
275 iterators.reverse();
276 let currentIterator =
277 /** @type {Iterator<T>} */
278 (iterators.pop());
279 return {
280 next: () => {
281 const res = currentIterator.next();
282 if (res.done) {
283 if (iterators.length === 0) return res;
284 currentIterator = /** @type {Iterator<T>} */ (iterators.pop());
285 return currentIterator.next();
286 }
287 return res;
288 }
289 };
290 }
291}
292
293module.exports = LazyBucketSortedSet;
Note: See TracBrowser for help on using the repository browser.