Revision: 6777
Author: [email protected]
Date: Mon Feb 14 09:25:12 2011
Log: Introduce new runtime function to make join with lower memory usage.

Do not use generic StringBuilderConcat which requires array passed
to keep both elements and separator (which roughly double size
of the array).  That should be faster as well.

BUG=crbug.com/54580

Review URL: http://codereview.chromium.org/6520004
http://code.google.com/p/v8/source/detail?r=6777

Modified:
 /branches/bleeding_edge/src/array.js
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/runtime.h
 /branches/bleeding_edge/test/mjsunit/fuzz-natives.js

=======================================
--- /branches/bleeding_edge/src/array.js        Mon Feb  7 00:57:06 2011
+++ /branches/bleeding_edge/src/array.js        Mon Feb 14 09:25:12 2011
@@ -161,15 +161,7 @@
     var result = %_FastAsciiArrayJoin(elements, separator);
     if (!IS_UNDEFINED(result)) return result;

-    var length2 = (length << 1) - 1;
-    var j = length2;
-    var i = length;
-    elements[--j] = elements[--i];
-    while (i > 0) {
-      elements[--j] = separator;
-      elements[--j] = elements[--i];
-    }
-    return %StringBuilderConcat(elements, length2, '');
+    return %StringBuilderJoin(elements, length, separator);
   } finally {
     // Make sure to remove the last element of the visited array no
     // matter what happens.
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Mon Feb 14 02:43:21 2011
+++ /branches/bleeding_edge/src/runtime.cc      Mon Feb 14 09:25:12 2011
@@ -5801,6 +5801,84 @@
     return answer;
   }
 }
+
+
+static MaybeObject* Runtime_StringBuilderJoin(Arguments args) {
+  NoHandleAllocation ha;
+  ASSERT(args.length() == 3);
+  CONVERT_CHECKED(JSArray, array, args[0]);
+  if (!args[1]->IsSmi()) {
+    Top::context()->mark_out_of_memory();
+    return Failure::OutOfMemoryException();
+  }
+  int array_length = Smi::cast(args[1])->value();
+  CONVERT_CHECKED(String, separator, args[2]);
+
+  if (!array->HasFastElements()) {
+    return Top::Throw(Heap::illegal_argument_symbol());
+  }
+  FixedArray* fixed_array = FixedArray::cast(array->elements());
+  if (fixed_array->length() < array_length) {
+    array_length = fixed_array->length();
+  }
+
+  if (array_length == 0) {
+    return Heap::empty_string();
+  } else if (array_length == 1) {
+    Object* first = fixed_array->get(0);
+    if (first->IsString()) return first;
+  }
+
+  int separator_length = separator->length();
+  int max_nof_separators =
+      (String::kMaxLength + separator_length - 1) / separator_length;
+  if (max_nof_separators < (array_length - 1)) {
+      Top::context()->mark_out_of_memory();
+      return Failure::OutOfMemoryException();
+  }
+  int length = (array_length - 1) * separator_length;
+  for (int i = 0; i < array_length; i++) {
+    String* element = String::cast(fixed_array->get(i));
+    int increment = element->length();
+    if (increment > String::kMaxLength - length) {
+      Top::context()->mark_out_of_memory();
+      return Failure::OutOfMemoryException();
+    }
+    length += increment;
+  }
+
+  Object* object;
+  { MaybeObject* maybe_object = Heap::AllocateRawTwoByteString(length);
+    if (!maybe_object->ToObject(&object)) return maybe_object;
+  }
+  SeqTwoByteString* answer = SeqTwoByteString::cast(object);
+
+  uc16* sink = answer->GetChars();
+#ifdef DEBUG
+  uc16* end = sink + length;
+#endif
+
+  String* first = String::cast(fixed_array->get(0));
+  int first_length = first->length();
+  String::WriteToFlat(first, sink, 0, first_length);
+  sink += first_length;
+
+  for (int i = 1; i < array_length; i++) {
+    ASSERT(sink + separator_length <= end);
+    String::WriteToFlat(separator, sink, 0, separator_length);
+    sink += separator_length;
+
+    String* element = String::cast(fixed_array->get(i));
+    int element_length = element->length();
+    ASSERT(sink + element_length <= end);
+    String::WriteToFlat(element, sink, 0, element_length);
+    sink += element_length;
+  }
+  ASSERT(sink == end);
+
+ ASSERT(!answer->HasOnlyAsciiChars()); // Use %_FastAsciiArrayJoin instead.
+  return answer;
+}


 static MaybeObject* Runtime_NumberOr(Arguments args) {
=======================================
--- /branches/bleeding_edge/src/runtime.h       Fri Feb  4 10:15:49 2011
+++ /branches/bleeding_edge/src/runtime.h       Mon Feb 14 09:25:12 2011
@@ -128,6 +128,7 @@
   \
   F(StringAdd, 2, 1) \
   F(StringBuilderConcat, 3, 1) \
+  F(StringBuilderJoin, 3, 1) \
   \
   /* Bit operations */ \
   F(NumberOr, 2, 1) \
=======================================
--- /branches/bleeding_edge/test/mjsunit/fuzz-natives.js Wed Dec 15 10:13:02 2010 +++ /branches/bleeding_edge/test/mjsunit/fuzz-natives.js Mon Feb 14 09:25:12 2011
@@ -118,8 +118,9 @@
   "Abort": true,

   // Avoid calling the concat operation, because weird lengths
-  // may lead to out-of-memory.
+  // may lead to out-of-memory.  Ditto for StringBuilderJoin.
   "StringBuilderConcat": true,
+  "StringBuilderJoin": true,

   // These functions use pseudo-stack-pointers and are not robust
   // to unexpected integer values.

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to