From 748f8bf7e1096a0e1a50915c5bf4af831d06bbfd Mon Sep 17 00:00:00 2001 From: "yangguo@chromium.org" Date: Tue, 23 Oct 2012 09:05:56 +0000 Subject: [PATCH] Optimize inner-loop in JSON.stringify. R=verwaest@chromium.org BUG= Review URL: https://chromiumcodereview.appspot.com/11234031 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12798 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/json-stringifier.h | 111 +++++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 44 deletions(-) diff --git a/src/json-stringifier.h b/src/json-stringifier.h index cd3053ae12..e4eb06e462 100644 --- a/src/json-stringifier.h +++ b/src/json-stringifier.h @@ -58,15 +58,9 @@ class BasicJsonStringifier BASE_EMBEDDED { template INLINE(void Append_(Char c)); - template - INLINE(void AppendUnchecked_(Char c)); - template INLINE(void Append_(const Char* chars)); - template - INLINE(void AppendUnchecked_(const Char* chars)); - INLINE(void Append(char c)) { if (is_ascii_) { Append_(c); @@ -123,10 +117,18 @@ class BasicJsonStringifier BASE_EMBEDDED { void SerializeString(Handle object); + template + INLINE(void SerializeStringUnchecked_(const SrcChar* src, + DestChar* dest, + int length)); + template INLINE(void SerializeString_(Vector vector, Handle string)); + template + INLINE(bool DoNotEscape(Char c)); + template INLINE(Vector GetCharVector(Handle string)); @@ -152,12 +154,14 @@ class BasicJsonStringifier BASE_EMBEDDED { int part_length_; bool is_ascii_; - static const int kJsonQuotesCharactersPerEntry = 8; - static const char* const JsonQuotes; + static const int kJsonEscapeTableEntrySize = 8; + static const char* const JsonEscapeTable; }; -const char* const BasicJsonStringifier::JsonQuotes = +// Translation table to escape ASCII characters. +// Table entries start at a multiple of 8 and are null-terminated. +const char* const BasicJsonStringifier::JsonEscapeTable = "\\u0000\0 \\u0001\0 \\u0002\0 \\u0003\0 " "\\u0004\0 \\u0005\0 \\u0006\0 \\u0007\0 " "\\b\0 \\t\0 \\n\0 \\u000b\0 " @@ -235,31 +239,12 @@ void BasicJsonStringifier::Append_(Char c) { } -template -void BasicJsonStringifier::AppendUnchecked_(Char c) { - if (is_ascii) { - SeqAsciiString::cast(*current_part_)->SeqAsciiStringSet( - current_index_++, c); - } else { - SeqTwoByteString::cast(*current_part_)->SeqTwoByteStringSet( - current_index_++, c); - } - ASSERT(current_index_ < part_length_); -} - - template void BasicJsonStringifier::Append_(const Char* chars) { for ( ; *chars != '\0'; chars++) Append_(*chars); } -template -void BasicJsonStringifier::AppendUnchecked_(const Char* chars) { - for ( ; *chars != '\0'; chars++) AppendUnchecked_(*chars); -} - - Handle BasicJsonStringifier::GetProperty(Handle object, Handle key) { LookupResult lookup(isolate_); @@ -579,34 +564,60 @@ void BasicJsonStringifier::ChangeEncoding() { } +template +void BasicJsonStringifier::SerializeStringUnchecked_(const SrcChar* src, + DestChar* dest, + int length) { + dest += current_index_; + DestChar* dest_start = dest; + + *(dest++) = '"'; + for (int i = 0; i < length; i++) { + SrcChar c = src[i]; + if (DoNotEscape(c)) { + *(dest++) = c; + } else { + const char* chars = &JsonEscapeTable[c * kJsonEscapeTableEntrySize]; + while (*chars != '\0') *(dest++) = *(chars++); + } + } + + *(dest++) = '"'; + current_index_ += dest - dest_start; +} + + template void BasicJsonStringifier::SerializeString_(Vector vector, Handle string) { int length = vector.length(); - if (current_index_ + (length << 3) < (part_length_ - 2)) { - AssertNoAllocation no_allocation_scope; - AppendUnchecked_('"'); - for (int i = 0; i < length; i++) { - Char c = vector[i]; - if ((c >= '#' && c <= '~' && c != '\\') || - (!is_ascii && ((c & 0xFF80) != 0))) { - AppendUnchecked_(c); - } else { - AppendUnchecked_( - &JsonQuotes[c * kJsonQuotesCharactersPerEntry]); - } + // We make a rough estimate to find out if the current string can be + // serialized without allocating a new string part. The worst case length of + // an escaped character is 6. Shifting left by 3 is a more pessimistic + // estimate than multiplying by 6, but faster to calculate. + static const int kEnclosingQuotesLength = 2; + if (current_index_ + (length << 3) + kEnclosingQuotesLength < part_length_) { + if (is_ascii) { + SerializeStringUnchecked_( + vector.start(), + SeqAsciiString::cast(*current_part_)->GetChars(), + length); + } else { + SerializeStringUnchecked_( + vector.start(), + SeqTwoByteString::cast(*current_part_)->GetChars(), + length); } - AppendUnchecked_('"'); } else { Append_('"'); String* string_location = *string; for (int i = 0; i < length; i++) { Char c = vector[i]; - if ((c >= '#' && c <= '~' && c != '\\') || - (!is_ascii && ((c & 0xFF80) != 0))) { + if (DoNotEscape(c)) { Append_(c); } else { - Append_(&JsonQuotes[c * kJsonQuotesCharactersPerEntry]); + Append_( + &JsonEscapeTable[c * kJsonEscapeTableEntrySize]); } // If GC moved the string, we need to refresh the vector. if (*string != string_location) { @@ -619,6 +630,18 @@ void BasicJsonStringifier::SerializeString_(Vector vector, } +template <> +bool BasicJsonStringifier::DoNotEscape(char c) { + return c >= '#' && c <= '~' && c != '\\'; +} + + +template <> +bool BasicJsonStringifier::DoNotEscape(uc16 c) { + return (c >= 0x80) || (c >= '#' && c <= '~' && c != '\\'); +} + + template <> Vector BasicJsonStringifier::GetCharVector(Handle string) { String::FlatContent flat = string->GetFlatContent();