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

Changeset 183961 in webkit


Ignore:
Timestamp:
May 7, 2015, 5:17:48 PM (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 for 16-bit strings, since the maximum number of output characters per
input character is 6.

  • 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

    r183955 r183961  
     12015-05-07  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  Yusuke Suzuki  <utatane.tea@gmail.com>
    226
  • trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r183955 r183961  
    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

    r183928 r183961  
    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

    r182747 r183961  
    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

    r183936 r183961  
     12015-05-07  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 for 16-bit strings, since the maximum number of output characters per
     15        input character is 6.
     16
     17        * wtf/text/StringBuilder.cpp:
     18        (WTF::appendQuotedJSONStringInternal):
     19        (WTF::StringBuilder::appendQuotedJSONString):
     20        * wtf/text/StringBuilder.h:
     21
    1222015-05-07  Martin Robinson  <mrobinson@igalia.com>
    223
  • trunk/Source/WTF/wtf/text/StringBuilder.cpp

    r166120 r183961  
    2828#include "StringBuilder.h"
    2929
     30#include "ASCIICType.h"
    3031#include "IntegerToStringConversion.h"
     32#include "MathExtras.h"
    3133#include "WTFString.h"
    3234#include <wtf/dtoa.h>
     
    361363}
    362364
     365template <typename OutputCharacterType, typename InputCharacterType>
     366static void appendQuotedJSONStringInternal(OutputCharacterType*& output, const InputCharacterType* input, unsigned length)
     367{
     368    for (const InputCharacterType* end = input + length; input != end; ++input) {
     369        if (*input > 0x1F && *input != '"' && *input != '\\') {
     370            *output++ = *input;
     371            continue;
     372        }
     373        switch (*input) {
     374        case '\t':
     375            *output++ = '\\';
     376            *output++ = 't';
     377            break;
     378        case '\r':
     379            *output++ = '\\';
     380            *output++ = 'r';
     381            break;
     382        case '\n':
     383            *output++ = '\\';
     384            *output++ = 'n';
     385            break;
     386        case '\f':
     387            *output++ = '\\';
     388            *output++ = 'f';
     389            break;
     390        case '\b':
     391            *output++ = '\\';
     392            *output++ = 'b';
     393            break;
     394        case '"':
     395            *output++ = '\\';
     396            *output++ = '"';
     397            break;
     398        case '\\':
     399            *output++ = '\\';
     400            *output++ = '\\';
     401            break;
     402        default:
     403            ASSERT((*input & 0xFF00) == 0);
     404            *output++ = '\\';
     405            *output++ = 'u';
     406            *output++ = '0';
     407            *output++ = '0';
     408            *output++ = upperNibbleToASCIIHexDigit(*input);
     409            *output++ = lowerNibbleToASCIIHexDigit(*input);
     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 'maximumOutputCharactersPerInputCharacter' is 6 for 16-bit characters
     421    // since they may need \uNNNN-style representation. 2 for 8-bit strings.
     422    unsigned maximumOutputCharactersPerInputCharacter = string.is8Bit() ? 2 : 6;
     423    size_t maximumCapacityRequired = length() + 2 + (string.length() * maximumOutputCharactersPerInputCharacter);
     424    RELEASE_ASSERT(maximumCapacityRequired < std::numeric_limits<unsigned>::max());
     425
     426    if (is8Bit() && !string.is8Bit())
     427        allocateBufferUpConvert(m_bufferCharacters8, roundUpToPowerOfTwo(maximumCapacityRequired));
     428    else
     429        reserveCapacity(roundUpToPowerOfTwo(maximumCapacityRequired));
     430
     431    if (is8Bit()) {
     432        ASSERT(string.is8Bit());
     433        LChar* output = m_bufferCharacters8 + m_length;
     434        *output++ = '"';
     435        appendQuotedJSONStringInternal(output, string.characters8(), string.length());
     436        *output++ = '"';
     437        m_length = output - m_bufferCharacters8;
     438    } else {
     439        UChar* output = m_bufferCharacters16 + m_length;
     440        *output++ = '"';
     441        if (string.is8Bit())
     442            appendQuotedJSONStringInternal(output, string.characters8(), string.length());
     443        else
     444            appendQuotedJSONStringInternal(output, string.characters16(), string.length());
     445        *output++ = '"';
     446        m_length = output - m_bufferCharacters16;
     447    }
     448}
     449
    363450} // namespace WTF
  • trunk/Source/WTF/wtf/text/StringBuilder.h

    r166120 r183961  
    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

    r183949 r183961  
     12015-05-07  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-07  Martin Robinson  <mrobinson@igalia.com>
    212
  • trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheEntry.cpp

    r183531 r183961  
    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.