Форк Rambox
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
7.3 KiB

/**
* This class is used to manage simple, LRU caches. It provides an absolutely minimal
* container interface. It is created like this:
*
* this.itemCache = new Ext.util.Cache({
* miss: function (key) {
* return new CacheItem(key);
* }
* });
*
* The `{@link #miss}` abstract method must be implemented by either a derived class or
* at the instance level as shown above.
*
* Once the cache exists and it can handle cache misses, the cache is used like so:
*
* var item = this.itemCache.get(key);
*
* The `key` is some value that uniquely identifies the cached item.
*
* In some cases, creating the cache item may require more than just the lookup key. In
* that case, any extra arguments passed to `get` will be passed to `miss`.
*
* this.otherCache = new Ext.util.Cache({
* miss: function (key, extra) {
* return new CacheItem(key, extra);
* }
* });
*
* var item = this.otherCache.get(key, extra);
*
* To process items as they are removed, you can provide an `{@link #evict}` method. The
* stock method is `Ext.emptyFn` and so does nothing.
*
* For example:
*
* this.itemCache = new Ext.util.Cache({
* miss: function (key) {
* return new CacheItem(key);
* },
*
* evict: function (key, cacheItem) {
* cacheItem.destroy();
* }
* });
*
* @class Ext.util.Cache
* @private
* @since 5.1.0
*/
(function (Cache, prototype) {
// @define Ext.util.Cache
// NOTE: We have to implement this class old-school because it is used by the
// platformConfig class processor (so Ext.define is not yet ready for action).
(Ext.util || (Ext.util = {})).Cache = Cache = function (config) {
var me = this,
head;
if (config) {
Ext.apply(me, config);
}
// Give all entries the same object shape.
me.head = head = {
//<debug>
id: (me.seed = 0),
//</debug>
key: null,
value: null
};
me.map = {};
head.next = head.prev = head;
};
Cache.prototype = prototype = {
/**
* @cfg {Number} maxSize The maximum size the cache is allowed to grow to before
* further additions cause removal of the least recently used entry.
*/
maxSize: 100,
/**
* @property {Number} count
* The number of items in this cache.
* @readonly
*/
count: 0,
/**
* This method is called by `{@link #get}` when the key is not found in the cache.
* The implementation of this method should create the (expensive) value and return
* it. Whatever arguments were passed to `{@link #get}` will be passed on to this
* method.
*
* @param {String} key The cache lookup key for the item.
* @param {Object...} args Any other arguments originally passed to `{@link #get}`.
* @method miss
* @abstract
* @protected
*/
/**
* Removes all items from this cache.
*/
clear: function () {
var me = this,
head = me.head,
entry = head.next;
head.next = head.prev = head;
if (!me.evict.$nullFn) {
for ( ; entry !== head; entry = entry.next) {
me.evict(entry.key, entry.value);
}
}
me.count = 0;
},
/**
* Calls the given function `fn` for each item in the cache. The items will be passed
* to `fn` from most-to-least recently used.
* @param {Function} fn The function to call for each cache item.
* @param {String} fn.key The cache key for the item.
* @param {Object} fn.value The value in the cache for the item.
* @param {Object} [scope] The `this` pointer to use for `fn`.
*/
each: function (fn, scope) {
scope = scope || this;
for (var head = this.head, ent = head.next; ent !== head; ent = ent.next) {
if (fn.call(scope, ent.key, ent.value)) {
break;
}
}
},
/**
* Finds an item in this cache and returns its value. If the item is present, it is
* shuffled into the MRU (most-recently-used) position as necessary. If the item is
* missing, the `{@link #miss}` method is called to create the item.
*
* @param {String} key The cache key of the item.
* @param {Object...} args Arguments for the `miss` method should it be needed.
* @return {Object} The cached object.
*/
get: function (key) {
var me = this,
head = me.head,
map = me.map,
entry = map[key];
if (entry) {
if (entry.prev !== head) {
// The entry is not at the front, so remove it and insert it at the front
// (to make it the MRU - Most Recently Used).
me.unlinkEntry(entry);
me.linkEntry(entry);
}
} else {
map[key] = entry = {
//<debug>
id: ++me.seed,
//</debug>
key: key,
value: me.miss.apply(me, arguments)
};
me.linkEntry(entry);
++me.count;
while (me.count > me.maxSize) {
me.unlinkEntry(head.prev, true);
--me.count;
}
}
return entry.value;
},
//-------------------------------------------------------------------------
// Internals
/**
* This method is called internally from `{@link #get}` when the cache is full and
* the least-recently-used (LRU) item has been removed.
*
* @param {String} key The cache lookup key for the item being removed.
* @param {Object} value The cache value (returned by `{@link #miss}`) for the item
* being removed.
* @method evict
* @template
* @protected
*/
evict: Ext.emptyFn,
/**
* Inserts the given entry at the front (MRU) end of the entry list.
* @param {Object} entry The cache item entry.
* @private
*/
linkEntry: function (entry) {
var head = this.head,
first = head.next;
entry.next = first;
entry.prev = head;
head.next = entry;
first.prev = entry;
},
/**
* Removes the given entry from the entry list.
* @param {Object} entry The cache item entry.
* @param {Boolean} evicted Pass `true` if `{@link #evict}` should be called.
* @private
*/
unlinkEntry: function (entry, evicted) {
var next = entry.next,
prev = entry.prev;
prev.next = next;
next.prev = prev;
if (evicted) {
this.evict(entry.key, entry.value);
}
}
};
prototype.destroy = prototype.clear;
}());