Move quoting of a JSON string to a specialized runtime function.
authorlrn@chromium.org <lrn@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 1 Dec 2010 10:04:34 +0000 (10:04 +0000)
committerlrn@chromium.org <lrn@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 1 Dec 2010 10:04:34 +0000 (10:04 +0000)
Previously used string replace regexp with function replacement.

Review URL: http://codereview.chromium.org/5443001

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

src/json.js
src/runtime.cc
src/runtime.h
src/v8-counters.h

index 5993100f5395a336b36089231e3e16344bd46bd2..feeedda76ef6f76d4ea30e3f5ed68f6a98430620 100644 (file)
@@ -66,34 +66,10 @@ function JSONParse(text, reviver) {
   }
 }
 
-var characterQuoteCache = {
-  '\b': '\\b',  // ASCII 8, Backspace
-  '\t': '\\t',  // ASCII 9, Tab
-  '\n': '\\n',  // ASCII 10, Newline
-  '\f': '\\f',  // ASCII 12, Formfeed
-  '\r': '\\r',  // ASCII 13, Carriage Return
-  '\"': '\\"',
-  '\\': '\\\\'
-};
-
-function QuoteSingleJSONCharacter(c) {
-  if (c in characterQuoteCache) {
-    return characterQuoteCache[c];
-  }
-  var charCode = c.charCodeAt(0);
-  var result;
-  if (charCode < 16) result = '\\u000';
-  else if (charCode < 256) result = '\\u00';
-  else if (charCode < 4096) result = '\\u0';
-  else result = '\\u';
-  result += charCode.toString(16);
-  characterQuoteCache[c] = result;
-  return result;
-}
-
 function QuoteJSONString(str) {
-  var quotable = /[\\\"\x00-\x1f]/g;
-  return '"' + str.replace(quotable, QuoteSingleJSONCharacter) + '"';
+  var quoted_str = %QuoteJSONString(str);
+  if (IS_STRING(quoted_str)) return quoted_str;
+  return '"' + str + '"';
 }
 
 function StackContains(stack, val) {
index 6c66850647390656a62c50cec6b92de4146fbdd2..3691fe6a704ae66c7c3d23a786901244493028e9 100644 (file)
@@ -4521,6 +4521,167 @@ static MaybeObject* Runtime_URIUnescape(Arguments args) {
 }
 
 
+static const char* const JsonQuotes[128] = {
+    "\\u0000", "\\u0001", "\\u0002", "\\u0003",
+    "\\u0004", "\\u0005", "\\u0006", "\\u0007",
+    "\\b", "\\t", "\\n", "\\u000b",
+    "\\f", "\\r", "\\u000e", "\\u000f",
+    "\\u0010", "\\u0011", "\\u0012", "\\u0013",
+    "\\u0014", "\\u0015", "\\u0016", "\\u0017",
+    "\\u0018", "\\u0019", "\\u001a", "\\u001b",
+    "\\u001c", "\\u001d", "\\u001e", "\\u001f",
+    NULL, NULL, "\\\"", NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    "\\\\", NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL,
+};
+
+
+static const byte JsonQuoteLengths[128] = {
+    6, 6, 6, 6, 6, 6, 6, 6,
+    2, 2, 2, 6, 2, 2, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6,
+    1, 1, 2, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 2, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+
+template <typename Char>
+Char* WriteString(Char* dst, const char* src_string) {
+  char c;
+  for (c = *src_string; c; c = *src_string) {
+    *dst = c;
+    dst++;
+    src_string++;
+  }
+  return dst;
+}
+
+
+template <typename StringType>
+MaybeObject* AllocateRawString(int length);
+
+
+template <>
+MaybeObject* AllocateRawString<SeqTwoByteString>(int length) {
+  return Heap::AllocateRawTwoByteString(length);
+}
+
+
+template <>
+MaybeObject* AllocateRawString<SeqAsciiString>(int length) {
+  return Heap::AllocateRawAsciiString(length);
+}
+
+
+template <typename Char, typename StringType>
+static MaybeObject* QuoteJsonString(String* original,
+                                    Vector<const Char> characters) {
+  int length = characters.length();
+  int quoted_length = 0;
+  for (int i = 0; i < length; i++) {
+    unsigned int c = characters[i];
+    if (sizeof(Char) > 1u) {
+      quoted_length +=
+          (c >= sizeof(JsonQuoteLengths)) ? 1 : JsonQuoteLengths[c];
+    } else {
+      quoted_length += JsonQuoteLengths[c];
+    }
+  }
+  if (quoted_length == length) {
+    return Heap::undefined_value();
+  }
+  Counters::quote_json_char_count.Increment(length);
+
+  // Add space for quotes.
+  quoted_length += 2;
+
+  MaybeObject* new_alloc = AllocateRawString<StringType>(quoted_length);
+  Object* new_object;
+  if (!new_alloc->ToObject(&new_object)) {
+    Counters::quote_json_char_recount.Increment(length);
+    return new_alloc;
+  }
+  StringType* new_string = StringType::cast(new_object);
+
+
+  STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize);
+  Char* write_cursor = reinterpret_cast<Char*>(
+      new_string->address() + SeqAsciiString::kHeaderSize);
+  *(write_cursor++) = '"';
+  const Char* read_cursor = characters.start();
+  const Char* end = read_cursor + length;
+  while (read_cursor < end) {
+    Char c = *(read_cursor++);
+    if (sizeof(Char) > 1u && static_cast<unsigned>(c) >= sizeof(JsonQuotes)) {
+      *(write_cursor++) = c;
+    } else {
+      const char* replacement = JsonQuotes[static_cast<unsigned>(c)];
+      if (!replacement) {
+        *(write_cursor++) = c;
+      } else {
+        write_cursor = WriteString(write_cursor, replacement);
+      }
+    }
+  }
+  *(write_cursor++) = '"';
+  ASSERT_EQ(SeqAsciiString::kHeaderSize + quoted_length * sizeof(Char),
+            reinterpret_cast<Address>(write_cursor) - new_string->address());
+  return new_string;
+}
+
+
+static MaybeObject* Runtime_QuoteJSONString(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>(str, str->ToUC16Vector());
+  } else {
+    return QuoteJsonString<char, SeqAsciiString>(str, str->ToAsciiVector());
+  }
+}
+
+
 static MaybeObject* Runtime_StringParseInt(Arguments args) {
   NoHandleAllocation ha;
 
index f9ebbc42ef8f62f6cbd63685b641cf413fee82d1..98782b1e4a1c839c84674e78aae95fca7d92c10b 100644 (file)
@@ -100,6 +100,7 @@ namespace internal {
   F(CharFromCode, 1, 1) \
   F(URIEscape, 1, 1) \
   F(URIUnescape, 1, 1) \
+  F(QuoteJSONString, 1, 1) \
   \
   F(NumberToString, 1, 1) \
   F(NumberToStringSkipCache, 1, 1) \
index 60e8741d895f2c289fca5cc0de6461328a764bb2..af5095de8b44b07a9536624e7dd23b62974d856e 100644 (file)
@@ -224,7 +224,9 @@ namespace internal {
   SC(math_sqrt, V8.MathSqrt)                                          \
   SC(math_tan, V8.MathTan)                                            \
   SC(transcendental_cache_hit, V8.TranscendentalCacheHit)             \
-  SC(transcendental_cache_miss, V8.TranscendentalCacheMiss)
+  SC(transcendental_cache_miss, V8.TranscendentalCacheMiss)           \
+  SC(quote_json_char_count, V8.QuoteJsonCharacterCount)               \
+  SC(quote_json_char_recount, V8.QuoteJsonCharacterReCount)           \
 
 
 // This file contains all the v8 counters that are in use.