sussy-util
Version:
Util package made by me
272 lines (271 loc) • 8.47 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const ObjectPool_1 = __importDefault(require("./ObjectPool"));
const Optional_1 = __importDefault(require("./Optional"));
/**
* A Least Recently Used (LRU) Cache implementation with a fixed capacity.
*
* @template Key - The type of keys stored in the cache.
* @template Value - The type of values stored in the cache.
*/
class LRUCache {
/**
* Create an LRUCache instance with a specified capacity.
*
* @param {number} capacity - The maximum number of entries the cache can hold.
*/
constructor(capacity) {
this.evictionCallback = (key, value) => void 0;
this.capacity = capacity;
this.cache = new Map();
this.usageOrder = new DoublyLinkedList();
this.objectPool = new ObjectPool_1.default(() => new DoublyLinkedListNode());
}
/**
* Retrieve a value from the cache based on the provided key.
* Moves the accessed item to the front of the usage order.
*
* @param {Key} key - The key for the desired value.
* @returns {Value | undefined} - The value associated with the key, or undefined if not found.
*/
get(key) {
if (this.cache.has(key)) {
const value = this.cache.get(key);
this.usageOrder.moveToFront(key);
return Optional_1.default.of(value);
}
return Optional_1.default.empty();
}
/**
* Add a key-value pair to the cache. If the cache exceeds capacity, the least recently used item is evicted.
* Moves the added or accessed item to the front of the usage order.
*
* @param {Key} key - The key for the new item.
* @param {Value} value - The value associated with the key.
*/
put(key, value) {
if (this.cache.size >= this.capacity) {
const evictedKey = this.usageOrder.removeLast();
if (!!evictedKey) {
this.evictionCallback(evictedKey, this.cache.get(evictedKey));
this.cache.delete(evictedKey);
this.objectPool.release(this.usageOrder.find(evictedKey));
this.usageOrder.remove(evictedKey);
}
}
if (this.cache.has(key)) {
this.cache.set(key, value);
this.usageOrder.moveToFront(key);
}
else {
const newNode = this.objectPool.acquire();
newNode.value = key;
this.usageOrder.addToFront(newNode);
this.cache.set(key, value);
}
}
/**
* Add multiple key-value pairs to the cache. If the cache exceeds capacity, the least recently used items are evicted.
* Moves the added or accessed items to the front of the usage order.
*
* @param {[Key, Value][]} entries - An array of key-value pairs to add to the cache.
*/
putAll(entries) {
for (const [key, value] of entries) {
this.put(key, value);
}
}
/**
* Clear all entries from the cache.
*/
clear() {
this.cache.clear();
this.usageOrder.clear();
}
/**
* Get an array of all keys currently in the cache.
*
* @returns {Key[]} - An array of keys in the cache.
*/
getAllKeys() {
return Array.from(this.cache.keys());
}
/**
* Get an array of all values currently in the cache.
*
* @returns {Value[]} - An array of values in the cache.
*/
getAllValues() {
return Array.from(this.cache.values());
}
/**
* Get a generator yielding all entries (key-value pairs) in the cache.
*
* @yields {[Key, Value]} - A key-value pair from the cache.
*/
*entries() {
for (const [key, value] of this.cache.entries()) {
yield [key, value];
}
}
/**
* Set a callback function to be called when an item is evicted from the cache.
*
* @param {(key: Key, value: Value) => void} callback - The callback function.
*/
setEvictionCallback(callback) {
this.evictionCallback = callback;
}
/**
* Remove an item from the cache based on the provided key.
*
* @param {Key} key - The key of the item to remove.
* @returns {boolean} - True if the item was removed, false if the key was not found.
*/
remove(key) {
if (this.cache.has(key)) {
this.cache.delete(key);
this.objectPool.release(this.usageOrder.find(key));
this.usageOrder.remove(key);
return true;
}
return false;
}
}
exports.default = LRUCache;
/**
* A node in a doubly linked list.
*
* @template T - The type of value stored in the node.
*/
class DoublyLinkedListNode {
constructor() {
this.value = null;
this.next = null;
this.prev = null;
}
}
/**
* A doubly linked list data structure.
*
* @template T - The type of values stored in the list.
*/
class DoublyLinkedList {
constructor() {
this.head = null;
this.tail = null;
}
/**
* Add a node to the front of the linked list.
*
* @param {DoublyLinkedListNode<T>} node - The node to add.
*/
addToFront(node) {
if (this.head === null) {
this.head = node;
this.tail = node;
}
else {
node.next = this.head;
this.head.prev = node;
this.head = node;
}
}
/**
* Remove the last node from the linked list and return its value.
*
* @returns {T | null} - The value of the removed node, or null if the list is empty.
*/
removeLast() {
if (this.tail === null) {
return null;
}
const lastNode = this.tail;
if (lastNode.prev) {
lastNode.prev.next = null;
this.tail = lastNode.prev;
}
else {
this.head = null;
this.tail = null;
}
return lastNode.value;
}
/**
* Remove a node with a specific value from the linked list.
*
* @param {T} value - The value of the node to remove.
* @returns {boolean} - True if the node was found and removed, false otherwise.
*/
remove(value) {
let currentNode = this.head;
while (currentNode !== null && currentNode.value !== value) {
currentNode = currentNode.next;
}
if (currentNode !== null) {
if (currentNode.prev) {
currentNode.prev.next = currentNode.next;
}
else {
this.head = currentNode.next;
}
if (currentNode.next) {
currentNode.next.prev = currentNode.prev;
}
else {
this.tail = currentNode.prev;
}
return true;
}
return false;
}
/**
* Move a node with a specific value to the front of the linked list.
*
* @param {T} value - The value of the node to move to the front.
*/
moveToFront(value) {
let currentNode = this.head;
while (currentNode !== null && currentNode.value !== value) {
currentNode = currentNode.next;
}
if (currentNode !== null) {
if (currentNode.prev) {
currentNode.prev.next = currentNode.next;
if (currentNode === this.tail) {
this.tail = currentNode.prev;
}
}
currentNode.next = this.head;
currentNode.prev = null;
this.head.prev = currentNode;
this.head = currentNode;
}
}
/**
* Find a node with a specific value in the linked list.
*
* @param {T} value - The value to search for.
* @returns {DoublyLinkedListNode<T> | null} - The node with the specified value, or null if not found.
*/
find(value) {
let currentNode = this.head;
while (currentNode !== null) {
if (currentNode.value === value) {
return currentNode;
}
currentNode = currentNode.next;
}
return null;
}
/**
* Clear the linked list, removing all nodes.
*/
clear() {
this.head = null;
this.tail = null;
}
}