Simplify the object pool and large array cache.

Former-commit-id: d15df51efe575cd6fa773622f135ccfb6f675545
This commit is contained in:
John-David Dalton
2013-06-05 08:06:05 -07:00
parent 2c950f74bc
commit 036483d195

208
lodash.js
View File

@@ -166,23 +166,17 @@
return objectPool.pop() || { return objectPool.pop() || {
'args': null, 'args': null,
'array': null, 'array': null,
'arrays': null,
'bottom': null, 'bottom': null,
'contains': null,
'criteria': null, 'criteria': null,
'false': null, 'false': null,
'firstArg': null, 'firstArg': null,
'function': null,
'index': null, 'index': null,
'indexOf': null,
'init': null, 'init': null,
'initedArray': null,
'loop': null, 'loop': null,
'null': null, 'null': null,
'number': null, 'number': null,
'object': null, 'object': null,
'push': null, 'push': null,
'release': null,
'shadowedProps': null, 'shadowedProps': null,
'string': null, 'string': null,
'support': null, 'support': null,
@@ -216,6 +210,10 @@
* @param {Object} [object] The object to release. * @param {Object} [object] The object to release.
*/ */
function releaseObject(object) { function releaseObject(object) {
var cache = object.cache;
if (cache) {
releaseObject(cache);
}
if (objectPool.length == maxPoolSize) { if (objectPool.length == maxPoolSize) {
objectPool.length = maxPoolSize - 1; objectPool.length = maxPoolSize - 1;
} }
@@ -614,9 +612,9 @@
'<%= top %>;' + '<%= top %>;' +
// array-like iteration: // array-like iteration:
'<% if (arrays) { %>\n' + '<% if (array) { %>\n' +
'var length = iterable.length; index = -1;\n' + 'var length = iterable.length; index = -1;\n' +
'if (<%= arrays %>) {' + 'if (<%= array %>) {' +
// add support for accessing string characters by index if needed // add support for accessing string characters by index if needed
' <% if (support.unindexedChars) { %>\n' + ' <% if (support.unindexedChars) { %>\n' +
@@ -703,7 +701,7 @@
' }' + ' }' +
' <% } %>' + ' <% } %>' +
' <% } %>' + ' <% } %>' +
' <% if (arrays || support.nonEnumArgs) { %>\n}<% } %>\n' + ' <% if (array || support.nonEnumArgs) { %>\n}<% } %>\n' +
// add code to the bottom of the iteration function // add code to the bottom of the iteration function
'<%= bottom %>;\n' + '<%= bottom %>;\n' +
@@ -729,14 +727,14 @@
var eachIteratorOptions = { var eachIteratorOptions = {
'args': 'collection, callback, thisArg', 'args': 'collection, callback, thisArg',
'top': "callback = callback && typeof thisArg == 'undefined' ? callback : lodash.createCallback(callback, thisArg)", 'top': "callback = callback && typeof thisArg == 'undefined' ? callback : lodash.createCallback(callback, thisArg)",
'arrays': "typeof length == 'number'", 'array': "typeof length == 'number'",
'loop': 'if (callback(iterable[index], index, collection) === false) return result' 'loop': 'if (callback(iterable[index], index, collection) === false) return result'
}; };
/** Reusable iterator options for `forIn` and `forOwn` */ /** Reusable iterator options for `forIn` and `forOwn` */
var forOwnIteratorOptions = { var forOwnIteratorOptions = {
'top': 'if (!objectTypes[typeof iterable]) return result;\n' + eachIteratorOptions.top, 'top': 'if (!objectTypes[typeof iterable]) return result;\n' + eachIteratorOptions.top,
'arrays': false 'array': false
}; };
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
@@ -871,33 +869,43 @@
* @param {Mixed} value The value to search for. * @param {Mixed} value The value to search for.
* @returns {Boolean} Returns `true`, if `value` is found, else `false`. * @returns {Boolean} Returns `true`, if `value` is found, else `false`.
*/ */
var createCache = (function() { function createCache(array) {
var index = -1,
length = array.length;
function basicContains(value) { var cache = getObject();
return this.indexOf(this.array, value) > -1; cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false;
var result = getObject();
result.array = array;
result.cache = cache;
result.push = cachePush;
while (++index < length) {
result.push(array[index]);
} }
return cache.object === false
? releaseObject(result)
: result;
}
function basicPush(value) { function cacheIndexOf(cache, value) {
this.array.push(value); var type = typeof value;
cache = cache.cache;
if (type == 'boolean' || value == null) {
return cache[value];
} }
if (type != 'number' && type != 'string') {
function cacheContains(value) { type = 'object';
var cache = this.cache,
type = typeof value;
if (type == 'boolean' || value == null) {
return cache[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)
: !!cache[key];
} }
var key = type == 'number' ? value : keyPrefix + value;
cache = cache[type] || (cache[type] = {});
return type == 'object'
? (cache[key] && basicIndexOf(cache[key], value) > -1 ? 0 : -1)
: (cache[key] ? 0 : -1);
}
function cachePush(value) { function cachePush(value) {
var cache = this.cache, var cache = this.cache,
@@ -909,68 +917,26 @@
if (type != 'number' && type != 'string') { if (type != 'number' && type != 'string') {
type = 'object'; type = 'object';
} }
var key = type == 'number' ? value : keyPrefix + value; var key = type == 'number' ? value : keyPrefix + value,
cache = cache[type] || (cache[type] = {}); typeCache = cache[type] || (cache[type] = {});
if (type == 'object') { if (type == 'object') {
bailout = (cache[key] || (cache[key] = [])).push(value) == length; if ((typeCache[key] || (typeCache[key] = [])).push(value) === this.array.length) {
cache[type] = false;
}
} else { } else {
cache[key] = true; typeCache[key] = true;
} }
} }
} }
function release() {
var cache = this.cache;
if (cache.initedArray) {
releaseArray(this.array);
}
releaseObject(cache);
}
return function(array) {
var bailout,
index = -1,
indexOf = getIndexOf(),
initedArray = !array && (array = getArray()),
length = array.length,
isLarge = length >= largeArraySize && lodash.indexOf !== indexOf;
var cache = getObject();
cache.initedArray = initedArray;
cache['false'] = cache['function'] = cache['null'] = cache['true'] = cache['undefined'] = false;
var result = getObject();
result.array = array;
result.cache = cache;
result.contains = cacheContains;
result.indexOf = indexOf;
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. * Creates compiled iteration functions.
* *
* @private * @private
* @param {Object} [options1, options2, ...] The compile options object(s). * @param {Object} [options1, options2, ...] The compile options object(s).
* arrays - A string of code to determine if the iterable is an array or array-like. * array - A string of code to determine if the iterable is an array or array-like.
* useHas - A boolean to specify using `hasOwnProperty` checks in the object loop. * useHas - A boolean to specify using `hasOwnProperty` checks in the object loop.
* useKeys - A boolean to specify using `_.keys` for own property iteration. * useKeys - A boolean to specify using `_.keys` for own property iteration.
* args - A string of comma separated arguments the iteration function will accept. * args - A string of comma separated arguments the iteration function will accept.
@@ -987,7 +953,7 @@
data.support = support; data.support = support;
// iterator options // iterator options
data.arrays = data.bottom = data.loop = data.top = ''; data.array = data.bottom = data.loop = data.top = '';
data.init = 'iterable'; data.init = 'iterable';
data.useHas = true; data.useHas = true;
data.useKeys = !!keys; data.useKeys = !!keys;
@@ -3676,18 +3642,31 @@
*/ */
function difference(array) { function difference(array) {
var index = -1, var index = -1,
indexOf = getIndexOf(),
length = array ? array.length : 0, length = array ? array.length : 0,
flattened = concat.apply(arrayProto, nativeSlice.call(arguments, 1)), seen = concat.apply(arrayProto, nativeSlice.call(arguments, 1)),
cache = createCache(flattened),
result = []; result = [];
var isLarge = length >= largeArraySize && indexOf == basicIndexOf;
if (isLarge) {
var cache = createCache(seen);
if (cache) {
indexOf = cacheIndexOf;
seen = cache;
} else {
isLarge = false;
}
}
while (++index < length) { while (++index < length) {
var value = array[index]; var value = array[index];
if (!cache.contains(value)) { if (indexOf(seen, value) < 0) {
result.push(value); result.push(value);
} }
} }
cache.release(); if (isLarge) {
releaseObject(seen);
}
return result; return result;
} }
@@ -3991,33 +3970,48 @@
function intersection(array) { function intersection(array) {
var args = arguments, var args = arguments,
argsLength = args.length, argsLength = args.length,
caches = getArray(),
index = -1, index = -1,
indexOf = getIndexOf(),
length = array ? array.length : 0, length = array ? array.length : 0,
result = []; result = [];
var caches = getArray(); var isLarge = length >= largeArraySize && indexOf == basicIndexOf;
caches[0] = createCache(); caches[0] = getArray();
if (isLarge) {
var cache = createCache(caches[0]);
if (cache) {
indexOf = cacheIndexOf;
caches[0] = cache;
} else {
isLarge = false;
}
}
outer: outer:
while (++index < length) { while (++index < length) {
var cache = caches[0], var seen = caches[0],
value = array[index]; value = array[index];
if (!cache.contains(value)) { if (indexOf(seen, value) < 0) {
var argsIndex = argsLength; var argsIndex = argsLength;
cache.push(value); seen.push(value);
while (--argsIndex) { while (--argsIndex) {
cache = caches[argsIndex] || (caches[argsIndex] = createCache(args[argsIndex])); seen = isLarge ? (caches[argsIndex] || (caches[argsIndex] = createCache(args[argsIndex]))) : args[argsIndex];
if (!cache.contains(value)) { if (indexOf(seen, value) < 0) {
continue outer; continue outer;
} }
} }
result.push(value); result.push(value);
} }
} }
while (argsLength--) { seen = isLarge ? caches[0].array : caches[0];
caches[argsLength].release(); if (isLarge) {
while (argsLength--) {
releaseObject(caches[argsLength]);
}
} }
releaseArray(seen);
releaseArray(caches); releaseArray(caches);
return result; return result;
} }
@@ -4390,17 +4384,28 @@
var index = -1, var index = -1,
indexOf = getIndexOf(), indexOf = getIndexOf(),
length = array ? array.length : 0, length = array ? array.length : 0,
isLarge = !isSorted && length >= largeArraySize && lodash.indexOf !== indexOf, result = [];
result = [],
seen = isLarge ? createCache() : (callback ? getArray() : result);
var isLarge = !isSorted && length >= largeArraySize && indexOf == basicIndexOf,
seen = (callback || isLarge) ? getArray() : result;
if (isLarge) {
var cache = createCache(seen);
if (cache) {
indexOf = cacheIndexOf;
seen = cache;
} else {
isLarge = false;
seen = callback ? seen : (releaseArray(seen), result);
}
}
while (++index < length) { while (++index < length) {
var value = array[index], var value = array[index],
computed = callback ? callback(value, index, array) : value; computed = callback ? callback(value, index, array) : value;
if (isSorted if (isSorted
? !index || seen[seen.length - 1] !== computed ? !index || seen[seen.length - 1] !== computed
: (isLarge ? !seen.contains(computed) : indexOf(seen, computed) < 0) : indexOf(seen, computed) < 0
) { ) {
if (callback || isLarge) { if (callback || isLarge) {
seen.push(computed); seen.push(computed);
@@ -4409,7 +4414,8 @@
} }
} }
if (isLarge) { if (isLarge) {
seen.release(); releaseArray(seen.array);
releaseObject(seen);
} else if (callback) { } else if (callback) {
releaseArray(seen); releaseArray(seen);
} }