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.
191 lines
4.2 KiB
191 lines
4.2 KiB
'use strict';
|
|
|
|
function Cache () {
|
|
var _cache = Object.create(null);
|
|
var _hitCount = 0;
|
|
var _missCount = 0;
|
|
var _size = 0;
|
|
var _debug = false;
|
|
|
|
this.put = function(key, value, time, timeoutCallback) {
|
|
if (_debug) {
|
|
console.log('caching: %s = %j (@%s)', key, value, time);
|
|
}
|
|
|
|
if (typeof time !== 'undefined' && (typeof time !== 'number' || isNaN(time) || time <= 0)) {
|
|
throw new Error('Cache timeout must be a positive number');
|
|
} else if (typeof timeoutCallback !== 'undefined' && typeof timeoutCallback !== 'function') {
|
|
throw new Error('Cache timeout callback must be a function');
|
|
}
|
|
|
|
var oldRecord = _cache[key];
|
|
if (oldRecord) {
|
|
clearTimeout(oldRecord.timeout);
|
|
} else {
|
|
_size++;
|
|
}
|
|
|
|
var record = {
|
|
value: value,
|
|
expire: time + Date.now()
|
|
};
|
|
|
|
if (!isNaN(record.expire)) {
|
|
record.timeout = setTimeout(function() {
|
|
_del(key);
|
|
if (timeoutCallback) {
|
|
timeoutCallback(key, value);
|
|
}
|
|
}.bind(this), time);
|
|
}
|
|
|
|
_cache[key] = record;
|
|
|
|
return value;
|
|
};
|
|
|
|
this.del = function(key) {
|
|
var canDelete = true;
|
|
|
|
var oldRecord = _cache[key];
|
|
if (oldRecord) {
|
|
clearTimeout(oldRecord.timeout);
|
|
if (!isNaN(oldRecord.expire) && oldRecord.expire < Date.now()) {
|
|
canDelete = false;
|
|
}
|
|
} else {
|
|
canDelete = false;
|
|
}
|
|
|
|
if (canDelete) {
|
|
_del(key);
|
|
}
|
|
|
|
return canDelete;
|
|
};
|
|
|
|
function _del(key){
|
|
_size--;
|
|
delete _cache[key];
|
|
}
|
|
|
|
this.clear = function() {
|
|
for (var key in _cache) {
|
|
clearTimeout(_cache[key].timeout);
|
|
}
|
|
_size = 0;
|
|
_cache = Object.create(null);
|
|
if (_debug) {
|
|
_hitCount = 0;
|
|
_missCount = 0;
|
|
}
|
|
};
|
|
|
|
this.get = function(key) {
|
|
var data = _cache[key];
|
|
if (typeof data != "undefined") {
|
|
if (isNaN(data.expire) || data.expire >= Date.now()) {
|
|
if (_debug) _hitCount++;
|
|
return data.value;
|
|
} else {
|
|
// free some space
|
|
if (_debug) _missCount++;
|
|
_size--;
|
|
delete _cache[key];
|
|
}
|
|
} else if (_debug) {
|
|
_missCount++;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
this.size = function() {
|
|
return _size;
|
|
};
|
|
|
|
this.memsize = function() {
|
|
var size = 0,
|
|
key;
|
|
for (key in _cache) {
|
|
size++;
|
|
}
|
|
return size;
|
|
};
|
|
|
|
this.debug = function(bool) {
|
|
_debug = bool;
|
|
};
|
|
|
|
this.hits = function() {
|
|
return _hitCount;
|
|
};
|
|
|
|
this.misses = function() {
|
|
return _missCount;
|
|
};
|
|
|
|
this.keys = function() {
|
|
return Object.keys(_cache);
|
|
};
|
|
|
|
this.exportJson = function() {
|
|
var plainJsCache = {};
|
|
|
|
// Discard the `timeout` property.
|
|
// Note: JSON doesn't support `NaN`, so convert it to `'NaN'`.
|
|
for (var key in _cache) {
|
|
var record = _cache[key];
|
|
plainJsCache[key] = {
|
|
value: record.value,
|
|
expire: record.expire || 'NaN',
|
|
};
|
|
}
|
|
|
|
return JSON.stringify(plainJsCache);
|
|
};
|
|
|
|
this.importJson = function(jsonToImport, options) {
|
|
var cacheToImport = JSON.parse(jsonToImport);
|
|
var currTime = Date.now();
|
|
|
|
var skipDuplicates = options && options.skipDuplicates;
|
|
|
|
for (var key in cacheToImport) {
|
|
if (cacheToImport.hasOwnProperty(key)) {
|
|
if (skipDuplicates) {
|
|
var existingRecord = _cache[key];
|
|
if (existingRecord) {
|
|
if (_debug) {
|
|
console.log('Skipping duplicate imported key \'%s\'', key);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
var record = cacheToImport[key];
|
|
|
|
// record.expire could be `'NaN'` if no expiry was set.
|
|
// Try to subtract from it; a string minus a number is `NaN`, which is perfectly fine here.
|
|
var remainingTime = record.expire - currTime;
|
|
|
|
if (remainingTime <= 0) {
|
|
// Delete any record that might exist with the same key, since this key is expired.
|
|
this.del(key);
|
|
continue;
|
|
}
|
|
|
|
// Remaining time must now be either positive or `NaN`,
|
|
// but `put` will throw an error if we try to give it `NaN`.
|
|
remainingTime = remainingTime > 0 ? remainingTime : undefined;
|
|
|
|
this.put(key, record.value, remainingTime);
|
|
}
|
|
}
|
|
|
|
return this.size();
|
|
};
|
|
}
|
|
|
|
module.exports = new Cache();
|
|
module.exports.Cache = Cache;
|