initial commit

This commit is contained in:
2026-06-25 21:30:32 +00:00
commit 328faf6251
220 changed files with 162103 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Defines the collection interface.
*/
goog.provide('goog.structs.Collection');
/**
* An interface for a collection of values.
* @interface
* @template T
*/
goog.structs.Collection = function() {};
/**
* @param {T} value Value to add to the collection.
*/
goog.structs.Collection.prototype.add;
/**
* @param {T} value Value to remove from the collection.
*/
goog.structs.Collection.prototype.remove;
/**
* @param {T} value Value to find in the collection.
* @return {boolean} Whether the collection contains the specified value.
*/
goog.structs.Collection.prototype.contains;
/**
* @return {number} The number of values stored in the collection.
*/
goog.structs.Collection.prototype.getCount;
+348
View File
@@ -0,0 +1,348 @@
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Datastructure: Heap.
*
*
* This file provides the implementation of a Heap datastructure. Smaller keys
* rise to the top.
*
* The big-O notation for all operations are below:
* <pre>
* Method big-O
* ----------------------------------------------------------------------------
* - insert O(logn)
* - remove O(logn)
* - peek O(1)
* - contains O(n)
* </pre>
*/
// TODO(user): Should this rely on natural ordering via some Comparable
// interface?
goog.provide('goog.structs.Heap');
goog.require('goog.array');
goog.require('goog.object');
goog.require('goog.structs.Node');
/**
* Class for a Heap datastructure.
*
* @param {goog.structs.Heap|Object=} opt_heap Optional goog.structs.Heap or
* Object to initialize heap with.
* @constructor
* @template K, V
*/
goog.structs.Heap = function(opt_heap) {
'use strict';
/**
* The nodes of the heap.
* @private
* @type {Array<goog.structs.Node>}
*/
this.nodes_ = [];
if (opt_heap) {
this.insertAll(opt_heap);
}
};
/**
* Insert the given value into the heap with the given key.
* @param {K} key The key.
* @param {V} value The value.
*/
goog.structs.Heap.prototype.insert = function(key, value) {
'use strict';
var node = new goog.structs.Node(key, value);
var nodes = this.nodes_;
nodes.push(node);
this.moveUp_(nodes.length - 1);
};
/**
* Adds multiple key-value pairs from another goog.structs.Heap or Object
* @param {goog.structs.Heap|Object} heap Object containing the data to add.
*/
goog.structs.Heap.prototype.insertAll = function(heap) {
'use strict';
var keys, values;
if (heap instanceof goog.structs.Heap) {
keys = heap.getKeys();
values = heap.getValues();
// If it is a heap and the current heap is empty, I can rely on the fact
// that the keys/values are in the correct order to put in the underlying
// structure.
if (this.getCount() <= 0) {
var nodes = this.nodes_;
for (var i = 0; i < keys.length; i++) {
nodes.push(new goog.structs.Node(keys[i], values[i]));
}
return;
}
} else {
keys = goog.object.getKeys(heap);
values = goog.object.getValues(heap);
}
for (var i = 0; i < keys.length; i++) {
this.insert(keys[i], values[i]);
}
};
/**
* Retrieves and removes the root value of this heap.
* @return {V} The value removed from the root of the heap. Returns
* undefined if the heap is empty.
*/
goog.structs.Heap.prototype.remove = function() {
'use strict';
var nodes = this.nodes_;
var count = nodes.length;
var rootNode = nodes[0];
if (count <= 0) {
return undefined;
} else if (count == 1) {
goog.array.clear(nodes);
} else {
nodes[0] = nodes.pop();
this.moveDown_(0);
}
return rootNode.getValue();
};
/**
* Retrieves but does not remove the root value of this heap.
* @return {V} The value at the root of the heap. Returns
* undefined if the heap is empty.
*/
goog.structs.Heap.prototype.peek = function() {
'use strict';
var nodes = this.nodes_;
if (nodes.length == 0) {
return undefined;
}
return nodes[0].getValue();
};
/**
* Retrieves but does not remove the key of the root node of this heap.
* @return {K} The key at the root of the heap. Returns undefined if the
* heap is empty.
*/
goog.structs.Heap.prototype.peekKey = function() {
'use strict';
return this.nodes_[0] && this.nodes_[0].getKey();
};
/**
* Moves the node at the given index down to its proper place in the heap.
* @param {number} index The index of the node to move down.
* @private
*/
goog.structs.Heap.prototype.moveDown_ = function(index) {
'use strict';
var nodes = this.nodes_;
var count = nodes.length;
// Save the node being moved down.
var node = nodes[index];
// While the current node has a child.
while (index < (count >> 1)) {
var leftChildIndex = this.getLeftChildIndex_(index);
var rightChildIndex = this.getRightChildIndex_(index);
// Determine the index of the smaller child.
var smallerChildIndex = rightChildIndex < count &&
nodes[rightChildIndex].getKey() < nodes[leftChildIndex].getKey() ?
rightChildIndex :
leftChildIndex;
// If the node being moved down is smaller than its children, the node
// has found the correct index it should be at.
if (nodes[smallerChildIndex].getKey() > node.getKey()) {
break;
}
// If not, then take the smaller child as the current node.
nodes[index] = nodes[smallerChildIndex];
index = smallerChildIndex;
}
nodes[index] = node;
};
/**
* Moves the node at the given index up to its proper place in the heap.
* @param {number} index The index of the node to move up.
* @private
*/
goog.structs.Heap.prototype.moveUp_ = function(index) {
'use strict';
var nodes = this.nodes_;
var node = nodes[index];
// While the node being moved up is not at the root.
while (index > 0) {
// If the parent is less than the node being moved up, move the parent down.
var parentIndex = this.getParentIndex_(index);
if (nodes[parentIndex].getKey() > node.getKey()) {
nodes[index] = nodes[parentIndex];
index = parentIndex;
} else {
break;
}
}
nodes[index] = node;
};
/**
* Gets the index of the left child of the node at the given index.
* @param {number} index The index of the node to get the left child for.
* @return {number} The index of the left child.
* @private
*/
goog.structs.Heap.prototype.getLeftChildIndex_ = function(index) {
'use strict';
return index * 2 + 1;
};
/**
* Gets the index of the right child of the node at the given index.
* @param {number} index The index of the node to get the right child for.
* @return {number} The index of the right child.
* @private
*/
goog.structs.Heap.prototype.getRightChildIndex_ = function(index) {
'use strict';
return index * 2 + 2;
};
/**
* Gets the index of the parent of the node at the given index.
* @param {number} index The index of the node to get the parent for.
* @return {number} The index of the parent.
* @private
*/
goog.structs.Heap.prototype.getParentIndex_ = function(index) {
'use strict';
return (index - 1) >> 1;
};
/**
* Gets the values of the heap.
* @return {!Array<V>} The values in the heap.
*/
goog.structs.Heap.prototype.getValues = function() {
'use strict';
var nodes = this.nodes_;
var rv = [];
var l = nodes.length;
for (var i = 0; i < l; i++) {
rv.push(nodes[i].getValue());
}
return rv;
};
/**
* Gets the keys of the heap.
* @return {!Array<K>} The keys in the heap.
*/
goog.structs.Heap.prototype.getKeys = function() {
'use strict';
var nodes = this.nodes_;
var rv = [];
var l = nodes.length;
for (var i = 0; i < l; i++) {
rv.push(nodes[i].getKey());
}
return rv;
};
/**
* Whether the heap contains the given value.
* @param {V} val The value to check for.
* @return {boolean} Whether the heap contains the value.
*/
goog.structs.Heap.prototype.containsValue = function(val) {
'use strict';
return goog.array.some(this.nodes_, function(node) {
'use strict';
return node.getValue() == val;
});
};
/**
* Whether the heap contains the given key.
* @param {K} key The key to check for.
* @return {boolean} Whether the heap contains the key.
*/
goog.structs.Heap.prototype.containsKey = function(key) {
'use strict';
return goog.array.some(this.nodes_, function(node) {
'use strict';
return node.getKey() == key;
});
};
/**
* Clones a heap and returns a new heap
* @return {!goog.structs.Heap} A new goog.structs.Heap with the same key-value
* pairs.
*/
goog.structs.Heap.prototype.clone = function() {
'use strict';
return new goog.structs.Heap(this);
};
/**
* The number of key-value pairs in the map
* @return {number} The number of pairs.
*/
goog.structs.Heap.prototype.getCount = function() {
'use strict';
return this.nodes_.length;
};
/**
* Returns true if this heap contains no elements.
* @return {boolean} Whether this heap contains no elements.
*/
goog.structs.Heap.prototype.isEmpty = function() {
'use strict';
return this.nodes_.length === 0;
};
/**
* Removes all elements from the heap.
*/
goog.structs.Heap.prototype.clear = function() {
'use strict';
goog.array.clear(this.nodes_);
};
+560
View File
@@ -0,0 +1,560 @@
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Datastructure: Hash Map.
*
*
* This file contains an implementation of a Map structure. It implements a lot
* of the methods used in goog.structs so those functions work on hashes. This
* is best suited for complex key types. For simple keys such as numbers and
* strings consider using the lighter-weight utilities in goog.object.
* @deprecated goog.structs.Map is deprecated in favour of ES6 Maps.
*/
goog.provide('goog.structs.Map');
goog.require('goog.collections.iters');
goog.require('goog.iter.Iterator');
goog.require('goog.iter.StopIteration');
goog.require('goog.iter.es6');
/**
* Class for Hash Map datastructure.
* @param {*=} opt_map Map or Object to initialize the map with.
* @param {...*} var_args If 2 or more arguments are present then they
* will be used as key-value pairs.
* @constructor
* @template K, V
* @deprecated This type is misleading: use ES6 Map instead.
*/
goog.structs.Map = function(opt_map, var_args) {
'use strict';
/**
* Underlying JS object used to implement the map.
* @private {!Object}
*/
this.map_ = {};
/**
* An array of keys. This is necessary for two reasons:
* 1. Iterating the keys using for (var key in this.map_) allocates an
* object for every key in IE which is really bad for IE6 GC perf.
* 2. Without a side data structure, we would need to escape all the keys
* as that would be the only way we could tell during iteration if the
* key was an internal key or a property of the object.
*
* This array can contain deleted keys so it's necessary to check the map
* as well to see if the key is still in the map (this doesn't require a
* memory allocation in IE).
* @private {!Array<string>}
*/
this.keys_ = [];
/**
* The number of key value pairs in the map.
* @const {number}
*/
this.size = 0;
/**
* Version used to detect changes while iterating.
* @private {number}
*/
this.version_ = 0;
var argLength = arguments.length;
if (argLength > 1) {
if (argLength % 2) {
throw new Error('Uneven number of arguments');
}
for (var i = 0; i < argLength; i += 2) {
this.set(arguments[i], arguments[i + 1]);
}
} else if (opt_map) {
this.addAll(/** @type {!Object} */ (opt_map));
}
};
/**
* @return {number} The number of key-value pairs in the map.
* @deprecated Use the `size` property instead, for alignment with ES6 Map.
*/
goog.structs.Map.prototype.getCount = function() {
'use strict';
return this.size;
};
/**
* Returns the values of the map.
* @return {!Array<V>} The values in the map.
* @deprecated Use `Array.from(map.values())` instead, for alignment with ES6
* Map.
*/
goog.structs.Map.prototype.getValues = function() {
'use strict';
this.cleanupKeysArray_();
var rv = [];
for (var i = 0; i < this.keys_.length; i++) {
var key = this.keys_[i];
rv.push(this.map_[key]);
}
return rv;
};
/**
* Returns the keys of the map.
* @return {!Array<string>} Array of string values.
* @deprecated Use `Array.from(map.keys())` instead, for alignment with ES6 Map.
*/
goog.structs.Map.prototype.getKeys = function() {
'use strict';
this.cleanupKeysArray_();
return /** @type {!Array<string>} */ (this.keys_.concat());
};
/**
* Whether the map contains the given key.
* @param {*} key The key to check for.
* @return {boolean} Whether the map contains the key.
* @deprecated Use `has` instead, for alignment with ES6 Map.
*/
goog.structs.Map.prototype.containsKey = function(key) {
'use strict';
return this.has(key);
};
/**
* Whether the map contains the given key.
* @param {*} key The key to check for.
* @return {boolean} Whether the map contains the key.
*/
goog.structs.Map.prototype.has = function(key) {
'use strict';
return goog.structs.Map.hasKey_(this.map_, key);
};
/**
* Whether the map contains the given value. This is O(n).
* @param {V} val The value to check for.
* @return {boolean} Whether the map contains the value.
*/
goog.structs.Map.prototype.containsValue = function(val) {
'use strict';
for (var i = 0; i < this.keys_.length; i++) {
var key = this.keys_[i];
if (goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) {
return true;
}
}
return false;
};
/**
* Whether this map is equal to the argument map.
* @param {goog.structs.Map} otherMap The map against which to test equality.
* @param {function(V, V): boolean=} opt_equalityFn Optional equality function
* to test equality of values. If not specified, this will test whether
* the values contained in each map are identical objects.
* @return {boolean} Whether the maps are equal.
* @deprecated Use goog.collections.maps.equals(thisMap, otherMap,
* opt_equalityFn) instead, for alignment with ES6 Map.
*/
goog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) {
'use strict';
if (this === otherMap) {
return true;
}
if (this.size != otherMap.getCount()) {
return false;
}
var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals;
this.cleanupKeysArray_();
for (var key, i = 0; key = this.keys_[i]; i++) {
if (!equalityFn(this.get(key), otherMap.get(key))) {
return false;
}
}
return true;
};
/**
* Default equality test for values.
* @param {*} a The first value.
* @param {*} b The second value.
* @return {boolean} Whether a and b reference the same object.
*/
goog.structs.Map.defaultEquals = function(a, b) {
'use strict';
return a === b;
};
/**
* @return {boolean} Whether the map is empty.
* @deprecated Use the size property and compare against 0, for alignment with
* ES6 Map.
*/
goog.structs.Map.prototype.isEmpty = function() {
'use strict';
return this.size == 0;
};
/**
* Removes all key-value pairs from the map.
*/
goog.structs.Map.prototype.clear = function() {
'use strict';
this.map_ = {};
this.keys_.length = 0;
this.setSizeInternal_(0);
this.version_ = 0;
};
/**
* Removes a key-value pair based on the key. This is O(logN) amortized due to
* updating the keys array whenever the count becomes half the size of the keys
* in the keys array.
* @param {*} key The key to remove.
* @return {boolean} Whether object was removed.
* @deprecated Use `delete` instead, for alignment with ES6 Map.
*/
goog.structs.Map.prototype.remove = function(key) {
return this.delete(key);
};
/**
* Removes a key-value pair based on the key. This is O(logN) amortized due
* to updating the keys array whenever the count becomes half the size of
* the keys in the keys array.
* @param {*} key The key to remove.
* @return {boolean} Whether object was removed.
*/
goog.structs.Map.prototype.delete = function(key) {
'use strict';
if (goog.structs.Map.hasKey_(this.map_, key)) {
delete this.map_[key];
this.setSizeInternal_(this.size - 1);
this.version_++;
// clean up the keys array if the threshold is hit
if (this.keys_.length > 2 * this.size) {
this.cleanupKeysArray_();
}
return true;
}
return false;
};
/**
* Cleans up the temp keys array by removing entries that are no longer in the
* map.
* @private
*/
goog.structs.Map.prototype.cleanupKeysArray_ = function() {
'use strict';
if (this.size != this.keys_.length) {
// First remove keys that are no longer in the map.
var srcIndex = 0;
var destIndex = 0;
while (srcIndex < this.keys_.length) {
var key = this.keys_[srcIndex];
if (goog.structs.Map.hasKey_(this.map_, key)) {
this.keys_[destIndex++] = key;
}
srcIndex++;
}
this.keys_.length = destIndex;
}
if (this.size != this.keys_.length) {
// If the count still isn't correct, that means we have duplicates. This can
// happen when the same key is added and removed multiple times. Now we have
// to allocate one extra Object to remove the duplicates. This could have
// been done in the first pass, but in the common case, we can avoid
// allocating an extra object by only doing this when necessary.
var seen = {};
var srcIndex = 0;
var destIndex = 0;
while (srcIndex < this.keys_.length) {
var key = this.keys_[srcIndex];
if (!(goog.structs.Map.hasKey_(seen, key))) {
this.keys_[destIndex++] = key;
seen[key] = 1;
}
srcIndex++;
}
this.keys_.length = destIndex;
}
};
/**
* Returns the value for the given key. If the key is not found and the default
* value is not given this will return `undefined`.
* @param {*} key The key to get the value for.
* @param {DEFAULT=} opt_val The value to return if no item is found for the
* given key, defaults to undefined.
* @return {V|DEFAULT} The value for the given key.
* @template DEFAULT
*/
goog.structs.Map.prototype.get = function(key, opt_val) {
'use strict';
if (goog.structs.Map.hasKey_(this.map_, key)) {
return this.map_[key];
}
return opt_val;
};
/**
* Adds a key-value pair to the map.
* @param {*} key The key.
* @param {V} value The value to add.
*/
goog.structs.Map.prototype.set = function(key, value) {
'use strict';
if (!(goog.structs.Map.hasKey_(this.map_, key))) {
this.setSizeInternal_(this.size + 1);
// TODO(johnlenz): This class lies, it claims to return an array of string
// keys, but instead returns the original object used.
this.keys_.push(/** @type {?} */ (key));
// Only change the version if we add a new key.
this.version_++;
}
this.map_[key] = value;
};
/**
* Adds multiple key-value pairs from another goog.structs.Map or Object.
* @param {?Object} map Object containing the data to add.
* @deprecated Use goog.collections.maps.setAll(thisMap, map.entries()) if map
* is an ES6 or goog.structs Map, or
* goog.collections.maps.setAll(thisMap, Object.entries(map)) otherwise.
*/
goog.structs.Map.prototype.addAll = function(map) {
'use strict';
if (map instanceof goog.structs.Map) {
var keys = map.getKeys();
for (var i = 0; i < keys.length; i++) {
this.set(keys[i], map.get(keys[i]));
}
} else {
for (var key in map) {
this.set(key, map[key]);
}
}
};
/**
* Calls the given function on each entry in the map.
* @param {function(this:T, V, K, goog.structs.Map<K,V>)} f
* @param {T=} opt_obj The value of "this" inside f.
* @template T
* @deprecated Use ES6 Iteration instead.
*/
goog.structs.Map.prototype.forEach = function(f, opt_obj) {
'use strict';
var keys = this.getKeys();
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = this.get(key);
f.call(opt_obj, value, key, this);
}
};
/**
* Clones a map and returns a new map.
* @return {!goog.structs.Map} A new map with the same key-value pairs.
* @deprecated Use `new Map(thisMap.entries())` instead, for alignment with
* ES6 Map.
*/
goog.structs.Map.prototype.clone = function() {
'use strict';
return new goog.structs.Map(this);
};
/**
* Returns a new map in which all the keys and values are interchanged
* (keys become values and values become keys). If multiple keys map to the
* same value, the chosen transposed value is implementation-dependent.
*
* It acts very similarly to {goog.object.transpose(Object)}.
*
* @return {!goog.structs.Map} The transposed map.
* @deprecated Use goog.collections.maps.transpose instead, for alignment with
* ES6 Maps.
*/
goog.structs.Map.prototype.transpose = function() {
'use strict';
var transposed = new goog.structs.Map();
for (var i = 0; i < this.keys_.length; i++) {
var key = this.keys_[i];
var value = this.map_[key];
transposed.set(value, key);
}
return transposed;
};
/**
* @return {!Object} Object representation of the map.
* @deprecated Use goog.collections.maps.toObject(thisMap) instead, for aligment
* with ES6 Maps.
*/
goog.structs.Map.prototype.toObject = function() {
'use strict';
this.cleanupKeysArray_();
var obj = {};
for (var i = 0; i < this.keys_.length; i++) {
var key = this.keys_[i];
obj[key] = this.map_[key];
}
return obj;
};
/**
* Returns an iterator that iterates over the keys in the map. Removal of keys
* while iterating might have undesired side effects.
* @return {!goog.iter.Iterator} An iterator over the keys in the map.
* @deprecated Use `keys()` with native iteration protocols, for alignment
* with ES6 Map.
*/
goog.structs.Map.prototype.getKeyIterator = function() {
'use strict';
return this.__iterator__(true);
};
/**
* @return {!IteratorIterable<K>} An ES6 Iterator that iterates over the maps
* keys.
*/
goog.structs.Map.prototype.keys = function() {
'use strict';
return goog.iter.es6.ShimIterable.of(this.getKeyIterator()).toEs6();
};
/**
* Returns an iterator that iterates over the values in the map. Removal of
* keys while iterating might have undesired side effects.
* @return {!goog.iter.Iterator} An iterator over the values in the map.
* @deprecated Use `values()` with native iteration protocols, for alignment
* with ES6 Map.
*/
goog.structs.Map.prototype.getValueIterator = function() {
'use strict';
return this.__iterator__(false);
};
/**
* @return {!IteratorIterable<V>} An ES6 Iterator that iterates over the maps
* values.
*/
goog.structs.Map.prototype.values = function() {
'use strict';
return goog.iter.es6.ShimIterable.of(this.getValueIterator()).toEs6();
};
/**
* @return {!IteratorIterable<!Array<K|V>>} An iterator of entries in this map.
* The type is actually Array<[K,V]> but this is not representable in the
* Closure Type System.
*/
goog.structs.Map.prototype.entries = function() {
const self = this;
return goog.collections.iters.map(this.keys(), function(key) {
return [key, self.get(key)];
});
};
/**
* Returns an iterator that iterates over the values or the keys in the map.
* This throws an exception if the map was mutated since the iterator was
* created.
* @param {boolean=} opt_keys True to iterate over the keys. False to iterate
* over the values. The default value is false.
* @return {!goog.iter.Iterator} An iterator over the values or keys in the map.
* @deprecated Call either `keys` or `values` and use native iteration, for
* alignment with ES6 Map.
*/
goog.structs.Map.prototype.__iterator__ = function(opt_keys) {
'use strict';
// Clean up keys to minimize the risk of iterating over dead keys.
this.cleanupKeysArray_();
var i = 0;
var version = this.version_;
var selfObj = this;
var newIter = new goog.iter.Iterator;
newIter.nextValueOrThrow = function() {
'use strict';
if (version != selfObj.version_) {
throw new Error('The map has changed since the iterator was created');
}
if (i >= selfObj.keys_.length) {
throw goog.iter.StopIteration;
}
var key = selfObj.keys_[i++];
return opt_keys ? key : selfObj.map_[key];
};
return newIter;
};
/**
* Assigns to the size property to isolate supressions of const assignment to
* only where they are needed.
* @param {number} newSize The size to update to.
* @private
*/
goog.structs.Map.prototype.setSizeInternal_ = function(newSize) {
/** @suppress {const} */
this.size = newSize;
};
/**
* Safe way to test for hasOwnProperty. It even allows testing for
* 'hasOwnProperty'.
* @param {!Object} obj The object to test for presence of the given key.
* @param {*} key The key to check for.
* @return {boolean} Whether the object has the key.
* @private
*/
goog.structs.Map.hasKey_ = function(obj, key) {
'use strict';
return Object.prototype.hasOwnProperty.call(obj, key);
};
+68
View File
@@ -0,0 +1,68 @@
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Generic immutable node object to be used in collections.
*/
goog.provide('goog.structs.Node');
/**
* A generic immutable node. This can be used in various collections that
* require a node object for its item (such as a heap).
* @param {K} key Key.
* @param {V} value Value.
* @constructor
* @template K, V
*/
goog.structs.Node = function(key, value) {
'use strict';
/**
* The key.
* @private {K}
*/
this.key_ = key;
/**
* The value.
* @private {V}
*/
this.value_ = value;
};
/**
* Gets the key.
* @return {K} The key.
*/
goog.structs.Node.prototype.getKey = function() {
'use strict';
return this.key_;
};
/**
* Gets the value.
* @return {V} The value.
*/
goog.structs.Node.prototype.getValue = function() {
'use strict';
return this.value_;
};
/**
* Clones a node and returns a new node.
* @return {!goog.structs.Node<K, V>} A new goog.structs.Node with the same
* key value pair.
*/
goog.structs.Node.prototype.clone = function() {
'use strict';
return new goog.structs.Node(this.key_, this.value_);
};
+380
View File
@@ -0,0 +1,380 @@
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Datastructure: Pool.
*
*
* A generic class for handling pools of objects.
* When an object is released, it is attempted to be reused.
*/
goog.provide('goog.structs.Pool');
goog.require('goog.Disposable');
goog.require('goog.structs.Queue');
goog.require('goog.structs.Set');
/**
* A generic pool class. If min is greater than max, an error is thrown.
* @param {number=} opt_minCount Min. number of objects (Default: 0).
* @param {number=} opt_maxCount Max. number of objects (Default: 10).
* @constructor
* @extends {goog.Disposable}
* @template T
*/
goog.structs.Pool = function(opt_minCount, opt_maxCount) {
'use strict';
goog.Disposable.call(this);
/**
* Minimum number of objects allowed
* @private {number}
*/
this.minCount_ = opt_minCount || 0;
/**
* Maximum number of objects allowed
* @private {number}
*/
this.maxCount_ = opt_maxCount || 10;
// Make sure that the max and min constraints are valid.
if (this.minCount_ > this.maxCount_) {
throw new Error(goog.structs.Pool.ERROR_MIN_MAX_);
}
/**
* Set used to store objects that are currently in the pool and available
* to be used.
* @private {goog.structs.Queue<T>}
*/
this.freeQueue_ = new goog.structs.Queue();
/**
* Set used to store objects that are currently in the pool and in use.
* @private {goog.structs.Set<T>}
*/
this.inUseSet_ = new goog.structs.Set();
/**
* The minimum delay between objects being made available, in milliseconds. If
* this is 0, no minimum delay is enforced.
* @protected {number}
*/
this.delay = 0;
/**
* The time of the last object being made available, in milliseconds since the
* epoch (i.e., the result of Date#toTime). If this is null, no access has
* occurred yet.
* @protected {number?}
*/
this.lastAccess = null;
// Make sure that the minCount constraint is satisfied.
this.adjustForMinMax();
};
goog.inherits(goog.structs.Pool, goog.Disposable);
/**
* Error to throw when the max/min constraint is attempted to be invalidated.
* I.e., when it is attempted for maxCount to be less than minCount.
* @type {string}
* @private
*/
goog.structs.Pool.ERROR_MIN_MAX_ =
'[goog.structs.Pool] Min can not be greater than max';
/**
* Error to throw when the Pool is attempted to be disposed and it is asked to
* make sure that there are no objects that are in use (i.e., haven't been
* released).
* @type {string}
* @private
*/
goog.structs.Pool.ERROR_DISPOSE_UNRELEASED_OBJS_ =
'[goog.structs.Pool] Objects not released';
/**
* Sets the minimum count of the pool.
* If min is greater than the max count of the pool, an error is thrown.
* @param {number} min The minimum count of the pool.
*/
goog.structs.Pool.prototype.setMinimumCount = function(min) {
'use strict';
// Check count constraints.
if (min > this.maxCount_) {
throw new Error(goog.structs.Pool.ERROR_MIN_MAX_);
}
this.minCount_ = min;
// Adjust the objects in the pool as needed.
this.adjustForMinMax();
};
/**
* Sets the maximum count of the pool.
* If max is less than the min count of the pool, an error is thrown.
* @param {number} max The maximum count of the pool.
*/
goog.structs.Pool.prototype.setMaximumCount = function(max) {
'use strict';
// Check count constraints.
if (max < this.minCount_) {
throw new Error(goog.structs.Pool.ERROR_MIN_MAX_);
}
this.maxCount_ = max;
// Adjust the objects in the pool as needed.
this.adjustForMinMax();
};
/**
* Sets the minimum delay between objects being returned by getObject, in
* milliseconds. This defaults to zero, meaning that no minimum delay is
* enforced and objects may be used as soon as they're available.
* @param {number} delay The minimum delay, in milliseconds.
*/
goog.structs.Pool.prototype.setDelay = function(delay) {
'use strict';
this.delay = delay;
};
/**
* @return {T|undefined} A new object from the pool if there is one available,
* otherwise undefined.
*/
goog.structs.Pool.prototype.getObject = function() {
'use strict';
var time = Date.now();
if (this.lastAccess != null && time - this.lastAccess < this.delay) {
return undefined;
}
var obj = this.removeFreeObject_();
if (obj) {
this.lastAccess = time;
this.inUseSet_.add(obj);
}
return obj;
};
/**
* Returns an object to the pool of available objects so that it can be reused.
* @param {T} obj The object to return to the pool of free objects.
* @return {boolean} Whether the object was found in the Pool's set of in-use
* objects (in other words, whether any action was taken).
*/
goog.structs.Pool.prototype.releaseObject = function(obj) {
'use strict';
if (this.inUseSet_.remove(obj)) {
this.addFreeObject(obj);
return true;
}
return false;
};
/**
* Removes a free object from the collection of objects that are free so that it
* can be used.
*
* NOTE: This method does not mark the returned object as in use.
*
* @return {T|undefined} The object removed from the free collection, if there
* is one available. Otherwise, undefined.
* @private
*/
goog.structs.Pool.prototype.removeFreeObject_ = function() {
'use strict';
var obj;
while (this.getFreeCount() > 0) {
obj = this.freeQueue_.dequeue();
if (!this.objectCanBeReused(obj)) {
this.adjustForMinMax();
} else {
break;
}
}
if (!obj && this.getCount() < this.maxCount_) {
obj = this.createObject();
}
return obj;
};
/**
* Adds an object to the collection of objects that are free. If the object can
* not be added, then it is disposed.
*
* @param {T} obj The object to add to collection of free objects.
*/
goog.structs.Pool.prototype.addFreeObject = function(obj) {
'use strict';
this.inUseSet_.remove(obj);
if (this.objectCanBeReused(obj) && this.getCount() < this.maxCount_) {
this.freeQueue_.enqueue(obj);
} else {
this.disposeObject(obj);
}
};
/**
* Adjusts the objects held in the pool to be within the min/max constraints.
*
* NOTE: It is possible that the number of objects in the pool will still be
* greater than the maximum count of objects allowed. This will be the case
* if no more free objects can be disposed of to get below the minimum count
* (i.e., all objects are in use).
*/
goog.structs.Pool.prototype.adjustForMinMax = function() {
'use strict';
var freeQueue = this.freeQueue_;
// Make sure the at least the minimum number of objects are created.
while (this.getCount() < this.minCount_) {
freeQueue.enqueue(this.createObject());
}
// Make sure no more than the maximum number of objects are created.
while (this.getCount() > this.maxCount_ && this.getFreeCount() > 0) {
this.disposeObject(freeQueue.dequeue());
}
};
/**
* Should be overridden by sub-classes to return an instance of the object type
* that is expected in the pool.
* @return {T} The created object.
*/
goog.structs.Pool.prototype.createObject = function() {
'use strict';
return {};
};
/**
* Should be overridden to dispose of an object. Default implementation is to
* remove all its members, which should render it useless. Calls the object's
* `dispose()` method, if available.
* @param {T} obj The object to dispose.
*/
goog.structs.Pool.prototype.disposeObject = function(obj) {
'use strict';
if (typeof obj.dispose == 'function') {
obj.dispose();
} else {
for (var i in obj) {
obj[i] = null;
}
}
};
/**
* Should be overridden to determine whether an object has become unusable and
* should not be returned by getObject(). Calls the object's
* `canBeReused()` method, if available.
* @param {T} obj The object to test.
* @return {boolean} Whether the object can be reused.
*/
goog.structs.Pool.prototype.objectCanBeReused = function(obj) {
'use strict';
if (typeof obj.canBeReused == 'function') {
return obj.canBeReused();
}
return true;
};
/**
* Returns true if the given object is in the pool.
* @param {T} obj The object to check the pool for.
* @return {boolean} Whether the pool contains the object.
*/
goog.structs.Pool.prototype.contains = function(obj) {
'use strict';
return this.freeQueue_.contains(obj) || this.inUseSet_.contains(obj);
};
/**
* Returns the number of objects currently in the pool.
* @return {number} Number of objects currently in the pool.
*/
goog.structs.Pool.prototype.getCount = function() {
'use strict';
return this.freeQueue_.getCount() + this.inUseSet_.getCount();
};
/**
* Returns the number of objects currently in use in the pool.
* @return {number} Number of objects currently in use in the pool.
*/
goog.structs.Pool.prototype.getInUseCount = function() {
'use strict';
return this.inUseSet_.getCount();
};
/**
* Returns the number of objects currently free in the pool.
* @return {number} Number of objects currently free in the pool.
*/
goog.structs.Pool.prototype.getFreeCount = function() {
'use strict';
return this.freeQueue_.getCount();
};
/**
* Determines if the pool contains no objects.
* @return {boolean} Whether the pool contains no objects.
*/
goog.structs.Pool.prototype.isEmpty = function() {
'use strict';
return this.freeQueue_.isEmpty() && this.inUseSet_.isEmpty();
};
/**
* Disposes of the pool and all objects currently held in the pool.
* @override
* @protected
*/
goog.structs.Pool.prototype.disposeInternal = function() {
'use strict';
goog.structs.Pool.superClass_.disposeInternal.call(this);
if (this.getInUseCount() > 0) {
throw new Error(goog.structs.Pool.ERROR_DISPOSE_UNRELEASED_OBJS_);
}
delete this.inUseSet_;
// Call disposeObject on each object held by the pool.
var freeQueue = this.freeQueue_;
while (!freeQueue.isEmpty()) {
this.disposeObject(freeQueue.dequeue());
}
delete this.freeQueue_;
};
+181
View File
@@ -0,0 +1,181 @@
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Datastructure: Priority Pool.
*
*
* An extending of Pool that handles queueing and prioritization.
*/
goog.provide('goog.structs.PriorityPool');
goog.require('goog.structs.Pool');
goog.require('goog.structs.PriorityQueue');
/**
* A generic pool class. If min is greater than max, an error is thrown.
* @param {number=} opt_minCount Min. number of objects (Default: 0).
* @param {number=} opt_maxCount Max. number of objects (Default: 10).
* @constructor
* @extends {goog.structs.Pool<VALUE>}
* @template VALUE
*/
goog.structs.PriorityPool = function(opt_minCount, opt_maxCount) {
'use strict';
/**
* The key for the most recent timeout created.
* @private {number|undefined}
*/
this.delayTimeout_ = undefined;
/**
* Queue of requests for pool objects.
* @private {goog.structs.PriorityQueue<VALUE>}
*/
this.requestQueue_ = new goog.structs.PriorityQueue();
// Must break convention of putting the super-class's constructor first. This
// is because the super-class constructor calls adjustForMinMax, which this
// class overrides. In this class's implementation, it assumes that there
// is a requestQueue_, and will error if not present.
goog.structs.Pool.call(this, opt_minCount, opt_maxCount);
};
goog.inherits(goog.structs.PriorityPool, goog.structs.Pool);
/**
* Default priority for pool objects requests.
* @type {number}
* @private
*/
goog.structs.PriorityPool.DEFAULT_PRIORITY_ = 100;
/** @override */
goog.structs.PriorityPool.prototype.setDelay = function(delay) {
'use strict';
goog.structs.PriorityPool.base(this, 'setDelay', delay);
// If the pool hasn't been accessed yet, no need to do anything.
if (this.lastAccess == null) {
return;
}
goog.global.clearTimeout(this.delayTimeout_);
this.delayTimeout_ = goog.global.setTimeout(
goog.bind(this.handleQueueRequests_, this),
this.delay + this.lastAccess - Date.now());
// Handle all requests.
this.handleQueueRequests_();
};
/**
* Get a new object from the pool, if there is one available, otherwise
* return undefined.
* @param {Function=} opt_callback The function to callback when an object is
* available. This could be immediately. If this is not present, then an
* object is immediately returned if available, or undefined if not.
* @param {number=} opt_priority The priority of the request. A smaller value
* means a higher priority.
* @return {VALUE|undefined} The new object from the pool if there is one
* available and a callback is not given. Otherwise, undefined.
* @override
*/
goog.structs.PriorityPool.prototype.getObject = function(
opt_callback, opt_priority) {
'use strict';
if (!opt_callback) {
var result = goog.structs.PriorityPool.base(this, 'getObject');
if (result && this.delay) {
this.delayTimeout_ = goog.global.setTimeout(
goog.bind(this.handleQueueRequests_, this), this.delay);
}
return result;
}
var priority = (opt_priority !== undefined) ?
opt_priority :
goog.structs.PriorityPool.DEFAULT_PRIORITY_;
this.requestQueue_.enqueue(priority, opt_callback);
// Handle all requests.
this.handleQueueRequests_();
return undefined;
};
/**
* Handles the request queue. Tries to fires off as many queued requests as
* possible.
* @private
*/
goog.structs.PriorityPool.prototype.handleQueueRequests_ = function() {
'use strict';
var requestQueue = this.requestQueue_;
while (requestQueue.getCount() > 0) {
var obj = this.getObject();
if (!obj) {
return;
} else {
var requestCallback = requestQueue.dequeue();
requestCallback.apply(this, [obj]);
}
}
};
/**
* Adds an object to the collection of objects that are free. If the object can
* not be added, then it is disposed.
*
* NOTE: This method does not remove the object from the in use collection.
*
* @param {VALUE} obj The object to add to the collection of free objects.
* @override
*/
goog.structs.PriorityPool.prototype.addFreeObject = function(obj) {
'use strict';
goog.structs.PriorityPool.superClass_.addFreeObject.call(this, obj);
// Handle all requests.
this.handleQueueRequests_();
};
/**
* Adjusts the objects held in the pool to be within the min/max constraints.
*
* NOTE: It is possible that the number of objects in the pool will still be
* greater than the maximum count of objects allowed. This will be the case
* if no more free objects can be disposed of to get below the minimum count
* (i.e., all objects are in use).
* @override
*/
goog.structs.PriorityPool.prototype.adjustForMinMax = function() {
'use strict';
goog.structs.PriorityPool.superClass_.adjustForMinMax.call(this);
// Handle all requests.
this.handleQueueRequests_();
};
/** @override */
goog.structs.PriorityPool.prototype.disposeInternal = function() {
'use strict';
goog.structs.PriorityPool.superClass_.disposeInternal.call(this);
goog.global.clearTimeout(this.delayTimeout_);
this.requestQueue_.clear();
this.requestQueue_ = null;
};
+61
View File
@@ -0,0 +1,61 @@
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Datastructure: Priority Queue.
*
*
* This file provides the implementation of a Priority Queue. Smaller priorities
* move to the front of the queue. If two values have the same priority,
* it is arbitrary which value will come to the front of the queue first.
*/
// TODO(user): Should this rely on natural ordering via some Comparable
// interface?
goog.provide('goog.structs.PriorityQueue');
goog.require('goog.structs.Heap');
/**
* Class for Priority Queue datastructure.
*
* @constructor
* @extends {goog.structs.Heap<number, VALUE>}
* @template VALUE
* @final
*/
goog.structs.PriorityQueue = function() {
'use strict';
goog.structs.Heap.call(this);
};
goog.inherits(goog.structs.PriorityQueue, goog.structs.Heap);
/**
* Puts the specified value in the queue.
* @param {number} priority The priority of the value. A smaller value here
* means a higher priority.
* @param {VALUE} value The value.
*/
goog.structs.PriorityQueue.prototype.enqueue = function(priority, value) {
'use strict';
this.insert(priority, value);
};
/**
* Retrieves and removes the head of this queue.
* @return {VALUE} The element at the head of this queue. Returns undefined if
* the queue is empty.
*/
goog.structs.PriorityQueue.prototype.dequeue = function() {
'use strict';
return this.remove();
};
+183
View File
@@ -0,0 +1,183 @@
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Datastructure: Queue.
*
*
* This file provides the implementation of a FIFO Queue structure.
* API is similar to that of com.google.common.collect.IntQueue
*
* The implementation is a classic 2-stack queue.
* There's a "front" stack and a "back" stack.
* Items are pushed onto "back" and popped from "front".
* When "front" is empty, we replace "front" with reverse(back).
*
* Example:
* front back op
* [] [] enqueue 1
* [] [1] enqueue 2
* [] [1,2] enqueue 3
* [] [1,2,3] dequeue -> ...
* [3,2,1] [] ... -> 1
* [3,2] [] enqueue 4
* [3,2] [4] dequeue -> 2
* [3] [4]
*
* Front and back are simple javascript arrays. We rely on
* Array.push and Array.pop being O(1) amortized.
*
* Note: In V8, queues, up to a certain size, can be implemented
* just fine using Array.push and Array.shift, but other JavaScript
* engines do not have the optimization of Array.shift.
*/
goog.provide('goog.structs.Queue');
goog.require('goog.array');
/**
* Class for FIFO Queue data structure.
*
* @constructor
* @template T
*/
goog.structs.Queue = function() {
'use strict';
/**
* @private {!Array<T>} Front stack. Items are pop()'ed from here.
*/
this.front_ = [];
/**
* @private {!Array<T>} Back stack. Items are push()'ed here.
*/
this.back_ = [];
};
/**
* Flips the back stack onto the front stack if front is empty,
* to prepare for peek() or dequeue().
*
* @private
*/
goog.structs.Queue.prototype.maybeFlip_ = function() {
'use strict';
if (this.front_.length === 0) {
this.front_ = this.back_;
this.front_.reverse();
this.back_ = [];
}
};
/**
* Puts the specified element on this queue.
* @param {T} element The element to be added to the queue.
*/
goog.structs.Queue.prototype.enqueue = function(element) {
'use strict';
this.back_.push(element);
};
/**
* Retrieves and removes the head of this queue.
* @return {T} The element at the head of this queue. Returns undefined if the
* queue is empty.
*/
goog.structs.Queue.prototype.dequeue = function() {
'use strict';
this.maybeFlip_();
return this.front_.pop();
};
/**
* Retrieves but does not remove the head of this queue.
* @return {T} The element at the head of this queue. Returns undefined if the
* queue is empty.
*/
goog.structs.Queue.prototype.peek = function() {
'use strict';
this.maybeFlip_();
return goog.array.peek(this.front_);
};
/**
* Returns the number of elements in this queue.
* @return {number} The number of elements in this queue.
*/
goog.structs.Queue.prototype.getCount = function() {
'use strict';
return this.front_.length + this.back_.length;
};
/**
* Returns true if this queue contains no elements.
* @return {boolean} true if this queue contains no elements.
*/
goog.structs.Queue.prototype.isEmpty = function() {
'use strict';
return this.front_.length === 0 && this.back_.length === 0;
};
/**
* Removes all elements from the queue.
*/
goog.structs.Queue.prototype.clear = function() {
'use strict';
this.front_ = [];
this.back_ = [];
};
/**
* Returns true if the given value is in the queue.
* @param {T} obj The value to look for.
* @return {boolean} Whether the object is in the queue.
*/
goog.structs.Queue.prototype.contains = function(obj) {
'use strict';
return goog.array.contains(this.front_, obj) ||
goog.array.contains(this.back_, obj);
};
/**
* Removes the first occurrence of a particular value from the queue.
* @param {T} obj Object to remove.
* @return {boolean} True if an element was removed.
*/
goog.structs.Queue.prototype.remove = function(obj) {
'use strict';
return goog.array.removeLast(this.front_, obj) ||
goog.array.remove(this.back_, obj);
};
/**
* Returns all the values in the queue.
* @return {!Array<T>} An array of the values in the queue.
*/
goog.structs.Queue.prototype.getValues = function() {
'use strict';
var res = [];
// Add the front array in reverse, then the back array.
for (var i = this.front_.length - 1; i >= 0; --i) {
res.push(this.front_[i]);
}
var len = this.back_.length;
for (var i = 0; i < len; ++i) {
res.push(this.back_[i]);
}
return res;
};
+381
View File
@@ -0,0 +1,381 @@
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Datastructure: Set.
*
*
* This class implements a set data structure. Adding and removing is O(1). It
* supports both object and primitive values. Be careful because you can add
* both 1 and new Number(1), because these are not the same. You can even add
* multiple new Number(1) because these are not equal.
*/
goog.provide('goog.structs.Set');
goog.require('goog.structs');
goog.require('goog.structs.Collection');
goog.require('goog.structs.Map');
goog.requireType('goog.iter.Iterator');
/**
* A set that can contain both primitives and objects. Adding and removing
* elements is O(1). Primitives are treated as identical if they have the same
* type and convert to the same string. Objects are treated as identical only
* if they are references to the same object. WARNING: A goog.structs.Set can
* contain both 1 and (new Number(1)), because they are not the same. WARNING:
* Adding (new Number(1)) twice will yield two distinct elements, because they
* are two different objects. WARNING: Any object that is added to a
* goog.structs.Set will be modified! Because goog.getUid() is used to
* identify objects, every object in the set will be mutated.
* @param {Array<T>|Object<?,T>=} opt_values Initial values to start with.
* @constructor
* @implements {goog.structs.Collection<T>}
* @implements {Iterable<T>}
* @final
* @template T
* @deprecated This type is misleading: use ES6 Set instead.
*/
goog.structs.Set = function(opt_values) {
'use strict';
this.map_ = new goog.structs.Map();
/**
* The number of items in this set.
* @const {number}
*/
this.size = 0;
if (opt_values) {
this.addAll(opt_values);
}
};
/**
* A function that returns a unique id.
* @private @const {function(?Object): number}
*/
goog.structs.Set.getUid_ = goog.getUid;
/**
* Obtains a unique key for an element of the set. Primitives will yield the
* same key if they have the same type and convert to the same string. Object
* references will yield the same key only if they refer to the same object.
* @param {*} val Object or primitive value to get a key for.
* @return {string} A unique key for this value/object.
* @private
*/
goog.structs.Set.getKey_ = function(val) {
'use strict';
var type = typeof val;
if (type == 'object' && val || type == 'function') {
return 'o' + goog.structs.Set.getUid_(/** @type {Object} */ (val));
} else {
return type.substr(0, 1) + val;
}
};
/**
* @return {number} The number of elements in the set.
* @override
* @deprecated Use the `size` property instead, for alignment with ES6 Set.
*/
goog.structs.Set.prototype.getCount = function() {
'use strict';
return this.map_.size;
};
/**
* Add a primitive or an object to the set.
* @param {T} element The primitive or object to add.
* @override
*/
goog.structs.Set.prototype.add = function(element) {
'use strict';
this.map_.set(goog.structs.Set.getKey_(element), element);
this.setSizeInternal_(this.map_.size);
};
/**
* Adds all the values in the given collection to this set.
* @param {Array<T>|goog.structs.Collection<T>|Object<?,T>} col A collection
* containing the elements to add.
* @deprecated Use `goog.collections.sets.addAll(thisSet, col)` instead,
* converting Objects to their values using `Object.values`, for alignment
* with ES6 Set.
*/
goog.structs.Set.prototype.addAll = function(col) {
'use strict';
var values = goog.structs.getValues(col);
var l = values.length;
for (var i = 0; i < l; i++) {
this.add(values[i]);
}
this.setSizeInternal_(this.map_.size);
};
/**
* Removes all values in the given collection from this set.
* @param {Array<T>|goog.structs.Collection<T>|Object<?,T>} col A collection
* containing the elements to remove.
* @deprecated Use `goog.collections.sets.removeAll(thisSet, col)` instead,
* converting Objects to their values using `Object.values`, for alignment
* with ES6 Set.
*/
goog.structs.Set.prototype.removeAll = function(col) {
'use strict';
var values = goog.structs.getValues(col);
var l = values.length;
for (var i = 0; i < l; i++) {
this.remove(values[i]);
}
this.setSizeInternal_(this.map_.size);
};
/**
* Removes the given element from this set.
* @param {T} element The primitive or object to remove.
* @return {boolean} Whether the element was found and removed.
*/
goog.structs.Set.prototype.delete = function(element) {
'use strict';
const rv = this.map_.remove(goog.structs.Set.getKey_(element));
this.setSizeInternal_(this.map_.size);
return rv;
};
/**
* Removes the given element from this set.
* @param {T} element The primitive or object to remove.
* @return {boolean} Whether the element was found and removed.
* @override
* @deprecated Use `delete`, for alignment with ES6 Set.
*/
goog.structs.Set.prototype.remove = function(element) {
'use strict';
return this.delete(element);
};
/**
* Removes all elements from this set.
*/
goog.structs.Set.prototype.clear = function() {
'use strict';
this.map_.clear();
this.setSizeInternal_(0);
};
/**
* Tests whether this set is empty.
* @return {boolean} True if there are no elements in this set.
* @deprecated Use the size property and compare against 0, for alignment with
* ES6 Set.
*/
goog.structs.Set.prototype.isEmpty = function() {
'use strict';
return this.map_.size === 0;
};
/**
* Tests whether this set contains the given element.
* @param {T} element The primitive or object to test for.
* @return {boolean} True if this set contains the given element.
*/
goog.structs.Set.prototype.has = function(element) {
'use strict';
return this.map_.containsKey(goog.structs.Set.getKey_(element));
};
/**
* Tests whether this set contains the given element.
* @param {T} element The primitive or object to test for.
* @return {boolean} True if this set contains the given element.
* @override
* @deprecated Use `has` instead, for alignment with ES6 Set.
*/
goog.structs.Set.prototype.contains = function(element) {
'use strict';
return this.map_.containsKey(goog.structs.Set.getKey_(element));
};
/**
* Tests whether this set contains all the values in a given collection.
* Repeated elements in the collection are ignored, e.g. (new
* goog.structs.Set([1, 2])).containsAll([1, 1]) is True.
* @param {goog.structs.Collection<T>|Object} col A collection-like object.
* @return {boolean} True if the set contains all elements.
* @deprecated Use `goog.collections.sets.hasAll(thisSet, col)`, converting
* Objects to arrays using Object.values, for alignment with ES6 Set.
*/
goog.structs.Set.prototype.containsAll = function(col) {
'use strict';
return goog.structs.every(col, this.contains, this);
};
/**
* Finds all values that are present in both this set and the given collection.
* @param {Array<S>|Object<?,S>} col A collection.
* @return {!goog.structs.Set<T|S>} A new set containing all the values
* (primitives or objects) present in both this set and the given
* collection.
* @template S
* @deprecated Use `goog.collections.sets.intersection(thisSet, col)`,
* converting Objects to arrays using Object.values, instead for alignment
* with ES6 Set.
*/
goog.structs.Set.prototype.intersection = function(col) {
'use strict';
var result = new goog.structs.Set();
var values = goog.structs.getValues(col);
for (var i = 0; i < values.length; i++) {
var value = values[i];
if (this.contains(value)) {
result.add(value);
}
}
return result;
};
/**
* Finds all values that are present in this set and not in the given
* collection.
* @param {Array<T>|goog.structs.Collection<T>|Object<?,T>} col A collection.
* @return {!goog.structs.Set} A new set containing all the values
* (primitives or objects) present in this set but not in the given
* collection.
*/
goog.structs.Set.prototype.difference = function(col) {
'use strict';
var result = this.clone();
result.removeAll(col);
return result;
};
/**
* Returns an array containing all the elements in this set.
* @return {!Array<T>} An array containing all the elements in this set.
* @deprecated Use `Array.from(set.values())` instead, for alignment with ES6
* Set.
*/
goog.structs.Set.prototype.getValues = function() {
'use strict';
return this.map_.getValues();
};
/**
* @returns {!IteratorIterable<T>} An ES6 Iterator that iterates over the values
* in the set.
*/
goog.structs.Set.prototype.values = function() {
'use strict';
return this.map_.values();
};
/**
* Creates a shallow clone of this set.
* @return {!goog.structs.Set<T>} A new set containing all the same elements as
* this set.
* @deprecated Use `new Set(thisSet.values())` for alignment with ES6 Set.
*/
goog.structs.Set.prototype.clone = function() {
'use strict';
return new goog.structs.Set(this);
};
/**
* Tests whether the given collection consists of the same elements as this set,
* regardless of order, without repetition. Primitives are treated as equal if
* they have the same type and convert to the same string; objects are treated
* as equal if they are references to the same object. This operation is O(n).
* @param {goog.structs.Collection<T>|Object} col A collection.
* @return {boolean} True if the given collection consists of the same elements
* as this set, regardless of order, without repetition.
* @deprecated Use `goog.collections.equals(thisSet, col)`, converting Objects
* to arrays using Object.values, instead for alignment with ES6 Set.
*/
goog.structs.Set.prototype.equals = function(col) {
'use strict';
return this.getCount() == goog.structs.getCount(col) && this.isSubsetOf(col);
};
/**
* Tests whether the given collection contains all the elements in this set.
* Primitives are treated as equal if they have the same type and convert to the
* same string; objects are treated as equal if they are references to the same
* object. This operation is O(n).
* @param {goog.structs.Collection<T>|Object} col A collection.
* @return {boolean} True if this set is a subset of the given collection.
* @deprecated Use `goog.collections.isSubsetOf(thisSet, col)`, converting
* Objects to arrays using Object.values, instead for alignment with ES6
* Set.
*/
goog.structs.Set.prototype.isSubsetOf = function(col) {
'use strict';
var colCount = goog.structs.getCount(col);
if (this.getCount() > colCount) {
return false;
}
if (!(col instanceof goog.structs.Set) && colCount > 5) {
// Convert to a goog.structs.Set so that goog.structs.contains runs in
// O(1) time instead of O(n) time.
col = new goog.structs.Set(col);
}
return goog.structs.every(this, function(value) {
'use strict';
return goog.structs.contains(col, value);
});
};
/**
* Returns an iterator that iterates over the elements in this set.
* @param {boolean=} opt_keys This argument is ignored.
* @return {!goog.iter.Iterator} An iterator over the elements in this set.
* @deprecated Call `values` and use native iteration, for alignment with ES6
* Set.
*/
goog.structs.Set.prototype.__iterator__ = function(opt_keys) {
'use strict';
return this.map_.__iterator__(false);
};
/**
* @return {!IteratorIterable<T>} An ES6 Iterator that iterates over the values
* in the set.
*/
goog.structs.Set.prototype[Symbol.iterator] = function() {
return this.values();
};
/**
* Assigns to the size property to isolate supressions of const assignment
* to only where they are needed.
* @param {number} newSize The size to update to.
* @private
*/
goog.structs.Set.prototype.setSizeInternal_ = function(newSize) {
/** @suppress {const} */
this.size = newSize;
};
+380
View File
@@ -0,0 +1,380 @@
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Generics method for collection-like classes and objects.
*
*
* This file contains functions to work with collections. It supports using
* Map, Set, Array and Object and other classes that implement collection-like
* methods.
* @suppress {strictMissingProperties}
*/
goog.provide('goog.structs');
goog.require('goog.array');
goog.require('goog.object');
// We treat an object as a dictionary if it has getKeys or it is an object that
// isn't arrayLike.
/**
* Returns the number of values in the collection-like object.
* @param {Object} col The collection-like object.
* @return {number} The number of values in the collection-like object.
*/
goog.structs.getCount = function(col) {
'use strict';
if (col.getCount && typeof col.getCount == 'function') {
return col.getCount();
}
if (goog.isArrayLike(col) || typeof col === 'string') {
return col.length;
}
return goog.object.getCount(col);
};
/**
* Returns the values of the collection-like object.
* @param {Object} col The collection-like object.
* @return {!Array<?>} The values in the collection-like object.
*/
goog.structs.getValues = function(col) {
'use strict';
if (col.getValues && typeof col.getValues == 'function') {
return col.getValues();
}
// ES6 Map and Set both define a values function that returns an iterator.
// The typeof check allows the compiler to remove the Map and Set polyfills
// if they are otherwise unused throughout the entire binary.
if ((typeof Map !== 'undefined' && col instanceof Map) ||
(typeof Set !== 'undefined' && col instanceof Set)) {
return Array.from(col.values());
}
if (typeof col === 'string') {
return col.split('');
}
if (goog.isArrayLike(col)) {
var rv = [];
var l = col.length;
for (var i = 0; i < l; i++) {
rv.push(col[i]);
}
return rv;
}
return goog.object.getValues(col);
};
/**
* Returns the keys of the collection. Some collections have no notion of
* keys/indexes and this function will return undefined in those cases.
* @param {Object} col The collection-like object.
* @return {!Array|undefined} The keys in the collection.
*/
goog.structs.getKeys = function(col) {
'use strict';
if (col.getKeys && typeof col.getKeys == 'function') {
return col.getKeys();
}
// if we have getValues but no getKeys we know this is a key-less collection
if (col.getValues && typeof col.getValues == 'function') {
return undefined;
}
// ES6 Map and Set both define a keys function that returns an iterator. For
// Sets this iterates over the same values as the values iterator.
// The typeof check allows the compiler to remove the Map and Set polyfills
// if they are otherwise unused throughout the entire binary.
if (typeof Map !== 'undefined' && col instanceof Map) {
return Array.from(col.keys());
}
// Unlike the native Set, goog.structs.Set does not expose keys as the values.
if (typeof Set !== 'undefined' && col instanceof Set) {
return undefined;
}
if (goog.isArrayLike(col) || typeof col === 'string') {
var rv = [];
var l = col.length;
for (var i = 0; i < l; i++) {
rv.push(i);
}
return rv;
}
return goog.object.getKeys(col);
};
/**
* Whether the collection contains the given value. This is O(n) and uses
* equals (==) to test the existence.
* @param {Object} col The collection-like object.
* @param {*} val The value to check for.
* @return {boolean} True if the map contains the value.
*/
goog.structs.contains = function(col, val) {
'use strict';
if (col.contains && typeof col.contains == 'function') {
return col.contains(val);
}
if (col.containsValue && typeof col.containsValue == 'function') {
return col.containsValue(val);
}
if (goog.isArrayLike(col) || typeof col === 'string') {
return goog.array.contains(/** @type {!Array<?>} */ (col), val);
}
return goog.object.containsValue(col, val);
};
/**
* Whether the collection is empty.
* @param {Object} col The collection-like object.
* @return {boolean} True if empty.
*/
goog.structs.isEmpty = function(col) {
'use strict';
if (col.isEmpty && typeof col.isEmpty == 'function') {
return col.isEmpty();
}
// We do not use goog.string.isEmptyOrWhitespace because here we treat the
// string as
// collection and as such even whitespace matters
if (goog.isArrayLike(col) || typeof col === 'string') {
return /** @type {!Array<?>} */ (col).length === 0;
}
return goog.object.isEmpty(col);
};
/**
* Removes all the elements from the collection.
* @param {Object} col The collection-like object.
* @return {void}
*/
goog.structs.clear = function(col) {
'use strict';
// NOTE(arv): This should not contain strings because strings are immutable
if (col.clear && typeof col.clear == 'function') {
col.clear();
} else if (goog.isArrayLike(col)) {
goog.array.clear(/** @type {IArrayLike<?>} */ (col));
} else {
goog.object.clear(col);
}
};
/**
* Calls a function for each value in a collection. The function takes
* three arguments; the value, the key and the collection.
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):?} f The function to call for every value.
* This function takes
* 3 arguments (the value, the key or undefined if the collection has no
* notion of keys, and the collection) and the return value is irrelevant.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within `f`.
* @return {void}
* @template T,S
* @deprecated Use a more specific method, e.g. native Array.prototype.forEach,
* or for-of.
*/
goog.structs.forEach = function(col, f, opt_obj) {
'use strict';
if (col.forEach && typeof col.forEach == 'function') {
col.forEach(f, opt_obj);
} else if (goog.isArrayLike(col) || typeof col === 'string') {
Array.prototype.forEach.call(/** @type {!Array<?>} */ (col), f, opt_obj);
} else {
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
for (var i = 0; i < l; i++) {
f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col);
}
}
};
/**
* Calls a function for every value in the collection. When a call returns true,
* adds the value to a new collection (Array is returned by default).
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):boolean} f The function to call for every
* value. This function takes
* 3 arguments (the value, the key or undefined if the collection has no
* notion of keys, and the collection) and should return a Boolean. If the
* return value is true the value is added to the result collection. If it
* is false the value is not included.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within `f`.
* @return {!Object|!Array<?>} A new collection where the passed values are
* present. If col is a key-less collection an array is returned. If col
* has keys and values a plain old JS object is returned.
* @template T,S
*/
goog.structs.filter = function(col, f, opt_obj) {
'use strict';
if (typeof col.filter == 'function') {
return col.filter(f, opt_obj);
}
if (goog.isArrayLike(col) || typeof col === 'string') {
return Array.prototype.filter.call(
/** @type {!Array<?>} */ (col), f, opt_obj);
}
var rv;
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
if (keys) {
rv = {};
for (var i = 0; i < l; i++) {
if (f.call(/** @type {?} */ (opt_obj), values[i], keys[i], col)) {
rv[keys[i]] = values[i];
}
}
} else {
// We should not use Array#filter here since we want to make sure that
// the index is undefined as well as make sure that col is passed to the
// function.
rv = [];
for (var i = 0; i < l; i++) {
if (f.call(opt_obj, values[i], undefined, col)) {
rv.push(values[i]);
}
}
}
return rv;
};
/**
* Calls a function for every value in the collection and adds the result into a
* new collection (defaults to creating a new Array).
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):V} f The function to call for every value.
* This function takes 3 arguments (the value, the key or undefined if the
* collection has no notion of keys, and the collection) and should return
* something. The result will be used as the value in the new collection.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within `f`.
* @return {!Object<V>|!Array<V>} A new collection with the new values. If
* col is a key-less collection an array is returned. If col has keys and
* values a plain old JS object is returned.
* @template T,S,V
*/
goog.structs.map = function(col, f, opt_obj) {
'use strict';
if (typeof col.map == 'function') {
return col.map(f, opt_obj);
}
if (goog.isArrayLike(col) || typeof col === 'string') {
return Array.prototype.map.call(/** @type {!Array<?>} */ (col), f, opt_obj);
}
var rv;
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
if (keys) {
rv = {};
for (var i = 0; i < l; i++) {
rv[keys[i]] = f.call(/** @type {?} */ (opt_obj), values[i], keys[i], col);
}
} else {
// We should not use Array#map here since we want to make sure that
// the index is undefined as well as make sure that col is passed to the
// function.
rv = [];
for (var i = 0; i < l; i++) {
rv[i] = f.call(/** @type {?} */ (opt_obj), values[i], undefined, col);
}
}
return rv;
};
/**
* Calls f for each value in a collection. If any call returns true this returns
* true (without checking the rest). If all returns false this returns false.
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):boolean} f The function to call for every
* value. This function takes 3 arguments (the value, the key or undefined
* if the collection has no notion of keys, and the collection) and should
* return a boolean.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within `f`.
* @return {boolean} True if any value passes the test.
* @template T,S
*/
goog.structs.some = function(col, f, opt_obj) {
'use strict';
if (typeof col.some == 'function') {
return col.some(f, opt_obj);
}
if (goog.isArrayLike(col) || typeof col === 'string') {
return Array.prototype.some.call(
/** @type {!Array<?>} */ (col), f, opt_obj);
}
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
for (var i = 0; i < l; i++) {
if (f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) {
return true;
}
}
return false;
};
/**
* Calls f for each value in a collection. If all calls return true this return
* true this returns true. If any returns false this returns false at this point
* and does not continue to check the remaining values.
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):boolean} f The function to call for every
* value. This function takes 3 arguments (the value, the key or
* undefined if the collection has no notion of keys, and the collection)
* and should return a boolean.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within `f`.
* @return {boolean} True if all key-value pairs pass the test.
* @template T,S
*/
goog.structs.every = function(col, f, opt_obj) {
'use strict';
if (typeof col.every == 'function') {
return col.every(f, opt_obj);
}
if (goog.isArrayLike(col) || typeof col === 'string') {
return Array.prototype.every.call(
/** @type {!Array<?>} */ (col), f, opt_obj);
}
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
for (var i = 0; i < l; i++) {
if (!f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) {
return false;
}
}
return true;
};