257 lines
6.6 KiB
JavaScript
257 lines
6.6 KiB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.memoizerific = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
|
|
module.exports = function(forceSimilar) {
|
|
if (typeof Map !== 'function' || forceSimilar) {
|
|
var Similar = _dereq_('./similar');
|
|
return new Similar();
|
|
}
|
|
else {
|
|
return new Map();
|
|
}
|
|
}
|
|
|
|
},{"./similar":2}],2:[function(_dereq_,module,exports){
|
|
function Similar() {
|
|
this.list = [];
|
|
this.lastItem = undefined;
|
|
this.size = 0;
|
|
|
|
return this;
|
|
}
|
|
|
|
Similar.prototype.get = function(key) {
|
|
var index;
|
|
|
|
if (this.lastItem && this.isEqual(this.lastItem.key, key)) {
|
|
return this.lastItem.val;
|
|
}
|
|
|
|
index = this.indexOf(key);
|
|
if (index >= 0) {
|
|
this.lastItem = this.list[index];
|
|
return this.list[index].val;
|
|
}
|
|
|
|
return undefined;
|
|
};
|
|
|
|
Similar.prototype.set = function(key, val) {
|
|
var index;
|
|
|
|
if (this.lastItem && this.isEqual(this.lastItem.key, key)) {
|
|
this.lastItem.val = val;
|
|
return this;
|
|
}
|
|
|
|
index = this.indexOf(key);
|
|
if (index >= 0) {
|
|
this.lastItem = this.list[index];
|
|
this.list[index].val = val;
|
|
return this;
|
|
}
|
|
|
|
this.lastItem = { key: key, val: val };
|
|
this.list.push(this.lastItem);
|
|
this.size++;
|
|
|
|
return this;
|
|
};
|
|
|
|
Similar.prototype.delete = function(key) {
|
|
var index;
|
|
|
|
if (this.lastItem && this.isEqual(this.lastItem.key, key)) {
|
|
this.lastItem = undefined;
|
|
}
|
|
|
|
index = this.indexOf(key);
|
|
if (index >= 0) {
|
|
this.size--;
|
|
return this.list.splice(index, 1)[0];
|
|
}
|
|
|
|
return undefined;
|
|
};
|
|
|
|
|
|
// important that has() doesn't use get() in case an existing key has a falsy value, in which case has() would return false
|
|
Similar.prototype.has = function(key) {
|
|
var index;
|
|
|
|
if (this.lastItem && this.isEqual(this.lastItem.key, key)) {
|
|
return true;
|
|
}
|
|
|
|
index = this.indexOf(key);
|
|
if (index >= 0) {
|
|
this.lastItem = this.list[index];
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
Similar.prototype.forEach = function(callback, thisArg) {
|
|
var i;
|
|
for (i = 0; i < this.size; i++) {
|
|
callback.call(thisArg || this, this.list[i].val, this.list[i].key, this);
|
|
}
|
|
};
|
|
|
|
Similar.prototype.indexOf = function(key) {
|
|
var i;
|
|
for (i = 0; i < this.size; i++) {
|
|
if (this.isEqual(this.list[i].key, key)) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
// check if the numbers are equal, or whether they are both precisely NaN (isNaN returns true for all non-numbers)
|
|
Similar.prototype.isEqual = function(val1, val2) {
|
|
return val1 === val2 || (val1 !== val1 && val2 !== val2);
|
|
};
|
|
|
|
module.exports = Similar;
|
|
},{}],3:[function(_dereq_,module,exports){
|
|
var MapOrSimilar = _dereq_('map-or-similar');
|
|
|
|
module.exports = function (limit) {
|
|
var cache = new MapOrSimilar(undefined === 'true'),
|
|
lru = [];
|
|
|
|
return function (fn) {
|
|
var memoizerific = function () {
|
|
var currentCache = cache,
|
|
newMap,
|
|
fnResult,
|
|
argsLengthMinusOne = arguments.length - 1,
|
|
lruPath = Array(argsLengthMinusOne + 1),
|
|
isMemoized = true,
|
|
i;
|
|
|
|
if ((memoizerific.numArgs || memoizerific.numArgs === 0) && memoizerific.numArgs !== argsLengthMinusOne + 1) {
|
|
throw new Error('Memoizerific functions should always be called with the same number of arguments');
|
|
}
|
|
|
|
// loop through each argument to traverse the map tree
|
|
for (i = 0; i < argsLengthMinusOne; i++) {
|
|
lruPath[i] = {
|
|
cacheItem: currentCache,
|
|
arg: arguments[i]
|
|
};
|
|
|
|
// climb through the hierarchical map tree until the second-last argument has been found, or an argument is missing.
|
|
// if all arguments up to the second-last have been found, this will potentially be a cache hit (determined later)
|
|
if (currentCache.has(arguments[i])) {
|
|
currentCache = currentCache.get(arguments[i]);
|
|
continue;
|
|
}
|
|
|
|
isMemoized = false;
|
|
|
|
// make maps until last value
|
|
newMap = new MapOrSimilar(undefined === 'true');
|
|
currentCache.set(arguments[i], newMap);
|
|
currentCache = newMap;
|
|
}
|
|
|
|
// we are at the last arg, check if it is really memoized
|
|
if (isMemoized) {
|
|
if (currentCache.has(arguments[argsLengthMinusOne])) {
|
|
fnResult = currentCache.get(arguments[argsLengthMinusOne]);
|
|
}
|
|
else {
|
|
isMemoized = false;
|
|
}
|
|
}
|
|
|
|
if (!isMemoized) {
|
|
fnResult = fn.apply(null, arguments);
|
|
currentCache.set(arguments[argsLengthMinusOne], fnResult);
|
|
}
|
|
|
|
if (limit > 0) {
|
|
lruPath[argsLengthMinusOne] = {
|
|
cacheItem: currentCache,
|
|
arg: arguments[argsLengthMinusOne]
|
|
};
|
|
|
|
if (isMemoized) {
|
|
moveToMostRecentLru(lru, lruPath);
|
|
}
|
|
else {
|
|
lru.push(lruPath);
|
|
}
|
|
|
|
if (lru.length > limit) {
|
|
removeCachedResult(lru.shift());
|
|
}
|
|
}
|
|
|
|
memoizerific.wasMemoized = isMemoized;
|
|
memoizerific.numArgs = argsLengthMinusOne + 1;
|
|
|
|
return fnResult;
|
|
};
|
|
|
|
memoizerific.limit = limit;
|
|
memoizerific.wasMemoized = false;
|
|
memoizerific.cache = cache;
|
|
memoizerific.lru = lru;
|
|
|
|
return memoizerific;
|
|
};
|
|
};
|
|
|
|
// move current args to most recent position
|
|
function moveToMostRecentLru(lru, lruPath) {
|
|
var lruLen = lru.length,
|
|
lruPathLen = lruPath.length,
|
|
isMatch,
|
|
i, ii;
|
|
|
|
for (i = 0; i < lruLen; i++) {
|
|
isMatch = true;
|
|
for (ii = 0; ii < lruPathLen; ii++) {
|
|
if (!isEqual(lru[i][ii].arg, lruPath[ii].arg)) {
|
|
isMatch = false;
|
|
break;
|
|
}
|
|
}
|
|
if (isMatch) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
lru.push(lru.splice(i, 1)[0]);
|
|
}
|
|
|
|
// remove least recently used cache item and all dead branches
|
|
function removeCachedResult(removedLru) {
|
|
var removedLruLen = removedLru.length,
|
|
currentLru = removedLru[removedLruLen - 1],
|
|
tmp,
|
|
i;
|
|
|
|
currentLru.cacheItem.delete(currentLru.arg);
|
|
|
|
// walk down the tree removing dead branches (size 0) along the way
|
|
for (i = removedLruLen - 2; i >= 0; i--) {
|
|
currentLru = removedLru[i];
|
|
tmp = currentLru.cacheItem.get(currentLru.arg);
|
|
|
|
if (!tmp || !tmp.size) {
|
|
currentLru.cacheItem.delete(currentLru.arg);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check if the numbers are equal, or whether they are both precisely NaN (isNaN returns true for all non-numbers)
|
|
function isEqual(val1, val2) {
|
|
return val1 === val2 || (val1 !== val1 && val2 !== val2);
|
|
}
|
|
},{"map-or-similar":1}]},{},[3])(3)
|
|
}); |