diff --git a/include/v8.h b/include/v8.h index 34995d09f7..5c2c8b6671 100644 --- a/include/v8.h +++ b/include/v8.h @@ -1064,7 +1064,8 @@ class String : public Primitive { enum WriteOptions { NO_OPTIONS = 0, HINT_MANY_WRITES_EXPECTED = 1, - NO_NULL_TERMINATION = 2 + NO_NULL_TERMINATION = 2, + PRESERVE_ASCII_NULL = 4 }; // 16-bit character codes. diff --git a/src/api.cc b/src/api.cc index e1d57e3cb2..cd0684b57a 100644 --- a/src/api.cc +++ b/src/api.cc @@ -3847,6 +3847,9 @@ int String::WriteUtf8(char* buffer, LOG_API(isolate, "String::WriteUtf8"); ENTER_V8(isolate); i::Handle str = Utils::OpenHandle(this); + if (options & HINT_MANY_WRITES_EXPECTED) { + FlattenString(str); // Flatten the string for efficiency. + } int string_length = str->length(); if (str->IsAsciiRepresentation()) { int len; @@ -3903,11 +3906,7 @@ int String::WriteUtf8(char* buffer, // Slow case. i::StringInputBuffer& write_input_buffer = *isolate->write_input_buffer(); isolate->string_tracker()->RecordWrite(str); - if (options & HINT_MANY_WRITES_EXPECTED) { - // Flatten the string for efficiency. This applies whether we are - // using StringInputBuffer or Get(i) to access the characters. - FlattenString(str); - } + write_input_buffer.Reset(0, *str); int len = str->length(); // Encode the first K - 3 bytes directly into the buffer since we @@ -3949,8 +3948,9 @@ int String::WriteUtf8(char* buffer, c, unibrow::Utf16::kNoPreviousCharacter); if (pos + written <= capacity) { - for (int j = 0; j < written; j++) + for (int j = 0; j < written; j++) { buffer[pos + j] = intermediate[j]; + } pos += written; nchars++; } else { @@ -3963,8 +3963,9 @@ int String::WriteUtf8(char* buffer, } if (nchars_ref != NULL) *nchars_ref = nchars; if (!(options & NO_NULL_TERMINATION) && - (i == len && (capacity == -1 || pos < capacity))) + (i == len && (capacity == -1 || pos < capacity))) { buffer[pos++] = '\0'; + } return pos; } @@ -3977,28 +3978,45 @@ int String::WriteAscii(char* buffer, if (IsDeadCheck(isolate, "v8::String::WriteAscii()")) return 0; LOG_API(isolate, "String::WriteAscii"); ENTER_V8(isolate); - i::StringInputBuffer& write_input_buffer = *isolate->write_input_buffer(); ASSERT(start >= 0 && length >= -1); i::Handle str = Utils::OpenHandle(this); isolate->string_tracker()->RecordWrite(str); if (options & HINT_MANY_WRITES_EXPECTED) { - // Flatten the string for efficiency. This applies whether we are - // using StringInputBuffer or Get(i) to access the characters. - str->TryFlatten(); + FlattenString(str); // Flatten the string for efficiency. } + + if (str->IsAsciiRepresentation()) { + // WriteToFlat is faster than using the StringInputBuffer. + if (length == -1) length = str->length() + 1; + int len = i::Min(length, str->length() - start); + i::String::WriteToFlat(*str, buffer, start, start + len); + if (!(options & PRESERVE_ASCII_NULL)) { + for (int i = 0; i < len; i++) { + if (buffer[i] == '\0') buffer[i] = ' '; + } + } + if (!(options & NO_NULL_TERMINATION) && length > len) { + buffer[len] = '\0'; + } + return len; + } + + i::StringInputBuffer& write_input_buffer = *isolate->write_input_buffer(); int end = length; - if ( (length == -1) || (length > str->length() - start) ) + if ((length == -1) || (length > str->length() - start)) { end = str->length() - start; + } if (end < 0) return 0; write_input_buffer.Reset(start, *str); int i; for (i = 0; i < end; i++) { char c = static_cast(write_input_buffer.GetNext()); - if (c == '\0') c = ' '; + if (c == '\0' && !(options & PRESERVE_ASCII_NULL)) c = ' '; buffer[i] = c; } - if (!(options & NO_NULL_TERMINATION) && (length == -1 || i < length)) + if (!(options & NO_NULL_TERMINATION) && (length == -1 || i < length)) { buffer[i] = '\0'; + } return i; } @@ -4017,7 +4035,7 @@ int String::Write(uint16_t* buffer, if (options & HINT_MANY_WRITES_EXPECTED) { // Flatten the string for efficiency. This applies whether we are // using StringInputBuffer or Get(i) to access the characters. - str->TryFlatten(); + FlattenString(str); } int end = start + length; if ((length == -1) || (length > str->length() - start) ) @@ -4203,8 +4221,9 @@ void v8::Object::SetPointerInInternalField(int index, void* value) { i::Handle foreign = isolate->factory()->NewForeign( reinterpret_cast(value), i::TENURED); - if (!foreign.is_null()) - Utils::OpenHandle(this)->SetInternalField(index, *foreign); + if (!foreign.is_null()) { + Utils::OpenHandle(this)->SetInternalField(index, *foreign); + } } ASSERT_EQ(value, GetPointerFromInternalField(index)); } diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 5a9df90e21..26483e019c 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -5570,6 +5570,7 @@ THREADED_TEST(StringWrite) { v8::Handle str = v8_str("abcde"); // abc. v8::Handle str2 = v8_str("abc\303\260\342\230\203"); + v8::Handle str3 = v8::String::New("abc\0def", 7); const int kStride = 4; // Must match stride in for loops in JS below. CompileRun( "var left = '';" @@ -5780,6 +5781,28 @@ THREADED_TEST(StringWrite) { CHECK_NE(0, strcmp(utf8buf, "abc\303\260\342\230\203")); utf8buf[8] = '\0'; CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203")); + + memset(utf8buf, 0x1, sizeof(utf8buf)); + utf8buf[5] = 'X'; + len = str->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen, + String::NO_NULL_TERMINATION); + CHECK_EQ(5, len); + CHECK_EQ('X', utf8buf[5]); // Test that the sixth character is untouched. + CHECK_EQ(5, charlen); + utf8buf[5] = '\0'; + CHECK_EQ(0, strcmp(utf8buf, "abcde")); + + memset(buf, 0x1, sizeof(buf)); + len = str3->WriteAscii(buf); + CHECK_EQ(7, len); + CHECK_EQ(0, strcmp("abc def", buf)); + + memset(buf, 0x1, sizeof(buf)); + len = str3->WriteAscii(buf, 0, -1, String::PRESERVE_ASCII_NULL); + CHECK_EQ(7, len); + CHECK_EQ(0, strcmp("abc", buf)); + CHECK_EQ(0, buf[3]); + CHECK_EQ(0, strcmp("def", buf + 4)); }