fcoin/lib/bcoin/lru.js
Christopher Jeffrey 569016e752 refactor. numbers.
2016-03-25 15:17:43 -07:00

275 lines
4.8 KiB
JavaScript

/**
* lru.js - LRU cache for bcoin
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
* https://github.com/indutny/bcoin
*/
var utils = require('./utils');
var assert = utils.assert;
/**
* LRU
*/
function LRU(maxSize, getSize, onRemove) {
if (!(this instanceof LRU))
return new LRU(maxSize, getSize);
this.data = {};
this.size = 0;
this.maxSize = maxSize;
this.getSize = this._createGetSize(getSize);
this.onRemove = onRemove;
this.head = null;
this.tail = null;
}
LRU.prototype._createGetSize = function _createGetSize(size) {
if (!size)
return;
if (typeof size === 'number')
return function() { return size; };
if (typeof size === 'function')
return size;
assert(false, 'Bad getSize callback.');
};
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 (typeof item.value === 'string')
return keySize + item.value.length * 2;
if (typeof item.value.length === 'number')
return keySize + item.value.length;
return 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];
if (this.onRemove)
this.onRemove(item.key, item.value);
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.reset = function reset() {
var item, next;
for (item = this.head; item; item = next) {
delete this.data[item.key];
if (this.onRemove)
this.onRemove(item.key, item.value);
next = item.next;
item.prev = null;
item.next = null;
}
assert(!item);
this.size = 0;
this.head = null;
this.tail = 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];
if (this.onRemove)
this.onRemove(item.key, item.value);
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;
};
LRU.prototype.toArray = function toArray() {
var items = [];
var item;
for (item = this.head; item; item = item.next)
items.push(item);
return items;
};
LRU.prototype.iterator = function iterator() {
return {
item: { next: this.head },
next: function() {
this.item = this.item.next;
return this.item;
}
};
};
/**
* Expose
*/
module.exports = LRU;