Index: src/api.cc |
=================================================================== |
--- src/api.cc (revision 11018) |
+++ src/api.cc (working copy) |
@@ -3694,6 +3694,93 @@ |
} |
+// Will fail with a negative answer if the recursion depth is too high. |
+static int RecursivelySerializeToUtf8(i::String* string, |
+ char* buffer, |
+ int start, |
+ int end, |
+ int recursion_budget, |
+ int32_t previous_character, |
+ int32_t* last_character) { |
+ int utf8_bytes = 0; |
+ while (true) { |
+ if (string->IsAsciiRepresentation()) { |
+ i::String::WriteToFlat(string, buffer, start, end); |
+ *last_character = unibrow::Utf16::kNoPreviousCharacter; |
+ return utf8_bytes + end - start; |
+ } |
+ switch (i::StringShape(string).representation_tag()) { |
+ case i::kExternalStringTag: { |
+ const uint16_t* data = i::ExternalTwoByteString::cast(string)-> |
+ ExternalTwoByteStringGetData(start); |
+ char* current = buffer; |
+ for (int i = start; i < end; i++) { |
+ uint16_t character = data[i]; |
+ current += |
+ unibrow::Utf8::Encode(current, character, previous_character); |
+ previous_character = character; |
+ } |
+ *last_character = previous_character; |
+ return utf8_bytes + current - buffer; |
+ } |
+ case i::kSeqStringTag: { |
+ const uint16_t* data = |
+ i::SeqTwoByteString::cast(string)->SeqTwoByteStringGetData(start); |
+ char* current = buffer; |
+ for (int i = start; i < end; i++) { |
+ uint16_t character = data[i]; |
+ current += |
+ unibrow::Utf8::Encode(current, character, previous_character); |
+ previous_character = character; |
+ } |
+ *last_character = previous_character; |
+ return utf8_bytes + current - buffer; |
+ } |
+ case i::kSlicedStringTag: { |
+ i::SlicedString* slice = i::SlicedString::cast(string); |
+ unsigned offset = slice->offset(); |
+ string = slice->parent(); |
+ start += offset; |
+ end += offset; |
+ } |
fschneider
2012/03/20 09:45:49
Accidental fall-through here?
|
+ case i::kConsStringTag: { |
+ i::ConsString* cons_string = i::ConsString::cast(string); |
+ i::String* first = cons_string->first(); |
+ int boundary = first->length(); |
+ if (start >= boundary) { |
+ // Only need RHS. |
+ string = cons_string->second(); |
+ start -= boundary; |
+ end -= boundary; |
+ continue; |
+ } else if (end <= boundary) { |
+ // Only need LHS. |
+ string = first; |
+ } else { |
+ if (recursion_budget == 0) return -1; |
+ int extra_utf8_bytes = |
+ RecursivelySerializeToUtf8(first, |
+ buffer, |
+ start, |
+ boundary, |
+ recursion_budget - 1, |
+ previous_character, |
+ &previous_character); |
+ if (extra_utf8_bytes < 0) return extra_utf8_bytes; |
+ buffer += extra_utf8_bytes; |
+ utf8_bytes += extra_utf8_bytes; |
+ string = cons_string->second(); |
+ start = 0; |
+ end -= boundary; |
+ } |
+ } |
+ } |
+ } |
+ UNREACHABLE(); |
+ return 0; |
+} |
+ |
+ |
int String::WriteUtf8(char* buffer, |
int capacity, |
int* nchars_ref, |
@@ -3703,11 +3790,12 @@ |
LOG_API(isolate, "String::WriteUtf8"); |
ENTER_V8(isolate); |
i::Handle<i::String> str = Utils::OpenHandle(this); |
+ int string_length = str->length(); |
if (str->IsAsciiRepresentation()) { |
int len; |
if (capacity == -1) { |
capacity = str->length() + 1; |
- len = str->length(); |
+ len = string_length; |
} else { |
len = i::Min(capacity, str->length()); |
} |
@@ -3720,6 +3808,42 @@ |
return len; |
} |
+ if (capacity == -1 || capacity >= string_length * 3) { |
+ int32_t previous = unibrow::Utf16::kNoPreviousCharacter; |
+ const int kMaxRecursion = 100; |
+ int utf8_bytes = |
+ RecursivelySerializeToUtf8(*str, |
+ buffer, |
+ 0, |
+ string_length, |
+ kMaxRecursion, |
+ previous, |
+ &previous); |
+ if (utf8_bytes >= 0) { |
+ // Success serializing with recursion. |
+ if ((options & NO_NULL_TERMINATION) == 0 && |
+ (capacity > utf8_bytes || capacity == -1)) { |
+ buffer[utf8_bytes++] = '\0'; |
+ } |
+ if (nchars_ref != NULL) *nchars_ref = string_length; |
+ return utf8_bytes; |
+ } |
+ FlattenString(str); |
+ // Recurse once. This time around the string is flat and the serializing |
+ // with recursion will certainly succeed. |
+ return WriteUtf8(buffer, capacity, nchars_ref, options); |
+ } else if (capacity >= string_length) { |
+ // First check that the buffer is large enough. If it is, then recurse |
+ // once without a capacity limit, which will get into the other branch of |
+ // this 'if'. |
+ int utf8_bytes = i::Utf8Length(str); |
+ if ((options & NO_NULL_TERMINATION) == 0) utf8_bytes++; |
+ if (utf8_bytes <= capacity) { |
+ return WriteUtf8(buffer, -1, nchars_ref, options); |
+ } |
+ } |
+ |
+ // Slow case. |
i::StringInputBuffer& write_input_buffer = *isolate->write_input_buffer(); |
isolate->string_tracker()->RecordWrite(str); |
if (options & HINT_MANY_WRITES_EXPECTED) { |