Projektstart
This commit is contained in:
436
backend/node_modules/mnemonist/lru-cache.js
generated
vendored
Normal file
436
backend/node_modules/mnemonist/lru-cache.js
generated
vendored
Normal file
@@ -0,0 +1,436 @@
|
||||
/**
|
||||
* Mnemonist LRUCache
|
||||
* ===================
|
||||
*
|
||||
* JavaScript implementation of the LRU Cache data structure. To save up
|
||||
* memory and allocations this implementation represents its underlying
|
||||
* doubly-linked list as static arrays and pointers. Thus, memory is allocated
|
||||
* only once at instantiation and JS objects are never created to serve as
|
||||
* pointers. This also means this implementation does not trigger too many
|
||||
* garbage collections.
|
||||
*
|
||||
* Note that to save up memory, a LRU Cache can be implemented using a singly
|
||||
* linked list by storing predecessors' pointers as hashmap values.
|
||||
* However, this means more hashmap lookups and would probably slow the whole
|
||||
* thing down. What's more, pointers are not the things taking most space in
|
||||
* memory.
|
||||
*/
|
||||
var Iterator = require('obliterator/iterator'),
|
||||
forEach = require('obliterator/foreach'),
|
||||
typed = require('./utils/typed-arrays.js'),
|
||||
iterables = require('./utils/iterables.js');
|
||||
|
||||
/**
|
||||
* LRUCache.
|
||||
*
|
||||
* @constructor
|
||||
* @param {function} Keys - Array class for storing keys.
|
||||
* @param {function} Values - Array class for storing values.
|
||||
* @param {number} capacity - Desired capacity.
|
||||
*/
|
||||
function LRUCache(Keys, Values, capacity) {
|
||||
if (arguments.length < 2) {
|
||||
capacity = Keys;
|
||||
Keys = null;
|
||||
Values = null;
|
||||
}
|
||||
|
||||
this.capacity = capacity;
|
||||
|
||||
if (typeof this.capacity !== 'number' || this.capacity <= 0)
|
||||
throw new Error('mnemonist/lru-cache: capacity should be positive number.');
|
||||
else if (!isFinite(this.capacity) || Math.floor(this.capacity) !== this.capacity)
|
||||
throw new Error('mnemonist/lru-cache: capacity should be a finite positive integer.');
|
||||
|
||||
var PointerArray = typed.getPointerArray(capacity);
|
||||
|
||||
this.forward = new PointerArray(capacity);
|
||||
this.backward = new PointerArray(capacity);
|
||||
this.K = typeof Keys === 'function' ? new Keys(capacity) : new Array(capacity);
|
||||
this.V = typeof Values === 'function' ? new Values(capacity) : new Array(capacity);
|
||||
|
||||
// Properties
|
||||
this.size = 0;
|
||||
this.head = 0;
|
||||
this.tail = 0;
|
||||
this.items = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to clear the structure.
|
||||
*
|
||||
* @return {undefined}
|
||||
*/
|
||||
LRUCache.prototype.clear = function() {
|
||||
this.size = 0;
|
||||
this.head = 0;
|
||||
this.tail = 0;
|
||||
this.items = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to splay a value on top.
|
||||
*
|
||||
* @param {number} pointer - Pointer of the value to splay on top.
|
||||
* @return {LRUCache}
|
||||
*/
|
||||
LRUCache.prototype.splayOnTop = function(pointer) {
|
||||
var oldHead = this.head;
|
||||
|
||||
if (this.head === pointer)
|
||||
return this;
|
||||
|
||||
var previous = this.backward[pointer],
|
||||
next = this.forward[pointer];
|
||||
|
||||
if (this.tail === pointer) {
|
||||
this.tail = previous;
|
||||
}
|
||||
else {
|
||||
this.backward[next] = previous;
|
||||
}
|
||||
|
||||
this.forward[previous] = next;
|
||||
|
||||
this.backward[oldHead] = pointer;
|
||||
this.head = pointer;
|
||||
this.forward[pointer] = oldHead;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to set the value for the given key in the cache.
|
||||
*
|
||||
* @param {any} key - Key.
|
||||
* @param {any} value - Value.
|
||||
* @return {undefined}
|
||||
*/
|
||||
LRUCache.prototype.set = function(key, value) {
|
||||
|
||||
var pointer = this.items[key];
|
||||
|
||||
// The key already exists, we just need to update the value and splay on top
|
||||
if (typeof pointer !== 'undefined') {
|
||||
this.splayOnTop(pointer);
|
||||
this.V[pointer] = value;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// The cache is not yet full
|
||||
if (this.size < this.capacity) {
|
||||
pointer = this.size++;
|
||||
}
|
||||
|
||||
// Cache is full, we need to drop the last value
|
||||
else {
|
||||
pointer = this.tail;
|
||||
this.tail = this.backward[pointer];
|
||||
delete this.items[this.K[pointer]];
|
||||
}
|
||||
|
||||
// Storing key & value
|
||||
this.items[key] = pointer;
|
||||
this.K[pointer] = key;
|
||||
this.V[pointer] = value;
|
||||
|
||||
// Moving the item at the front of the list
|
||||
this.forward[pointer] = this.head;
|
||||
this.backward[this.head] = pointer;
|
||||
this.head = pointer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to set the value for the given key in the cache
|
||||
*
|
||||
* @param {any} key - Key.
|
||||
* @param {any} value - Value.
|
||||
* @return {{evicted: boolean, key: any, value: any}} An object containing the
|
||||
* key and value of an item that was overwritten or evicted in the set
|
||||
* operation, as well as a boolean indicating whether it was evicted due to
|
||||
* limited capacity. Return value is null if nothing was evicted or overwritten
|
||||
* during the set operation.
|
||||
*/
|
||||
LRUCache.prototype.setpop = function(key, value) {
|
||||
var oldValue = null;
|
||||
var oldKey = null;
|
||||
|
||||
var pointer = this.items[key];
|
||||
|
||||
// The key already exists, we just need to update the value and splay on top
|
||||
if (typeof pointer !== 'undefined') {
|
||||
this.splayOnTop(pointer);
|
||||
oldValue = this.V[pointer];
|
||||
this.V[pointer] = value;
|
||||
return {evicted: false, key: key, value: oldValue};
|
||||
}
|
||||
|
||||
// The cache is not yet full
|
||||
if (this.size < this.capacity) {
|
||||
pointer = this.size++;
|
||||
}
|
||||
|
||||
// Cache is full, we need to drop the last value
|
||||
else {
|
||||
pointer = this.tail;
|
||||
this.tail = this.backward[pointer];
|
||||
oldValue = this.V[pointer];
|
||||
oldKey = this.K[pointer];
|
||||
delete this.items[this.K[pointer]];
|
||||
}
|
||||
|
||||
// Storing key & value
|
||||
this.items[key] = pointer;
|
||||
this.K[pointer] = key;
|
||||
this.V[pointer] = value;
|
||||
|
||||
// Moving the item at the front of the list
|
||||
this.forward[pointer] = this.head;
|
||||
this.backward[this.head] = pointer;
|
||||
this.head = pointer;
|
||||
|
||||
// Return object if eviction took place, otherwise return null
|
||||
if (oldKey) {
|
||||
return {evicted: true, key: oldKey, value: oldValue};
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to check whether the key exists in the cache.
|
||||
*
|
||||
* @param {any} key - Key.
|
||||
* @return {boolean}
|
||||
*/
|
||||
LRUCache.prototype.has = function(key) {
|
||||
return key in this.items;
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to get the value attached to the given key. Will move the
|
||||
* related key to the front of the underlying linked list.
|
||||
*
|
||||
* @param {any} key - Key.
|
||||
* @return {any}
|
||||
*/
|
||||
LRUCache.prototype.get = function(key) {
|
||||
var pointer = this.items[key];
|
||||
|
||||
if (typeof pointer === 'undefined')
|
||||
return;
|
||||
|
||||
this.splayOnTop(pointer);
|
||||
|
||||
return this.V[pointer];
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to get the value attached to the given key. Does not modify
|
||||
* the ordering of the underlying linked list.
|
||||
*
|
||||
* @param {any} key - Key.
|
||||
* @return {any}
|
||||
*/
|
||||
LRUCache.prototype.peek = function(key) {
|
||||
var pointer = this.items[key];
|
||||
|
||||
if (typeof pointer === 'undefined')
|
||||
return;
|
||||
|
||||
return this.V[pointer];
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to iterate over the cache's entries using a callback.
|
||||
*
|
||||
* @param {function} callback - Function to call for each item.
|
||||
* @param {object} scope - Optional scope.
|
||||
* @return {undefined}
|
||||
*/
|
||||
LRUCache.prototype.forEach = function(callback, scope) {
|
||||
scope = arguments.length > 1 ? scope : this;
|
||||
|
||||
var i = 0,
|
||||
l = this.size;
|
||||
|
||||
var pointer = this.head,
|
||||
keys = this.K,
|
||||
values = this.V,
|
||||
forward = this.forward;
|
||||
|
||||
while (i < l) {
|
||||
|
||||
callback.call(scope, values[pointer], keys[pointer], this);
|
||||
pointer = forward[pointer];
|
||||
|
||||
i++;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to create an iterator over the cache's keys from most
|
||||
* recently used to least recently used.
|
||||
*
|
||||
* @return {Iterator}
|
||||
*/
|
||||
LRUCache.prototype.keys = function() {
|
||||
var i = 0,
|
||||
l = this.size;
|
||||
|
||||
var pointer = this.head,
|
||||
keys = this.K,
|
||||
forward = this.forward;
|
||||
|
||||
return new Iterator(function() {
|
||||
if (i >= l)
|
||||
return {done: true};
|
||||
|
||||
var key = keys[pointer];
|
||||
|
||||
i++;
|
||||
|
||||
if (i < l)
|
||||
pointer = forward[pointer];
|
||||
|
||||
return {
|
||||
done: false,
|
||||
value: key
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to create an iterator over the cache's values from most
|
||||
* recently used to least recently used.
|
||||
*
|
||||
* @return {Iterator}
|
||||
*/
|
||||
LRUCache.prototype.values = function() {
|
||||
var i = 0,
|
||||
l = this.size;
|
||||
|
||||
var pointer = this.head,
|
||||
values = this.V,
|
||||
forward = this.forward;
|
||||
|
||||
return new Iterator(function() {
|
||||
if (i >= l)
|
||||
return {done: true};
|
||||
|
||||
var value = values[pointer];
|
||||
|
||||
i++;
|
||||
|
||||
if (i < l)
|
||||
pointer = forward[pointer];
|
||||
|
||||
return {
|
||||
done: false,
|
||||
value: value
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Method used to create an iterator over the cache's entries from most
|
||||
* recently used to least recently used.
|
||||
*
|
||||
* @return {Iterator}
|
||||
*/
|
||||
LRUCache.prototype.entries = function() {
|
||||
var i = 0,
|
||||
l = this.size;
|
||||
|
||||
var pointer = this.head,
|
||||
keys = this.K,
|
||||
values = this.V,
|
||||
forward = this.forward;
|
||||
|
||||
return new Iterator(function() {
|
||||
if (i >= l)
|
||||
return {done: true};
|
||||
|
||||
var key = keys[pointer],
|
||||
value = values[pointer];
|
||||
|
||||
i++;
|
||||
|
||||
if (i < l)
|
||||
pointer = forward[pointer];
|
||||
|
||||
return {
|
||||
done: false,
|
||||
value: [key, value]
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaching the #.entries method to Symbol.iterator if possible.
|
||||
*/
|
||||
if (typeof Symbol !== 'undefined')
|
||||
LRUCache.prototype[Symbol.iterator] = LRUCache.prototype.entries;
|
||||
|
||||
/**
|
||||
* Convenience known methods.
|
||||
*/
|
||||
LRUCache.prototype.inspect = function() {
|
||||
var proxy = new Map();
|
||||
|
||||
var iterator = this.entries(),
|
||||
step;
|
||||
|
||||
while ((step = iterator.next(), !step.done))
|
||||
proxy.set(step.value[0], step.value[1]);
|
||||
|
||||
// Trick so that node displays the name of the constructor
|
||||
Object.defineProperty(proxy, 'constructor', {
|
||||
value: LRUCache,
|
||||
enumerable: false
|
||||
});
|
||||
|
||||
return proxy;
|
||||
};
|
||||
|
||||
if (typeof Symbol !== 'undefined')
|
||||
LRUCache.prototype[Symbol.for('nodejs.util.inspect.custom')] = LRUCache.prototype.inspect;
|
||||
|
||||
/**
|
||||
* Static @.from function taking an arbitrary iterable & converting it into
|
||||
* a structure.
|
||||
*
|
||||
* @param {Iterable} iterable - Target iterable.
|
||||
* @param {function} Keys - Array class for storing keys.
|
||||
* @param {function} Values - Array class for storing values.
|
||||
* @param {number} capacity - Cache's capacity.
|
||||
* @return {LRUCache}
|
||||
*/
|
||||
LRUCache.from = function(iterable, Keys, Values, capacity) {
|
||||
if (arguments.length < 2) {
|
||||
capacity = iterables.guessLength(iterable);
|
||||
|
||||
if (typeof capacity !== 'number')
|
||||
throw new Error('mnemonist/lru-cache.from: could not guess iterable length. Please provide desired capacity as last argument.');
|
||||
}
|
||||
else if (arguments.length === 2) {
|
||||
capacity = Keys;
|
||||
Keys = null;
|
||||
Values = null;
|
||||
}
|
||||
|
||||
var cache = new LRUCache(Keys, Values, capacity);
|
||||
|
||||
forEach(iterable, function(value, key) {
|
||||
cache.set(key, value);
|
||||
});
|
||||
|
||||
return cache;
|
||||
};
|
||||
|
||||
/**
|
||||
* Exporting.
|
||||
*/
|
||||
module.exports = LRUCache;
|
||||
Reference in New Issue
Block a user