UNPKG

choo-shortcache

Version:

choo nanocomponent cache shortcut

137 lines (109 loc) 3.25 kB
module.exports = LRU function LRU (opts) { if (!(this instanceof LRU)) return new LRU(opts) if (typeof opts === 'number') opts = {max: opts} if (!opts) opts = {} this.cache = {} this.head = this.tail = null this.length = 0 this.max = opts.max || 1000 this.maxAge = opts.maxAge || 0 } Object.defineProperty(LRU.prototype, 'keys', { get: function () { return Object.keys(this.cache) } }) LRU.prototype.clear = function () { this.cache = {} this.head = this.tail = null this.length = 0 } LRU.prototype.remove = function (key) { if (typeof key !== 'string') key = '' + key if (!this.cache.hasOwnProperty(key)) return var element = this.cache[key] delete this.cache[key] this._unlink(key, element.prev, element.next) return element.value } LRU.prototype._unlink = function (key, prev, next) { this.length-- if (this.length === 0) { this.head = this.tail = null } else { if (this.head === key) { this.head = prev this.cache[this.head].next = null } else if (this.tail === key) { this.tail = next this.cache[this.tail].prev = null } else { this.cache[prev].next = next this.cache[next].prev = prev } } } LRU.prototype.peek = function (key) { if (!this.cache.hasOwnProperty(key)) return var element = this.cache[key] if (!this._checkAge(key, element)) return return element.value } LRU.prototype.set = function (key, value) { if (typeof key !== 'string') key = '' + key var element if (this.cache.hasOwnProperty(key)) { element = this.cache[key] element.value = value if (this.maxAge) element.modified = Date.now() // If it's already the head, there's nothing more to do: if (key === this.head) return value this._unlink(key, element.prev, element.next) } else { element = {value: value, modified: 0, next: null, prev: null} if (this.maxAge) element.modified = Date.now() this.cache[key] = element // Eviction is only possible if the key didn't already exist: if (this.length === this.max) this.evict() } this.length++ element.next = null element.prev = this.head if (this.head) this.cache[this.head].next = key this.head = key if (!this.tail) this.tail = key return value } LRU.prototype._checkAge = function (key, element) { if (this.maxAge && (Date.now() - element.modified) > this.maxAge) { this.remove(key) return false } return true } LRU.prototype.get = function (key) { if (typeof key !== 'string') key = '' + key if (!this.cache.hasOwnProperty(key)) return var element = this.cache[key] if (!this._checkAge(key, element)) return if (this.head !== key) { if (key === this.tail) { this.tail = element.next this.cache[this.tail].prev = null } else { // Set prev.next -> element.next: this.cache[element.prev].next = element.next } // Set element.next.prev -> element.prev: this.cache[element.next].prev = element.prev // Element is the new head this.cache[this.head].next = key element.prev = this.head element.next = null this.head = key } return element.value } LRU.prototype.evict = function () { if (!this.tail) return this.remove(this.tail) }