Comparing version 0.1.0 to 0.2.0
@@ -1,1 +0,1 @@ | ||
var createLRU=m=>{var x=m.max,f=null,i=null,y=0,t=0,v=new Map,d=m==null?void 0:m.dispose,K=m==null?void 0:m.maxAge,V=e=>{e.next=f,f&&(f.prev=e),f=e,i||(i=e),y++},g=e=>{e.prev&&(e.prev.next=e.next),e.next&&(e.next.prev=e.prev),e===f&&(f=e.next),e===i&&(i=e.prev),y--},A=e=>{g(e),V(e)};function n(e){var C,b;if(!(K||t))return!0;var a=Date.now();if(e){var r=v.get(e);if(!(r!=null&&r.timestamp))return!0;var u=(C=r.maxAge)!=null?C:K;return u&&a-r.timestamp>u?(s(r.key),!1):!0}for(var l=i;l!==null;l=l.prev)if(l!=null&&l.timestamp){var u=(b=l.maxAge)!=null?b:K;u&&a-l.timestamp>u&&s(l.key)}}var h=(e=1)=>{for(var a=0;a<e;a++){if(!i)return;var r=i.key,u=i.value;i.maxAge&&t>0&&t--,g(i),v.delete(r),d&&d(u,r)}},N=(e,a,r)=>{var u=v.get(e),l=K||r!=null&&r.maxAge?Date.now():void 0;if(r!=null&&r.maxAge&&!v.has(e)&&t++,u){u.value=a,u.timestamp=l,u.maxAge=r==null?void 0:r.maxAge,f!==u&&A(u);return}u={key:e,value:a,prev:null,next:null,timestamp:l},v.set(e,u),V(u),y>x&&h()},k=e=>{var a=v.get(e);if(a&&n(e))return f!==a&&A(a),a.value},p=e=>{var l;var a=v.get(e);if(a&&n(e)){if(a!=null&&a.timestamp){var r=Date.now(),u=(l=a.maxAge)!=null?l:K;if(u&&r-a.timestamp>u){s(e);return}}return a.value}},w=e=>n(e)?v.has(e):!1,z=()=>{for(var e=[],a=f;a!==null;a=a.next)n(a.key)&&e.push(a.key);return e},O=()=>{for(var e=[],a=f;a!==null;a=a.next)n(a.key)&&e.push(a.value);return e},D=()=>{for(var e=[],a=f;a!==null;a=a.next)n(a.key)&&e.push([a.key,a.value]);return e},L=e=>{for(var a=f;a!==null;a=a.next)n(a.key)&&e(a.value,a.key)},s=e=>{var a=v.get(e);return a?(a.maxAge&&t>0&&t--,g(a),v.delete(e),d&&d(a.value,e),!0):!1},R=()=>{if(d)for(var e of v.values())d(e.value,e.key);v.clear(),f=i=null,y=0,t=0},S=e=>{x=e;for(var a=y;a>x;a--)h()},U=()=>(n(),x-v.size),E=()=>(n(),v.size),H=()=>x;return{set:N,get:k,peek:p,has:w,keys:z,values:O,entries:D,forEach:L,del:s,evict:h,clear:R,resize:S,available:U,stored:E,maxSize:H}}; | ||
var createLRU=g=>{var{max:d,onEviction:t}=g;if(!(Number.isInteger(d)&&d>0))throw new TypeError("`max` must be a positive integer");var a=0,f=0,s=0,y=[],l=new Map,i=new Array(d).fill(void 0),n=new Array(d).fill(void 0),v=new Array(d).fill(0),m=new Array(d).fill(0),o=e=>{if(e!==s){var r=v[e];if(e===f)f=r;else{var u=m[e];v[u]=r,m[r]=u}v[s]=e,m[e]=s,s=e}},b=()=>{var e=f,r=i[e];return t==null||t(n[e],r),l.delete(r),i[e]=void 0,n[e]=void 0,f=v[e],a--,e};return{set(e,r){var u=l.get(e);u===void 0?(u=a===d?b():y.length>0?y.pop():a,l.set(e,u),i[u]=e,a++):t==null||t(n[u],e),n[u]=r,o(u)},get(e){var r=l.get(e);if(r!==void 0)return o(r),n[r]},peek:e=>{var r=l.get(e);return r!==void 0?n[r]:void 0},has:e=>l.has(e),*keys(){for(var e=f,r=0;r<a;r++)yield i[e],e=v[e]},*values(){for(var e=f,r=0;r<a;r++)yield n[e],e=v[e]},*entries(){for(var e=f,r=0;r<a;r++)yield[i[e],n[e]],e=v[e]},forEach:e=>{for(var r=f,u=0;u<a;u++){var K=i[r],h=n[r];e(h,K),r=v[r]}},delete(e){var r=l.get(e);return r!==void 0?(t==null||t(n[r],e),l.delete(e),y.push(r),i[r]=void 0,n[r]=void 0,a--,!0):!1},evict:e=>{for(var r=Math.min(e,a);r>0;)b(),r--},clear(){for(var e of l.values())t==null||t(n[e],i[e]);l.clear(),i.fill(void 0),n.fill(void 0),y=[],a=0,f=s=0},resize:e=>{if(!(Number.isInteger(e)&&e>0))throw new TypeError("`max` must be a positive integer");for(d=e;a>d;)b()},get max(){return d},get size(){return a},get available(){return d-a}}}; |
@@ -1,40 +0,37 @@ | ||
export interface LRUCacheOptions<Key extends string, Value> { | ||
export declare const createLRU: <Key, Value>(options: { | ||
/** Maximum number of items the cache can hold. */ | ||
max: number; | ||
maxAge?: number; | ||
dispose?: (value: Value, key: Key) => undefined; | ||
} | ||
export interface SetOptions { | ||
maxAge?: number; | ||
} | ||
export declare const createLRU: <Key extends string, Value>(options: LRUCacheOptions<Key, Value>) => { | ||
/** Function called when an item is evicted from the cache. */ | ||
onEviction?: (value: Value, key: Key) => unknown; | ||
}) => { | ||
/** Adds a key-value pair to the cache. Updates the value if the key already exists. */ | ||
set: (key: Key, value: Value, options?: SetOptions) => undefined; | ||
set(key: Key, value: Value): undefined; | ||
/** Retrieves the value for a given key and moves the key to the most recent position. */ | ||
get: (key: Key) => Value | undefined; | ||
/** Retrieves the value for a given key without changing its position in the cache. */ | ||
get(key: Key): Value | undefined; | ||
/** Retrieves the value for a given key without changing its position. */ | ||
peek: (key: Key) => Value | undefined; | ||
/** Checks if a key exists in the cache. */ | ||
has: (key: Key) => boolean; | ||
/** Returns an array of all keys in the cache, from most recent to least recent. */ | ||
keys: () => Key[]; | ||
/** Returns an array of all values in the cache, from most recent to least recent. */ | ||
values: () => Value[]; | ||
/** Returns an array of [key, value] pairs, from most recent to least recent. */ | ||
entries: () => [Key, Value][]; | ||
/** Iterates over all keys in the cache, from least recent to most recent. */ | ||
keys(): IterableIterator<Key>; | ||
/** Iterates over all values in the cache, from least recent to most recent. */ | ||
values(): IterableIterator<Value>; | ||
/** Iterates over `[key, value]` pairs in the cache, from least recent to most recent. */ | ||
entries(): IterableIterator<[Key, Value]>; | ||
/** Iterates over each key-value pair in the cache, from most recent to least recent. */ | ||
forEach: (callback: (value: Value, key: Key) => undefined) => undefined; | ||
forEach: (callback: (value: Value, key: Key) => unknown) => undefined; | ||
/** Deletes a key-value pair from the cache. */ | ||
del: (key: Key) => boolean; | ||
delete(key: Key): boolean; | ||
/** Evicts the oldest item or the specified number of the oldest items from the cache. */ | ||
evict: (size?: number) => undefined; | ||
evict: (number: number) => undefined; | ||
/** Clears all key-value pairs from the cache. */ | ||
clear: () => undefined; | ||
clear(): undefined; | ||
/** Resizes the cache to a new maximum size, evicting items if necessary. */ | ||
resize: (newMax: number) => undefined; | ||
/** Returns the number of available slots in the cache before reaching the maximum size. */ | ||
available: () => number; | ||
/** Returns the maximum number of items that can be stored in the cache. */ | ||
readonly max: number; | ||
/** Returns the number of items currently stored in the cache. */ | ||
stored: () => number; | ||
/** Returns the maximum number of items that can be stored in the cache. */ | ||
maxSize: () => number; | ||
readonly size: number; | ||
/** Returns the number of currently available slots in the cache before reaching the maximum size. */ | ||
readonly available: number; | ||
}; |
384
lib/index.js
"use strict"; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __export = (target, all) => { | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __copyProps = (to, from, except, desc) => { | ||
if (from && typeof from === "object" || typeof from === "function") { | ||
for (let key of __getOwnPropNames(from)) | ||
if (!__hasOwnProp.call(to, key) && key !== except) | ||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
} | ||
return to; | ||
}; | ||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
var src_exports = {}; | ||
__export(src_exports, { | ||
createLRU: () => createLRU | ||
}); | ||
module.exports = __toCommonJS(src_exports); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createLRU = void 0; | ||
const createLRU = (options) => { | ||
let max = options.max; | ||
let head = null; | ||
let tail = null; | ||
let size = 0; | ||
let maxAgeCount = 0; | ||
const map = /* @__PURE__ */ new Map(); | ||
const dispose = options == null ? void 0 : options.dispose; | ||
const maxAge = options == null ? void 0 : options.maxAge; | ||
const addNode = (node) => { | ||
node.next = head; | ||
if (head) head.prev = node; | ||
head = node; | ||
if (!tail) tail = node; | ||
size++; | ||
}; | ||
const removeNode = (node) => { | ||
if (node.prev) node.prev.next = node.next; | ||
if (node.next) node.next.prev = node.prev; | ||
if (node === head) head = node.next; | ||
if (node === tail) tail = node.prev; | ||
size--; | ||
}; | ||
const moveToHead = (node) => { | ||
removeNode(node); | ||
addNode(node); | ||
}; | ||
function refresh(key) { | ||
var _a, _b; | ||
if (!(maxAge || maxAgeCount)) return true; | ||
const now = Date.now(); | ||
if (key) { | ||
const node = map.get(key); | ||
if (!(node == null ? void 0 : node.timestamp)) return true; | ||
const effectiveMaxAge = (_a = node.maxAge) != null ? _a : maxAge; | ||
if (effectiveMaxAge && now - node.timestamp > effectiveMaxAge) { | ||
del(node.key); | ||
return false; | ||
} | ||
return true; | ||
} | ||
for (let current = tail; current !== null; current = current.prev) { | ||
if (!(current == null ? void 0 : current.timestamp)) continue; | ||
const effectiveMaxAge = (_b = current.maxAge) != null ? _b : maxAge; | ||
if (effectiveMaxAge && now - current.timestamp > effectiveMaxAge) | ||
del(current.key); | ||
} | ||
} | ||
const evict = (size2 = 1) => { | ||
for (let i = 0; i < size2; i++) { | ||
if (!tail) return; | ||
const tailKey = tail.key; | ||
const tailValue = tail.value; | ||
if (tail.maxAge && maxAgeCount > 0) { | ||
maxAgeCount--; | ||
} | ||
removeNode(tail); | ||
map.delete(tailKey); | ||
if (dispose) dispose(tailValue, tailKey); | ||
} | ||
}; | ||
const set = (key, value, options2) => { | ||
let node = map.get(key); | ||
const now = maxAge || (options2 == null ? void 0 : options2.maxAge) ? Date.now() : void 0; | ||
if ((options2 == null ? void 0 : options2.maxAge) && !map.has(key)) maxAgeCount++; | ||
if (node) { | ||
node.value = value; | ||
node.timestamp = now; | ||
node.maxAge = options2 == null ? void 0 : options2.maxAge; | ||
if (head !== node) moveToHead(node); | ||
return; | ||
} | ||
node = { | ||
key, | ||
value, | ||
prev: null, | ||
next: null, | ||
timestamp: now | ||
let { max, onEviction } = options; | ||
if (!(Number.isInteger(max) && max > 0)) | ||
throw new TypeError('`max` must be a positive integer'); | ||
let size = 0; | ||
let head = 0; | ||
let tail = 0; | ||
let free = []; | ||
const keyMap = new Map(); | ||
const keyList = new Array(max).fill(undefined); | ||
const valList = new Array(max).fill(undefined); | ||
const next = new Array(max).fill(0); | ||
const prev = new Array(max).fill(0); | ||
const moveToTail = (index) => { | ||
if (index === tail) | ||
return; | ||
const nextIndex = next[index]; | ||
if (index === head) | ||
head = nextIndex; | ||
else { | ||
const prevIndex = prev[index]; | ||
next[prevIndex] = nextIndex; | ||
prev[nextIndex] = prevIndex; | ||
} | ||
next[tail] = index; | ||
prev[index] = tail; | ||
tail = index; | ||
}; | ||
map.set(key, node); | ||
addNode(node); | ||
if (size > max) evict(); | ||
}; | ||
const get = (key) => { | ||
const node = map.get(key); | ||
if (!node) return; | ||
if (!refresh(key)) return; | ||
if (head !== node) moveToHead(node); | ||
return node.value; | ||
}; | ||
const peek = (key) => { | ||
var _a; | ||
const node = map.get(key); | ||
if (!node) return; | ||
if (!refresh(key)) return; | ||
if (node == null ? void 0 : node.timestamp) { | ||
const now = Date.now(); | ||
const effectiveMaxAge = (_a = node.maxAge) != null ? _a : maxAge; | ||
if (effectiveMaxAge && now - node.timestamp > effectiveMaxAge) { | ||
del(key); | ||
return; | ||
} | ||
} | ||
return node.value; | ||
}; | ||
const has = (key) => { | ||
if (!refresh(key)) return false; | ||
return map.has(key); | ||
}; | ||
const keys = () => { | ||
const result = []; | ||
for (let current = head; current !== null; current = current.next) { | ||
if (!refresh(current.key)) continue; | ||
result.push(current.key); | ||
} | ||
return result; | ||
}; | ||
const values = () => { | ||
const result = []; | ||
for (let current = head; current !== null; current = current.next) { | ||
if (!refresh(current.key)) continue; | ||
result.push(current.value); | ||
} | ||
return result; | ||
}; | ||
const entries = () => { | ||
const result = []; | ||
for (let current = head; current !== null; current = current.next) { | ||
if (!refresh(current.key)) continue; | ||
result.push([current.key, current.value]); | ||
} | ||
return result; | ||
}; | ||
const forEach = (callback) => { | ||
for (let current = head; current !== null; current = current.next) { | ||
if (!refresh(current.key)) continue; | ||
callback(current.value, current.key); | ||
} | ||
}; | ||
const del = (key) => { | ||
const node = map.get(key); | ||
if (!node) return false; | ||
if (node.maxAge && maxAgeCount > 0) { | ||
maxAgeCount--; | ||
} | ||
removeNode(node); | ||
map.delete(key); | ||
if (dispose) dispose(node.value, key); | ||
return true; | ||
}; | ||
const clear = () => { | ||
if (dispose) for (const node of map.values()) dispose(node.value, node.key); | ||
map.clear(); | ||
head = tail = null; | ||
size = 0; | ||
maxAgeCount = 0; | ||
}; | ||
const resize = (newMax) => { | ||
max = newMax; | ||
for (let i = size; i > max; i--) evict(); | ||
}; | ||
const available = () => { | ||
refresh(); | ||
return max - map.size; | ||
}; | ||
const stored = () => { | ||
refresh(); | ||
return map.size; | ||
}; | ||
const maxSize = () => max; | ||
return { | ||
/** Adds a key-value pair to the cache. Updates the value if the key already exists. */ | ||
set, | ||
/** Retrieves the value for a given key and moves the key to the most recent position. */ | ||
get, | ||
/** Retrieves the value for a given key without changing its position in the cache. */ | ||
peek, | ||
/** Checks if a key exists in the cache. */ | ||
has, | ||
/** Returns an array of all keys in the cache, from most recent to least recent. */ | ||
keys, | ||
/** Returns an array of all values in the cache, from most recent to least recent. */ | ||
values, | ||
/** Returns an array of [key, value] pairs, from most recent to least recent. */ | ||
entries, | ||
/** Iterates over each key-value pair in the cache, from most recent to least recent. */ | ||
forEach, | ||
/** Deletes a key-value pair from the cache. */ | ||
del, | ||
/** Evicts the oldest item or the specified number of the oldest items from the cache. */ | ||
evict, | ||
/** Clears all key-value pairs from the cache. */ | ||
clear, | ||
/** Resizes the cache to a new maximum size, evicting items if necessary. */ | ||
resize, | ||
/** Returns the number of available slots in the cache before reaching the maximum size. */ | ||
available, | ||
/** Returns the number of items currently stored in the cache. */ | ||
stored, | ||
/** Returns the maximum number of items that can be stored in the cache. */ | ||
maxSize | ||
}; | ||
const _evict = () => { | ||
const evictHead = head; | ||
const key = keyList[evictHead]; | ||
onEviction === null || onEviction === void 0 ? void 0 : onEviction(valList[evictHead], key); | ||
keyMap.delete(key); | ||
keyList[evictHead] = undefined; | ||
valList[evictHead] = undefined; | ||
head = next[evictHead]; | ||
size--; | ||
return evictHead; | ||
}; | ||
return { | ||
/** Adds a key-value pair to the cache. Updates the value if the key already exists. */ | ||
set(key, value) { | ||
let index = keyMap.get(key); | ||
if (index === undefined) { | ||
index = size === max ? _evict() : free.length > 0 ? free.pop() : size; | ||
keyMap.set(key, index); | ||
keyList[index] = key; | ||
size++; | ||
} | ||
else | ||
onEviction === null || onEviction === void 0 ? void 0 : onEviction(valList[index], key); | ||
valList[index] = value; | ||
moveToTail(index); | ||
}, | ||
/** Retrieves the value for a given key and moves the key to the most recent position. */ | ||
get(key) { | ||
const index = keyMap.get(key); | ||
if (index === undefined) | ||
return; | ||
moveToTail(index); | ||
return valList[index]; | ||
}, | ||
/** Retrieves the value for a given key without changing its position. */ | ||
peek: (key) => { | ||
const index = keyMap.get(key); | ||
return index !== undefined ? valList[index] : undefined; | ||
}, | ||
/** Checks if a key exists in the cache. */ | ||
has: (key) => keyMap.has(key), | ||
/** Iterates over all keys in the cache, from least recent to most recent. */ | ||
*keys() { | ||
let current = head; | ||
for (let i = 0; i < size; i++) { | ||
yield keyList[current]; | ||
current = next[current]; | ||
} | ||
}, | ||
/** Iterates over all values in the cache, from least recent to most recent. */ | ||
*values() { | ||
let current = head; | ||
for (let i = 0; i < size; i++) { | ||
yield valList[current]; | ||
current = next[current]; | ||
} | ||
}, | ||
/** Iterates over `[key, value]` pairs in the cache, from least recent to most recent. */ | ||
*entries() { | ||
let current = head; | ||
for (let i = 0; i < size; i++) { | ||
yield [keyList[current], valList[current]]; | ||
current = next[current]; | ||
} | ||
}, | ||
/** Iterates over each key-value pair in the cache, from most recent to least recent. */ | ||
forEach: (callback) => { | ||
let current = head; | ||
for (let i = 0; i < size; i++) { | ||
const key = keyList[current]; | ||
const value = valList[current]; | ||
callback(value, key); | ||
current = next[current]; | ||
} | ||
}, | ||
/** Deletes a key-value pair from the cache. */ | ||
delete(key) { | ||
const index = keyMap.get(key); | ||
if (index !== undefined) { | ||
onEviction === null || onEviction === void 0 ? void 0 : onEviction(valList[index], key); | ||
keyMap.delete(key); | ||
free.push(index); | ||
keyList[index] = undefined; | ||
valList[index] = undefined; | ||
size--; | ||
return true; | ||
} | ||
return false; | ||
}, | ||
/** Evicts the oldest item or the specified number of the oldest items from the cache. */ | ||
evict: (number) => { | ||
let toPrune = Math.min(number, size); | ||
while (toPrune > 0) { | ||
_evict(); | ||
toPrune--; | ||
} | ||
}, | ||
/** Clears all key-value pairs from the cache. */ | ||
clear() { | ||
for (const index of keyMap.values()) | ||
onEviction === null || onEviction === void 0 ? void 0 : onEviction(valList[index], keyList[index]); | ||
keyMap.clear(); | ||
keyList.fill(undefined); | ||
valList.fill(undefined); | ||
free = []; | ||
size = 0; | ||
head = tail = 0; | ||
}, | ||
/** Resizes the cache to a new maximum size, evicting items if necessary. */ | ||
resize: (newMax) => { | ||
if (!(Number.isInteger(newMax) && newMax > 0)) | ||
throw new TypeError('`max` must be a positive integer'); | ||
max = newMax; | ||
while (size > max) | ||
_evict(); | ||
}, | ||
/** Returns the maximum number of items that can be stored in the cache. */ | ||
get max() { | ||
return max; | ||
}, | ||
/** Returns the number of items currently stored in the cache. */ | ||
get size() { | ||
return size; | ||
}, | ||
/** Returns the number of currently available slots in the cache before reaching the maximum size. */ | ||
get available() { | ||
return max - size; | ||
}, | ||
}; | ||
}; | ||
// Annotate the CommonJS export names for ESM import in node: | ||
0 && (module.exports = { | ||
createLRU | ||
}); | ||
exports.createLRU = createLRU; |
{ | ||
"name": "lru.min", | ||
"version": "0.1.0", | ||
"description": "π₯ Extremely fast LRU cache for JavaScript (Browser compatible) β 6.1KB.", | ||
"version": "0.2.0", | ||
"description": "π₯ An extremely fast and efficient LRU cache for JavaScript (Browser compatible) β 4.8KB.", | ||
"main": "./lib/index.js", | ||
@@ -34,5 +34,4 @@ "module": "./lib/index.mjs", | ||
"build:browser": "tsx tools/browserfy.ts", | ||
"build:cjs": "esbuild src/index.ts --outfile=lib/index.js --platform=node --target=node8 --format=cjs", | ||
"build:esm": "esbuild src/index.ts --outfile=lib/index.mjs --platform=node --target=node12 --format=esm", | ||
"build": "tsc && npm run build:cjs && npm run build:esm && npm run build:browser", | ||
"build": "tsc && npm run build:esm && npm run build:browser", | ||
"test:node": "poku --node -p", | ||
@@ -72,3 +71,2 @@ "test:bun": "poku --bun -p", | ||
"lru", | ||
"mru", | ||
"cache", | ||
@@ -75,0 +73,0 @@ "caching", |
250
README.md
<h1 align="center">lru.min</h1> | ||
<div align="center"> | ||
π₯ Extremely fast <strong>LRU Cache</strong> for <strong>JavaScript</strong> (<strong>Browser</strong> compatible) β **6.1KB**. | ||
π₯ An extremely fast and efficient <strong><a href="https://en.m.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29">LRU</a> Cache</strong> for <strong>JavaScript</strong> (<strong>Browser</strong> compatible) β **4.8KB**. | ||
</div> | ||
> | ||
## Why | ||
@@ -12,15 +14,10 @@ | ||
- **lru.min** ensures is fully compatible with both **Node.js** _(8+)_, **Bun**, **Deno** and, browser environments.<br /> | ||
- **lru.min** ensures is fully compatible with both **Node.js** _(8+)_, **Bun**, **Deno** and, browser environments. | ||
#### π¨π»βπ» Features | ||
ποΈ Performance | ||
- π’ Smart and dynamic **TTL**. | ||
- π§Ή Efficient **disposal** handling. | ||
- β Truly respects the maximum **cache size**. | ||
- π Take full caching control _(highly debuggable)_. | ||
- [Check comparisons between **Node.js**, **Bun** and **Deno**](#performance). | ||
#### ποΈ [Performance](#performance) | ||
> Note that although performance is indeed important, the main purpose behind **lru.min** is the high compatibility level. | ||
- **lru.min** has beaten the two most used and popular **LRU** packages. | ||
--- | ||
@@ -50,3 +47,3 @@ | ||
> - β οΈ Please wait until `v1.x.x` before using this package. | ||
> - π Complete documentation an public repository coming soon. | ||
> - π Public repository coming soon. | ||
@@ -73,39 +70,205 @@ ### Import | ||
### Create a new LRU Cache | ||
> Set maximum size when creating **LRU**. | ||
```ts | ||
import { createLRU } from 'lru.min'; | ||
const LRU = createLRU({ max: 1000 }); | ||
``` | ||
Also, you can set a callback for every deletion: | ||
```ts | ||
const LRU = createLRU({ | ||
max: 1000, | ||
dispose: (key, value) => { | ||
// do something | ||
}, | ||
}); | ||
``` | ||
### Set a cache | ||
Adds a key-value pair to the cache. Updates the value if the key already exists | ||
```ts | ||
LRU.set('key', 'value'); | ||
``` | ||
### Get a cache | ||
Retrieves the value for a given key and moves the key to the most recent position. | ||
```ts | ||
LRU.get('key'); | ||
``` | ||
### Peek a cache | ||
Retrieves the value for a given key without changing its position. | ||
```ts | ||
LRU.get('key'); | ||
``` | ||
### Check if a key exists | ||
```ts | ||
LRU.has('key'); | ||
``` | ||
### Delete a cache | ||
```ts | ||
LRU.delete('key'); | ||
``` | ||
### Evict from the oldest cache | ||
Evicts the specified number of the oldest items from the cache. | ||
```ts | ||
LRU.evict(10); | ||
``` | ||
### Resize the cache | ||
Resizes the cache to a new maximum size, evicting items if necessary. | ||
```ts | ||
LRU.resize(500); | ||
``` | ||
### Clear the cache | ||
Clears and disposes (if used) all key-value pairs from the cache. | ||
```ts | ||
LRU.clear(); | ||
``` | ||
### Debugging | ||
#### Get the max size of the cache | ||
```ts | ||
LRU.max; | ||
``` | ||
#### Get the current size of the cache | ||
```ts | ||
LRU.size; | ||
``` | ||
#### Get the available slots in the cache | ||
```ts | ||
LRU.available; | ||
``` | ||
#### Get all keys | ||
Iterates over all keys in the cache, from least recent to most recent. | ||
```ts | ||
const keys = [...LRU.keys()]; | ||
``` | ||
#### Get all values | ||
Iterates over all values in the cache, from least recent to most recent. | ||
```ts | ||
const keys = [...LRU.values()]; | ||
``` | ||
#### Get all entries | ||
Iterates over `[key, value]` pairs in the cache, from least recent to most recent. | ||
```ts | ||
const entries = [...LRU.entries()]; | ||
``` | ||
#### Get all entries | ||
Iterates over `[key, value]` pairs in the cache, from least recent to most recent. | ||
```ts | ||
const entries = [...LRU.entries()]; | ||
``` | ||
#### Run a callback for each entry | ||
Iterates over each key-value pair in the cache, from most recent to least recent. | ||
```ts | ||
LRU.forEach((key, value) => { | ||
// do something | ||
}); | ||
``` | ||
--- | ||
## Performance | ||
### Performance | ||
In **Bun**, lru.min achieves up to **11** more operations per second than [**lru-cache**](https://github.com/isaacs/node-lru-cache) `v11.x.x` and up to **5** more ops per second than [**quick-lru**](https://github.com/sindresorhus/quick-lru) `v7.x.x` for essential usage (_get_, _set_, _evict_, and _delete_ the cache): | ||
The benchmark is performed by comparing `1,000,000` runs through a maximum cache limit of `100,000`, getting `333,333` caches and delenting `200,000` keys 10 consecutive times, clearing the cache every run. | ||
> You can see how the tests are run and compared in the [benchmark](https://github.com/wellwelwel/lru.min/tree/main/benchmark) directory. | ||
> | ||
> - [**lru-cache**](https://github.com/isaacs/node-lru-cache) `v11.0.0` | ||
> - [**quick-lru**](https://github.com/sindresorhus/quick-lru) `v7.0.0` | ||
#### Node.js | ||
```sh | ||
# Total Number of Cores: 24 (16 performance and 8 efficiency) | ||
# Memory: 64 GB | ||
# ES Modules | ||
lru.min: 247.45ms | ||
lru-cache: 255.93ms | ||
quick-lru: 312.90ms | ||
``` | ||
βΊ Node.js | ||
lru-cache x 24.42 ops/sec β Β±0.76% (45 runs sampled) | ||
quick-lru x 25.62 ops/sec β Β±0.33% (47 runs sampled) | ||
lru.min x 26.28 ops/sec β Β±0.19% (48 runs sampled) | ||
```sh | ||
# CommonJS | ||
lru.min: 236.35ms | ||
lru-cache: 258.74ms | ||
quick-lru: not compatible | ||
``` | ||
π Fastest is lru.min | ||
#### Bun | ||
βΊ Bun | ||
lru-cache x 97.75 ops/sec β Β±0.32% (85 runs sampled) | ||
quick-lru x 105.72 ops/sec β Β±0.33% (79 runs sampled) | ||
lru.min x 110.08 ops/sec β Β±0.34% (82 runs sampled) | ||
```sh | ||
# ES Modules | ||
lru.min: 298.42ms | ||
quick-lru: 315.37ms | ||
lru-cache: 359.23ms | ||
``` | ||
π Fastest is lru.min | ||
```sh | ||
# CommonJS | ||
lru.min: 363.79ms | ||
lru-cache: 371.39ms | ||
quick-lru: not compatible | ||
``` | ||
βΊ Deno | ||
lru-cache x 52.79 ops/sec β Β±0.38% (70 runs sampled) | ||
quick-lru x 54.23 ops/sec β Β±0.34% (71 runs sampled) | ||
lru.min x 56.39 ops/sec β Β±0.33% (74 runs sampled) | ||
#### Deno | ||
π Fastest is lru.min | ||
```sh | ||
# ES Modules | ||
lru.min: 222.60ms | ||
lru-cache: 227.80ms | ||
quick-lru: 253.00ms | ||
``` | ||
- You can see how the tests are run and compared in the [benchmark](https://github.com/wellwelwel/lru.min/tree/main/benchmark) directory. | ||
- **lru.min** is [continuously tested](https://github.com/wellwelwel/lru.min/blob/main/.github/workflows/ci_benchmark.yml) to ensure the above expectations. | ||
[**lru-cache**](https://github.com/isaacs/node-lru-cache) `v11.x.x` | ||
<!-- | ||
--- | ||
## Security Policy | ||
Please check the [**SECURITY.md**](https://github.com/wellwelwel/lru.min/blob/main/SECURITY.md). | ||
--- | ||
@@ -115,4 +278,21 @@ | ||
[![Contributors](https://img.shields.io/github/contributors/wellwelwel/lru.min?label=Contributors)](https://github.com/wellwelwel/lru.min/graphs/contributors) | ||
> **lru.min** is based on the architecture and code of [**lru-cache**](https://github.com/isaacs/node-lru-cache) and [**quick-lru**](https://github.com/sindresorhus/quick-lru), simplifying their core concepts for enhanced performance and compatibility. | ||
> | ||
> For more comprehensive features such as **TTL** support, consider using and supporting them π€ | ||
--> | ||
- [@isaacs](https://github.com/isaacs) and [lru-cache](https://github.com/isaacs/node-lru-cache). | ||
- [@sindresorhus](https://github.com/sindresorhus) and [quick-lru](https://github.com/sindresorhus/quick-lru). | ||
- [![Contributors](https://img.shields.io/github/contributors/wellwelwel/lru.min?label=Contributors)](https://github.com/wellwelwel/lru.min/graphs/contributors) | ||
--- | ||
## License | ||
**lru.min** is under the [**MIT License**](https://github.com/wellwelwel/lru.min/blob/main/LICENSE).<br /> | ||
Copyright Β© 2024-present [Weslley AraΓΊjo](https://github.com/wellwelwel) and **lru.min** [contributors](https://github.com/wellwelwel/lru.min/graphs/contributors). | ||
**lru-cache** is licensed under the [**ISC License**](https://github.com/isaacs/node-lru-cache/blob/main/LICENSE).<br /> | ||
Copyright Β© 2010-2023 Isaac Z. Schlueter and Contributors. | ||
**quick-lru** is licensed under the [**MIT License**](https://github.com/sindresorhus/quick-lru/blob/main/license).<br /> | ||
Copyright Β© Sindre Sorhus. |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
295
23489
360
1