⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 183988 in webkit


Ignore:
Timestamp:
May 8, 2015, 1:44:23 AM (11 years ago)
Author:
akling@apple.com
Message:

Optimize serialization of quoted JSON strings.
<https://webkit.org/b/144754>

Reviewed by Darin Adler.

Source/JavaScriptCore:

Optimized the serialization of quoted strings into JSON by moving the logic into
StringBuilder so it can make smarter decisions about buffering.

12% progression on Kraken/json-stringify-tinderbox (on my Mac Pro.)

  • bytecompiler/NodesCodegen.cpp:

(JSC::ObjectPatternNode::toString): Use the new StringBuilder API.

  • runtime/JSONObject.h:
  • runtime/JSONObject.cpp:

(JSC::Stringifier::Holder::appendNextProperty):
(JSC::appendStringToStringBuilder): Deleted.
(JSC::appendQuotedJSONStringToBuilder): Deleted.
(JSC::Stringifier::appendQuotedString): Deleted.
(JSC::Stringifier::appendStringifiedValue): Moved the bulk of this logic
to StringBuilder and call that from here.

Source/WebKit2:

  • NetworkProcess/cache/NetworkCacheEntry.cpp:

(WebKit::NetworkCache::Entry::asJSON): Use the new StringBuilder API.

Source/WTF:

Add a StringBuilder API for appending a quoted JSON string. This is used by
JSON.stringify() to implement efficient appending of strings while escaping
quotes, control characters and \uNNNN-style characters.

The main benefit comes from only doing a single buffer expansion up front,
instead of doing it every time we append something. The fudge factor is pretty
large, since the maximum number of output characters per input character is 6.

The first landing of this patch had two bugs in it:

  • Made \uNNNN escapes uppercase hexadecimal instead of lowercase.
  • Didn't preallocate enough space for 8-bit input strings.

Both were caught by existing tests on our bots, and both were due to last-minute
changes before landing. :/

  • wtf/text/StringBuilder.cpp:

(WTF::appendQuotedJSONStringInternal):
(WTF::StringBuilder::appendQuotedJSONString):

  • wtf/text/StringBuilder.h:
