| Index: src/js/array.js
|
| diff --git a/src/js/array.js b/src/js/array.js
|
| index 1468d57c6cae52f3f4c307609d1c58a9517382f5..ce92b82b482f2e1e6e8e653dc48667e1cde8b461 100644
|
| --- a/src/js/array.js
|
| +++ b/src/js/array.js
|
| @@ -73,17 +73,13 @@ function DefineIndexedProperty(array, i, value) {
|
| }
|
| }
|
|
|
| +function KeySortCompare(a, b) {
|
| + return a - b;
|
| +}
|
|
|
| -// Global list of arrays visited during toString, toLocaleString and
|
| -// join invocations.
|
| -var visited_arrays = new InternalArray();
|
| -
|
| -
|
| -// Gets a sorted array of array keys. Useful for operations on sparse
|
| -// arrays. Dupes have not been removed.
|
| function GetSortedArrayKeys(array, indices) {
|
| - var keys = new InternalArray();
|
| if (IS_NUMBER(indices)) {
|
| + var keys = new InternalArray();
|
| // It's an interval
|
| var limit = indices;
|
| for (var i = 0; i < limit; ++i) {
|
| @@ -92,61 +88,33 @@ function GetSortedArrayKeys(array, indices) {
|
| keys.push(i);
|
| }
|
| }
|
| - } else {
|
| - var length = indices.length;
|
| - for (var k = 0; k < length; ++k) {
|
| - var key = indices[k];
|
| - if (!IS_UNDEFINED(key)) {
|
| - var e = array[key];
|
| - if (!IS_UNDEFINED(e) || key in array) {
|
| - keys.push(key);
|
| - }
|
| - }
|
| - }
|
| - keys.sort(function(a, b) { return a - b; });
|
| }
|
| - return keys;
|
| + return InnerArraySort(indices, indices.length, KeySortCompare);
|
| }
|
|
|
|
|
| -function SparseJoinWithSeparatorJS(array, len, convert, separator) {
|
| - var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
|
| - var totalLength = 0;
|
| - var elements = new InternalArray(keys.length * 2);
|
| - var previousKey = -1;
|
| - for (var i = 0; i < keys.length; i++) {
|
| +function SparseJoinWithSeparatorJS(array, keys, length, convert, separator) {
|
| + var keys_length = keys.length;
|
| + var elements = new InternalArray(keys_length * 2);
|
| + for (var i = 0; i < keys_length; i++) {
|
| var key = keys[i];
|
| - if (key != previousKey) { // keys may contain duplicates.
|
| - var e = array[key];
|
| - if (!IS_STRING(e)) e = convert(e);
|
| - elements[i * 2] = key;
|
| - elements[i * 2 + 1] = e;
|
| - previousKey = key;
|
| - }
|
| + var e = array[key];
|
| + elements[i * 2] = key;
|
| + elements[i * 2 + 1] = IS_STRING(e) ? e : convert(e);
|
| }
|
| - return %SparseJoinWithSeparator(elements, len, separator);
|
| + return %SparseJoinWithSeparator(elements, length, separator);
|
| }
|
|
|
|
|
| // Optimized for sparse arrays if separator is ''.
|
| -function SparseJoin(array, len, convert) {
|
| - var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
|
| - var last_key = -1;
|
| +function SparseJoin(array, keys, convert) {
|
| var keys_length = keys.length;
|
| -
|
| var elements = new InternalArray(keys_length);
|
| - var elements_length = 0;
|
| -
|
| for (var i = 0; i < keys_length; i++) {
|
| - var key = keys[i];
|
| - if (key != last_key) {
|
| - var e = array[key];
|
| - if (!IS_STRING(e)) e = convert(e);
|
| - elements[elements_length++] = e;
|
| - last_key = key;
|
| - }
|
| + var e = array[keys[i]];
|
| + elements[i] = IS_STRING(e) ? e : convert(e);
|
| }
|
| - return %StringBuilderConcat(elements, elements_length, '');
|
| + return %StringBuilderConcat(elements, keys_length, '');
|
| }
|
|
|
|
|
| @@ -167,98 +135,122 @@ function UseSparseVariant(array, length, is_array, touched) {
|
| (touched > estimated_elements * 4);
|
| }
|
|
|
| +function Stack() {
|
| + this.length = 0;
|
| + this.values = new InternalArray();
|
| +}
|
|
|
| -function Join(array, length, separator, convert) {
|
| - if (length == 0) return '';
|
| +// Predeclare the instance variables on the prototype. Otherwise setting them in
|
| +// the constructor will leak the instance through settings on Object.prototype.
|
| +Stack.prototype.length = null;
|
| +Stack.prototype.values = null;
|
|
|
| - var is_array = IS_ARRAY(array);
|
| +function StackPush(stack, value) {
|
| + stack.values[stack.length++] = value;
|
| +}
|
|
|
| - if (is_array) {
|
| - // If the array is cyclic, return the empty string for already
|
| - // visited arrays.
|
| - if (!%PushIfAbsent(visited_arrays, array)) return '';
|
| +function StackPop(stack) {
|
| + stack.values[--stack.length] = null
|
| +}
|
| +
|
| +function StackHas(stack, v) {
|
| + var length = stack.length;
|
| + var values = stack.values;
|
| + for (var i = 0; i < length; i++) {
|
| + if (values[i] === v) return true;
|
| }
|
| + return false;
|
| +}
|
|
|
| - // Attempt to convert the elements.
|
| - try {
|
| - if (UseSparseVariant(array, length, is_array, length)) {
|
| - %NormalizeElements(array);
|
| - if (separator.length == 0) {
|
| - return SparseJoin(array, length, convert);
|
| - } else {
|
| - return SparseJoinWithSeparatorJS(array, length, convert, separator);
|
| - }
|
| - }
|
| +// Global list of arrays visited during toString, toLocaleString and
|
| +// join invocations.
|
| +var visited_arrays = new Stack();
|
|
|
| - // Fast case for one-element arrays.
|
| - if (length == 1) {
|
| - var e = array[0];
|
| - if (IS_STRING(e)) return e;
|
| - return convert(e);
|
| +function DoJoin(array, length, is_array, separator, convert) {
|
| + if (UseSparseVariant(array, length, is_array, length)) {
|
| + %NormalizeElements(array);
|
| + var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, length));
|
| + if (separator === '') {
|
| + if (keys.length === 0) return '';
|
| + return SparseJoin(array, keys, convert);
|
| + } else {
|
| + return SparseJoinWithSeparatorJS(array, keys, length, convert, separator);
|
| }
|
| + }
|
|
|
| - // Construct an array for the elements.
|
| - var elements = new InternalArray(length);
|
| + // Fast case for one-element arrays.
|
| + if (length === 1) {
|
| + var e = array[0];
|
| + return IS_STRING(e) ? e : convert(e);
|
| + }
|
|
|
| - // We pull the empty separator check outside the loop for speed!
|
| - if (separator.length == 0) {
|
| - var elements_length = 0;
|
| - for (var i = 0; i < length; i++) {
|
| - var e = array[i];
|
| - if (!IS_STRING(e)) e = convert(e);
|
| - elements[elements_length++] = e;
|
| - }
|
| - elements.length = elements_length;
|
| - return %StringBuilderConcat(elements, elements_length, '');
|
| + // Construct an array for the elements.
|
| + var elements = new InternalArray(length);
|
| +
|
| + // We pull the empty separator check outside the loop for speed!
|
| + if (separator === '') {
|
| + for (var i = 0; i < length; i++) {
|
| + var e = array[i];
|
| + elements[i] = IS_STRING(e) ? e : convert(e);
|
| }
|
| - // Non-empty separator case.
|
| - // If the first element is a number then use the heuristic that the
|
| - // remaining elements are also likely to be numbers.
|
| - var e = array[0];
|
| - if (!IS_NUMBER(e)) {
|
| - if (!IS_STRING(e)) e = convert(e);
|
| - elements[0] = e;
|
| - for (var i = 1; i < length; i++) {
|
| - e = array[i];
|
| - if (!IS_STRING(e)) e = convert(e);
|
| - elements[i] = e;
|
| - }
|
| - } else {
|
| - elements[0] = %_NumberToString(e);
|
| - for (var i = 1; i < length; i++) {
|
| - e = array[i];
|
| - if (IS_NUMBER(e)) {
|
| - e = %_NumberToString(e);
|
| - } else if (!IS_STRING(e)) {
|
| - e = convert(e);
|
| - }
|
| - elements[i] = e;
|
| + return %StringBuilderConcat(elements, length, '');
|
| + }
|
| + // Non-empty separator case.
|
| + // If the first element is a number then use the heuristic that the
|
| + // remaining elements are also likely to be numbers.
|
| + var e = array[0];
|
| + if (IS_NUMBER(e)) {
|
| + elements[0] = %_NumberToString(e);
|
| + for (var i = 1; i < length; i++) {
|
| + e = array[i];
|
| + if (IS_NUMBER(e)) {
|
| + elements[i] = %_NumberToString(e);
|
| + } else {
|
| + elements[i] = IS_STRING(e) ? e : convert(e);
|
| }
|
| }
|
| - return %StringBuilderJoin(elements, length, separator);
|
| + } else {
|
| + elements[0] = IS_STRING(e) ? e : convert(e);
|
| + for (var i = 1; i < length; i++) {
|
| + e = array[i];
|
| + elements[i] = IS_STRING(e) ? e : convert(e);
|
| + }
|
| + }
|
| + return %StringBuilderJoin(elements, length, separator);
|
| +}
|
| +
|
| +function Join(array, length, separator, convert) {
|
| + if (length === 0) return '';
|
| +
|
| + var is_array = IS_ARRAY(array);
|
| +
|
| + if (is_array) {
|
| + // If the array is cyclic, return the empty string for already
|
| + // visited arrays.
|
| + if (StackHas(visited_arrays, array)) return '';
|
| + StackPush(visited_arrays, array);
|
| + }
|
| +
|
| + // Attempt to convert the elements.
|
| + try {
|
| + return DoJoin(array, length, is_array, separator, convert);
|
| } finally {
|
| // Make sure to remove the last element of the visited array no
|
| // matter what happens.
|
| - if (is_array) visited_arrays.length = visited_arrays.length - 1;
|
| + if (is_array) StackPop(visited_arrays);
|
| }
|
| }
|
|
|
|
|
| function ConvertToString(x) {
|
| - if (IS_NULL_OR_UNDEFINED(x)) {
|
| - return '';
|
| - } else {
|
| - return TO_STRING(x);
|
| - }
|
| + if (IS_NULL_OR_UNDEFINED(x)) return '';
|
| + return TO_STRING(x);
|
| }
|
|
|
|
|
| function ConvertToLocaleString(e) {
|
| - if (IS_NULL_OR_UNDEFINED(e)) {
|
| - return '';
|
| - } else {
|
| - return TO_STRING(e.toLocaleString());
|
| - }
|
| + if (IS_NULL_OR_UNDEFINED(e)) return '';
|
| + return TO_STRING(e.toLocaleString());
|
| }
|
|
|
|
|
| @@ -1959,6 +1951,10 @@ utils.Export(function(to) {
|
| to.InnerArraySort = InnerArraySort;
|
| to.InnerArrayToLocaleString = InnerArrayToLocaleString;
|
| to.PackedArrayReverse = PackedArrayReverse;
|
| + to.Stack = Stack;
|
| + to.StackHas = StackHas;
|
| + to.StackPush = StackPush;
|
| + to.StackPop = StackPop;
|
| });
|
|
|
| %InstallToContext([
|
|
|