UNPKG

@akashbabu/lfu-cache

Version:

LFU cache implementation with a complexity of `O(1)` for all transactions

2 lines (1 loc) 4.33 kB
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).LFUCache=e()}(this,(function(){"use strict";class t{constructor(t,e=null,s=null){this.data=t,this.prev=e,this.next=s}}class e{grantAccess(t){return t.__dllItem__}revokeAccess(t){return t?new s(t):null}}class s{constructor(t){this.dllItem=t,this.dllItemAccessRestrictor=new e,this.__dllItem__=t}get data(){return this.dllItem.data}set data(t){this.dllItem.data=t}get prev(){return this.dllItemAccessRestrictor.revokeAccess(this.dllItem.prev)}get next(){return this.dllItemAccessRestrictor.revokeAccess(this.dllItem.next)}}class a{constructor(){this.state=this.getFreshState(),this.dllItemAccessRestrictor=new e}get head(){return this.dllItemAccessRestrictor.revokeAccess(this.state.head)}get tail(){return this.dllItemAccessRestrictor.revokeAccess(this.state.tail)}get length(){return this.state.length}shift(){let e=this.state.head;if(!(e instanceof t))return;this.remove(e);const s=e.data;return e=null,s}unshift(e){const s=this.state.head,a=new t(e,null,s);this.state.head=a,s instanceof t?s.prev=a:this.state.tail=a,this.state.length++}forEach(t){this.iterate((e,s)=>{t(e.data,s)})}map(t){const e=[];return this.forEach((s,a)=>{e.push(t(s,a))}),e}push(t){return this.appendAfter(this.state.tail,t)}appendAfter(e,a){let i;if(null===e&&this.state.length>0)throw Error("Invalid Node `null`: DLL is not empty, hence can't append to the given node");i=e instanceof s?this.dllItemAccessRestrictor.grantAccess(e):e;const r=new t(a);return null===i?this.state.head=this.state.tail=r:(r.prev=i,r.next=i.next,i.next=r,i===this.state.tail&&(this.state.tail=r)),this.state.length++,this.dllItemAccessRestrictor.revokeAccess(r)}remove(e){let a;if(e instanceof s)a=this.dllItemAccessRestrictor.grantAccess(e);else{if(!(e instanceof t))return!1;a=e}return a.prev?a.prev.next=a.next:this.state.head=a.next,a.next?a.next.prev=a.prev:this.state.tail=a.prev,this.state.length--,!0}clear(){this.iterate(t=>{t.prev=t.next=null}),this.state=this.getFreshState()}getFreshState(){return{length:0,head:null,tail:null}}iterate(t){let e=this.state.head,s=0;for(;e;)t(e,s++),e=e.next}}class i{constructor(t={}){this.state=this.getFreshState(),t.max&&!t.evictCount&&(t.evictCount=Math.max(1,.1*t.max)),this.options={max:t.max||100,evictCount:t.evictCount||10,maxAge:t.maxAge}}get size(){return this.state.size}set(t,e,s){const a=this.state.byKey.get(t);if(a)return s?(a.data.value=e,e):a.data.value;this.state.size++;const i=this.addToFreqList(t),r=this.state.nodeList.push({key:t,value:e,utime:Date.now(),parent:i});return this.state.byKey.set(t,r),this.state.size>this.options.max&&this.evict(this.options.evictCount),e}get(t){const e=this.state.byKey.get(t);if(!e)return;if(this.options.maxAge&&e.data.utime+this.options.maxAge<Date.now())return void this.delete(t);const s=e.data.parent;let a=s.next;return a&&a.data.value===s.data.value+1?a.data.items.add(t):a=this.state.freqList.appendAfter(s,{value:s.data.value+1,items:new Set([t])}),this.removeKeyFromFreqItem(s,t),e.data.parent=a,e.data.utime=Date.now(),e.data.value}delete(t){const e=this.state.byKey.get(t);if(e){return this.removeKeyFromFreqItem(e.data.parent,t),this.state.nodeList.remove(e),this.state.byKey.delete(t),this.state.size--,!0}return!1}peek(t){return this.state.byKey.get(t)?this.state.byKey.get(t).data.value:void 0}forEach(t){this.state.nodeList.forEach((e,s)=>{t([e.key,e.value],s)})}map(t){const e=[];return this.forEach((s,a)=>{e.push(t(s,a))}),e}clear(){this.state=this.getFreshState()}dangerously_getState(){return this.state}getFreshState(){return{freqList:new a,nodeList:new a,byKey:new Map,size:0}}removeKeyFromFreqItem(t,e){t.data.items.delete(e),0===t.data.items.size&&this.state.freqList.remove(t)}evict(t){for(;t--;){const t=this.state.freqList.head;if(t){const e=t.data.items.keys().next().value;this.delete(e)}}}addToFreqList(t){const e=this.state.freqList.head;return e&&1===e.data.value?e.data.items.add(t):this.state.freqList.unshift({value:1,items:new Set([t])}),this.state.freqList.head}}if(require.main===module){const t=3,e=new i({max:t});Array(t).fill(0).forEach((t,s)=>{e.set("foo_"+(s+1),"bar_"+(s+1))}),e.get("foo_1"),e.set("foo_1","bar_1_2",!0),e.set("foo_4","bar_4"),e.set("foo_5","bar_5")}return i}));