Location:
trunk/Source
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r183977 r183988  
     12015-05-08  Andreas Kling  <akling@apple.com>
     2
     3        Optimize serialization of quoted JSON strings.
     4        <https://webkit.org/b/144754>
     5
     6        Reviewed by Darin Adler.
     7
     8        Optimized the serialization of quoted strings into JSON by moving the logic into
     9        StringBuilder so it can make smarter decisions about buffering.
     10
     11        12% progression on Kraken/json-stringify-tinderbox (on my Mac Pro.)
     12
     13        * bytecompiler/NodesCodegen.cpp:
     14        (JSC::ObjectPatternNode::toString): Use the new StringBuilder API.
     15
     16        * runtime/JSONObject.h:
     17        * runtime/JSONObject.cpp:
     18        (JSC::Stringifier::Holder::appendNextProperty):
     19        (JSC::appendStringToStringBuilder): Deleted.
     20        (JSC::appendQuotedJSONStringToBuilder): Deleted.
     21        (JSC::Stringifier::appendQuotedString): Deleted.
     22        (JSC::Stringifier::appendStringifiedValue): Moved the bulk of this logic
     23        to StringBuilder and call that from here.
     24
    1252015-05-07  Commit Queue  <commit-queue@webkit.org>
    226
  • trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r183977 r183988  
    30883088    for (size_t i = 0; i < m_targetPatterns.size(); i++) {
    30893089        if (m_targetPatterns[i].wasString)
    3090             appendQuotedJSONStringToBuilder(builder, m_targetPatterns[i].propertyName.string());
     3090            builder.appendQuotedJSONString(m_targetPatterns[i].propertyName.string());
    30913091        else
    30923092            builder.append(m_targetPatterns[i].propertyName.string());
  • trunk/Source/JavaScriptCore/runtime/JSONObject.cpp

    r183977 r183988  
    108108    friend class Holder;
    109109
    110     static void appendQuotedString(StringBuilder&, const String&);
    111 
    112110    JSValue toJSON(JSValue, const PropertyNameForFunctionCall&);
    113111
     
    256254}
    257255
    258 template <typename CharType>
    259 static void appendStringToStringBuilder(StringBuilder& builder, const CharType* data, int length)
    260 {
    261     for (int i = 0; i < length; ++i) {
    262         int start = i;
    263         while (i < length && (data[i] > 0x1F && data[i] != '"' && data[i] != '\\'))
    264             ++i;
    265         builder.append(data + start, i - start);
    266         if (i >= length)
    267             break;
    268         switch (data[i]) {
    269         case '\t':
    270             builder.append('\\');
    271             builder.append('t');
    272             break;
    273         case '\r':
    274             builder.append('\\');
    275             builder.append('r');
    276             break;
    277         case '\n':
    278             builder.append('\\');
    279             builder.append('n');
    280             break;
    281         case '\f':
    282             builder.append('\\');
    283             builder.append('f');
    284             break;
    285         case '\b':
    286             builder.append('\\');
    287             builder.append('b');
    288             break;
    289         case '"':
    290             builder.append('\\');
    291             builder.append('"');
    292             break;
    293         case '\\':
    294             builder.append('\\');
    295             builder.append('\\');
    296             break;
    297         default:
    298             static const char hexDigits[] = "0123456789abcdef";
    299             UChar ch = data[i];
    300             LChar hex[] = { '\\', 'u', static_cast<LChar>(hexDigits[(ch >> 12) & 0xF]), static_cast<LChar>(hexDigits[(ch >> 8) & 0xF]), static_cast<LChar>(hexDigits[(ch >> 4) & 0xF]), static_cast<LChar>(hexDigits[ch & 0xF]) };
    301             builder.append(hex, WTF_ARRAY_LENGTH(hex));
    302             break;
    303         }
    304     }
    305 }
    306 
    307 void appendQuotedJSONStringToBuilder(StringBuilder& builder, const String& message)
    308 {
    309     builder.append('"');
    310 
    311     if (message.is8Bit())
    312         appendStringToStringBuilder(builder, message.characters8(), message.length());
    313     else
    314         appendStringToStringBuilder(builder, message.characters16(), message.length());
    315 
    316     builder.append('"');
    317 }
    318 
    319 void Stringifier::appendQuotedString(StringBuilder& builder, const String& value)
    320 {
    321     appendQuotedJSONStringToBuilder(builder, value);
    322 }
    323 
    324256inline JSValue Stringifier::toJSON(JSValue value, const PropertyNameForFunctionCall& propertyName)
    325257{
     
    386318    String stringValue;
    387319    if (value.getString(m_exec, stringValue)) {
    388         appendQuotedString(builder, stringValue);
     320        builder.appendQuotedJSONString(stringValue);
    389321        return StringifySucceeded;
    390322    }
     
    557489
    558490        // Append the property name.
    559         appendQuotedString(builder, propertyName.string());
     491        builder.appendQuotedJSONString(propertyName.string());
    560492        builder.append(':');
    561493        if (stringifier.willIndent())
  • trunk/Source/JavaScriptCore/runtime/JSONObject.h

    r183977 r183988  
    6363JS_EXPORT_PRIVATE String JSONStringify(ExecState*, JSValue, unsigned indent);
    6464
    65 JS_EXPORT_PRIVATE void appendQuotedJSONStringToBuilder(StringBuilder&, const String&);
    66 
    6765   
    6866} // namespace JSC
  • trunk/Source/WTF/ChangeLog

    r183977 r183988  
     12015-05-08  Andreas Kling  <akling@apple.com>
     2
     3        Optimize serialization of quoted JSON strings.
     4        <https://webkit.org/b/144754>
     5
     6        Reviewed by Darin Adler.
     7
     8        Add a StringBuilder API for appending a quoted JSON string. This is used by
     9        JSON.stringify() to implement efficient appending of strings while escaping
     10        quotes, control characters and \uNNNN-style characters.
     11
     12        The main benefit comes from only doing a single buffer expansion up front,
     13        instead of doing it every time we append something. The fudge factor is pretty
     14        large, since the maximum number of output characters per input character is 6.
     15
     16        The first landing of this patch had two bugs in it:
     17
     18        - Made \uNNNN escapes uppercase hexadecimal instead of lowercase.
     19        - Didn't preallocate enough space for 8-bit input strings.
     20
     21        Both were caught by existing tests on our bots, and both were due to last-minute
     22        changes before landing. :/
     23
     24        * wtf/text/StringBuilder.cpp:
     25        (WTF::appendQuotedJSONStringInternal):
     26        (WTF::StringBuilder::appendQuotedJSONString):
     27        * wtf/text/StringBuilder.h:
     28
    1292015-05-07  Commit Queue  <commit-queue@webkit.org>
    230
  • trunk/Source/WTF/wtf/text/StringBuilder.cpp

    r183977 r183988  
    2929
    3030#include "IntegerToStringConversion.h"
     31#include "MathExtras.h"
    3132#include "WTFString.h"
    3233#include <wtf/dtoa.h>
     
    361362}
    362363
     364template <typename OutputCharacterType, typename InputCharacterType>
     365static void appendQuotedJSONStringInternal(OutputCharacterType*& output, const InputCharacterType* input, unsigned length)
     366{
     367    for (const InputCharacterType* end = input + length; input != end; ++input) {
     368        if (*input > 0x1F && *input != '"' && *input != '\\') {
     369            *output++ = *input;
     370            continue;
     371        }
     372        switch (*input) {
     373        case '\t':
     374            *output++ = '\\';
     375            *output++ = 't';
     376            break;
     377        case '\r':
     378            *output++ = '\\';
     379            *output++ = 'r';
     380            break;
     381        case '\n':
     382            *output++ = '\\';
     383            *output++ = 'n';
     384            break;
     385        case '\f':
     386            *output++ = '\\';
     387            *output++ = 'f';
     388            break;
     389        case '\b':
     390            *output++ = '\\';
     391            *output++ = 'b';
     392            break;
     393        case '"':
     394            *output++ = '\\';
     395            *output++ = '"';
     396            break;
     397        case '\\':
     398            *output++ = '\\';
     399            *output++ = '\\';
     400            break;
     401        default:
     402            ASSERT((*input & 0xFF00) == 0);
     403            static const char hexDigits[] = "0123456789abcdef";
     404            *output++ = '\\';
     405            *output++ = 'u';
     406            *output++ = '0';
     407            *output++ = '0';
     408            *output++ = static_cast<LChar>(hexDigits[(*input >> 4) & 0xF]);
     409            *output++ = static_cast<LChar>(hexDigits[*input & 0xF]);
     410            break;
     411        }
     412    }
     413}
     414
     415void StringBuilder::appendQuotedJSONString(const String& string)
     416{
     417    // Make sure we have enough buffer space to append this string without having
     418    // to worry about reallocating in the middle.
     419    // The 2 is for the '"' quotes on each end.
     420    // The 6 is for characters that need to be \uNNNN encoded.
     421    size_t maximumCapacityRequired = length() + 2 + string.length() * 6;
     422    RELEASE_ASSERT(maximumCapacityRequired < std::numeric_limits<unsigned>::max());
     423
     424    if (is8Bit() && !string.is8Bit())
     425        allocateBufferUpConvert(m_bufferCharacters8, roundUpToPowerOfTwo(maximumCapacityRequired));
     426    else
     427        reserveCapacity(roundUpToPowerOfTwo(maximumCapacityRequired));
     428
     429    if (is8Bit()) {
     430        ASSERT(string.is8Bit());
     431        LChar* output = m_bufferCharacters8 + m_length;
     432        *output++ = '"';
     433        appendQuotedJSONStringInternal(output, string.characters8(), string.length());
     434        *output++ = '"';
     435        m_length = output - m_bufferCharacters8;
     436    } else {
     437        UChar* output = m_bufferCharacters16 + m_length;
     438        *output++ = '"';
     439        if (string.is8Bit())
     440            appendQuotedJSONStringInternal(output, string.characters8(), string.length());
     441        else
     442            appendQuotedJSONStringInternal(output, string.characters16(), string.length());
     443        *output++ = '"';
     444        m_length = output - m_bufferCharacters16;
     445    }
     446}
     447
    363448} // namespace WTF
  • trunk/Source/WTF/wtf/text/StringBuilder.h

    r183977 r183988  
    160160    }
    161161
     162    WTF_EXPORT_PRIVATE void appendQuotedJSONString(const String&);
     163
    162164    template<unsigned charactersCount>
    163165    ALWAYS_INLINE void appendLiteral(const char (&characters)[charactersCount]) { append(characters, charactersCount - 1); }
  • trunk/Source/WebKit2/ChangeLog

    r183986 r183988  
     12015-05-08  Andreas Kling  <akling@apple.com>
     2
     3        Optimize serialization of quoted JSON strings.
     4        <https://webkit.org/b/144754>
     5
     6        Reviewed by Darin Adler.
     7
     8        * NetworkProcess/cache/NetworkCacheEntry.cpp:
     9        (WebKit::NetworkCache::Entry::asJSON): Use the new StringBuilder API.
     10
    1112015-05-08  Commit Queue  <commit-queue@webkit.org>
    212
  • trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheEntry.cpp

    r183977 r183988  
    3131#include "NetworkCacheDecoder.h"
    3232#include "NetworkCacheEncoder.h"
    33 #include <JavaScriptCore/JSONObject.h>
    3433#include <WebCore/ResourceRequest.h>
    3534#include <WebCore/SharedBuffer.h>
     
    160159    json.appendLiteral("{\n");
    161160    json.appendLiteral("\"hash\": ");
    162     JSC::appendQuotedJSONStringToBuilder(json, m_key.hashAsString());
     161    json.appendQuotedJSONString(m_key.hashAsString());
    163162    json.appendLiteral(",\n");
    164163    json.appendLiteral("\"bodySize\": ");
     
    169168    json.appendLiteral(",\n");
    170169    json.appendLiteral("\"partition\": ");
    171     JSC::appendQuotedJSONStringToBuilder(json, m_key.partition());
     170    json.appendQuotedJSONString(m_key.partition());
    172171    json.appendLiteral(",\n");
    173172    json.appendLiteral("\"timestamp\": ");
     
    175174    json.appendLiteral(",\n");
    176175    json.appendLiteral("\"URL\": ");
    177     JSC::appendQuotedJSONStringToBuilder(json, m_response.url().string());
     176    json.appendQuotedJSONString(m_response.url().string());
    178177    json.appendLiteral(",\n");
    179178    json.appendLiteral("\"bodyHash\": ");
    180     JSC::appendQuotedJSONStringToBuilder(json, info.bodyHash);
     179    json.appendQuotedJSONString(info.bodyHash);
    181180    json.appendLiteral(",\n");
    182181    json.appendLiteral("\"bodyShareCount\": ");
     
    190189        firstHeader = false;
    191190        json.appendLiteral("    ");
    192         JSC::appendQuotedJSONStringToBuilder(json, header.key);
     191        json.appendQuotedJSONString(header.key);
    193192        json.appendLiteral(": ");
    194         JSC::appendQuotedJSONStringToBuilder(json, header.value);
     193        json.appendQuotedJSONString(header.value);
    195194    }
    196195    json.appendLiteral("\n}\n");
Note: See TracChangeset for help on using the changeset viewer.