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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6777 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
antonm@chromium.org 2011-02-14 17:25:12 +00:00
parent 1e4800b918
commit 186d832c79
4 changed files with 82 additions and 10 deletions

View File

@ -161,15 +161,7 @@ function Join(array, length, separator, convert) {
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.

View File

@ -5803,6 +5803,84 @@ static MaybeObject* Runtime_StringBuilderConcat(Arguments args) {
}
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) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);

View File

@ -128,6 +128,7 @@ namespace internal {
\
F(StringAdd, 2, 1) \
F(StringBuilderConcat, 3, 1) \
F(StringBuilderJoin, 3, 1) \
\
/* Bit operations */ \
F(NumberOr, 2, 1) \

View File

@ -118,8 +118,9 @@ var knownProblems = {
"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.