@stringsync/vexml
Version:
MusicXML to Vexflow
61 lines (60 loc) • 2.65 kB
JavaScript
import { LRU } from './lru';
/**
* Memoizes a method.
*
* This decorator is backed by an LRU cache to avoid memory leaks. Each argument provided can yield another LRU true if
* there are arguments after it. The `degree` parameter is the capacity of each LRU cache "level". It must be declared
* statically.
*
* NOTE: LRU caches depend on _identity_ for indexing. If you need values to resolve based on _equivalency_, do not use
* this decorator.
*/
export function memoize(opts = { degree: 1 }) {
return function (target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
// Using symbols to ensure uniqueness and avoid potential property name collisions
const cacheSymbol = Symbol(`cache_${String(propertyKey)}`);
descriptor.value = function (...args) {
// Sidestep TypeScript typings.
const self = this;
self[cacheSymbol] ??= new LRU(opts.degree);
let node = self[cacheSymbol];
const depth = args.length;
// When there are no args, we don't need to traverse the LRU tree. However, we do need to store a key in the root
// map in case the function is variadic.
if (depth === 0) {
if (!node.has(depth)) {
node.put(depth, originalMethod.apply(self, args));
}
return node.get(depth);
}
// We need to traverse the LRU tree, starting with the argument length as a namespace. This takes up more space,
// but it prevents unintentional exposure of the underlying LRU caches.
let value;
if (!node.has(depth)) {
node.put(depth, new LRU(opts.degree));
}
node = node.get(depth);
for (let index = 0; index < depth; index++) {
const arg = args[index];
const isLast = index === depth - 1;
if (isLast) {
// We're on the last arg, so now is the time to check the value.
if (!node.has(arg)) {
node.put(arg, originalMethod.apply(self, args));
}
value = node.get(arg);
}
else {
// We still have other args, so we'll create a new LRU to memoize them.
if (!node.has(arg)) {
node.put(arg, new LRU(opts.degree));
}
node = node.get(arg);
}
}
return value;
};
return descriptor;
};
}