<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[209173] trunk/Source</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/209173">209173</a></dd>
<dt>Author</dt> <dd>darin@apple.com</dd>
<dt>Date</dt> <dd>2016-11-30 20:32:50 -0800 (Wed, 30 Nov 2016)</dd>
</dl>
<h3>Log Message</h3>
<pre>Roll out StringBuilder changes from the previous patch.
They were a slowdown on a Kraken JSON test.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSONObjectcpp">trunk/Source/JavaScriptCore/runtime/JSONObject.cpp</a></li>
<li><a href="#trunkSourceWTFChangeLog">trunk/Source/WTF/ChangeLog</a></li>
<li><a href="#trunkSourceWTFwtftextStringBuildercpp">trunk/Source/WTF/wtf/text/StringBuilder.cpp</a></li>
<li><a href="#trunkSourceWTFwtftextStringBuilderh">trunk/Source/WTF/wtf/text/StringBuilder.h</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (209172 => 209173)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-12-01 03:56:12 UTC (rev 209172)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-12-01 04:32:50 UTC (rev 209173)
</span><span class="lines">@@ -1,3 +1,11 @@
</span><ins>+2016-11-30 Darin Adler <darin@apple.com>
+
+ Roll out StringBuilder changes from the previous patch.
+ They were a slowdown on a Kraken JSON test.
+
+ * runtime/JSONObject.cpp:
+ Roll out changes from below.
+
</ins><span class="cx"> 2016-11-30 Yusuke Suzuki <utatane.tea@gmail.com>
</span><span class="cx">
</span><span class="cx"> [JSC] Specifying same module entry point multiple times cause TypeError
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSONObjectcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSONObject.cpp (209172 => 209173)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSONObject.cpp        2016-12-01 03:56:12 UTC (rev 209172)
+++ trunk/Source/JavaScriptCore/runtime/JSONObject.cpp        2016-12-01 04:32:50 UTC (rev 209173)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2009-2016 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2009, 2016 Apple Inc. All rights reserved.
</ins><span class="cx"> *
</span><span class="cx"> * Redistribution and use in source and binary forms, with or without
</span><span class="cx"> * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -354,7 +354,7 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> if (value.isString()) {
</span><del>- builder.appendQuotedJSONString(asString(value)->viewWithUnderlyingString(*m_exec).view);
</del><ins>+ builder.appendQuotedJSONString(asString(value)->value(m_exec));
</ins><span class="cx"> return StringifySucceeded;
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWTFChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/ChangeLog (209172 => 209173)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/ChangeLog        2016-12-01 03:56:12 UTC (rev 209172)
+++ trunk/Source/WTF/ChangeLog        2016-12-01 04:32:50 UTC (rev 209173)
</span><span class="lines">@@ -1,5 +1,14 @@
</span><span class="cx"> 2016-11-30 Darin Adler <darin@apple.com>
</span><span class="cx">
</span><ins>+ Roll out StringBuilder changes from the previous patch.
+ They were a slowdown on a Kraken JSON test.
+
+ * wtf/text/StringBuilder.cpp:
+ * wtf/text/StringBuilder.h:
+ Roll out changes from below.
+
+2016-11-30 Darin Adler <darin@apple.com>
+
</ins><span class="cx"> Streamline and speed up tokenizer and segmented string classes
</span><span class="cx"> https://bugs.webkit.org/show_bug.cgi?id=165003
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWTFwtftextStringBuildercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/text/StringBuilder.cpp (209172 => 209173)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/text/StringBuilder.cpp        2016-12-01 03:56:12 UTC (rev 209172)
+++ trunk/Source/WTF/wtf/text/StringBuilder.cpp        2016-12-01 04:32:50 UTC (rev 209173)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2010-2016 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2010, 2013, 2016 Apple Inc. All rights reserved.
</ins><span class="cx"> * Copyright (C) 2012 Google Inc. All rights reserved.
</span><span class="cx"> *
</span><span class="cx"> * Redistribution and use in source and binary forms, with or without
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx">
</span><span class="cx"> #include "IntegerToStringConversion.h"
</span><span class="cx"> #include "MathExtras.h"
</span><ins>+#include "WTFString.h"
</ins><span class="cx"> #include <wtf/dtoa.h>
</span><span class="cx">
</span><span class="cx"> namespace WTF {
</span><span class="lines">@@ -39,18 +40,6 @@
</span><span class="cx"> return std::max(requiredLength, std::max(minimumCapacity, capacity * 2));
</span><span class="cx"> }
</span><span class="cx">
</span><del>-template<> ALWAYS_INLINE LChar* StringBuilder::bufferCharacters<LChar>()
-{
- ASSERT(m_is8Bit);
- return m_bufferCharacters8;
-}
-
-template<> ALWAYS_INLINE UChar* StringBuilder::bufferCharacters<UChar>()
-{
- ASSERT(!m_is8Bit);
- return m_bufferCharacters16;
-}
-
</del><span class="cx"> void StringBuilder::reifyString() const
</span><span class="cx"> {
</span><span class="cx"> // Check if the string already exists.
</span><span class="lines">@@ -108,7 +97,6 @@
</span><span class="cx"> void StringBuilder::allocateBuffer(const LChar* currentCharacters, unsigned requiredLength)
</span><span class="cx"> {
</span><span class="cx"> ASSERT(m_is8Bit);
</span><del>-
</del><span class="cx"> // Copy the existing data into a new buffer, set result to point to the end of the existing data.
</span><span class="cx"> auto buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters8);
</span><span class="cx"> memcpy(m_bufferCharacters8, currentCharacters, static_cast<size_t>(m_length) * sizeof(LChar)); // This can't overflow.
</span><span class="lines">@@ -124,7 +112,6 @@
</span><span class="cx"> void StringBuilder::allocateBuffer(const UChar* currentCharacters, unsigned requiredLength)
</span><span class="cx"> {
</span><span class="cx"> ASSERT(!m_is8Bit);
</span><del>-
</del><span class="cx"> // Copy the existing data into a new buffer, set result to point to the end of the existing data.
</span><span class="cx"> auto buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16);
</span><span class="cx"> memcpy(m_bufferCharacters16, currentCharacters, static_cast<size_t>(m_length) * sizeof(UChar)); // This can't overflow.
</span><span class="lines">@@ -137,11 +124,10 @@
</span><span class="cx">
</span><span class="cx"> // Allocate a new 16 bit buffer, copying in currentCharacters (which is 8 bit and may come
</span><span class="cx"> // from either m_string or m_buffer, neither will be reassigned until the copy has completed).
</span><del>-void StringBuilder::allocateBufferUpconvert(const LChar* currentCharacters, unsigned requiredLength)
</del><ins>+void StringBuilder::allocateBufferUpConvert(const LChar* currentCharacters, unsigned requiredLength)
</ins><span class="cx"> {
</span><span class="cx"> ASSERT(m_is8Bit);
</span><span class="cx"> ASSERT(requiredLength >= m_length);
</span><del>-
</del><span class="cx"> // Copy the existing data into a new buffer, set result to point to the end of the existing data.
</span><span class="cx"> auto buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16);
</span><span class="cx"> for (unsigned i = 0; i < m_length; ++i)
</span><span class="lines">@@ -155,7 +141,8 @@
</span><span class="cx"> ASSERT(m_buffer->length() == requiredLength);
</span><span class="cx"> }
</span><span class="cx">
</span><del>-template<> void StringBuilder::reallocateBuffer<LChar>(unsigned requiredLength)
</del><ins>+template <>
+void StringBuilder::reallocateBuffer<LChar>(unsigned requiredLength)
</ins><span class="cx"> {
</span><span class="cx"> // If the buffer has only one ref (by this StringBuilder), reallocate it,
</span><span class="cx"> // otherwise fall back to "allocate and copy" method.
</span><span class="lines">@@ -171,7 +158,8 @@
</span><span class="cx"> ASSERT(m_buffer->length() == requiredLength);
</span><span class="cx"> }
</span><span class="cx">
</span><del>-template<> void StringBuilder::reallocateBuffer<UChar>(unsigned requiredLength)
</del><ins>+template <>
+void StringBuilder::reallocateBuffer<UChar>(unsigned requiredLength)
</ins><span class="cx"> {
</span><span class="cx"> // If the buffer has only one ref (by this StringBuilder), reallocate it,
</span><span class="cx"> // otherwise fall back to "allocate and copy" method.
</span><span class="lines">@@ -178,7 +166,7 @@
</span><span class="cx"> m_string = String();
</span><span class="cx">
</span><span class="cx"> if (m_buffer->is8Bit())
</span><del>- allocateBufferUpconvert(m_buffer->characters8(), requiredLength);
</del><ins>+ allocateBufferUpConvert(m_buffer->characters8(), requiredLength);
</ins><span class="cx"> else if (m_buffer->hasOneRef())
</span><span class="cx"> m_buffer = StringImpl::reallocate(m_buffer.releaseNonNull(), requiredLength, m_bufferCharacters16);
</span><span class="cx"> else
</span><span class="lines">@@ -200,7 +188,7 @@
</span><span class="cx"> // Grow the string, if necessary.
</span><span class="cx"> if (newCapacity > m_length) {
</span><span class="cx"> if (!m_length) {
</span><del>- LChar* nullPlaceholder = nullptr;
</del><ins>+ LChar* nullPlaceholder = 0;
</ins><span class="cx"> allocateBuffer(nullPlaceholder, newCapacity);
</span><span class="cx"> } else if (m_string.is8Bit())
</span><span class="cx"> allocateBuffer(m_string.characters8(), newCapacity);
</span><span class="lines">@@ -213,7 +201,8 @@
</span><span class="cx">
</span><span class="cx"> // Make 'length' additional capacity be available in m_buffer, update m_string & m_length,
</span><span class="cx"> // return a pointer to the newly allocated storage.
</span><del>-template<typename CharacterType> ALWAYS_INLINE CharacterType* StringBuilder::appendUninitialized(unsigned length)
</del><ins>+template <typename CharType>
+ALWAYS_INLINE CharType* StringBuilder::appendUninitialized(unsigned length)
</ins><span class="cx"> {
</span><span class="cx"> ASSERT(length);
</span><span class="cx">
</span><span class="lines">@@ -228,15 +217,16 @@
</span><span class="cx"> unsigned currentLength = m_length;
</span><span class="cx"> m_string = String();
</span><span class="cx"> m_length = requiredLength;
</span><del>- return bufferCharacters<CharacterType>() + currentLength;
</del><ins>+ return getBufferCharacters<CharType>() + currentLength;
</ins><span class="cx"> }
</span><del>-
- return appendUninitializedSlow<CharacterType>(requiredLength);
</del><ins>+
+ return appendUninitializedSlow<CharType>(requiredLength);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> // Make 'length' additional capacity be available in m_buffer, update m_string & m_length,
</span><span class="cx"> // return a pointer to the newly allocated storage.
</span><del>-template<typename CharacterType> CharacterType* StringBuilder::appendUninitializedSlow(unsigned requiredLength)
</del><ins>+template <typename CharType>
+CharType* StringBuilder::appendUninitializedSlow(unsigned requiredLength)
</ins><span class="cx"> {
</span><span class="cx"> ASSERT(requiredLength);
</span><span class="cx">
</span><span class="lines">@@ -243,38 +233,19 @@
</span><span class="cx"> if (m_buffer) {
</span><span class="cx"> // If the buffer is valid it must be at least as long as the current builder contents!
</span><span class="cx"> ASSERT(m_buffer->length() >= m_length);
</span><del>- reallocateBuffer<CharacterType>(expandedCapacity(capacity(), requiredLength));
</del><ins>+
+ reallocateBuffer<CharType>(expandedCapacity(capacity(), requiredLength));
</ins><span class="cx"> } else {
</span><span class="cx"> ASSERT(m_string.length() == m_length);
</span><del>- allocateBuffer(m_length ? m_string.characters<CharacterType>() : nullptr, expandedCapacity(capacity(), requiredLength));
</del><ins>+ allocateBuffer(m_length ? m_string.characters<CharType>() : 0, expandedCapacity(capacity(), requiredLength));
</ins><span class="cx"> }
</span><span class="cx">
</span><del>- auto* result = bufferCharacters<CharacterType>() + m_length;
</del><ins>+ CharType* result = getBufferCharacters<CharType>() + m_length;
</ins><span class="cx"> m_length = requiredLength;
</span><span class="cx"> ASSERT(m_buffer->length() >= m_length);
</span><span class="cx"> return result;
</span><span class="cx"> }
</span><span class="cx">
</span><del>-inline UChar* StringBuilder::appendUninitializedUpconvert(unsigned length)
-{
- unsigned requiredLength = length + m_length;
- if (requiredLength < length)
- CRASH();
-
- if (m_buffer) {
- // If the buffer is valid it must be at least as long as the current builder contents!
- ASSERT(m_buffer->length() >= m_length);
- allocateBufferUpconvert(m_buffer->characters8(), expandedCapacity(capacity(), requiredLength));
- } else {
- ASSERT(m_string.length() == m_length);
- allocateBufferUpconvert(m_string.isNull() ? nullptr : m_string.characters8(), expandedCapacity(capacity(), requiredLength));
- }
-
- auto* result = m_bufferCharacters16 + m_length;
- m_length = requiredLength;
- return result;
-}
-
</del><span class="cx"> void StringBuilder::append(const UChar* characters, unsigned length)
</span><span class="cx"> {
</span><span class="cx"> if (!length)
</span><span class="lines">@@ -283,16 +254,32 @@
</span><span class="cx"> ASSERT(characters);
</span><span class="cx">
</span><span class="cx"> if (m_is8Bit) {
</span><del>- if (length == 1 && !(*characters & ~0xFF)) {
</del><ins>+ if (length == 1 && !(*characters & ~0xff)) {
</ins><span class="cx"> // Append as 8 bit character
</span><span class="cx"> LChar lChar = static_cast<LChar>(*characters);
</span><span class="cx"> append(&lChar, 1);
</span><span class="cx"> return;
</span><span class="cx"> }
</span><del>- memcpy(appendUninitializedUpconvert(length), characters, static_cast<size_t>(length) * sizeof(UChar));
</del><ins>+
+ // Calculate the new size of the builder after appending.
+ unsigned requiredLength = length + m_length;
+ if (requiredLength < length)
+ CRASH();
+
+ if (m_buffer) {
+ // If the buffer is valid it must be at least as long as the current builder contents!
+ ASSERT(m_buffer->length() >= m_length);
+
+ allocateBufferUpConvert(m_buffer->characters8(), expandedCapacity(capacity(), requiredLength));
+ } else {
+ ASSERT(m_string.length() == m_length);
+ allocateBufferUpConvert(m_string.isNull() ? 0 : m_string.characters8(), expandedCapacity(capacity(), requiredLength));
+ }
+
+ memcpy(m_bufferCharacters16 + m_length, characters, static_cast<size_t>(length) * sizeof(UChar));
+ m_length = requiredLength;
</ins><span class="cx"> } else
</span><span class="cx"> memcpy(appendUninitialized<UChar>(length), characters, static_cast<size_t>(length) * sizeof(UChar));
</span><del>-
</del><span class="cx"> ASSERT(m_buffer->length() >= m_length);
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -303,22 +290,19 @@
</span><span class="cx"> ASSERT(characters);
</span><span class="cx">
</span><span class="cx"> if (m_is8Bit) {
</span><del>- auto* destination = appendUninitialized<LChar>(length);
- // FIXME: How did we determine a threshold of 8 here was the right one?
- // Also, this kind of optimization could be useful anywhere else we have a
- // performance-sensitive code path that calls memcpy.
</del><ins>+ LChar* dest = appendUninitialized<LChar>(length);
</ins><span class="cx"> if (length > 8)
</span><del>- memcpy(destination, characters, length);
</del><ins>+ memcpy(dest, characters, static_cast<size_t>(length) * sizeof(LChar));
</ins><span class="cx"> else {
</span><span class="cx"> const LChar* end = characters + length;
</span><span class="cx"> while (characters < end)
</span><del>- *destination++ = *characters++;
</del><ins>+ *(dest++) = *(characters++);
</ins><span class="cx"> }
</span><span class="cx"> } else {
</span><del>- auto* destination = appendUninitialized<UChar>(length);
</del><ins>+ UChar* dest = appendUninitialized<UChar>(length);
</ins><span class="cx"> const LChar* end = characters + length;
</span><span class="cx"> while (characters < end)
</span><del>- *destination++ = *characters++;
</del><ins>+ *(dest++) = *(characters++);
</ins><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -401,58 +385,17 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><del>-template<typename LengthType, typename CharacterType> static LengthType quotedJSONStringLength(const CharacterType* input, unsigned length)
</del><ins>+template <typename OutputCharacterType, typename InputCharacterType>
+static void appendQuotedJSONStringInternal(OutputCharacterType*& output, const InputCharacterType* input, unsigned length)
</ins><span class="cx"> {
</span><del>- LengthType quotedLength = 2;
- for (unsigned i = 0; i < length; ++i) {
- auto character = input[i];
- if (LIKELY(character > 0x1F)) {
- switch (character) {
- case '"':
- case '\\':
- quotedLength += 2;
- break;
- default:
- ++quotedLength;
- break;
- }
- } else {
- switch (character) {
- case '\t':
- case '\r':
- case '\n':
- case '\f':
- case '\b':
- quotedLength += 2;
- break;
- default:
- quotedLength += 6;
- }
- }
- }
- return quotedLength;
-}
-
-template<typename CharacterType> static inline unsigned quotedJSONStringLength(const CharacterType* input, unsigned length)
-{
- constexpr auto maxSafeLength = (std::numeric_limits<unsigned>::max() - 2) / 6;
- if (length <= maxSafeLength)
- return quotedJSONStringLength<unsigned>(input, length);
- return quotedJSONStringLength<Checked<unsigned>>(input, length).unsafeGet();
-}
-
-template<typename OutputCharacterType, typename InputCharacterType> static inline void appendQuotedJSONStringInternal(OutputCharacterType* output, const InputCharacterType* input, unsigned length)
-{
- *output++ = '"';
- for (unsigned i = 0; i < length; ++i) {
- auto character = input[i];
- if (LIKELY(character > 0x1F)) {
- if (UNLIKELY(character == '"' || character == '\\'))
</del><ins>+ for (const InputCharacterType* end = input + length; input != end; ++input) {
+ if (LIKELY(*input > 0x1F)) {
+ if (*input == '"' || *input == '\\')
</ins><span class="cx"> *output++ = '\\';
</span><del>- *output++ = character;
</del><ins>+ *output++ = *input;
</ins><span class="cx"> continue;
</span><span class="cx"> }
</span><del>- switch (character) {
</del><ins>+ switch (*input) {
</ins><span class="cx"> case '\t':
</span><span class="cx"> *output++ = '\\';
</span><span class="cx"> *output++ = 't';
</span><span class="lines">@@ -474,35 +417,56 @@
</span><span class="cx"> *output++ = 'b';
</span><span class="cx"> break;
</span><span class="cx"> default:
</span><del>- ASSERT(!(character & ~0xFF));
</del><ins>+ ASSERT((*input & 0xFF00) == 0);
+ static const char hexDigits[] = "0123456789abcdef";
</ins><span class="cx"> *output++ = '\\';
</span><span class="cx"> *output++ = 'u';
</span><span class="cx"> *output++ = '0';
</span><span class="cx"> *output++ = '0';
</span><del>- *output++ = upperNibbleToLowercaseASCIIHexDigit(character);
- *output++ = lowerNibbleToLowercaseASCIIHexDigit(character);
</del><ins>+ *output++ = static_cast<LChar>(hexDigits[(*input >> 4) & 0xF]);
+ *output++ = static_cast<LChar>(hexDigits[*input & 0xF]);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx"> }
</span><del>- *output = '"';
</del><span class="cx"> }
</span><span class="cx">
</span><del>-void StringBuilder::appendQuotedJSONString(StringView string)
</del><ins>+void StringBuilder::appendQuotedJSONString(const String& string)
</ins><span class="cx"> {
</span><del>- unsigned length = string.length();
- if (string.is8Bit()) {
- auto* characters = string.characters8();
- if (m_is8Bit)
- appendQuotedJSONStringInternal(appendUninitialized<LChar>(quotedJSONStringLength(characters, length)), characters, length);
- else
- appendQuotedJSONStringInternal(appendUninitialized<UChar>(quotedJSONStringLength(characters, length)), characters, length);
</del><ins>+ // Make sure we have enough buffer space to append this string without having
+ // to worry about reallocating in the middle.
+ // The 2 is for the '"' quotes on each end.
+ // The 6 is for characters that need to be \uNNNN encoded.
+ Checked<unsigned> stringLength = string.length();
+ Checked<unsigned> maximumCapacityRequired = length();
+ maximumCapacityRequired += 2 + stringLength * 6;
+ unsigned allocationSize = maximumCapacityRequired.unsafeGet();
+ // This max() is here to allow us to allocate sizes between the range [2^31, 2^32 - 2] because roundUpToPowerOfTwo(1<<31 + some int smaller than 1<<31) == 0.
+ allocationSize = std::max(allocationSize, roundUpToPowerOfTwo(allocationSize));
+
+ if (is8Bit() && !string.is8Bit())
+ allocateBufferUpConvert(m_bufferCharacters8, allocationSize);
+ else
+ reserveCapacity(allocationSize);
+ ASSERT(m_buffer->length() >= allocationSize);
+
+ if (is8Bit()) {
+ ASSERT(string.is8Bit());
+ LChar* output = m_bufferCharacters8 + m_length;
+ *output++ = '"';
+ appendQuotedJSONStringInternal(output, string.characters8(), string.length());
+ *output++ = '"';
+ m_length = output - m_bufferCharacters8;
</ins><span class="cx"> } else {
</span><del>- auto* characters = string.characters16();
- if (m_is8Bit)
- appendQuotedJSONStringInternal(appendUninitializedUpconvert(quotedJSONStringLength(characters, length)), characters, length);
</del><ins>+ UChar* output = m_bufferCharacters16 + m_length;
+ *output++ = '"';
+ if (string.is8Bit())
+ appendQuotedJSONStringInternal(output, string.characters8(), string.length());
</ins><span class="cx"> else
</span><del>- appendQuotedJSONStringInternal(appendUninitialized<UChar>(quotedJSONStringLength(characters, length)), characters, length);
</del><ins>+ appendQuotedJSONStringInternal(output, string.characters16(), string.length());
+ *output++ = '"';
+ m_length = output - m_bufferCharacters16;
</ins><span class="cx"> }
</span><ins>+ ASSERT(m_buffer->length() >= m_length);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> } // namespace WTF
</span></span></pre></div>
<a id="trunkSourceWTFwtftextStringBuilderh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/text/StringBuilder.h (209172 => 209173)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/text/StringBuilder.h        2016-12-01 03:56:12 UTC (rev 209172)
+++ trunk/Source/WTF/wtf/text/StringBuilder.h        2016-12-01 04:32:50 UTC (rev 209173)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2009-2016 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2009-2010, 2012-2013, 2016 Apple Inc. All rights reserved.
</ins><span class="cx"> * Copyright (C) 2012 Google Inc. All rights reserved.
</span><span class="cx"> *
</span><span class="cx"> * Redistribution and use in source and binary forms, with or without
</span><span class="lines">@@ -24,18 +24,26 @@
</span><span class="cx"> * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</span><span class="cx"> */
</span><span class="cx">
</span><del>-#pragma once
</del><ins>+#ifndef StringBuilder_h
+#define StringBuilder_h
</ins><span class="cx">
</span><ins>+#include <wtf/text/AtomicString.h>
</ins><span class="cx"> #include <wtf/text/StringView.h>
</span><ins>+#include <wtf/text/WTFString.h>
</ins><span class="cx">
</span><span class="cx"> namespace WTF {
</span><span class="cx">
</span><span class="cx"> class StringBuilder {
</span><del>- // Disallow copying since it's expensive and we don't want anyone to do it by accident.
</del><ins>+ // Disallow copying since it's expensive and we don't want code to do it by accident.
</ins><span class="cx"> WTF_MAKE_NONCOPYABLE(StringBuilder);
</span><span class="cx">
</span><span class="cx"> public:
</span><del>- StringBuilder() = default;
</del><ins>+ StringBuilder()
+ : m_length(0)
+ , m_is8Bit(true)
+ , m_bufferCharacters8(0)
+ {
+ }
</ins><span class="cx">
</span><span class="cx"> WTF_EXPORT_PRIVATE void append(const UChar*, unsigned);
</span><span class="cx"> WTF_EXPORT_PRIVATE void append(const LChar*, unsigned);
</span><span class="lines">@@ -42,27 +50,29 @@
</span><span class="cx">
</span><span class="cx"> ALWAYS_INLINE void append(const char* characters, unsigned length) { append(reinterpret_cast<const LChar*>(characters), length); }
</span><span class="cx">
</span><del>- void append(const AtomicString& atomicString) { append(atomicString.string()); }
</del><ins>+ void append(const AtomicString& atomicString)
+ {
+ append(atomicString.string());
+ }
</ins><span class="cx">
</span><span class="cx"> void append(const String& string)
</span><span class="cx"> {
</span><del>- unsigned length = string.length();
- if (!length)
</del><ins>+ if (!string.length())
</ins><span class="cx"> return;
</span><span class="cx">
</span><del>- // If we're appending to an empty string, and there is not a buffer
- // (reserveCapacity has not been called) then just retain the string.
</del><ins>+ // If we're appending to an empty string, and there is not a buffer (reserveCapacity has not been called)
+ // then just retain the string.
</ins><span class="cx"> if (!m_length && !m_buffer) {
</span><span class="cx"> m_string = string;
</span><del>- m_length = length;
- m_is8Bit = string.is8Bit();
</del><ins>+ m_length = string.length();
+ m_is8Bit = m_string.is8Bit();
</ins><span class="cx"> return;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> if (string.is8Bit())
</span><del>- append(string.characters8(), length);
</del><ins>+ append(string.characters8(), string.length());
</ins><span class="cx"> else
</span><del>- append(string.characters16(), length);
</del><ins>+ append(string.characters16(), string.length());
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void append(const StringBuilder& other)
</span><span class="lines">@@ -70,12 +80,11 @@
</span><span class="cx"> if (!other.m_length)
</span><span class="cx"> return;
</span><span class="cx">
</span><del>- // If we're appending to an empty string, and there is not a buffer
- // (reserveCapacity has not been called) then just retain the string.
</del><ins>+ // If we're appending to an empty string, and there is not a buffer (reserveCapacity has not been called)
+ // then just retain the string.
</ins><span class="cx"> if (!m_length && !m_buffer && !other.m_string.isNull()) {
</span><span class="cx"> m_string = other.m_string;
</span><span class="cx"> m_length = other.m_length;
</span><del>- m_is8Bit = other.m_is8Bit;
</del><span class="cx"> return;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -96,7 +105,6 @@
</span><span class="cx"> #if USE(CF)
</span><span class="cx"> WTF_EXPORT_PRIVATE void append(CFStringRef);
</span><span class="cx"> #endif
</span><del>-
</del><span class="cx"> #if USE(CF) && defined(__OBJC__)
</span><span class="cx"> void append(NSString *string) { append((__bridge CFStringRef)string); }
</span><span class="cx"> #endif
</span><span class="lines">@@ -103,20 +111,11 @@
</span><span class="cx">
</span><span class="cx"> void append(const String& string, unsigned offset, unsigned length)
</span><span class="cx"> {
</span><del>- ASSERT(offset <= string.length());
- ASSERT(offset + length <= string.length());
-
- if (!length)
</del><ins>+ if (!string.length())
</ins><span class="cx"> return;
</span><span class="cx">
</span><del>- // If we're appending to an empty string, and there is not a buffer
- // (reserveCapacity has not been called) then just retain the string.
- if (!offset && !m_length && !m_buffer && length == string.length()) {
- m_string = string;
- m_length = length;
- m_is8Bit = string.is8Bit();
</del><ins>+ if ((offset + length) > string.length())
</ins><span class="cx"> return;
</span><del>- }
</del><span class="cx">
</span><span class="cx"> if (string.is8Bit())
</span><span class="cx"> append(string.characters8() + offset, length);
</span><span class="lines">@@ -130,33 +129,37 @@
</span><span class="cx"> append(characters, strlen(characters));
</span><span class="cx"> }
</span><span class="cx">
</span><del>- void append(UChar character)
</del><ins>+ void append(UChar c)
</ins><span class="cx"> {
</span><span class="cx"> if (m_buffer && m_length < m_buffer->length() && m_string.isNull()) {
</span><span class="cx"> if (!m_is8Bit) {
</span><del>- m_bufferCharacters16[m_length++] = character;
</del><ins>+ m_bufferCharacters16[m_length++] = c;
</ins><span class="cx"> return;
</span><span class="cx"> }
</span><del>- if (!(character & ~0xFF)) {
- m_bufferCharacters8[m_length++] = static_cast<LChar>(character);
</del><ins>+
+ if (!(c & ~0xff)) {
+ m_bufferCharacters8[m_length++] = static_cast<LChar>(c);
</ins><span class="cx"> return;
</span><span class="cx"> }
</span><span class="cx"> }
</span><del>- append(&character, 1);
</del><ins>+ append(&c, 1);
</ins><span class="cx"> }
</span><span class="cx">
</span><del>- void append(LChar character)
</del><ins>+ void append(LChar c)
</ins><span class="cx"> {
</span><span class="cx"> if (m_buffer && m_length < m_buffer->length() && m_string.isNull()) {
</span><span class="cx"> if (m_is8Bit)
</span><del>- m_bufferCharacters8[m_length++] = character;
</del><ins>+ m_bufferCharacters8[m_length++] = c;
</ins><span class="cx"> else
</span><del>- m_bufferCharacters16[m_length++] = character;
</del><ins>+ m_bufferCharacters16[m_length++] = c;
</ins><span class="cx"> } else
</span><del>- append(&character, 1);
</del><ins>+ append(&c, 1);
</ins><span class="cx"> }
</span><span class="cx">
</span><del>- void append(char character) { append(static_cast<LChar>(character)); }
</del><ins>+ void append(char c)
+ {
+ append(static_cast<LChar>(c));
+ }
</ins><span class="cx">
</span><span class="cx"> void append(UChar32 c)
</span><span class="cx"> {
</span><span class="lines">@@ -168,9 +171,10 @@
</span><span class="cx"> append(U16_TRAIL(c));
</span><span class="cx"> }
</span><span class="cx">
</span><del>- WTF_EXPORT_PRIVATE void appendQuotedJSONString(StringView);
</del><ins>+ WTF_EXPORT_PRIVATE void appendQuotedJSONString(const String&);
</ins><span class="cx">
</span><del>- template<unsigned charactersCount> ALWAYS_INLINE void appendLiteral(const char (&characters)[charactersCount]) { append(characters, charactersCount - 1); }
</del><ins>+ template<unsigned charactersCount>
+ ALWAYS_INLINE void appendLiteral(const char (&characters)[charactersCount]) { append(characters, charactersCount - 1); }
</ins><span class="cx">
</span><span class="cx"> WTF_EXPORT_PRIVATE void appendNumber(int);
</span><span class="cx"> WTF_EXPORT_PRIVATE void appendNumber(unsigned int);
</span><span class="lines">@@ -216,15 +220,24 @@
</span><span class="cx"> return AtomicString(m_buffer.get(), 0, m_length);
</span><span class="cx"> }
</span><span class="cx">
</span><del>- unsigned length() const { return m_length; }
</del><ins>+ unsigned length() const
+ {
+ return m_length;
+ }
+
</ins><span class="cx"> bool isEmpty() const { return !m_length; }
</span><span class="cx">
</span><span class="cx"> WTF_EXPORT_PRIVATE void reserveCapacity(unsigned newCapacity);
</span><span class="cx">
</span><del>- unsigned capacity() const { return m_buffer ? m_buffer->length() : m_length; }
</del><ins>+ unsigned capacity() const
+ {
+ return m_buffer ? m_buffer->length() : m_length;
+ }
</ins><span class="cx">
</span><span class="cx"> WTF_EXPORT_PRIVATE void resize(unsigned newSize);
</span><ins>+
</ins><span class="cx"> WTF_EXPORT_PRIVATE bool canShrink() const;
</span><ins>+
</ins><span class="cx"> WTF_EXPORT_PRIVATE void shrinkToFit();
</span><span class="cx">
</span><span class="cx"> UChar operator[](unsigned i) const
</span><span class="lines">@@ -281,36 +294,43 @@
</span><span class="cx"> private:
</span><span class="cx"> void allocateBuffer(const LChar* currentCharacters, unsigned requiredLength);
</span><span class="cx"> void allocateBuffer(const UChar* currentCharacters, unsigned requiredLength);
</span><del>- void allocateBufferUpconvert(const LChar* currentCharacters, unsigned requiredLength);
- template<typename CharacterType> void reallocateBuffer(unsigned requiredLength);
- UChar* appendUninitializedUpconvert(unsigned length);
- template<typename CharacterType> CharacterType* appendUninitialized(unsigned length);
- template<typename CharacterType> CharacterType* appendUninitializedSlow(unsigned length);
- template<typename CharacterType> CharacterType* bufferCharacters();
</del><ins>+ void allocateBufferUpConvert(const LChar* currentCharacters, unsigned requiredLength);
+ template <typename CharType>
+ void reallocateBuffer(unsigned requiredLength);
+ template <typename CharType>
+ ALWAYS_INLINE CharType* appendUninitialized(unsigned length);
+ template <typename CharType>
+ CharType* appendUninitializedSlow(unsigned length);
+ template <typename CharType>
+ ALWAYS_INLINE CharType * getBufferCharacters();
</ins><span class="cx"> WTF_EXPORT_PRIVATE void reifyString() const;
</span><span class="cx">
</span><del>- unsigned m_length { 0 };
</del><ins>+ unsigned m_length;
</ins><span class="cx"> mutable String m_string;
</span><span class="cx"> RefPtr<StringImpl> m_buffer;
</span><del>- bool m_is8Bit { true };
</del><ins>+ bool m_is8Bit;
</ins><span class="cx"> union {
</span><del>- LChar* m_bufferCharacters8 { nullptr };
</del><ins>+ LChar* m_bufferCharacters8;
</ins><span class="cx"> UChar* m_bufferCharacters16;
</span><span class="cx"> };
</span><span class="cx"> };
</span><span class="cx">
</span><del>-template<typename StringType> bool equal(const StringBuilder&, const StringType&);
-bool equal(const StringBuilder&, const String&); // Only needed because is8Bit dereferences nullptr when the string is null.
-template<typename CharacterType> bool equal(const StringBuilder&, const CharacterType*, unsigned length);
</del><ins>+template <>
+ALWAYS_INLINE LChar* StringBuilder::getBufferCharacters<LChar>()
+{
+ ASSERT(m_is8Bit);
+ return m_bufferCharacters8;
+}
</ins><span class="cx">
</span><del>-bool operator==(const StringBuilder&, const StringBuilder&);
-bool operator!=(const StringBuilder&, const StringBuilder&);
-bool operator==(const StringBuilder&, const String&);
-bool operator!=(const StringBuilder&, const String&);
-bool operator==(const String&, const StringBuilder&);
-bool operator!=(const String&, const StringBuilder&);
</del><ins>+template <>
+ALWAYS_INLINE UChar* StringBuilder::getBufferCharacters<UChar>()
+{
+ ASSERT(!m_is8Bit);
+ return m_bufferCharacters16;
+}
</ins><span class="cx">
</span><del>-template<typename CharacterType> inline bool equal(const StringBuilder& s, const CharacterType* buffer, unsigned length)
</del><ins>+template <typename CharType>
+bool equal(const StringBuilder& s, const CharType* buffer, unsigned length)
</ins><span class="cx"> {
</span><span class="cx"> if (s.length() != length)
</span><span class="cx"> return false;
</span><span class="lines">@@ -321,18 +341,24 @@
</span><span class="cx"> return equal(s.characters16(), buffer, length);
</span><span class="cx"> }
</span><span class="cx">
</span><del>-template<typename StringType> inline bool equal(const StringBuilder& a, const StringType& b)
</del><ins>+template <typename StringType>
+bool equal(const StringBuilder& a, const StringType& b)
</ins><span class="cx"> {
</span><del>- return equalCommon(a, b);
-}
</del><ins>+ if (a.length() != b.length())
+ return false;
</ins><span class="cx">
</span><del>-inline bool equal(const StringBuilder& a, const String& b)
-{
- // FIXME: This preserves historic behavior where an empty StringBuilder compares as equal
- // to a null String. This does not seem like desirable behavior since a null String and
- // an empty String do not compare as equal, but we have regression tests expecting it,
- // and there is a slim chance we also have code depending on it.
- return b.isNull() ? a.isEmpty() : equalCommon(a, b);
</del><ins>+ if (!a.length())
+ return true;
+
+ if (a.is8Bit()) {
+ if (b.is8Bit())
+ return equal(a.characters8(), b.characters8(), a.length());
+ return equal(a.characters8(), b.characters16(), a.length());
+ }
+
+ if (b.is8Bit())
+ return equal(a.characters16(), b.characters8(), a.length());
+ return equal(a.characters16(), b.characters16(), a.length());
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> inline bool operator==(const StringBuilder& a, const StringBuilder& b) { return equal(a, b); }
</span><span class="lines">@@ -345,3 +371,5 @@
</span><span class="cx"> } // namespace WTF
</span><span class="cx">
</span><span class="cx"> using WTF::StringBuilder;
</span><ins>+
+#endif // StringBuilder_h
</ins></span></pre>
</div>
</div>
</body>
</html>