@httpx/lru
Version:
LruCache implementations with O(1) complexity
305 lines (304 loc) • 7.96 kB
JavaScript
var DoublyLinkedNode = class {
key;
prev = null;
next = null;
constructor(key) {
this.key = key;
}
};
var LruCache = class {
#maxSize;
#touchOnHas;
#onEviction;
#cache;
#head = null;
#tail = null;
constructor(params) {
const { maxSize, touchOnHas = false, onEviction } = params;
if (!Number.isSafeInteger(maxSize) || maxSize < 1) throw new TypeError("Invalid maxSize");
this.#touchOnHas = touchOnHas;
this.#onEviction = onEviction;
this.#maxSize = maxSize;
this.#cache = /* @__PURE__ */ new Map();
}
get params() {
return { maxSize: this.#maxSize };
}
get size() {
return this.#cache.size;
}
clear() {
const size = this.#cache.size;
this.#cache.clear();
this.#head = this.#tail = null;
return size;
}
has(key, options) {
const hasEntry = this.#cache.has(key);
if (hasEntry && (options?.touch ?? this.#touchOnHas)) this.#moveToHead(this.#cache.get(key).node);
return hasEntry;
}
set(key, value) {
if (this.#cache.has(key)) {
const data$1 = this.#cache.get(key);
data$1.value = value;
this.#moveToHead(data$1.node);
return false;
}
const newNode = new DoublyLinkedNode(key);
const data = {
value,
node: newNode
};
this.#cache.set(key, data);
this.#moveToHead(newNode);
if (this.#cache.size > this.#maxSize) this.#removeTail();
return true;
}
get(key) {
const data = this.#cache.get(key);
if (data === void 0) return;
this.#moveToHead(data.node);
return data.value;
}
getOrSet(key, valueOrFn) {
const val = this.get(key);
if (val === void 0) {
const value = typeof valueOrFn === "function" ? valueOrFn() : valueOrFn;
this.set(key, value);
return value;
}
return val;
}
peek(key) {
return this.#cache.get(key)?.value;
}
delete(key) {
const node = this.#cache.get(key)?.node;
if (node === void 0) return false;
this.#removeNode(node);
return this.#cache.delete(key);
}
*[Symbol.iterator]() {
let current = this.#tail;
while (current) {
const data = this.#cache.get(current.key);
yield [current.key, data.value];
current = current.prev;
}
}
#moveToHead(node) {
if (node === this.#head) return;
this.#removeNode(node);
node.next = this.#head;
node.prev = null;
if (this.#head) this.#head.prev = node;
this.#head = node;
this.#tail ??= node;
}
#removeNode(node) {
if (node.prev) node.prev.next = node.next;
if (node.next) node.next.prev = node.prev;
if (node === this.#head) this.#head = node.next;
if (node === this.#tail) this.#tail = node.prev;
}
#removeTail() {
if (this.#tail === null) return;
const tailKey = this.#tail.key;
this.#removeNode(this.#tail);
if (this.#onEviction) this.#onEviction(tailKey, this.#cache.get(tailKey).value);
this.#cache.delete(tailKey);
}
};
const getOrCreateLruCache = (name, lruCacheParams, options) => {
globalThis.__httpx_lru_cache_instances ??= /* @__PURE__ */ new Map();
if (!globalThis.__httpx_lru_cache_instances.has(name)) {
if (options?.onCreate instanceof Function) options.onCreate(name, lruCacheParams);
globalThis.__httpx_lru_cache_instances.set(name, new LruCache(lruCacheParams));
}
return globalThis.__httpx_lru_cache_instances.get(name);
};
var TimeLruCache = class {
#maxSize;
#touchOnHas;
#ttl;
#onEviction;
#cache;
#head = null;
#tail = null;
constructor(params) {
const { maxSize, touchOnHas = false, onEviction, defaultTTL } = params;
if (!Number.isSafeInteger(maxSize) || maxSize < 1) throw new TypeError("Invalid maxSize");
if (!Number.isSafeInteger(defaultTTL) || defaultTTL < 1) throw new TypeError("Invalid ttl");
this.#touchOnHas = touchOnHas;
this.#onEviction = onEviction;
this.#maxSize = maxSize;
this.#ttl = defaultTTL;
this.#cache = /* @__PURE__ */ new Map();
}
get params() {
return {
maxSize: this.#maxSize,
defaultTTL: this.#ttl
};
}
get size() {
return this.#cache.size;
}
clear() {
const size = this.#cache.size;
this.#cache.clear();
this.#head = this.#tail = null;
return size;
}
has(key, options) {
const value = this.#cache.get(key);
const hasEntry = value !== void 0;
if (hasEntry && value.expiry < Date.now()) {
if (this.#onEviction) this.#onEviction(key, this.#cache.get(key).value);
this.#removeNode(value.node);
return false;
}
if (hasEntry && (options?.touch ?? this.#touchOnHas)) this.#moveToHead(this.#cache.get(key).node);
return hasEntry;
}
set(key, value, ttl) {
if (this.#cache.has(key)) {
const data$1 = this.#cache.get(key);
data$1.value = value;
data$1.expiry = Date.now() + (ttl ?? this.#ttl);
this.#moveToHead(data$1.node);
return false;
}
const newNode = new DoublyLinkedNode(key);
const data = {
value,
expiry: Date.now() + (ttl ?? this.#ttl),
node: newNode
};
this.#cache.set(key, data);
this.#moveToHead(newNode);
if (this.#cache.size > this.#maxSize) this.#removeTail();
return true;
}
get(key) {
const data = this.#cache.get(key);
if (data === void 0) return;
if (data.expiry < Date.now()) {
if (this.#onEviction) this.#onEviction(key, data.value);
this.#removeNode(data.node);
return;
}
this.#moveToHead(data.node);
return data.value;
}
getOrSet(key, valueOrFn, ttl) {
const val = this.get(key);
if (val === void 0) {
const value = typeof valueOrFn === "function" ? valueOrFn() : valueOrFn;
this.set(key, value, ttl);
return value;
}
return val;
}
peek(key) {
const val = this.#cache.get(key);
if (val === void 0) return;
return val.expiry < Date.now() ? void 0 : val.value;
}
delete(key) {
const node = this.#cache.get(key)?.node;
if (node === void 0) return false;
this.#removeNode(node);
return this.#cache.delete(key);
}
*[Symbol.iterator]() {
let current = this.#tail;
while (current) {
const data = this.#cache.get(current.key);
yield [current.key, data.value];
current = current.prev;
}
}
#moveToHead(node) {
if (node === this.#head) return;
this.#removeNode(node);
node.next = this.#head;
node.prev = null;
if (this.#head) this.#head.prev = node;
this.#head = node;
this.#tail ??= node;
}
#removeNode(node) {
if (node.prev) node.prev.next = node.next;
if (node.next) node.next.prev = node.prev;
if (node === this.#head) this.#head = node.next;
if (node === this.#tail) this.#tail = node.prev;
}
#removeTail() {
if (this.#tail === null) return;
const tailKey = this.#tail.key;
this.#removeNode(this.#tail);
if (this.#onEviction) this.#onEviction(tailKey, this.#cache.get(tailKey).value);
this.#cache.delete(tailKey);
}
};
const getOrCreateTimeLruCache = (name, lruCacheParams, options) => {
globalThis.__httpx_time_lru_cache_instances ??= /* @__PURE__ */ new Map();
if (!globalThis.__httpx_time_lru_cache_instances.has(name)) {
if (options?.onCreate instanceof Function) options.onCreate(name, lruCacheParams);
globalThis.__httpx_time_lru_cache_instances.set(name, new TimeLruCache(lruCacheParams));
}
return globalThis.__httpx_time_lru_cache_instances.get(name);
};
var NullLruCache = class {
constructor(_params) {}
size = 0;
params = { maxSize: 0 };
clear() {
return 0;
}
has(_key, _options) {
return false;
}
set(_key, _value) {
return false;
}
get(_key) {}
getOrSet(_key, valueOrFn) {
return typeof valueOrFn === "function" ? valueOrFn() : valueOrFn;
}
peek(_key) {}
delete(_key) {
return false;
}
*[Symbol.iterator]() {}
};
var NullTimeLruCache = class {
constructor(_params) {}
size = 0;
params = {
maxSize: 0,
defaultTTL: 0
};
clear() {
return 0;
}
has(_key, _options) {
return false;
}
set(_key, _value, _ttl) {
return false;
}
get(_key) {}
getOrSet(_key, valueOrFn, _ttl) {
return typeof valueOrFn === "function" ? valueOrFn() : valueOrFn;
}
peek(_key) {}
delete(_key) {
return false;
}
*[Symbol.iterator]() {}
};
export { LruCache, NullLruCache, NullTimeLruCache, TimeLruCache, getOrCreateLruCache, getOrCreateTimeLruCache };
//# sourceMappingURL=index.js.map