mirror of https://github.com/webpack/webpack.git
				
				
				
			
		
			
	
	
		
			236 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
		
		
			
		
	
	
			236 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
|  | /* | ||
|  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | ||
|  | 	Author Tobias Koppers @sokra | ||
|  | */ | ||
|  | 
 | ||
|  | "use strict"; | ||
|  | 
 | ||
|  | const SortableSet = require("./SortableSet"); | ||
|  | 
 | ||
|  | /** | ||
|  |  * @template T | ||
|  |  * @template K | ||
|  |  * Multi layer bucket sorted set | ||
|  |  * Supports adding non-existing items (DO NOT ADD ITEM TWICE) | ||
|  |  * Supports removing exiting items (DO NOT REMOVE ITEM NOT IN SET) | ||
|  |  * Supports popping the first items according to defined order | ||
|  |  * Supports iterating all items without order | ||
|  |  * Supports updating an item in an efficient way | ||
|  |  * Supports size property, which is the number of items | ||
|  |  * Items are lazy partially sorted when needed | ||
|  |  */ | ||
|  | class LazyBucketSortedSet { | ||
|  | 	/** | ||
|  | 	 * @param {function(T): K} getKey function to get key from item | ||
|  | 	 * @param {function(K, K): number} comparator comparator to sort keys | ||
|  | 	 * @param  {...((function(T): any) | (function(any, any): number))} args more pairs of getKey and comparator plus optional final comparator for the last layer | ||
|  | 	 */ | ||
|  | 	constructor(getKey, comparator, ...args) { | ||
|  | 		this._getKey = getKey; | ||
|  | 		this._innerArgs = args; | ||
|  | 		this._leaf = args.length <= 1; | ||
|  | 		this._keys = new SortableSet(undefined, comparator); | ||
|  | 		/** @type {Map<K, LazyBucketSortedSet<T, any> | SortableSet<T>>} */ | ||
|  | 		this._map = new Map(); | ||
|  | 		this._unsortedItems = new Set(); | ||
|  | 		this.size = 0; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {T} item an item | ||
|  | 	 * @returns {void} | ||
|  | 	 */ | ||
|  | 	add(item) { | ||
|  | 		this.size++; | ||
|  | 		this._unsortedItems.add(item); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {K} key key of item | ||
|  | 	 * @param {T} item the item | ||
|  | 	 * @returns {void} | ||
|  | 	 */ | ||
|  | 	_addInternal(key, item) { | ||
|  | 		let entry = this._map.get(key); | ||
|  | 		if (entry === undefined) { | ||
|  | 			entry = this._leaf | ||
|  | 				? new SortableSet(undefined, this._innerArgs[0]) | ||
|  | 				: new /** @type {any} */ (LazyBucketSortedSet)(...this._innerArgs); | ||
|  | 			this._keys.add(key); | ||
|  | 			this._map.set(key, entry); | ||
|  | 		} | ||
|  | 		entry.add(item); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {T} item an item | ||
|  | 	 * @returns {void} | ||
|  | 	 */ | ||
|  | 	delete(item) { | ||
|  | 		this.size--; | ||
|  | 		if (this._unsortedItems.has(item)) { | ||
|  | 			this._unsortedItems.delete(item); | ||
|  | 			return; | ||
|  | 		} | ||
|  | 		const key = this._getKey(item); | ||
|  | 		const entry = this._map.get(key); | ||
|  | 		entry.delete(item); | ||
|  | 		if (entry.size === 0) { | ||
|  | 			this._deleteKey(key); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {K} key key to be removed | ||
|  | 	 * @returns {void} | ||
|  | 	 */ | ||
|  | 	_deleteKey(key) { | ||
|  | 		this._keys.delete(key); | ||
|  | 		this._map.delete(key); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @returns {T | undefined} an item | ||
|  | 	 */ | ||
|  | 	popFirst() { | ||
|  | 		if (this.size === 0) return undefined; | ||
|  | 		this.size--; | ||
|  | 		if (this._unsortedItems.size > 0) { | ||
|  | 			for (const item of this._unsortedItems) { | ||
|  | 				const key = this._getKey(item); | ||
|  | 				this._addInternal(key, item); | ||
|  | 			} | ||
|  | 			this._unsortedItems.clear(); | ||
|  | 		} | ||
|  | 		this._keys.sort(); | ||
|  | 		const key = this._keys.values().next().value; | ||
|  | 		const entry = this._map.get(key); | ||
|  | 		if (this._leaf) { | ||
|  | 			const leafEntry = /** @type {SortableSet<T>} */ (entry); | ||
|  | 			leafEntry.sort(); | ||
|  | 			const item = leafEntry.values().next().value; | ||
|  | 			leafEntry.delete(item); | ||
|  | 			if (leafEntry.size === 0) { | ||
|  | 				this._deleteKey(key); | ||
|  | 			} | ||
|  | 			return item; | ||
|  | 		} else { | ||
|  | 			const nodeEntry = /** @type {LazyBucketSortedSet<T, any>} */ (entry); | ||
|  | 			const item = nodeEntry.popFirst(); | ||
|  | 			if (nodeEntry.size === 0) { | ||
|  | 				this._deleteKey(key); | ||
|  | 			} | ||
|  | 			return item; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {T} item to be updated item | ||
|  | 	 * @returns {function(true=): void} finish update | ||
|  | 	 */ | ||
|  | 	startUpdate(item) { | ||
|  | 		if (this._unsortedItems.has(item)) { | ||
|  | 			return remove => { | ||
|  | 				if (remove) { | ||
|  | 					this._unsortedItems.delete(item); | ||
|  | 					this.size--; | ||
|  | 					return; | ||
|  | 				} | ||
|  | 			}; | ||
|  | 		} | ||
|  | 		const key = this._getKey(item); | ||
|  | 		if (this._leaf) { | ||
|  | 			const oldEntry = /** @type {SortableSet<T>} */ (this._map.get(key)); | ||
|  | 			return remove => { | ||
|  | 				if (remove) { | ||
|  | 					this.size--; | ||
|  | 					oldEntry.delete(item); | ||
|  | 					if (oldEntry.size === 0) { | ||
|  | 						this._deleteKey(key); | ||
|  | 					} | ||
|  | 					return; | ||
|  | 				} | ||
|  | 				const newKey = this._getKey(item); | ||
|  | 				if (key === newKey) { | ||
|  | 					// This flags the sortable set as unordered
 | ||
|  | 					oldEntry.add(item); | ||
|  | 				} else { | ||
|  | 					oldEntry.delete(item); | ||
|  | 					if (oldEntry.size === 0) { | ||
|  | 						this._deleteKey(key); | ||
|  | 					} | ||
|  | 					this._addInternal(newKey, item); | ||
|  | 				} | ||
|  | 			}; | ||
|  | 		} else { | ||
|  | 			const oldEntry = /** @type {LazyBucketSortedSet<T, any>} */ (this._map.get( | ||
|  | 				key | ||
|  | 			)); | ||
|  | 			const finishUpdate = oldEntry.startUpdate(item); | ||
|  | 			return remove => { | ||
|  | 				if (remove) { | ||
|  | 					this.size--; | ||
|  | 					finishUpdate(true); | ||
|  | 					if (oldEntry.size === 0) { | ||
|  | 						this._deleteKey(key); | ||
|  | 					} | ||
|  | 					return; | ||
|  | 				} | ||
|  | 				const newKey = this._getKey(item); | ||
|  | 				if (key === newKey) { | ||
|  | 					finishUpdate(); | ||
|  | 				} else { | ||
|  | 					finishUpdate(true); | ||
|  | 					if (oldEntry.size === 0) { | ||
|  | 						this._deleteKey(key); | ||
|  | 					} | ||
|  | 					this._addInternal(newKey, item); | ||
|  | 				} | ||
|  | 			}; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @param {Iterator<T>[]} iterators list of iterators to append to | ||
|  | 	 * @returns {void} | ||
|  | 	 */ | ||
|  | 	_appendIterators(iterators) { | ||
|  | 		if (this._unsortedItems.size > 0) | ||
|  | 			iterators.push(this._unsortedItems[Symbol.iterator]()); | ||
|  | 		for (const key of this._keys) { | ||
|  | 			const entry = this._map.get(key); | ||
|  | 			if (this._leaf) { | ||
|  | 				const leafEntry = /** @type {SortableSet<T>} */ (entry); | ||
|  | 				const iterator = leafEntry[Symbol.iterator](); | ||
|  | 				iterators.push(iterator); | ||
|  | 			} else { | ||
|  | 				const nodeEntry = /** @type {LazyBucketSortedSet<T, any>} */ (entry); | ||
|  | 				nodeEntry._appendIterators(iterators); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/** | ||
|  | 	 * @returns {Iterator<T>} the iterator | ||
|  | 	 */ | ||
|  | 	[Symbol.iterator]() { | ||
|  | 		const iterators = []; | ||
|  | 		this._appendIterators(iterators); | ||
|  | 		iterators.reverse(); | ||
|  | 		let currentIterator = iterators.pop(); | ||
|  | 		return { | ||
|  | 			next: () => { | ||
|  | 				const res = currentIterator.next(); | ||
|  | 				if (res.done) { | ||
|  | 					if (iterators.length === 0) return res; | ||
|  | 					currentIterator = iterators.pop(); | ||
|  | 					return currentIterator.next(); | ||
|  | 				} | ||
|  | 				return res; | ||
|  | 			} | ||
|  | 		}; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = LazyBucketSortedSet; |