Optimize JSON stringify by allowing QuoteJSONString to prefix with a comma.
authorsandholm@chromium.org <sandholm@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Sun, 16 Jan 2011 21:29:32 +0000 (21:29 +0000)
committersandholm@chromium.org <sandholm@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Sun, 16 Jan 2011 21:29:32 +0000 (21:29 +0000)
Review URL: http://codereview.chromium.org/6335004

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6337 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/json.js
src/runtime.cc
src/runtime.h

index e5364e6..e90d5d1 100644 (file)
@@ -193,14 +193,14 @@ function BasicSerializeArray(value, stack, builder) {
     // First entry is a string. Remaining entries are likely to be strings too.
     builder.push(%QuoteJSONString(val));
     for (var i = 1; i < len; i++) {
-      builder.push(",");
       val = value[i];
       if (IS_STRING(val)) {
-        builder.push(%QuoteJSONString(val));
+        builder.push(%QuoteJSONStringComma(val));
       } else {
+        builder.push(",");
         var before = builder.length;
         BasicJSONSerialize(i, value[i], stack, builder);
-        if (before == builder.length) builder.push("null");
+        if (before == builder.length) builder[before - 1] = ",null";
       }
     }
   } else if (IS_NUMBER(val)) {
@@ -216,7 +216,7 @@ function BasicSerializeArray(value, stack, builder) {
       } else {
         var before = builder.length;
         BasicJSONSerialize(i, value[i], stack, builder);
-        if (before == builder.length) builder.push("null");
+        if (before == builder.length) builder[before - 1] = ",null";
       }
     }
   } else {
@@ -228,7 +228,7 @@ function BasicSerializeArray(value, stack, builder) {
       before = builder.length;
       val = value[i];
       BasicJSONSerialize(i, val, stack, builder);
-      if (before == builder.length) builder.push("null");
+      if (before == builder.length) builder[before - 1] = ",null";
     }
   }
   stack.pop();
@@ -241,9 +241,14 @@ function BasicSerializeObject(value, stack, builder) {
     throw MakeTypeError('circular_structure', []);
   }
   builder.push("{");
+  var first = true;
   for (var p in value) {
     if (%HasLocalProperty(value, p)) {
-      builder.push(%QuoteJSONString(p));
+      if (!first) {
+        builder.push(%QuoteJSONStringComma(p));
+      } else {
+        builder.push(%QuoteJSONString(p));
+      }
       builder.push(":");
       var before = builder.length;
       BasicJSONSerialize(p, value[p], stack, builder);
@@ -251,16 +256,12 @@ function BasicSerializeObject(value, stack, builder) {
         builder.pop();
         builder.pop();
       } else {
-        builder.push(",");
+        first = false;
       }
     }
   }
   stack.pop();
-  if (builder.pop() != ",") {
-    builder.push("{}");  // Object has no own properties. Push "{" back on.
-  } else {
-    builder.push("}");
-  }
+  builder.push("}");
 }
 
 
index 316582d..58476d9 100644 (file)
@@ -4621,12 +4621,12 @@ MaybeObject* AllocateRawString<SeqAsciiString>(int length) {
 }
 
 
