You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
4.0 KiB
143 lines
4.0 KiB
5 months ago
|
function noop() { }
|
||
|
const defaultDispose = noop;
|
||
|
const _WeakRef = typeof WeakRef !== "undefined"
|
||
|
? WeakRef
|
||
|
: function (value) {
|
||
|
return { deref: () => value };
|
||
|
};
|
||
|
const _WeakMap = typeof WeakMap !== "undefined" ? WeakMap : Map;
|
||
|
const _FinalizationRegistry = typeof FinalizationRegistry !== "undefined"
|
||
|
? FinalizationRegistry
|
||
|
: function () {
|
||
|
return {
|
||
|
register: noop,
|
||
|
unregister: noop,
|
||
|
};
|
||
|
};
|
||
|
const finalizationBatchSize = 10024;
|
||
|
export class WeakCache {
|
||
|
constructor(max = Infinity, dispose = defaultDispose) {
|
||
|
this.max = max;
|
||
|
this.dispose = dispose;
|
||
|
this.map = new _WeakMap();
|
||
|
this.newest = null;
|
||
|
this.oldest = null;
|
||
|
this.unfinalizedNodes = new Set();
|
||
|
this.finalizationScheduled = false;
|
||
|
this.size = 0;
|
||
|
this.finalize = () => {
|
||
|
const iterator = this.unfinalizedNodes.values();
|
||
|
for (let i = 0; i < finalizationBatchSize; i++) {
|
||
|
const node = iterator.next().value;
|
||
|
if (!node)
|
||
|
break;
|
||
|
this.unfinalizedNodes.delete(node);
|
||
|
const key = node.key;
|
||
|
delete node.key;
|
||
|
node.keyRef = new _WeakRef(key);
|
||
|
this.registry.register(key, node, node);
|
||
|
}
|
||
|
if (this.unfinalizedNodes.size > 0) {
|
||
|
queueMicrotask(this.finalize);
|
||
|
}
|
||
|
else {
|
||
|
this.finalizationScheduled = false;
|
||
|
}
|
||
|
};
|
||
|
this.registry = new _FinalizationRegistry(this.deleteNode.bind(this));
|
||
|
}
|
||
|
has(key) {
|
||
|
return this.map.has(key);
|
||
|
}
|
||
|
get(key) {
|
||
|
const node = this.getNode(key);
|
||
|
return node && node.value;
|
||
|
}
|
||
|
getNode(key) {
|
||
|
const node = this.map.get(key);
|
||
|
if (node && node !== this.newest) {
|
||
|
const { older, newer } = node;
|
||
|
if (newer) {
|
||
|
newer.older = older;
|
||
|
}
|
||
|
if (older) {
|
||
|
older.newer = newer;
|
||
|
}
|
||
|
node.older = this.newest;
|
||
|
node.older.newer = node;
|
||
|
node.newer = null;
|
||
|
this.newest = node;
|
||
|
if (node === this.oldest) {
|
||
|
this.oldest = newer;
|
||
|
}
|
||
|
}
|
||
|
return node;
|
||
|
}
|
||
|
set(key, value) {
|
||
|
let node = this.getNode(key);
|
||
|
if (node) {
|
||
|
return (node.value = value);
|
||
|
}
|
||
|
node = {
|
||
|
key,
|
||
|
value,
|
||
|
newer: null,
|
||
|
older: this.newest,
|
||
|
};
|
||
|
if (this.newest) {
|
||
|
this.newest.newer = node;
|
||
|
}
|
||
|
this.newest = node;
|
||
|
this.oldest = this.oldest || node;
|
||
|
this.scheduleFinalization(node);
|
||
|
this.map.set(key, node);
|
||
|
this.size++;
|
||
|
return node.value;
|
||
|
}
|
||
|
clean() {
|
||
|
while (this.oldest && this.size > this.max) {
|
||
|
this.deleteNode(this.oldest);
|
||
|
}
|
||
|
}
|
||
|
deleteNode(node) {
|
||
|
if (node === this.newest) {
|
||
|
this.newest = node.older;
|
||
|
}
|
||
|
if (node === this.oldest) {
|
||
|
this.oldest = node.newer;
|
||
|
}
|
||
|
if (node.newer) {
|
||
|
node.newer.older = node.older;
|
||
|
}
|
||
|
if (node.older) {
|
||
|
node.older.newer = node.newer;
|
||
|
}
|
||
|
this.size--;
|
||
|
const key = node.key || (node.keyRef && node.keyRef.deref());
|
||
|
this.dispose(node.value, key);
|
||
|
if (!node.keyRef) {
|
||
|
this.unfinalizedNodes.delete(node);
|
||
|
}
|
||
|
else {
|
||
|
this.registry.unregister(node);
|
||
|
}
|
||
|
if (key)
|
||
|
this.map.delete(key);
|
||
|
}
|
||
|
delete(key) {
|
||
|
const node = this.map.get(key);
|
||
|
if (node) {
|
||
|
this.deleteNode(node);
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
scheduleFinalization(node) {
|
||
|
this.unfinalizedNodes.add(node);
|
||
|
if (!this.finalizationScheduled) {
|
||
|
this.finalizationScheduled = true;
|
||
|
queueMicrotask(this.finalize);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//# sourceMappingURL=weak.js.map
|