lru
Advanced tools
Comparing version 2.0.0 to 2.0.1
var LRU = require('../') | ||
var cache = new LRU(2); | ||
var cache = new LRU(2) | ||
var evicted | ||
cache.on('evict',function(data) { evicted = data }) | ||
cache.on('evict', function (data) { | ||
evicted = data | ||
}) | ||
cache.set('foo', 'bar'); // => 'bar' | ||
cache.get('foo'); // => 'bar' | ||
cache.set('foo', 'bar') // => 'bar' | ||
cache.get('foo') // => 'bar' | ||
cache.set('foo2', 'bar2'); // => 'bar2' | ||
cache.get('foo2'); // => 'bar2' | ||
cache.set('foo2', 'bar2') // => 'bar2' | ||
cache.get('foo2') // => 'bar2' | ||
cache.set('foo3', 'bar3'); // => 'bar3' | ||
cache.get('foo3'); // => 'bar3' | ||
cache.set('foo3', 'bar3') // => 'bar3' | ||
cache.get('foo3') // => 'bar3' | ||
@@ -18,0 +20,0 @@ console.log(cache.remove('foo2')) // => { key: 'foo2', value: 'bar2' } |
186
index.js
@@ -1,41 +0,47 @@ | ||
var events = require('events'); | ||
var util = require('util'); | ||
var events = require('events') | ||
var inherits = require('inherits') | ||
var LRU = module.exports = function (opts) { | ||
if (!(this instanceof LRU)) return new LRU(opts); | ||
if (typeof opts === 'number') opts = {max: opts}; | ||
if (!opts) opts = {}; | ||
events.EventEmitter.call(this); | ||
this.cache = {} | ||
this.head = this.tail = null; | ||
this.length = 0; | ||
this.max = opts.max || 1000; | ||
this.maxAge = opts.maxAge || 0; | ||
}; | ||
util.inherits(LRU, events.EventEmitter); | ||
module.exports = LRU | ||
function LRU (opts) { | ||
if (!(this instanceof LRU)) return new LRU(opts) | ||
if (typeof opts === 'number') opts = {max: opts} | ||
if (!opts) opts = {} | ||
events.EventEmitter.call(this) | ||
this.cache = {} | ||
this.head = this.tail = null | ||
this.length = 0 | ||
this.max = opts.max || 1000 | ||
this.maxAge = opts.maxAge || 0 | ||
} | ||
inherits(LRU, events.EventEmitter) | ||
LRU.prototype.remove = function (key) { | ||
if (!this.cache.hasOwnProperty(key)) return; | ||
if (typeof key !== 'string') key = '' + key | ||
if (!this.cache.hasOwnProperty(key)) return | ||
var element = this.cache[key]; | ||
delete this.cache[key]; | ||
var element = this.cache[key] | ||
delete this.cache[key] | ||
this._unlink(key, element.prev, element.next) | ||
return element.value | ||
} | ||
--this.length; | ||
LRU.prototype._unlink = function (key, prev, next) { | ||
this.length-- | ||
if (this.length === 0) { | ||
this.head = this.tail = null; | ||
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 { | ||
if (this.head == key) { | ||
this.head = element.prev; | ||
this.cache[this.head].next = null; | ||
} else if (this.tail == key) { | ||
this.tail = element.next; | ||
this.cache[this.tail].prev = null; | ||
} else { | ||
this.cache[element.prev].next = element.next; | ||
this.cache[element.next].prev = element.prev; | ||
} | ||
this.cache[prev].next = next | ||
this.cache[next].prev = prev | ||
} | ||
return element.value; | ||
} | ||
} | ||
@@ -48,77 +54,73 @@ | ||
LRU.prototype.set = function (key, value) { | ||
var element; | ||
if( this.cache.hasOwnProperty(key) ) { | ||
element = this.cache[key] | ||
element.value = value | ||
if (this.maxAge) element.modified = Date.now(); | ||
if (typeof key !== 'string') key = '' + key | ||
// If it's already the head, there's nothing more to do: | ||
if( key === this.head ) { | ||
return value; | ||
} | ||
} else { | ||
element = { value:value }; | ||
if (this.maxAge) element.modified = Date.now(); | ||
var element | ||
this.cache[key] = element; | ||
if (this.cache.hasOwnProperty(key)) { | ||
element = this.cache[key] | ||
element.value = value | ||
if (this.maxAge) element.modified = Date.now() | ||
// Eviction is only possible if the key didn't already exist: | ||
if (this.length === this.max) { | ||
this.evict(); | ||
} | ||
++this.length; | ||
} | ||
// 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 | ||
element.next = null; | ||
element.prev = this.head; | ||
// Eviction is only possible if the key didn't already exist: | ||
if (this.length === this.max) this.evict() | ||
} | ||
if (this.head) { | ||
this.cache[this.head].next = key; | ||
} | ||
this.head = key; | ||
this.length++ | ||
element.next = null | ||
element.prev = this.head | ||
if(!this.tail) { | ||
this.tail = key; | ||
} | ||
if (this.head) this.cache[this.head].next = key | ||
this.head = key | ||
return value | ||
}; | ||
if (!this.tail) this.tail = key | ||
return value | ||
} | ||
LRU.prototype.get = function (key) { | ||
if (!this.cache.hasOwnProperty(key)) return; | ||
var element = this.cache[key]; | ||
if (typeof key !== 'string') key = '' + key | ||
if (!this.cache.hasOwnProperty(key)) return | ||
if (this.maxAge && (Date.now() - element.modified) > this.maxAge) { | ||
this.remove(key); | ||
this.emit('evict', {key:key, value:element.value}); | ||
return; | ||
var element = this.cache[key] | ||
if (this.maxAge && (Date.now() - element.modified) > this.maxAge) { | ||
this.remove(key) | ||
this.emit('evict', {key: key, value: element.value}) | ||
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 | ||
} | ||
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 | ||
// 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 | ||
} | ||
// Element is the new head | ||
this.cache[this.head].next = key; | ||
element.prev = this.head; | ||
element.next = null; | ||
this.head = key; | ||
} | ||
return element.value | ||
} | ||
return element.value; | ||
}; | ||
LRU.prototype.evict = function () { | ||
if(!this.tail) { return; } | ||
var key = this.tail; | ||
var removedValue = this.remove(this.tail); | ||
this.emit('evict', {key:key, value:removedValue}); | ||
}; | ||
if (!this.tail) return | ||
var key = this.tail | ||
var value = this.remove(this.tail) | ||
this.emit('evict', {key: key, value: value}) | ||
} |
{ | ||
"name": "lru", | ||
"description": "A simple O(1) LRU cache", | ||
"version": "2.0.0", | ||
"version": "2.0.1", | ||
"author": "Chris O'Hara <cohara87@gmail.com>", | ||
@@ -20,8 +20,11 @@ "main": "index", | ||
"devDependencies": { | ||
"standard": "^6.0.8", | ||
"vows": "^0.8.1" | ||
}, | ||
"scripts": { | ||
"test": "vows test/*.js --spec" | ||
"test": "standard && vows test/*.js --spec" | ||
}, | ||
"dependencies": {} | ||
"dependencies": { | ||
"inherits": "^2.0.1" | ||
} | ||
} |
@@ -1,55 +0,55 @@ | ||
var assert = require('assert'); | ||
var vows = require('vows'); | ||
var LRU = require('../'); | ||
var assert = require('assert') | ||
var vows = require('vows') | ||
var LRU = require('../') | ||
function keys(obj) { | ||
var result = []; | ||
for(k in obj) { | ||
if(obj.hasOwnProperty(k)) { | ||
result.push(k); | ||
function keys (obj) { | ||
var result = [] | ||
for (var k in obj) { | ||
if (obj.hasOwnProperty(k)) { | ||
result.push(k) | ||
} | ||
} | ||
return result; | ||
return result | ||
} | ||
var suite = vows.describe('LRU'); | ||
var suite = vows.describe('LRU') | ||
suite.addBatch({ | ||
"setting keys doesn't grow past max size": function() { | ||
var lru = new LRU(3); | ||
assert.equal(0, lru.length); | ||
lru.set('foo1', 'bar1'); | ||
assert.equal(1, lru.length); | ||
lru.set('foo2', 'bar2'); | ||
assert.equal(2, lru.length); | ||
lru.set('foo3', 'bar3'); | ||
assert.equal(3, lru.length); | ||
'setting keys doesn\'t grow past max size': function () { | ||
var lru = new LRU(3) | ||
assert.equal(0, lru.length) | ||
lru.set('foo1', 'bar1') | ||
assert.equal(1, lru.length) | ||
lru.set('foo2', 'bar2') | ||
assert.equal(2, lru.length) | ||
lru.set('foo3', 'bar3') | ||
assert.equal(3, lru.length) | ||
lru.set('foo4', 'bar4'); | ||
assert.equal(3, lru.length); | ||
lru.set('foo4', 'bar4') | ||
assert.equal(3, lru.length) | ||
} | ||
}); | ||
}) | ||
suite.addBatch({ | ||
"setting keys returns the value": function() { | ||
var lru = new LRU(2); | ||
assert.equal('bar1', lru.set('foo1', 'bar1')); | ||
assert.equal('bar2', lru.set('foo2', 'bar2')); | ||
assert.equal('bar3', lru.set('foo3', 'bar3')); | ||
assert.equal('bar2', lru.get('foo2')); | ||
assert.equal(undefined, lru.get('foo1')); | ||
assert.equal('bar1', lru.set('foo1','bar1')); | ||
'setting keys returns the value': function () { | ||
var lru = new LRU(2) | ||
assert.equal('bar1', lru.set('foo1', 'bar1')) | ||
assert.equal('bar2', lru.set('foo2', 'bar2')) | ||
assert.equal('bar3', lru.set('foo3', 'bar3')) | ||
assert.equal('bar2', lru.get('foo2')) | ||
assert.equal(undefined, lru.get('foo1')) | ||
assert.equal('bar1', lru.set('foo1', 'bar1')) | ||
} | ||
}); | ||
}) | ||
suite.addBatch({ | ||
"lru invariant is maintained for set()": function() { | ||
var lru = new LRU(2); | ||
'lru invariant is maintained for set()': function () { | ||
var lru = new LRU(2) | ||
lru.set('foo1', 'bar1'); | ||
lru.set('foo2', 'bar2'); | ||
lru.set('foo3', 'bar3'); | ||
lru.set('foo4', 'bar4'); | ||
lru.set('foo1', 'bar1') | ||
lru.set('foo2', 'bar2') | ||
lru.set('foo3', 'bar3') | ||
lru.set('foo4', 'bar4') | ||
assert.deepEqual(['foo3','foo4'], keys(lru.cache)); | ||
assert.deepEqual(['foo3', 'foo4'], keys(lru.cache)) | ||
} | ||
@@ -59,7 +59,7 @@ }) | ||
suite.addBatch({ | ||
"ovrewriting a key updates the value": function() { | ||
var lru = new LRU(2); | ||
lru.set('foo1', 'bar1'); | ||
'ovrewriting a key updates the value': function () { | ||
var lru = new LRU(2) | ||
lru.set('foo1', 'bar1') | ||
assert.equal('bar1', lru.get('foo1')) | ||
lru.set('foo1', 'bar2'); | ||
lru.set('foo1', 'bar2') | ||
assert.equal('bar2', lru.get('foo1')) | ||
@@ -70,63 +70,63 @@ } | ||
suite.addBatch({ | ||
"lru invariant is maintained for get()": function() { | ||
var lru = new LRU(2); | ||
'lru invariant is maintained for get()': function () { | ||
var lru = new LRU(2) | ||
lru.set('foo1', 'bar1'); | ||
lru.set('foo2', 'bar2'); | ||
lru.set('foo1', 'bar1') | ||
lru.set('foo2', 'bar2') | ||
lru.get('foo1'); //now foo2 should be deleted instead of foo1 | ||
lru.get('foo1') // now foo2 should be deleted instead of foo1 | ||
lru.set('foo3', 'bar3'); | ||
lru.set('foo3', 'bar3') | ||
assert.deepEqual(['foo1','foo3'], keys(lru.cache)); | ||
assert.deepEqual(['foo1', 'foo3'], keys(lru.cache)) | ||
}, | ||
"lru invariant is maintained after set(), get() and remove()": function() { | ||
var lru = new LRU(2); | ||
lru.set('a', 1); | ||
lru.set('b', 2); | ||
assert.deepEqual(lru.get('a'), 1); | ||
lru.remove('a'); | ||
lru.set('c', 1); | ||
lru.set('d', 1); | ||
assert.deepEqual(['c','d'], keys(lru.cache)); | ||
'lru invariant is maintained after set(), get() and remove()': function () { | ||
var lru = new LRU(2) | ||
lru.set('a', 1) | ||
lru.set('b', 2) | ||
assert.deepEqual(lru.get('a'), 1) | ||
lru.remove('a') | ||
lru.set('c', 1) | ||
lru.set('d', 1) | ||
assert.deepEqual(['c', 'd'], keys(lru.cache)) | ||
} | ||
}); | ||
}) | ||
suite.addBatch({ | ||
"lru invariant is maintained for get()": function() { | ||
var lru = new LRU(2); | ||
'lru invariant is maintained for get()': function () { | ||
var lru = new LRU(2) | ||
lru.set('foo1', 'bar1'); | ||
lru.set('foo2', 'bar2'); | ||
lru.set('foo1', 'bar1') | ||
lru.set('foo2', 'bar2') | ||
lru.get('foo2'); //now foo2 should be deleted instead of foo1 | ||
lru.get('foo2') // now foo2 should be deleted instead of foo1 | ||
lru.set('foo3', 'bar3'); | ||
lru.set('foo3', 'bar3') | ||
assert.deepEqual(['foo2','foo3'], keys(lru.cache)); | ||
assert.deepEqual(['foo2', 'foo3'], keys(lru.cache)) | ||
}, | ||
"lru invariant is maintained in the corner case size == 1": function() { | ||
var lru = new LRU(1); | ||
'lru invariant is maintained in the corner case size == 1': function () { | ||
var lru = new LRU(1) | ||
lru.set('foo1', 'bar1'); | ||
lru.set('foo2', 'bar2'); | ||
lru.set('foo1', 'bar1') | ||
lru.set('foo2', 'bar2') | ||
lru.get('foo2'); //now foo2 should be deleted instead of foo1 | ||
lru.get('foo2') // now foo2 should be deleted instead of foo1 | ||
lru.set('foo3', 'bar3'); | ||
lru.set('foo3', 'bar3') | ||
assert.deepEqual(['foo3'], keys(lru.cache)); | ||
assert.deepEqual(['foo3'], keys(lru.cache)) | ||
} | ||
}); | ||
}) | ||
suite.addBatch({ | ||
"get() returns item value": function() { | ||
'get() returns item value': function () { | ||
var lru = new LRU(2) | ||
assert.equal( lru.set('foo','bar'), 'bar') | ||
assert.equal(lru.set('foo', 'bar'), 'bar') | ||
} | ||
}); | ||
}) | ||
suite.addBatch({ | ||
"peek() returns item value without changing the order": function() { | ||
'peek() returns item value without changing the order': function () { | ||
var lru = new LRU(2) | ||
@@ -139,117 +139,117 @@ lru.set('foo', 'bar') | ||
} | ||
}); | ||
}) | ||
suite.addBatch({ | ||
"evicting items by age": { | ||
'evicting items by age': { | ||
topic: function () { | ||
var lru = new LRU({maxAge: 5}); | ||
lru.set('foo', 'bar'); | ||
assert.equal(lru.get('foo'), 'bar'); | ||
var lru = new LRU({maxAge: 5}) | ||
lru.set('foo', 'bar') | ||
assert.equal(lru.get('foo'), 'bar') | ||
var callback = this.callback | ||
setTimeout(function () { | ||
callback(null, lru); | ||
}, 100); | ||
callback(null, lru) | ||
}, 100) | ||
}, | ||
"the entry is removed if age > max_age": function(lru) { | ||
assert.equal(lru.get('foo'), null); | ||
}, | ||
'the entry is removed if age > max_age': function (lru) { | ||
assert.equal(lru.get('foo'), null) | ||
} | ||
}, | ||
"evicting items by age (2)": { | ||
'evicting items by age (2)': { | ||
topic: function () { | ||
var lru = new LRU({maxAge: 100000}); | ||
lru.set('foo', 'bar'); | ||
assert.equal(lru.get('foo'), 'bar'); | ||
var lru = new LRU({maxAge: 100000}) | ||
lru.set('foo', 'bar') | ||
assert.equal(lru.get('foo'), 'bar') | ||
var callback = this.callback | ||
setTimeout(function () { | ||
callback(null, lru); | ||
}, 100); | ||
callback(null, lru) | ||
}, 100) | ||
}, | ||
"the entry is not removed if age < max_age": function(lru) { | ||
assert.equal(lru.get('foo'), 'bar'); | ||
'the entry is not removed if age < max_age': function (lru) { | ||
assert.equal(lru.get('foo'), 'bar') | ||
} | ||
} | ||
}); | ||
}) | ||
suite.addBatch({ | ||
"idempotent 'changes'": { | ||
"set() and remove() on empty LRU is idempotent": function() { | ||
var lru = new LRU(); | ||
var json1 = JSON.stringify(lru); | ||
'idempotent changes': { | ||
'set() and remove() on empty LRU is idempotent': function () { | ||
var lru = new LRU() | ||
var json1 = JSON.stringify(lru) | ||
lru.set('foo1', 'bar1'); | ||
lru.remove('foo1'); | ||
var json2 = JSON.stringify(lru); | ||
lru.set('foo1', 'bar1') | ||
lru.remove('foo1') | ||
var json2 = JSON.stringify(lru) | ||
assert.deepEqual(json2, json1); | ||
assert.deepEqual(json2, json1) | ||
}, | ||
"2 set()s and 2 remove()s on empty LRU is idempotent": function() { | ||
var lru = new LRU(); | ||
var json1 = JSON.stringify(lru); | ||
'2 set()s and 2 remove()s on empty LRU is idempotent': function () { | ||
var lru = new LRU() | ||
var json1 = JSON.stringify(lru) | ||
lru.set('foo1', 'bar1'); | ||
lru.set('foo2', 'bar2'); | ||
lru.remove('foo1'); | ||
lru.remove('foo2'); | ||
var json2 = JSON.stringify(lru); | ||
lru.set('foo1', 'bar1') | ||
lru.set('foo2', 'bar2') | ||
lru.remove('foo1') | ||
lru.remove('foo2') | ||
var json2 = JSON.stringify(lru) | ||
assert.deepEqual(json2, json1); | ||
assert.deepEqual(json2, json1) | ||
}, | ||
"2 set()s and 2 remove()s (in opposite order) on empty LRU is idempotent": function() { | ||
var lru = new LRU(); | ||
var json1 = JSON.stringify(lru); | ||
'2 set()s and 2 remove()s (in opposite order) on empty LRU is idempotent': function () { | ||
var lru = new LRU() | ||
var json1 = JSON.stringify(lru) | ||
lru.set('foo1', 'bar1'); | ||
lru.set('foo2', 'bar2'); | ||
lru.remove('foo2'); | ||
lru.remove('foo1'); | ||
var json2 = JSON.stringify(lru); | ||
lru.set('foo1', 'bar1') | ||
lru.set('foo2', 'bar2') | ||
lru.remove('foo2') | ||
lru.remove('foo1') | ||
var json2 = JSON.stringify(lru) | ||
assert.deepEqual(json2, json1); | ||
assert.deepEqual(json2, json1) | ||
}, | ||
"after setting one key, get() is idempotent" : function() { | ||
var lru = new LRU(2); | ||
lru.set('a', 'a'); | ||
var json1 = JSON.stringify(lru); | ||
'after setting one key, get() is idempotent': function () { | ||
var lru = new LRU(2) | ||
lru.set('a', 'a') | ||
var json1 = JSON.stringify(lru) | ||
lru.get('a'); | ||
var json2 = JSON.stringify(lru); | ||
lru.get('a') | ||
var json2 = JSON.stringify(lru) | ||
assert.equal(json2, json1); | ||
assert.equal(json2, json1) | ||
}, | ||
"after setting two keys, get() on last-set key is idempotent" : function() { | ||
var lru = new LRU(2); | ||
lru.set('a', 'a'); | ||
lru.set('b', 'b'); | ||
var json1 = JSON.stringify(lru); | ||
'after setting two keys, get() on last-set key is idempotent': function () { | ||
var lru = new LRU(2) | ||
lru.set('a', 'a') | ||
lru.set('b', 'b') | ||
var json1 = JSON.stringify(lru) | ||
lru.get('b'); | ||
var json2 = JSON.stringify(lru); | ||
lru.get('b') | ||
var json2 = JSON.stringify(lru) | ||
assert.equal(json2, json1); | ||
assert.equal(json2, json1) | ||
} | ||
} | ||
}); | ||
}) | ||
suite.addBatch({ | ||
"evict event": { | ||
"'evict' event is fired when evicting old keys": function() { | ||
var lru = new LRU(2); | ||
var events = []; | ||
lru.on('evict', function(element) { events.push(element); }); | ||
'evict event': { | ||
'\'evict\' event is fired when evicting old keys': function () { | ||
var lru = new LRU(2) | ||
var events = [] | ||
lru.on('evict', function (element) { events.push(element) }) | ||
lru.set('foo1', 'bar1'); | ||
lru.set('foo2', 'bar2'); | ||
lru.set('foo3', 'bar3'); | ||
lru.set('foo4', 'bar4'); | ||
lru.set('foo1', 'bar1') | ||
lru.set('foo2', 'bar2') | ||
lru.set('foo3', 'bar3') | ||
lru.set('foo4', 'bar4') | ||
var expect = [{key:'foo1', value:'bar1'}, {key:'foo2', value:'bar2'}]; | ||
assert.deepEqual(events, expect); | ||
var expect = [{key: 'foo1', value: 'bar1'}, {key: 'foo2', value: 'bar2'}] | ||
assert.deepEqual(events, expect) | ||
} | ||
} | ||
}); | ||
}) | ||
suite.export(module); | ||
suite.export(module) |
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
323
13534
1
2
2
+ Addedinherits@^2.0.1
+ Addedinherits@2.0.4(transitive)