-template <typename Char, typename StringType>
+template <typename Char, typename StringType, bool comma>
 static MaybeObject* SlowQuoteJsonString(Vector<const Char> characters) {
   int length = characters.length();
   const Char* read_cursor = characters.start();
   const Char* end = read_cursor + length;
-  const int kSpaceForQuotes = 2;
+  const int kSpaceForQuotes = 2 + (comma ? 1 :0);
   int quoted_length = kSpaceForQuotes;
   while (read_cursor < end) {
     Char c = *(read_cursor++);
@@ -4645,6 +4645,7 @@ static MaybeObject* SlowQuoteJsonString(Vector<const Char> characters) {
 
   Char* write_cursor = reinterpret_cast<Char*>(
       new_string->address() + SeqAsciiString::kHeaderSize);
+  if (comma) *(write_cursor++) = ',';
   *(write_cursor++) = '"';
 
   read_cursor = characters.start();
@@ -4666,14 +4667,14 @@ static MaybeObject* SlowQuoteJsonString(Vector<const Char> characters) {
 }
 
 
-template <typename Char, typename StringType>
+template <typename Char, typename StringType, bool comma>
 static MaybeObject* QuoteJsonString(Vector<const Char> characters) {
   int length = characters.length();
   Counters::quote_json_char_count.Increment(length);
-  const int kSpaceForQuotes = 2;
+  const int kSpaceForQuotes = 2 + (comma ? 1 :0);
   int worst_case_length = length * kJsonQuoteWorstCaseBlowup + kSpaceForQuotes;
   if (worst_case_length > kMaxGuaranteedNewSpaceString) {
-    return SlowQuoteJsonString<Char, StringType>(characters);
+    return SlowQuoteJsonString<Char, StringType, comma>(characters);
   }
 
   MaybeObject* new_alloc = AllocateRawString<StringType>(worst_case_length);
@@ -4686,7 +4687,7 @@ static MaybeObject* QuoteJsonString(Vector<const Char> characters) {
     // handle it being allocated in old space as may happen in the third
     // attempt.  See CALL_AND_RETRY in heap-inl.h and similar code in
     // CEntryStub::GenerateCore.
-    return SlowQuoteJsonString<Char, StringType>(characters);
+    return SlowQuoteJsonString<Char, StringType, comma>(characters);
   }
   StringType* new_string = StringType::cast(new_object);
   ASSERT(Heap::new_space()->Contains(new_string));
@@ -4694,6 +4695,7 @@ static MaybeObject* QuoteJsonString(Vector<const Char> characters) {
   STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize);
   Char* write_cursor = reinterpret_cast<Char*>(
       new_string->address() + SeqAsciiString::kHeaderSize);
+  if (comma) *(write_cursor++) = ',';
   *(write_cursor++) = '"';
 
   const Char* read_cursor = characters.start();
@@ -4744,13 +4746,32 @@ static MaybeObject* Runtime_QuoteJSONString(Arguments args) {
     ASSERT(str->IsFlat());
   }
   if (str->IsTwoByteRepresentation()) {
-    return QuoteJsonString<uc16, SeqTwoByteString>(str->ToUC16Vector());
+    return QuoteJsonString<uc16, SeqTwoByteString, false>(str->ToUC16Vector());
   } else {
-    return QuoteJsonString<char, SeqAsciiString>(str->ToAsciiVector());
+    return QuoteJsonString<char, SeqAsciiString, false>(str->ToAsciiVector());
   }
 }
 
 
+static MaybeObject* Runtime_QuoteJSONStringComma(Arguments args) {
+  NoHandleAllocation ha;
+  CONVERT_CHECKED(String, str, args[0]);
+  if (!str->IsFlat()) {
+    MaybeObject* try_flatten = str->TryFlatten();
+    Object* flat;
+    if (!try_flatten->ToObject(&flat)) {
+      return try_flatten;
+    }
+    str = String::cast(flat);
+    ASSERT(str->IsFlat());
+  }
+  if (str->IsTwoByteRepresentation()) {
+    return QuoteJsonString<uc16, SeqTwoByteString, true>(str->ToUC16Vector());
+  } else {
+    return QuoteJsonString<char, SeqAsciiString, true>(str->ToAsciiVector());
+  }
+}
+
 
 static MaybeObject* Runtime_StringParseInt(Arguments args) {
   NoHandleAllocation ha;
index 200b160..dbd8d64 100644 (file)
@@ -106,6 +106,7 @@ namespace internal {
   F(URIEscape, 1, 1) \
   F(URIUnescape, 1, 1) \
   F(QuoteJSONString, 1, 1) \
+  F(QuoteJSONStringComma, 1, 1) \
   \
   F(NumberToString, 1, 1) \
   F(NumberToStringSkipCache, 1, 1) \