202 lines
3.3 KiB
JavaScript
202 lines
3.3 KiB
JavaScript
// Simple LRU cache use doubly linked list
|
|
// @module zrender/core/LRU
|
|
|
|
/**
|
|
* Simple double linked list. Compared with array, it has O(1) remove operation.
|
|
* @constructor
|
|
*/
|
|
var LinkedList = function () {
|
|
/**
|
|
* @type {module:zrender/core/LRU~Entry}
|
|
*/
|
|
this.head = null;
|
|
/**
|
|
* @type {module:zrender/core/LRU~Entry}
|
|
*/
|
|
|
|
this.tail = null;
|
|
this._len = 0;
|
|
};
|
|
|
|
var linkedListProto = LinkedList.prototype;
|
|
/**
|
|
* Insert a new value at the tail
|
|
* @param {} val
|
|
* @return {module:zrender/core/LRU~Entry}
|
|
*/
|
|
|
|
linkedListProto.insert = function (val) {
|
|
var entry = new Entry(val);
|
|
this.insertEntry(entry);
|
|
return entry;
|
|
};
|
|
/**
|
|
* Insert an entry at the tail
|
|
* @param {module:zrender/core/LRU~Entry} entry
|
|
*/
|
|
|
|
|
|
linkedListProto.insertEntry = function (entry) {
|
|
if (!this.head) {
|
|
this.head = this.tail = entry;
|
|
} else {
|
|
this.tail.next = entry;
|
|
entry.prev = this.tail;
|
|
entry.next = null;
|
|
this.tail = entry;
|
|
}
|
|
|
|
this._len++;
|
|
};
|
|
/**
|
|
* Remove entry.
|
|
* @param {module:zrender/core/LRU~Entry} entry
|
|
*/
|
|
|
|
|
|
linkedListProto.remove = function (entry) {
|
|
var prev = entry.prev;
|
|
var next = entry.next;
|
|
|
|
if (prev) {
|
|
prev.next = next;
|
|
} else {
|
|
// Is head
|
|
this.head = next;
|
|
}
|
|
|
|
if (next) {
|
|
next.prev = prev;
|
|
} else {
|
|
// Is tail
|
|
this.tail = prev;
|
|
}
|
|
|
|
entry.next = entry.prev = null;
|
|
this._len--;
|
|
};
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
|
|
|
|
linkedListProto.len = function () {
|
|
return this._len;
|
|
};
|
|
/**
|
|
* Clear list
|
|
*/
|
|
|
|
|
|
linkedListProto.clear = function () {
|
|
this.head = this.tail = null;
|
|
this._len = 0;
|
|
};
|
|
/**
|
|
* @constructor
|
|
* @param {} val
|
|
*/
|
|
|
|
|
|
var Entry = function (val) {
|
|
/**
|
|
* @type {}
|
|
*/
|
|
this.value = val;
|
|
/**
|
|
* @type {module:zrender/core/LRU~Entry}
|
|
*/
|
|
|
|
this.next;
|
|
/**
|
|
* @type {module:zrender/core/LRU~Entry}
|
|
*/
|
|
|
|
this.prev;
|
|
};
|
|
/**
|
|
* LRU Cache
|
|
* @constructor
|
|
* @alias module:zrender/core/LRU
|
|
*/
|
|
|
|
|
|
var LRU = function (maxSize) {
|
|
this._list = new LinkedList();
|
|
this._map = {};
|
|
this._maxSize = maxSize || 10;
|
|
this._lastRemovedEntry = null;
|
|
};
|
|
|
|
var LRUProto = LRU.prototype;
|
|
/**
|
|
* @param {string} key
|
|
* @param {} value
|
|
* @return {} Removed value
|
|
*/
|
|
|
|
LRUProto.put = function (key, value) {
|
|
var list = this._list;
|
|
var map = this._map;
|
|
var removed = null;
|
|
|
|
if (map[key] == null) {
|
|
var len = list.len(); // Reuse last removed entry
|
|
|
|
var entry = this._lastRemovedEntry;
|
|
|
|
if (len >= this._maxSize && len > 0) {
|
|
// Remove the least recently used
|
|
var leastUsedEntry = list.head;
|
|
list.remove(leastUsedEntry);
|
|
delete map[leastUsedEntry.key];
|
|
removed = leastUsedEntry.value;
|
|
this._lastRemovedEntry = leastUsedEntry;
|
|
}
|
|
|
|
if (entry) {
|
|
entry.value = value;
|
|
} else {
|
|
entry = new Entry(value);
|
|
}
|
|
|
|
entry.key = key;
|
|
list.insertEntry(entry);
|
|
map[key] = entry;
|
|
}
|
|
|
|
return removed;
|
|
};
|
|
/**
|
|
* @param {string} key
|
|
* @return {}
|
|
*/
|
|
|
|
|
|
LRUProto.get = function (key) {
|
|
var entry = this._map[key];
|
|
var list = this._list;
|
|
|
|
if (entry != null) {
|
|
// Put the latest used entry in the tail
|
|
if (entry !== list.tail) {
|
|
list.remove(entry);
|
|
list.insertEntry(entry);
|
|
}
|
|
|
|
return entry.value;
|
|
}
|
|
};
|
|
/**
|
|
* Clear the cache
|
|
*/
|
|
|
|
|
|
LRUProto.clear = function () {
|
|
this._list.clear();
|
|
|
|
this._map = {};
|
|
};
|
|
|
|
var _default = LRU;
|
|
module.exports = _default; |