289 lines
5.5 KiB
JavaScript
289 lines
5.5 KiB
JavaScript
/**
|
|
* lru.js - LRU cache for bcoin
|
|
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
|
* https://github.com/indutny/bcoin
|
|
*/
|
|
|
|
var bcoin = require('../bcoin');
|
|
var utils = bcoin.utils;
|
|
var assert = utils.assert;
|
|
|
|
/**
|
|
* LRU
|
|
*/
|
|
|
|
function LRU(maxSize, getSize) {
|
|
if (!(this instanceof LRU))
|
|
return new LRU(maxSize, getSize);
|
|
|
|
this.data = {};
|
|
this.size = 0;
|
|
this.maxSize = maxSize;
|
|
this.getSize = getSize;
|
|
|
|
this.head = null;
|
|
this.tail = null;
|
|
}
|
|
|
|
LRU.prototype._getSize = function _getSize(item) {
|
|
var keySize = item.key.length * 2;
|
|
|
|
if (this.getSize)
|
|
return this.getSize(item.key, item.value);
|
|
|
|
if (item.value == null)
|
|
return keySize + 1;
|
|
|
|
if (typeof item.value === 'number')
|
|
return keySize + 4;
|
|
|
|
if (item.value._raw)
|
|
return keySize + item.value._raw.length;
|
|
|
|
if (item.value.getSize)
|
|
return keySize + item.value.getSize();
|
|
|
|
if (typeof item.value._size === 'number')
|
|
return keySize + item.value._size;
|
|
|
|
if (typeof item.value === 'string')
|
|
return keySize + item.value.length * 2;
|
|
|
|
if (typeof item.value.length === 'number')
|
|
return keySize + item.value.length;
|
|
|
|
return keySize + 1;
|
|
};
|
|
|
|
LRU.prototype._compact = function _compact() {
|
|
var item, next;
|
|
|
|
if (this.size <= this.maxSize)
|
|
return;
|
|
|
|
for (item = this.head; item; item = next) {
|
|
if (this.size <= this.maxSize)
|
|
break;
|
|
this.size -= this._getSize(item);
|
|
delete this.data[item.key];
|
|
next = item.next;
|
|
item.prev = null;
|
|
item.next = null;
|
|
}
|
|
|
|
if (!item) {
|
|
this.head = null;
|
|
this.tail = null;
|
|
return;
|
|
}
|
|
|
|
this.head = item;
|
|
item.prev = null;
|
|
};
|
|
|
|
LRU.prototype.set = function set(key, value) {
|
|
var item;
|
|
|
|
key = key + '';
|
|
|
|
item = this.data[key];
|
|
|
|
if (item) {
|
|
this.size -= this._getSize(item);
|
|
item.value = value;
|
|
this.size += this._getSize(item);
|
|
this._removeList(item);
|
|
this._appendList(item);
|
|
this._compact();
|
|
return;
|
|
}
|
|
|
|
item = { key: key, value: value };
|
|
|
|
this.data[key] = item;
|
|
|
|
this._appendList(item);
|
|
|
|
this.size += this._getSize(item);
|
|
|
|
this._compact();
|
|
};
|
|
|
|
LRU.prototype.get = function get(key) {
|
|
var item;
|
|
|
|
key = key + '';
|
|
|
|
item = this.data[key];
|
|
|
|
if (!item)
|
|
return;
|
|
|
|
this._removeList(item);
|
|
this._appendList(item);
|
|
|
|
return item.value;
|
|
};
|
|
|
|
LRU.prototype.has = function get(key) {
|
|
return this.data[key] != null;
|
|
};
|
|
|
|
LRU.prototype.remove = function remove(key) {
|
|
var item;
|
|
|
|
key = key + '';
|
|
|
|
item = this.data[key];
|
|
|
|
if (!item)
|
|
return false;
|
|
|
|
this.size -= this._getSize(item);
|
|
|
|
delete this.data[key];
|
|
|
|
this._removeList(item);
|
|
|
|
return true;
|
|
};
|
|
|
|
LRU.prototype._prependList = function prependList(item) {
|
|
this._insertList(null, item);
|
|
};
|
|
|
|
LRU.prototype._appendList = function appendList(item) {
|
|
this._insertList(this.tail, item);
|
|
};
|
|
|
|
LRU.prototype._insertList = function insertList(ref, item) {
|
|
assert(!item.next);
|
|
assert(!item.prev);
|
|
|
|
if (ref == null) {
|
|
if (!this.head) {
|
|
this.head = item;
|
|
this.tail = item;
|
|
} else {
|
|
this.head.prev = item;
|
|
item.next = this.head;
|
|
this.head = item;
|
|
}
|
|
return;
|
|
}
|
|
|
|
item.next = ref.next;
|
|
item.prev = ref;
|
|
ref.next = item;
|
|
|
|
if (ref === this.tail)
|
|
this.tail = item;
|
|
};
|
|
|
|
LRU.prototype._removeList = function removeList(item) {
|
|
if (item.prev)
|
|
item.prev.next = item.next;
|
|
|
|
if (item.next)
|
|
item.next.prev = item.prev;
|
|
|
|
if (item === this.head)
|
|
this.head = item.next;
|
|
|
|
if (item == this.tail)
|
|
this.tail = item.prev || this.head;
|
|
|
|
if (!this.head)
|
|
assert(!this.tail);
|
|
|
|
if (!this.tail)
|
|
assert(!this.head);
|
|
|
|
item.prev = null;
|
|
item.next = null;
|
|
};
|
|
|
|
LRU.prototype._keys = function _keys() {
|
|
var keys = [];
|
|
var item;
|
|
|
|
for (item = this.head; item; item = item.next) {
|
|
if (item === this.head)
|
|
assert(!item.prev);
|
|
if (!item.prev)
|
|
assert(item === this.head);
|
|
if (!item.next)
|
|
assert(item === this.tail);
|
|
keys.push(item.key);
|
|
}
|
|
|
|
return keys;
|
|
};
|
|
|
|
var a1 = '1';
|
|
var a2 = '2';
|
|
var a3 = '3';
|
|
var a4 = '4';
|
|
var a5 = '5';
|
|
var a6 = '6';
|
|
var lru = new LRU(4, function() { return 1; });
|
|
lru.set('a1', a1);
|
|
assert(lru.get('a1') === '1');
|
|
assert(lru.size === 1);
|
|
assert(lru.head.key === 'a1' && lru.tail.key === 'a1' && !lru.head.prev && !lru.head.next);
|
|
lru._keys();
|
|
// console.log(lru._keys());
|
|
lru.set('a2', a2);
|
|
assert(lru.get('a2') === '2');
|
|
assert(lru.size === 2);
|
|
assert(lru.head.key === 'a1' && lru.tail.key === 'a2'
|
|
&& !lru.head.prev && lru.head.next.key === 'a2'
|
|
&& !lru.tail.next && lru.tail.prev.key === 'a1');
|
|
lru._keys();
|
|
// console.log(lru._keys());
|
|
lru.set('a3', a3);
|
|
assert(lru.get('a3') === '3');
|
|
assert(lru.size === 3);
|
|
lru._keys();
|
|
// console.log(lru._keys());
|
|
lru.set('a3', a3);
|
|
assert(lru.get('a3') === '3');
|
|
assert(lru.size === 3);
|
|
lru._keys();
|
|
// console.log(lru._keys());
|
|
lru.set('a4', a4);
|
|
assert(lru.get('a4') === '4');
|
|
assert(lru.size === 4);
|
|
lru._keys();
|
|
// console.log(lru._keys());
|
|
assert(lru.get('a1'));
|
|
lru.remove('a1');
|
|
lru._keys();
|
|
// console.log(lru._keys());
|
|
var _a3 = lru.head.next;
|
|
assert(_a3.key === 'a3');
|
|
assert(_a3.prev === lru.head && _a3.next === lru.tail);
|
|
assert(lru.head.key === 'a2' && lru.tail.key === 'a4'
|
|
&& !lru.head.prev && lru.head.next.key === 'a3'
|
|
&& !lru.tail.next && lru.tail.prev.key === 'a3');
|
|
lru.set('a5', a5);
|
|
assert(lru.get('a5') === '5');
|
|
assert(lru.size === 4);
|
|
assert(!lru.get('a1'));
|
|
lru._keys();
|
|
// console.log(lru._keys());
|
|
lru.get('a2');
|
|
lru._keys();
|
|
lru.set('a6', a6);
|
|
assert(lru.get('a6') === '6');
|
|
assert(lru.size === 4);
|
|
assert(!lru.get('a3'));
|
|
lru._keys();
|
|
// console.log(lru._keys());
|
|
|
|
/**
|
|
* Expose
|
|
*/
|
|
|
|
module.exports = LRU;
|