Add array and object pools to lodash.

Former-commit-id: f038284d6a544e146dc271ed0fbea0d7401593d4
This commit is contained in:
John-David Dalton
2013-06-02 21:25:57 -07:00
parent 9e63270fc5
commit 13ead0085d
3 changed files with 336 additions and 122 deletions

288
lodash.js
View File

@@ -23,6 +23,10 @@
window = freeGlobal;
}
/** Used to pool arrays and objects used internally */
var arrayPool = [],
objectPool = [];
/** Used to generate unique IDs */
var idCounter = 0;
@@ -139,6 +143,74 @@
'\u2029': 'u2029'
};
/**
* Gets an array from the array pool or creates a new one if the pool is empty.
*
* @private
* @returns {Array} The array from the pool.
*/
function getArray() {
return arrayPool.pop() || [];
}
/**
* Gets an object from the object pool or creates a new one if the pool is empty.
*
* @private
* @returns {Object} The object from the pool.
*/
function getObject() {
return objectPool.pop() || {
'args': null,
'array': null,
'arrays': null,
'contains': null,
'criteria': null,
'false': null,
'firstArg': null,
'function': null,
'index': null,
'indexOf': null,
'init': null,
'initArray': null,
'null': null,
'number': null,
'object': null,
'push': null,
'release': null,
'shadowedProps': null,
'string': null,
'support': null,
'true': null,
'undefined': null,
'useHas': null,
'useKeys': null,
'value': null
};
}
/**
* Releases the given `array` back to the array pool.
*
* @private
* @param {Array} [array] The array to release.
*/
function releaseArray(array) {
array.length = 0;
arrayPool.push(array);
}
/**
* Releases the given `object` back to the object pool.
*
* @private
* @param {Object} [object] The object to release.
*/
function releaseObject(object) {
object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null;
objectPool.push(object);
}
/*--------------------------------------------------------------------------*/
/**
@@ -580,8 +652,8 @@
// iterate own properties using `Object.keys`
' <% if (useHas && useKeys) { %>\n' +
' var ownIndex = -1,\n' +
' ownProps = objectTypes[typeof iterable] ? keys(iterable) : [],\n' +
' length = ownProps.length;\n\n' +
' ownProps = objectTypes[typeof iterable] && keys(iterable),\n' +
' length = ownProps ? ownProps.length : 0;\n\n' +
' while (++ownIndex < length) {\n' +
' index = ownProps[ownIndex];\n<%' +
" if (conditions.length) { %> if (<%= conditions.join(' && ') %>) {\n <% } %>" +
@@ -778,7 +850,6 @@
return bound;
}
/**
* Creates a function optimized to search large arrays for a given `value`,
* starting at `fromIndex`, using strict equality for comparisons, i.e. `===`.
@@ -788,42 +859,28 @@
* @param {Mixed} value The value to search for.
* @returns {Boolean} Returns `true`, if `value` is found, else `false`.
*/
function createCache(array) {
array || (array = []);
var bailout,
index = -1,
indexOf = getIndexOf(),
length = array.length,
isLarge = length >= largeArraySize && lodash.indexOf != indexOf,
objCache = {};
var caches = {
'false': false,
'function': false,
'null': false,
'number': {},
'object': objCache,
'string': {},
'true': false,
'undefined': false
};
var createCache = (function() {
function basicContains(value) {
return indexOf(array, value) > -1;
return this.indexOf(this.array, value) > -1;
}
function basicPush(value) {
array.push(value);
this.array.push(value);
}
function cacheContains(value) {
var type = typeof value;
var cache = this.cache,
type = typeof value;
if (type == 'boolean' || value == null) {
return caches[value];
return cache[value];
}
var cache = caches[type] || (type = 'object', objCache),
key = type == 'number' ? value : keyPrefix + value;
if (type != 'number' && type != 'string') {
type = 'object';
}
var key = type == 'number' ? value : keyPrefix + value;
cache = cache[type] || (cache[type] = {});
return type == 'object'
? (cache[key] ? basicIndexOf(cache[key], value) > -1 : false)
@@ -831,12 +888,17 @@
}
function cachePush(value) {
var type = typeof value;
var cache = this.cache,
type = typeof value;
if (type == 'boolean' || value == null) {
caches[value] = true;
cache[value] = true;
} else {
var cache = caches[type] || (type = 'object', objCache),
key = type == 'number' ? value : keyPrefix + value;
if (type != 'number' && type != 'string') {
type = 'object';
}
var key = type == 'number' ? value : keyPrefix + value;
cache = cache[type] || (cache[type] = {});
if (type == 'object') {
bailout = (cache[key] || (cache[key] = [])).push(value) == length;
@@ -846,18 +908,49 @@
}
}
if (isLarge) {
while (++index < length) {
cachePush(array[index]);
}
if (bailout) {
isLarge = caches = objCache = null;
function release() {
var cache = this.cache;
if (cache.initArray) {
releaseArray(this.array);
}
releaseObject(cache);
}
return isLarge
? { 'contains': cacheContains, 'push': cachePush }
: { 'contains': basicContains, 'push': basicPush };
}
return function(array) {
var bailout,
index = -1,
initArray = !array && (array = getArray()),
length = array.length,
isLarge = length >= largeArraySize && lodash.indexOf != indexOf;
var cache = getObject();
cache.initArray = initArray;
cache['false'] = cache['function'] = cache['null'] = cache['true'] = cache['undefined'] = false;
var result = getObject();
result.array = array;
result.indexOf = getIndexOf();
result.cache = cache;
result.contains = cacheContains;
result.push = cachePush;
result.release = release;
if (isLarge) {
while (++index < length) {
result.push(array[index]);
}
if (bailout) {
isLarge = false;
result.release();
}
}
if (!isLarge) {
result.contains = basicContains;
result.push = basicPush;
}
return result;
};
}());
/**
* Creates compiled iteration functions.
@@ -874,20 +967,17 @@
* @returns {Function} Returns the compiled function.
*/
function createIterator() {
var data = {
// data properties
'shadowedProps': shadowedProps,
'support': support,
var data = getObject();
// iterator options
'arrays': '',
'bottom': '',
'init': 'iterable',
'loop': '',
'top': '',
'useHas': true,
'useKeys': !!keys
};
// data properties
data.shadowedProps = shadowedProps;
data.support = support;
// iterator options
data.arrays = data.bottom = data.loop = data.top = '';
data.init = 'iterable';
data.useHas = true;
data.useKeys = !!keys;
// merge options into a template data object
for (var object, index = 0; object = arguments[index]; index++) {
@@ -900,16 +990,19 @@
// create the function factory
var factory = Function(
'errorClass, errorProto, hasOwnProperty, isArguments, isArray, isString, ' +
'keys, lodash, objectProto, objectTypes, nonEnumProps, stringClass, ' +
'stringProto, toString',
'errorClass, errorProto, hasOwnProperty, isArguments, isArray, ' +
'isString, keys, lodash, objectProto, objectTypes, nonEnumProps, ' +
'stringClass, stringProto, toString',
'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
);
releaseObject(data);
// return the compiled function
return factory(
errorClass, errorProto, hasOwnProperty, isArguments, isArray, isString,
keys, lodash, objectProto, objectTypes, nonEnumProps, stringClass,
stringProto, toString
errorClass, errorProto, hasOwnProperty, isArguments, isArray,
isString, keys, lodash, objectProto, objectTypes, nonEnumProps,
stringClass, stringProto, toString
);
}
@@ -1368,8 +1461,9 @@
return ctor(result.source, reFlags.exec(result));
}
// check for circular references and return corresponding clone
stackA || (stackA = []);
stackB || (stackB = []);
var initStack = !stackA;
stackA || (stackA = getArray());
stackB || (stackB = getArray());
var length = stackA.length;
while (length--) {
@@ -1399,6 +1493,10 @@
result[key] = clone(objValue, deep, callback, undefined, stackA, stackB);
});
if (initStack) {
releaseArray(stackA);
releaseArray(stackB);
}
return result;
}
@@ -1845,8 +1943,9 @@
// assume cyclic structures are equal
// the algorithm for detecting cyclic structures is adapted from ES 5.1
// section 15.12.3, abstract operation `JO` (http://es5.github.com/#x15.12.3)
stackA || (stackA = []);
stackB || (stackB = []);
var initStack = !stackA;
stackA || (stackA = getArray());
stackB || (stackB = getArray());
var length = stackA.length;
while (length--) {
@@ -1908,6 +2007,10 @@
}
});
}
if (initStack) {
releaseArray(stackA);
releaseArray(stackB);
}
return result;
}
@@ -2217,8 +2320,9 @@
stackA = args[4],
stackB = args[5];
} else {
stackA = [];
stackB = [];
var initStack = true;
stackA = getArray();
stackB = getArray();
// allows working with `_.reduce` and `_.reduceRight` without
// using their `callback` arguments, `index|key` and `collection`
@@ -2284,6 +2388,11 @@
object[key] = value;
});
}
if (initStack) {
releaseArray(stackA);
releaseArray(stackB);
}
return object;
}
@@ -3443,17 +3552,18 @@
callback = lodash.createCallback(callback, thisArg);
forEach(collection, function(value, key, collection) {
result[++index] = {
'criteria': callback(value, key, collection),
'index': index,
'value': value
};
var object = result[++index] = getObject();
object.criteria = callback(value, key, collection);
object.index = index;
object.value = value;
});
length = result.length;
result.sort(compareAscending);
while (length--) {
result[length] = result[length].value;
var object = result[length];
result[length] = object.value;
releaseObject(object);
}
return result;
}
@@ -3555,15 +3665,16 @@
var index = -1,
length = array ? array.length : 0,
flattened = concat.apply(arrayProto, nativeSlice.call(arguments, 1)),
contains = createCache(flattened).contains,
cache = createCache(flattened),
result = [];
while (++index < length) {
var value = array[index];
if (!contains(value)) {
if (!cache.contains(value)) {
result.push(value);
}
}
cache.release();
return result;
}
@@ -3867,27 +3978,35 @@
function intersection(array) {
var args = arguments,
argsLength = args.length,
cache = createCache(),
caches = {},
caches = getArray(),
index = -1,
length = array ? array.length : 0,
isLarge = length >= largeArraySize,
result = [];
caches[0] = createCache();
outer:
while (++index < length) {
var value = array[index];
var cache = caches[0],
value = array[index];
if (!cache.contains(value)) {
var argsIndex = argsLength;
cache.push(value);
while (--argsIndex) {
if (!(caches[argsIndex] || (caches[argsIndex] = createCache(args[argsIndex]).contains))(value)) {
cache = caches[argsIndex] || (caches[argsIndex] = createCache(args[argsIndex]));
if (!cache.contains(value)) {
continue outer;
}
}
result.push(value);
}
}
while (argsLength--) {
caches[argsLength].release();
}
releaseArray(caches);
return result;
}
@@ -4257,11 +4376,11 @@
*/
var uniq = overloadWrapper(function(array, isSorted, callback) {
var index = -1,
indexOf = getIndexOf(),
length = array ? array.length : 0,
isLarge = !isSorted && length >= largeArraySize,
indexOf = isLarge || getIndexOf(),
result = [],
seen = isLarge ? createCache() : (callback ? [] : result);
seen = isLarge ? createCache() : (callback ? getArray() : result);
while (++index < length) {
var value = array[index],
@@ -4277,6 +4396,11 @@
result.push(value);
}
}
if (isLarge) {
seen.release();
} else if (callback) {
releaseArray(seen);
}
return result;
});