140 lines
4.0 KiB
JavaScript
140 lines
4.0 KiB
JavaScript
import { MultiKeyMap } from "./MultiKeyMap.js";
|
|
/**
|
|
* A utility class that works like a `Map`,
|
|
* but accepts multiple values as the key for each value,
|
|
* and does not keep strong references to the values (allowing them to be garbage collected).
|
|
*
|
|
* When a value is garbage collected, it is automatically removed from the map.
|
|
*/
|
|
export class WeakValueMultiKeyMap {
|
|
/** @internal */ _map = new MultiKeyMap();
|
|
constructor(entries) {
|
|
if (entries != null) {
|
|
for (const [key, value] of entries)
|
|
this.set(key, value);
|
|
}
|
|
}
|
|
/**
|
|
* Add or update a value for a given key.
|
|
*
|
|
* Time complexity: O(1), given that the length of the key is constant.
|
|
*/
|
|
set(key, value) {
|
|
const currentWeakValue = this._map.get(key);
|
|
if (currentWeakValue != null) {
|
|
const currentValue = currentWeakValue.ref.deref();
|
|
if (currentValue != null)
|
|
currentWeakValue.tracker.unregister(currentValue);
|
|
}
|
|
const weakValue = {
|
|
ref: new WeakRef(value),
|
|
tracker: null // will be set below
|
|
};
|
|
weakValue.tracker = new FinalizationRegistry(this._finalize.bind(this, weakValue));
|
|
weakValue.tracker.register(value, key.slice());
|
|
this._map.set(key, weakValue);
|
|
return this;
|
|
}
|
|
/**
|
|
* Get a value for a given key.
|
|
*
|
|
* Time complexity: O(1), given that the length of the key is constant.
|
|
*/
|
|
get(key) {
|
|
const weakValue = this._map.get(key);
|
|
if (weakValue == null)
|
|
return undefined;
|
|
const value = weakValue.ref.deref();
|
|
/* c8 ignore start */
|
|
if (value == null) {
|
|
this._map.delete(key);
|
|
return undefined;
|
|
} /* c8 ignore stop */
|
|
return value;
|
|
}
|
|
/**
|
|
* Check if a value exists for a given key.
|
|
*
|
|
* Time complexity: O(1), given that the length of the key is constant.
|
|
*/
|
|
has(key) {
|
|
return this.get(key) != null;
|
|
}
|
|
/**
|
|
* Delete the value for a given key.
|
|
*
|
|
* Time complexity: O(1), given that the length of the key is constant.
|
|
*/
|
|
delete(key) {
|
|
const weakValue = this._map.get(key);
|
|
if (weakValue == null)
|
|
return false;
|
|
const value = weakValue.ref.deref();
|
|
if (value != null)
|
|
weakValue.tracker.unregister(value);
|
|
this._map.delete(key);
|
|
return true;
|
|
}
|
|
/**
|
|
* Clear all values from the map.
|
|
*/
|
|
clear() {
|
|
for (const [, weakValue] of this._map.entries()) {
|
|
const value = weakValue.ref.deref();
|
|
if (value != null)
|
|
weakValue.tracker.unregister(value);
|
|
}
|
|
this._map.clear();
|
|
}
|
|
/**
|
|
* Get the number of entries in the map.
|
|
*/
|
|
get size() {
|
|
return this._map.size;
|
|
}
|
|
/**
|
|
* Get an iterator for all entries in the map.
|
|
*/
|
|
*entries() {
|
|
for (const [key, weakValue] of this._map.entries()) {
|
|
const value = weakValue.ref.deref();
|
|
if (value != null)
|
|
yield [key, value];
|
|
}
|
|
}
|
|
/**
|
|
* Get an iterator for all keys in the map.
|
|
*/
|
|
*keys() {
|
|
for (const [key] of this.entries())
|
|
yield key;
|
|
}
|
|
/**
|
|
* Get an iterator for all values in the map.
|
|
*/
|
|
*values() {
|
|
for (const [, value] of this.entries())
|
|
yield value;
|
|
}
|
|
/**
|
|
* Call a function for each entry in the map.
|
|
*/
|
|
forEach(callbackfn, thisArg) {
|
|
for (const [key, value] of this.entries()) {
|
|
if (thisArg !== undefined)
|
|
callbackfn.call(thisArg, value, key, this);
|
|
else
|
|
callbackfn.call(this, value, key, this);
|
|
}
|
|
}
|
|
[Symbol.iterator]() {
|
|
return this.entries();
|
|
}
|
|
/** @internal */
|
|
_finalize(value, key) {
|
|
const weakValue = this._map.get(key);
|
|
if (weakValue === value)
|
|
this._map.delete(key);
|
|
}
|
|
}
|
|
//# sourceMappingURL=WeakValueMultiKeyMap.js.map
|