<!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>[206044] trunk/Source/WebCore</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/206044">206044</a></dd>
<dt>Author</dt> <dd>achristensen@apple.com</dd>
<dt>Date</dt> <dd>2016-09-16 13:35:16 -0700 (Fri, 16 Sep 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Use Vector&lt;LChar&gt; instead of StringBuilder for the ASCII parts of URLParser
https://bugs.webkit.org/show_bug.cgi?id=162035

Reviewed by Chris Dumez.

StringBuilder::append checks to see whether its StringBuffer is 8-bit or 16-bit each time it is called.
When parsing URLs, almost all of the parsed URL is guaranteed to be 8-bit ASCII.
Using a Vector&lt;LChar&gt; for this allows us to use uncheckedAppend in some places, and it always eliminates the 8-bit check.
This is a ~20% speedup in url parsing.

Covered by existing API tests.

* platform/URLParser.cpp:
(WebCore::isWindowsDriveLetter):
(WebCore::percentEncode):
(WebCore::utf8PercentEncode):
(WebCore::utf8PercentEncodeQuery):
(WebCore::encodeQuery):
(WebCore::URLParser::copyURLPartsUntil):
(WebCore::URLParser::popPath):
(WebCore::URLParser::parse):
(WebCore::URLParser::parseAuthority):
(WebCore::appendNumber):
(WebCore::serializeIPv4):
(WebCore::serializeIPv6Piece):
(WebCore::serializeIPv6):
(WebCore::URLParser::parsePort):
(WebCore::URLParser::parseHost):
(WebCore::serializeURLEncodedForm):
(WebCore::URLParser::serialize):
(WebCore::bufferView): Deleted.
* platform/URLParser.h:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreplatformURLParsercpp">trunk/Source/WebCore/platform/URLParser.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformURLParserh">trunk/Source/WebCore/platform/URLParser.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (206043 => 206044)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-09-16 20:24:23 UTC (rev 206043)
+++ trunk/Source/WebCore/ChangeLog        2016-09-16 20:35:16 UTC (rev 206044)
</span><span class="lines">@@ -1,3 +1,38 @@
</span><ins>+2016-09-16  Alex Christensen  &lt;achristensen@webkit.org&gt;
+
+        Use Vector&lt;LChar&gt; instead of StringBuilder for the ASCII parts of URLParser
+        https://bugs.webkit.org/show_bug.cgi?id=162035
+
+        Reviewed by Chris Dumez.
+
+        StringBuilder::append checks to see whether its StringBuffer is 8-bit or 16-bit each time it is called.
+        When parsing URLs, almost all of the parsed URL is guaranteed to be 8-bit ASCII.
+        Using a Vector&lt;LChar&gt; for this allows us to use uncheckedAppend in some places, and it always eliminates the 8-bit check.
+        This is a ~20% speedup in url parsing.
+
+        Covered by existing API tests.
+
+        * platform/URLParser.cpp:
+        (WebCore::isWindowsDriveLetter):
+        (WebCore::percentEncode):
+        (WebCore::utf8PercentEncode):
+        (WebCore::utf8PercentEncodeQuery):
+        (WebCore::encodeQuery):
+        (WebCore::URLParser::copyURLPartsUntil):
+        (WebCore::URLParser::popPath):
+        (WebCore::URLParser::parse):
+        (WebCore::URLParser::parseAuthority):
+        (WebCore::appendNumber):
+        (WebCore::serializeIPv4):
+        (WebCore::serializeIPv6Piece):
+        (WebCore::serializeIPv6):
+        (WebCore::URLParser::parsePort):
+        (WebCore::URLParser::parseHost):
+        (WebCore::serializeURLEncodedForm):
+        (WebCore::URLParser::serialize):
+        (WebCore::bufferView): Deleted.
+        * platform/URLParser.h:
+
</ins><span class="cx"> 2016-09-16  Dave Hyatt  &lt;hyatt@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [CSS Parser] Get CSSPropertyParserHelpers.cpp compiling
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformURLParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/URLParser.cpp (206043 => 206044)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/URLParser.cpp        2016-09-16 20:24:23 UTC (rev 206043)
+++ trunk/Source/WebCore/platform/URLParser.cpp        2016-09-16 20:35:16 UTC (rev 206044)
</span><span class="lines">@@ -109,10 +109,10 @@
</span><span class="cx"> auto CodePointIterator&lt;UChar&gt;::operator++() -&gt; CodePointIterator&amp;
</span><span class="cx"> {
</span><span class="cx">     ASSERT(!atEnd());
</span><del>-    if (U16_IS_LEAD(m_begin[0]) &amp;&amp; m_begin &lt; m_end &amp;&amp; U16_IS_TRAIL(m_begin[1]))
-        m_begin += 2;
-    else
-        m_begin++;
</del><ins>+    unsigned i = 0;
+    size_t length = m_end - m_begin;
+    U16_FWD_1(m_begin, i, length);
+    m_begin += i;
</ins><span class="cx">     return *this;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -405,11 +405,11 @@
</span><span class="cx">     return *iterator == ':' || *iterator == '|';
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static bool isWindowsDriveLetter(const StringBuilder&amp; builder, size_t index)
</del><ins>+static bool isWindowsDriveLetter(const Vector&lt;LChar&gt;&amp; buffer, size_t index)
</ins><span class="cx"> {
</span><del>-    if (builder.length() &lt; index + 2)
</del><ins>+    if (buffer.size() &lt; index + 2)
</ins><span class="cx">         return false;
</span><del>-    return isASCIIAlpha(builder[index]) &amp;&amp; (builder[index + 1] == ':' || builder[index + 1] == '|');
</del><ins>+    return isASCIIAlpha(buffer[index]) &amp;&amp; (buffer[index + 1] == ':' || buffer[index + 1] == '|');
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> template&lt;typename CharacterType&gt;
</span><span class="lines">@@ -428,14 +428,14 @@
</span><span class="cx">     return !isSlashQuestionOrHash(*iterator);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void percentEncode(uint8_t byte, StringBuilder&amp; builder)
</del><ins>+static void percentEncode(uint8_t byte, Vector&lt;LChar&gt;&amp; buffer)
</ins><span class="cx"> {
</span><del>-    builder.append('%');
-    builder.append(upperNibbleToASCIIHexDigit(byte));
-    builder.append(lowerNibbleToASCIIHexDigit(byte));
</del><ins>+    buffer.append('%');
+    buffer.append(upperNibbleToASCIIHexDigit(byte));
+    buffer.append(lowerNibbleToASCIIHexDigit(byte));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void utf8PercentEncode(UChar32 codePoint, StringBuilder&amp; builder, bool(*isInCodeSet)(UChar32))
</del><ins>+static void utf8PercentEncode(UChar32 codePoint, Vector&lt;LChar&gt;&amp; destination, bool(*isInCodeSet)(UChar32))
</ins><span class="cx"> {
</span><span class="cx">     if (isInCodeSet(codePoint)) {
</span><span class="cx">         uint8_t buffer[U8_MAX_LENGTH];
</span><span class="lines">@@ -444,12 +444,14 @@
</span><span class="cx">         U8_APPEND(buffer, offset, U8_MAX_LENGTH, codePoint, error);
</span><span class="cx">         // FIXME: Check error.
</span><span class="cx">         for (int32_t i = 0; i &lt; offset; ++i)
</span><del>-            percentEncode(buffer[i], builder);
-    } else
-        builder.append(codePoint);
</del><ins>+            percentEncode(buffer[i], destination);
+    } else {
+        ASSERT_WITH_MESSAGE(isASCII(codePoint), &quot;isInCodeSet should always return true for non-ASCII characters&quot;);
+        destination.append(codePoint);
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void utf8PercentEncodeQuery(UChar32 codePoint, StringBuilder&amp; builder)
</del><ins>+static void utf8PercentEncodeQuery(UChar32 codePoint, Vector&lt;LChar&gt;&amp; destination)
</ins><span class="cx"> {
</span><span class="cx">     uint8_t buffer[U8_MAX_LENGTH];
</span><span class="cx">     int32_t offset = 0;
</span><span class="lines">@@ -460,13 +462,13 @@
</span><span class="cx">     for (int32_t i = 0; i &lt; offset; ++i) {
</span><span class="cx">         auto byte = buffer[i];
</span><span class="cx">         if (shouldPercentEncodeQueryByte(byte))
</span><del>-            percentEncode(byte, builder);
</del><ins>+            percentEncode(byte, destination);
</ins><span class="cx">         else
</span><del>-            builder.append(byte);
</del><ins>+            destination.append(byte);
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx">     
</span><del>-static void encodeQuery(const StringBuilder&amp; source, StringBuilder&amp; destination, const TextEncoding&amp; encoding)
</del><ins>+static void encodeQuery(const StringBuilder&amp; source, Vector&lt;LChar&gt;&amp; destination, const TextEncoding&amp; encoding)
</ins><span class="cx"> {
</span><span class="cx">     // FIXME: It is unclear in the spec what to do when encoding fails. The behavior should be specified and tested.
</span><span class="cx">     CString encoded = encoding.encode(source.toStringPreserveCapacity(), URLEncodedEntitiesForUnencodables);
</span><span class="lines">@@ -595,15 +597,6 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename T&gt;
-static StringView bufferView(const T&amp; buffer, unsigned start, unsigned length)
-{
-    ASSERT(buffer.length() &gt;= length);
-    if (buffer.is8Bit())
-        return StringView(buffer.characters8() + start, length);
-    return StringView(buffer.characters16() + start, length);
-}
-
</del><span class="cx"> enum class URLParser::URLPart {
</span><span class="cx">     SchemeEnd,
</span><span class="cx">     UserStart,
</span><span class="lines">@@ -644,11 +637,47 @@
</span><span class="cx">     ASSERT_NOT_REACHED();
</span><span class="cx">     return 0;
</span><span class="cx"> }
</span><del>-    
</del><ins>+
+static void copyASCIIStringUntil(Vector&lt;LChar&gt;&amp; destination, const String&amp; string, size_t lengthIf8Bit, size_t lengthIf16Bit)
+{
+    ASSERT(destination.isEmpty());
+    if (string.is8Bit()) {
+        RELEASE_ASSERT(lengthIf8Bit &lt;= string.length());
+        destination.append(string.characters8(), lengthIf8Bit);
+    } else {
+        RELEASE_ASSERT(lengthIf16Bit &lt;= string.length());
+        destination.reserveCapacity(lengthIf16Bit);
+        const UChar* characters = string.characters16();
+        for (size_t i = 0; i &lt; lengthIf16Bit; ++i) {
+            UChar c = characters[i];
+            ASSERT_WITH_SECURITY_IMPLICATION(isASCII(c));
+            destination.uncheckedAppend(c);
+        }
+    }
+}
+
</ins><span class="cx"> void URLParser::copyURLPartsUntil(const URL&amp; base, URLPart part)
</span><span class="cx"> {
</span><del>-    m_buffer.clear();
-    m_buffer.append(base.m_string.substring(0, urlLengthUntilPart(base, part)));
</del><ins>+    m_asciiBuffer.clear();
+    m_unicodeFragmentBuffer.clear();
+    if (part == URLPart::FragmentEnd) {
+        copyASCIIStringUntil(m_asciiBuffer, base.m_string, urlLengthUntilPart(base, URLPart::FragmentEnd), urlLengthUntilPart(base, URLPart::QueryEnd));
+        if (!base.m_string.is8Bit()) {
+            const String&amp; fragment = base.m_string;
+            bool seenUnicode = false;
+            for (size_t i = base.m_queryEnd; i &lt; base.m_fragmentEnd; ++i) {
+                if (!seenUnicode &amp;&amp; !isASCII(fragment[i]))
+                    seenUnicode = true;
+                if (seenUnicode)
+                    m_unicodeFragmentBuffer.uncheckedAppend(fragment[i]);
+                else
+                    m_asciiBuffer.uncheckedAppend(fragment[i]);
+            }
+        }
+    } else {
+        size_t length = urlLengthUntilPart(base, part);
+        copyASCIIStringUntil(m_asciiBuffer, base.m_string, length, length);
+    }
</ins><span class="cx">     switch (part) {
</span><span class="cx">     case URLPart::FragmentEnd:
</span><span class="cx">         m_url.m_fragmentEnd = base.m_fragmentEnd;
</span><span class="lines">@@ -682,7 +711,7 @@
</span><span class="cx">         m_url.m_protocolIsInHTTPFamily = base.m_protocolIsInHTTPFamily;
</span><span class="cx">         m_url.m_schemeEnd = base.m_schemeEnd;
</span><span class="cx">     }
</span><del>-    m_urlIsSpecial = isSpecialScheme(bufferView(m_buffer, 0, m_url.m_schemeEnd));
</del><ins>+    m_urlIsSpecial = isSpecialScheme(StringView(m_asciiBuffer.data(), m_url.m_schemeEnd));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static const char* dotASCIICode = &quot;2e&quot;;
</span><span class="lines">@@ -802,13 +831,13 @@
</span><span class="cx"> {
</span><span class="cx">     if (m_url.m_pathAfterLastSlash &gt; m_url.m_portEnd + 1) {
</span><span class="cx">         m_url.m_pathAfterLastSlash--;
</span><del>-        if (m_buffer[m_url.m_pathAfterLastSlash] == '/')
</del><ins>+        if (m_asciiBuffer[m_url.m_pathAfterLastSlash] == '/')
</ins><span class="cx">             m_url.m_pathAfterLastSlash--;
</span><del>-        while (m_url.m_pathAfterLastSlash &gt; m_url.m_portEnd &amp;&amp; m_buffer[m_url.m_pathAfterLastSlash] != '/')
</del><ins>+        while (m_url.m_pathAfterLastSlash &gt; m_url.m_portEnd &amp;&amp; m_asciiBuffer[m_url.m_pathAfterLastSlash] != '/')
</ins><span class="cx">             m_url.m_pathAfterLastSlash--;
</span><span class="cx">         m_url.m_pathAfterLastSlash++;
</span><span class="cx">     }
</span><del>-    m_buffer.resize(m_url.m_pathAfterLastSlash);
</del><ins>+    m_asciiBuffer.resize(m_url.m_pathAfterLastSlash);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> template&lt;typename CharacterType&gt;
</span><span class="lines">@@ -844,8 +873,9 @@
</span><span class="cx"> {
</span><span class="cx">     LOG(URLParser, &quot;Parsing URL &lt;%s&gt; base &lt;%s&gt;&quot;, String(input, length).utf8().data(), base.string().utf8().data());
</span><span class="cx">     m_url = { };
</span><del>-    m_buffer.clear();
-    m_buffer.reserveCapacity(length);
</del><ins>+    m_asciiBuffer.clear();
+    m_unicodeFragmentBuffer.clear();
+    m_asciiBuffer.reserveCapacity(length);
</ins><span class="cx">     
</span><span class="cx">     bool isUTF8Encoding = encoding == UTF8Encoding();
</span><span class="cx">     StringBuilder queryBuffer;
</span><span class="lines">@@ -881,7 +911,7 @@
</span><span class="cx">         Fragment,
</span><span class="cx">     };
</span><span class="cx"> 
</span><del>-#define LOG_STATE(x) LOG(URLParser, &quot;State %s, code point %c, buffer length %d&quot;, x, *c, m_buffer.length())
</del><ins>+#define LOG_STATE(x) LOG(URLParser, &quot;State %s, code point %c, asciiBuffer size %zu&quot;, x, *c, m_asciiBuffer.size())
</ins><span class="cx"> #define LOG_FINAL_STATE(x) LOG(URLParser, &quot;Final State: %s&quot;, x)
</span><span class="cx"> 
</span><span class="cx">     State state = State::SchemeStart;
</span><span class="lines">@@ -895,7 +925,7 @@
</span><span class="cx">         case State::SchemeStart:
</span><span class="cx">             LOG_STATE(&quot;SchemeStart&quot;);
</span><span class="cx">             if (isASCIIAlpha(*c)) {
</span><del>-                m_buffer.append(toASCIILower(*c));
</del><ins>+                m_asciiBuffer.uncheckedAppend(toASCIILower(*c));
</ins><span class="cx">                 ++c;
</span><span class="cx">                 state = State::Scheme;
</span><span class="cx">             } else
</span><span class="lines">@@ -904,19 +934,19 @@
</span><span class="cx">         case State::Scheme:
</span><span class="cx">             LOG_STATE(&quot;Scheme&quot;);
</span><span class="cx">             if (isASCIIAlphanumeric(*c) || *c == '+' || *c == '-' || *c == '.')
</span><del>-                m_buffer.append(toASCIILower(*c));
</del><ins>+                m_asciiBuffer.append(toASCIILower(*c));
</ins><span class="cx">             else if (*c == ':') {
</span><del>-                m_url.m_schemeEnd = m_buffer.length();
-                StringView urlScheme = bufferView(m_buffer, 0, m_url.m_schemeEnd);
</del><ins>+                m_url.m_schemeEnd = m_asciiBuffer.size();
+                StringView urlScheme = StringView(m_asciiBuffer.data(), m_url.m_schemeEnd);
</ins><span class="cx">                 m_url.m_protocolIsInHTTPFamily = urlScheme == &quot;http&quot; || urlScheme == &quot;https&quot;;
</span><span class="cx">                 if (urlScheme == &quot;file&quot;) {
</span><span class="cx">                     m_urlIsSpecial = true;
</span><span class="cx">                     state = State::File;
</span><del>-                    m_buffer.append(':');
</del><ins>+                    m_asciiBuffer.append(':');
</ins><span class="cx">                     ++c;
</span><span class="cx">                     break;
</span><span class="cx">                 }
</span><del>-                m_buffer.append(':');
</del><ins>+                m_asciiBuffer.append(':');
</ins><span class="cx">                 if (isSpecialScheme(urlScheme)) {
</span><span class="cx">                     m_urlIsSpecial = true;
</span><span class="cx">                     // FIXME: This is unnecessarily allocating a String.
</span><span class="lines">@@ -926,7 +956,7 @@
</span><span class="cx">                     else
</span><span class="cx">                         state = State::SpecialAuthoritySlashes;
</span><span class="cx">                 } else {
</span><del>-                    m_url.m_userStart = m_buffer.length();
</del><ins>+                    m_url.m_userStart = m_asciiBuffer.size();
</ins><span class="cx">                     m_url.m_userEnd = m_url.m_userStart;
</span><span class="cx">                     m_url.m_passwordEnd = m_url.m_userStart;
</span><span class="cx">                     m_url.m_hostEnd = m_url.m_userStart;
</span><span class="lines">@@ -936,7 +966,7 @@
</span><span class="cx">                     while (!maybeSlash.atEnd() &amp;&amp; isTabOrNewline(*maybeSlash))
</span><span class="cx">                         ++maybeSlash;
</span><span class="cx">                     if (!maybeSlash.atEnd() &amp;&amp; *maybeSlash == '/') {
</span><del>-                        m_buffer.append('/');
</del><ins>+                        m_asciiBuffer.append('/');
</ins><span class="cx">                         m_url.m_pathAfterLastSlash = m_url.m_userStart + 1;
</span><span class="cx">                         state = State::PathOrAuthority;
</span><span class="cx">                         c = maybeSlash;
</span><span class="lines">@@ -950,7 +980,7 @@
</span><span class="cx">                 ++c;
</span><span class="cx">                 break;
</span><span class="cx">             } else {
</span><del>-                m_buffer.clear();
</del><ins>+                m_asciiBuffer.clear();
</ins><span class="cx">                 state = State::NoScheme;
</span><span class="cx">                 c = beginAfterControlAndSpace;
</span><span class="cx">                 break;
</span><span class="lines">@@ -959,7 +989,7 @@
</span><span class="cx">             while (!c.atEnd() &amp;&amp; isTabOrNewline(*c))
</span><span class="cx">                 ++c;
</span><span class="cx">             if (c.atEnd()) {
</span><del>-                m_buffer.clear();
</del><ins>+                m_asciiBuffer.clear();
</ins><span class="cx">                 state = State::NoScheme;
</span><span class="cx">                 c = beginAfterControlAndSpace;
</span><span class="cx">             }
</span><span class="lines">@@ -971,7 +1001,7 @@
</span><span class="cx">             if (base.m_cannotBeABaseURL &amp;&amp; *c == '#') {
</span><span class="cx">                 copyURLPartsUntil(base, URLPart::QueryEnd);
</span><span class="cx">                 state = State::Fragment;
</span><del>-                m_buffer.append('#');
</del><ins>+                m_asciiBuffer.append('#');
</ins><span class="cx">                 ++c;
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="lines">@@ -980,13 +1010,13 @@
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="cx">             copyURLPartsUntil(base, URLPart::SchemeEnd);
</span><del>-            m_buffer.append(':');
</del><ins>+            m_asciiBuffer.append(':');
</ins><span class="cx">             state = State::File;
</span><span class="cx">             break;
</span><span class="cx">         case State::SpecialRelativeOrAuthority:
</span><span class="cx">             LOG_STATE(&quot;SpecialRelativeOrAuthority&quot;);
</span><span class="cx">             if (*c == '/') {
</span><del>-                m_buffer.append('/');
</del><ins>+                m_asciiBuffer.append('/');
</ins><span class="cx">                 ++c;
</span><span class="cx">                 while (!c.atEnd() &amp;&amp; isTabOrNewline(*c))
</span><span class="cx">                     ++c;
</span><span class="lines">@@ -993,7 +1023,7 @@
</span><span class="cx">                 if (c.atEnd())
</span><span class="cx">                     return failure(input, length);
</span><span class="cx">                 if (*c == '/') {
</span><del>-                    m_buffer.append('/');
</del><ins>+                    m_asciiBuffer.append('/');
</ins><span class="cx">                     state = State::SpecialAuthorityIgnoreSlashes;
</span><span class="cx">                     ++c;
</span><span class="cx">                 }
</span><span class="lines">@@ -1003,8 +1033,8 @@
</span><span class="cx">         case State::PathOrAuthority:
</span><span class="cx">             LOG_STATE(&quot;PathOrAuthority&quot;);
</span><span class="cx">             if (*c == '/') {
</span><del>-                m_buffer.append('/');
-                m_url.m_userStart = m_buffer.length();
</del><ins>+                m_asciiBuffer.append('/');
+                m_url.m_userStart = m_asciiBuffer.size();
</ins><span class="cx">                 state = State::AuthorityOrHost;
</span><span class="cx">                 ++c;
</span><span class="cx">                 authorityOrHostBegin = c;
</span><span class="lines">@@ -1021,13 +1051,13 @@
</span><span class="cx">                 break;
</span><span class="cx">             case '?':
</span><span class="cx">                 copyURLPartsUntil(base, URLPart::PathEnd);
</span><del>-                m_buffer.append('?');
</del><ins>+                m_asciiBuffer.append('?');
</ins><span class="cx">                 state = State::Query;
</span><span class="cx">                 ++c;
</span><span class="cx">                 break;
</span><span class="cx">             case '#':
</span><span class="cx">                 copyURLPartsUntil(base, URLPart::QueryEnd);
</span><del>-                m_buffer.append('#');
</del><ins>+                m_asciiBuffer.append('#');
</ins><span class="cx">                 state = State::Fragment;
</span><span class="cx">                 ++c;
</span><span class="cx">                 break;
</span><span class="lines">@@ -1042,11 +1072,11 @@
</span><span class="cx">             if (*c == '/' || *c == '\\') {
</span><span class="cx">                 ++c;
</span><span class="cx">                 copyURLPartsUntil(base, URLPart::SchemeEnd);
</span><del>-                m_buffer.append(&quot;://&quot;);
</del><ins>+                m_asciiBuffer.append(&quot;://&quot;, 3);
</ins><span class="cx">                 state = State::SpecialAuthorityIgnoreSlashes;
</span><span class="cx">             } else {
</span><span class="cx">                 copyURLPartsUntil(base, URLPart::PortEnd);
</span><del>-                m_buffer.append('/');
</del><ins>+                m_asciiBuffer.append('/');
</ins><span class="cx">                 m_url.m_pathAfterLastSlash = base.m_portEnd + 1;
</span><span class="cx">                 state = State::Path;
</span><span class="cx">             }
</span><span class="lines">@@ -1053,7 +1083,7 @@
</span><span class="cx">             break;
</span><span class="cx">         case State::SpecialAuthoritySlashes:
</span><span class="cx">             LOG_STATE(&quot;SpecialAuthoritySlashes&quot;);
</span><del>-            m_buffer.append(&quot;//&quot;);
</del><ins>+            m_asciiBuffer.append(&quot;//&quot;, 2);
</ins><span class="cx">             if (*c == '/' || *c == '\\') {
</span><span class="cx">                 ++c;
</span><span class="cx">                 while (!c.atEnd() &amp;&amp; isTabOrNewline(*c))
</span><span class="lines">@@ -1066,10 +1096,10 @@
</span><span class="cx">         case State::SpecialAuthorityIgnoreSlashes:
</span><span class="cx">             LOG_STATE(&quot;SpecialAuthorityIgnoreSlashes&quot;);
</span><span class="cx">             if (*c == '/' || *c == '\\') {
</span><del>-                m_buffer.append('/');
</del><ins>+                m_asciiBuffer.append('/');
</ins><span class="cx">                 ++c;
</span><span class="cx">             }
</span><del>-            m_url.m_userStart = m_buffer.length();
</del><ins>+            m_url.m_userStart = m_asciiBuffer.size();
</ins><span class="cx">             state = State::AuthorityOrHost;
</span><span class="cx">             authorityOrHostBegin = c;
</span><span class="cx">             break;
</span><span class="lines">@@ -1088,13 +1118,13 @@
</span><span class="cx">                 }
</span><span class="cx">                 bool isSlash = *c == '/' || (m_urlIsSpecial &amp;&amp; *c == '\\');
</span><span class="cx">                 if (isSlash || *c == '?' || *c == '#') {
</span><del>-                    m_url.m_userEnd = m_buffer.length();
</del><ins>+                    m_url.m_userEnd = m_asciiBuffer.size();
</ins><span class="cx">                     m_url.m_passwordEnd = m_url.m_userEnd;
</span><span class="cx">                     if (!parseHost(CodePointIterator&lt;CharacterType&gt;(authorityOrHostBegin, c)))
</span><span class="cx">                         return failure(input, length);
</span><span class="cx">                     if (!isSlash) {
</span><del>-                        m_buffer.append('/');
-                        m_url.m_pathAfterLastSlash = m_buffer.length();
</del><ins>+                        m_asciiBuffer.append('/');
+                        m_url.m_pathAfterLastSlash = m_asciiBuffer.size();
</ins><span class="cx">                     }
</span><span class="cx">                     state = State::Path;
</span><span class="cx">                     break;
</span><span class="lines">@@ -1121,7 +1151,7 @@
</span><span class="cx">             switch (*c) {
</span><span class="cx">             case '/':
</span><span class="cx">             case '\\':
</span><del>-                m_buffer.append('/');
</del><ins>+                m_asciiBuffer.append('/');
</ins><span class="cx">                 state = State::FileSlash;
</span><span class="cx">                 ++c;
</span><span class="cx">                 break;
</span><span class="lines">@@ -1128,8 +1158,8 @@
</span><span class="cx">             case '?':
</span><span class="cx">                 if (!base.isNull() &amp;&amp; base.protocolIs(&quot;file&quot;))
</span><span class="cx">                     copyURLPartsUntil(base, URLPart::PathEnd);
</span><del>-                m_buffer.append(&quot;///?&quot;);
-                m_url.m_userStart = m_buffer.length() - 2;
</del><ins>+                m_asciiBuffer.append(&quot;///?&quot;, 4);
+                m_url.m_userStart = m_asciiBuffer.size() - 2;
</ins><span class="cx">                 m_url.m_userEnd = m_url.m_userStart;
</span><span class="cx">                 m_url.m_passwordEnd = m_url.m_userStart;
</span><span class="cx">                 m_url.m_hostEnd = m_url.m_userStart;
</span><span class="lines">@@ -1142,8 +1172,8 @@
</span><span class="cx">             case '#':
</span><span class="cx">                 if (!base.isNull() &amp;&amp; base.protocolIs(&quot;file&quot;))
</span><span class="cx">                     copyURLPartsUntil(base, URLPart::QueryEnd);
</span><del>-                m_buffer.append(&quot;///#&quot;);
-                m_url.m_userStart = m_buffer.length() - 2;
</del><ins>+                m_asciiBuffer.append(&quot;///#&quot;, 4);
+                m_url.m_userStart = m_asciiBuffer.size() - 2;
</ins><span class="cx">                 m_url.m_userEnd = m_url.m_userStart;
</span><span class="cx">                 m_url.m_passwordEnd = m_url.m_userStart;
</span><span class="cx">                 m_url.m_hostEnd = m_url.m_userStart;
</span><span class="lines">@@ -1158,8 +1188,8 @@
</span><span class="cx">                 if (!base.isNull() &amp;&amp; base.protocolIs(&quot;file&quot;) &amp;&amp; shouldCopyFileURL(c))
</span><span class="cx">                     copyURLPartsUntil(base, URLPart::PathAfterLastSlash);
</span><span class="cx">                 else {
</span><del>-                    m_buffer.append(&quot;///&quot;);
-                    m_url.m_userStart = m_buffer.length() - 1;
</del><ins>+                    m_asciiBuffer.append(&quot;///&quot;, 3);
+                    m_url.m_userStart = m_asciiBuffer.size() - 1;
</ins><span class="cx">                     m_url.m_userEnd = m_url.m_userStart;
</span><span class="cx">                     m_url.m_passwordEnd = m_url.m_userStart;
</span><span class="cx">                     m_url.m_hostEnd = m_url.m_userStart;
</span><span class="lines">@@ -1174,8 +1204,8 @@
</span><span class="cx">             LOG_STATE(&quot;FileSlash&quot;);
</span><span class="cx">             if (*c == '/' || *c == '\\') {
</span><span class="cx">                 ++c;
</span><del>-                m_buffer.append('/');
-                m_url.m_userStart = m_buffer.length();
</del><ins>+                m_asciiBuffer.append('/');
+                m_url.m_userStart = m_asciiBuffer.size();
</ins><span class="cx">                 m_url.m_userEnd = m_url.m_userStart;
</span><span class="cx">                 m_url.m_passwordEnd = m_url.m_userStart;
</span><span class="cx">                 m_url.m_hostEnd = m_url.m_userStart;
</span><span class="lines">@@ -1192,15 +1222,15 @@
</span><span class="cx">                         ? isWindowsDriveLetter(CodePointIterator&lt;LChar&gt;(basePath.characters8(), basePath.characters8() + basePath.length()))
</span><span class="cx">                         : isWindowsDriveLetter(CodePointIterator&lt;UChar&gt;(basePath.characters16(), basePath.characters16() + basePath.length()));
</span><span class="cx">                     if (windowsQuirk) {
</span><del>-                        m_buffer.append(basePath[0]);
-                        m_buffer.append(basePath[1]);
</del><ins>+                        m_asciiBuffer.append(basePath[0]);
+                        m_asciiBuffer.append(basePath[1]);
</ins><span class="cx">                     }
</span><span class="cx">                 }
</span><span class="cx">                 state = State::Path;
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><del>-            m_buffer.append(&quot;//&quot;);
-            m_url.m_userStart = m_buffer.length() - 1;
</del><ins>+            m_asciiBuffer.append(&quot;//&quot;, 2);
+            m_url.m_userStart = m_asciiBuffer.size() - 1;
</ins><span class="cx">             m_url.m_userEnd = m_url.m_userStart;
</span><span class="cx">             m_url.m_passwordEnd = m_url.m_userStart;
</span><span class="cx">             m_url.m_hostEnd = m_url.m_userStart;
</span><span class="lines">@@ -1211,15 +1241,15 @@
</span><span class="cx">         case State::FileHost:
</span><span class="cx">             LOG_STATE(&quot;FileHost&quot;);
</span><span class="cx">             if (isSlashQuestionOrHash(*c)) {
</span><del>-                if (isWindowsDriveLetter(m_buffer, m_url.m_portEnd + 1)) {
</del><ins>+                if (isWindowsDriveLetter(m_asciiBuffer, m_url.m_portEnd + 1)) {
</ins><span class="cx">                     state = State::Path;
</span><span class="cx">                     break;
</span><span class="cx">                 }
</span><span class="cx">                 if (authorityOrHostBegin == c) {
</span><del>-                    ASSERT(m_buffer[m_buffer.length() - 1] == '/');
</del><ins>+                    ASSERT(m_asciiBuffer[m_asciiBuffer.size() - 1] == '/');
</ins><span class="cx">                     if (*c == '?') {
</span><del>-                        m_buffer.append(&quot;/?&quot;);
-                        m_url.m_pathAfterLastSlash = m_buffer.length() - 1;
</del><ins>+                        m_asciiBuffer.append(&quot;/?&quot;, 2);
+                        m_url.m_pathAfterLastSlash = m_asciiBuffer.size() - 1;
</ins><span class="cx">                         m_url.m_pathEnd = m_url.m_pathAfterLastSlash;
</span><span class="cx">                         state = State::Query;
</span><span class="cx">                         ++c;
</span><span class="lines">@@ -1226,8 +1256,8 @@
</span><span class="cx">                         break;
</span><span class="cx">                     }
</span><span class="cx">                     if (*c == '#') {
</span><del>-                        m_buffer.append(&quot;/#&quot;);
-                        m_url.m_pathAfterLastSlash = m_buffer.length() - 1;
</del><ins>+                        m_asciiBuffer.append(&quot;/#&quot;, 2);
+                        m_url.m_pathAfterLastSlash = m_asciiBuffer.size() - 1;
</ins><span class="cx">                         m_url.m_pathEnd = m_url.m_pathAfterLastSlash;
</span><span class="cx">                         m_url.m_queryEnd = m_url.m_pathAfterLastSlash;
</span><span class="cx">                         state = State::Fragment;
</span><span class="lines">@@ -1240,9 +1270,9 @@
</span><span class="cx">                 if (!parseHost(CodePointIterator&lt;CharacterType&gt;(authorityOrHostBegin, c)))
</span><span class="cx">                     return failure(input, length);
</span><span class="cx">                 
</span><del>-                if (bufferView(m_buffer, m_url.m_passwordEnd, m_buffer.length() - m_url.m_passwordEnd) == &quot;localhost&quot;)  {
-                    m_buffer.resize(m_url.m_passwordEnd);
-                    m_url.m_hostEnd = m_buffer.length();
</del><ins>+                if (StringView(m_asciiBuffer.data() + m_url.m_passwordEnd, m_asciiBuffer.size() - m_url.m_passwordEnd) == &quot;localhost&quot;)  {
+                    m_asciiBuffer.shrink(m_url.m_passwordEnd);
+                    m_url.m_hostEnd = m_asciiBuffer.size();
</ins><span class="cx">                     m_url.m_portEnd = m_url.m_hostEnd;
</span><span class="cx">                 }
</span><span class="cx">                 
</span><span class="lines">@@ -1262,35 +1292,35 @@
</span><span class="cx">         case State::Path:
</span><span class="cx">             LOG_STATE(&quot;Path&quot;);
</span><span class="cx">             if (*c == '/' || (m_urlIsSpecial &amp;&amp; *c == '\\')) {
</span><del>-                m_buffer.append('/');
-                m_url.m_pathAfterLastSlash = m_buffer.length();
</del><ins>+                m_asciiBuffer.append('/');
+                m_url.m_pathAfterLastSlash = m_asciiBuffer.size();
</ins><span class="cx">                 ++c;
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><del>-            if (m_buffer.length() &amp;&amp; m_buffer[m_buffer.length() - 1] == '/') {
</del><ins>+            if (m_asciiBuffer.size() &amp;&amp; m_asciiBuffer[m_asciiBuffer.size() - 1] == '/') {
</ins><span class="cx">                 if (isDoubleDotPathSegment(c)) {
</span><span class="cx">                     consumeDoubleDotPathSegment(c);
</span><span class="cx">                     popPath();
</span><span class="cx">                     break;
</span><span class="cx">                 }
</span><del>-                if (m_buffer[m_buffer.length() - 1] == '/' &amp;&amp; isSingleDotPathSegment(c)) {
</del><ins>+                if (m_asciiBuffer[m_asciiBuffer.size() - 1] == '/' &amp;&amp; isSingleDotPathSegment(c)) {
</ins><span class="cx">                     consumeSingleDotPathSegment(c);
</span><span class="cx">                     break;
</span><span class="cx">                 }
</span><span class="cx">             }
</span><span class="cx">             if (*c == '?') {
</span><del>-                m_url.m_pathEnd = m_buffer.length();
</del><ins>+                m_url.m_pathEnd = m_asciiBuffer.size();
</ins><span class="cx">                 state = State::Query;
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="cx">             if (*c == '#') {
</span><del>-                m_url.m_pathEnd = m_buffer.length();
</del><ins>+                m_url.m_pathEnd = m_asciiBuffer.size();
</ins><span class="cx">                 m_url.m_queryEnd = m_url.m_pathEnd;
</span><span class="cx">                 state = State::Fragment;
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="cx">             if (isPercentEncodedDot(c)) {
</span><del>-                m_buffer.append('.');
</del><ins>+                m_asciiBuffer.append('.');
</ins><span class="cx">                 ASSERT(*c == '%');
</span><span class="cx">                 ++c;
</span><span class="cx">                 ASSERT(*c == dotASCIICode[0]);
</span><span class="lines">@@ -1299,20 +1329,20 @@
</span><span class="cx">                 ++c;
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><del>-            utf8PercentEncode(*c, m_buffer, isInDefaultEncodeSet);
</del><ins>+            utf8PercentEncode(*c, m_asciiBuffer, isInDefaultEncodeSet);
</ins><span class="cx">             ++c;
</span><span class="cx">             break;
</span><span class="cx">         case State::CannotBeABaseURLPath:
</span><span class="cx">             LOG_STATE(&quot;CannotBeABaseURLPath&quot;);
</span><span class="cx">             if (*c == '?') {
</span><del>-                m_url.m_pathEnd = m_buffer.length();
</del><ins>+                m_url.m_pathEnd = m_asciiBuffer.size();
</ins><span class="cx">                 state = State::Query;
</span><span class="cx">             } else if (*c == '#') {
</span><del>-                m_url.m_pathEnd = m_buffer.length();
</del><ins>+                m_url.m_pathEnd = m_asciiBuffer.size();
</ins><span class="cx">                 m_url.m_queryEnd = m_url.m_pathEnd;
</span><span class="cx">                 state = State::Fragment;
</span><span class="cx">             } else {
</span><del>-                utf8PercentEncode(*c, m_buffer, isInSimpleEncodeSet);
</del><ins>+                utf8PercentEncode(*c, m_asciiBuffer, isInSimpleEncodeSet);
</ins><span class="cx">                 ++c;
</span><span class="cx">             }
</span><span class="cx">             break;
</span><span class="lines">@@ -1320,13 +1350,13 @@
</span><span class="cx">             LOG_STATE(&quot;Query&quot;);
</span><span class="cx">             if (*c == '#') {
</span><span class="cx">                 if (!isUTF8Encoding)
</span><del>-                    encodeQuery(queryBuffer, m_buffer, encoding);
-                m_url.m_queryEnd = m_buffer.length();
</del><ins>+                    encodeQuery(queryBuffer, m_asciiBuffer, encoding);
+                m_url.m_queryEnd = m_asciiBuffer.size();
</ins><span class="cx">                 state = State::Fragment;
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="cx">             if (isUTF8Encoding)
</span><del>-                utf8PercentEncodeQuery(*c, m_buffer);
</del><ins>+                utf8PercentEncodeQuery(*c, m_asciiBuffer);
</ins><span class="cx">             else
</span><span class="cx">                 queryBuffer.append(*c);
</span><span class="cx">             ++c;
</span><span class="lines">@@ -1333,7 +1363,10 @@
</span><span class="cx">             break;
</span><span class="cx">         case State::Fragment:
</span><span class="cx">             LOG_STATE(&quot;Fragment&quot;);
</span><del>-            m_buffer.append(*c);
</del><ins>+            if (m_unicodeFragmentBuffer.isEmpty() &amp;&amp; isASCII(*c))
+                m_asciiBuffer.append(*c);
+            else
+                m_unicodeFragmentBuffer.append(*c);
</ins><span class="cx">             ++c;
</span><span class="cx">             break;
</span><span class="cx">         }
</span><span class="lines">@@ -1342,7 +1375,7 @@
</span><span class="cx">     switch (state) {
</span><span class="cx">     case State::SchemeStart:
</span><span class="cx">         LOG_FINAL_STATE(&quot;SchemeStart&quot;);
</span><del>-        if (!m_buffer.length() &amp;&amp; !base.isNull())
</del><ins>+        if (!m_asciiBuffer.size() &amp;&amp; !base.isNull())
</ins><span class="cx">             return base;
</span><span class="cx">         return failure(input, length);
</span><span class="cx">     case State::Scheme:
</span><span class="lines">@@ -1369,7 +1402,7 @@
</span><span class="cx">     case State::RelativeSlash:
</span><span class="cx">         LOG_FINAL_STATE(&quot;RelativeSlash&quot;);
</span><span class="cx">         copyURLPartsUntil(base, URLPart::PortEnd);
</span><del>-        m_buffer.append('/');
</del><ins>+        m_asciiBuffer.append('/');
</ins><span class="cx">         m_url.m_pathAfterLastSlash = base.m_portEnd + 1;
</span><span class="cx">         m_url.m_pathEnd = m_url.m_pathAfterLastSlash;
</span><span class="cx">         m_url.m_queryEnd = m_url.m_pathAfterLastSlash;
</span><span class="lines">@@ -1377,7 +1410,7 @@
</span><span class="cx">         break;
</span><span class="cx">     case State::SpecialAuthoritySlashes:
</span><span class="cx">         LOG_FINAL_STATE(&quot;SpecialAuthoritySlashes&quot;);
</span><del>-        m_url.m_userStart = m_buffer.length();
</del><ins>+        m_url.m_userStart = m_asciiBuffer.size();
</ins><span class="cx">         m_url.m_userEnd = m_url.m_userStart;
</span><span class="cx">         m_url.m_passwordEnd = m_url.m_userStart;
</span><span class="cx">         m_url.m_hostEnd = m_url.m_userStart;
</span><span class="lines">@@ -1393,7 +1426,7 @@
</span><span class="cx">         break;
</span><span class="cx">     case State::AuthorityOrHost:
</span><span class="cx">         LOG_FINAL_STATE(&quot;AuthorityOrHost&quot;);
</span><del>-        m_url.m_userEnd = m_buffer.length();
</del><ins>+        m_url.m_userEnd = m_asciiBuffer.size();
</ins><span class="cx">         m_url.m_passwordEnd = m_url.m_userEnd;
</span><span class="cx">         FALLTHROUGH;
</span><span class="cx">     case State::Host:
</span><span class="lines">@@ -1401,7 +1434,7 @@
</span><span class="cx">             LOG_FINAL_STATE(&quot;Host&quot;);
</span><span class="cx">         if (!parseHost(authorityOrHostBegin))
</span><span class="cx">             return failure(input, length);
</span><del>-        m_buffer.append('/');
</del><ins>+        m_asciiBuffer.append('/');
</ins><span class="cx">         m_url.m_pathEnd = m_url.m_portEnd + 1;
</span><span class="cx">         m_url.m_pathAfterLastSlash = m_url.m_pathEnd;
</span><span class="cx">         m_url.m_queryEnd = m_url.m_pathEnd;
</span><span class="lines">@@ -1411,10 +1444,10 @@
</span><span class="cx">         LOG_FINAL_STATE(&quot;File&quot;);
</span><span class="cx">         if (!base.isNull() &amp;&amp; base.protocol() == &quot;file&quot;) {
</span><span class="cx">             copyURLPartsUntil(base, URLPart::QueryEnd);
</span><del>-            m_buffer.append(':');
</del><ins>+            m_asciiBuffer.append(':');
</ins><span class="cx">         }
</span><del>-        m_buffer.append(&quot;///&quot;);
-        m_url.m_userStart = m_buffer.length() - 1;
</del><ins>+        m_asciiBuffer.append(&quot;///&quot;, 3);
+        m_url.m_userStart = m_asciiBuffer.size() - 1;
</ins><span class="cx">         m_url.m_userEnd = m_url.m_userStart;
</span><span class="cx">         m_url.m_passwordEnd = m_url.m_userStart;
</span><span class="cx">         m_url.m_hostEnd = m_url.m_userStart;
</span><span class="lines">@@ -1426,8 +1459,8 @@
</span><span class="cx">         break;
</span><span class="cx">     case State::FileSlash:
</span><span class="cx">         LOG_FINAL_STATE(&quot;FileSlash&quot;);
</span><del>-        m_buffer.append(&quot;//&quot;);
-        m_url.m_userStart = m_buffer.length() - 1;
</del><ins>+        m_asciiBuffer.append(&quot;//&quot;, 2);
+        m_url.m_userStart = m_asciiBuffer.size() - 1;
</ins><span class="cx">         m_url.m_userEnd = m_url.m_userStart;
</span><span class="cx">         m_url.m_passwordEnd = m_url.m_userStart;
</span><span class="cx">         m_url.m_hostEnd = m_url.m_userStart;
</span><span class="lines">@@ -1440,8 +1473,8 @@
</span><span class="cx">     case State::FileHost:
</span><span class="cx">         LOG_FINAL_STATE(&quot;FileHost&quot;);
</span><span class="cx">         if (authorityOrHostBegin == c) {
</span><del>-            m_buffer.append('/');
-            m_url.m_userStart = m_buffer.length() - 1;
</del><ins>+            m_asciiBuffer.append('/');
+            m_url.m_userStart = m_asciiBuffer.size() - 1;
</ins><span class="cx">             m_url.m_userEnd = m_url.m_userStart;
</span><span class="cx">             m_url.m_passwordEnd = m_url.m_userStart;
</span><span class="cx">             m_url.m_hostEnd = m_url.m_userStart;
</span><span class="lines">@@ -1455,13 +1488,13 @@
</span><span class="cx"> 
</span><span class="cx">         if (!parseHost(CodePointIterator&lt;CharacterType&gt;(authorityOrHostBegin, c)))
</span><span class="cx">             return failure(input, length);
</span><del>-        
-        if (bufferView(m_buffer, m_url.m_passwordEnd, m_buffer.length() - m_url.m_passwordEnd) == &quot;localhost&quot;)  {
-            m_buffer.resize(m_url.m_passwordEnd);
-            m_url.m_hostEnd = m_buffer.length();
</del><ins>+
+        if (StringView(m_asciiBuffer.data() + m_url.m_passwordEnd, m_asciiBuffer.size() - m_url.m_passwordEnd) == &quot;localhost&quot;)  {
+            m_asciiBuffer.shrink(m_url.m_passwordEnd);
+            m_url.m_hostEnd = m_asciiBuffer.size();
</ins><span class="cx">             m_url.m_portEnd = m_url.m_hostEnd;
</span><span class="cx">         }
</span><del>-        m_buffer.append('/');
</del><ins>+        m_asciiBuffer.append('/');
</ins><span class="cx">         m_url.m_pathAfterLastSlash = m_url.m_hostEnd + 1;
</span><span class="cx">         m_url.m_pathEnd = m_url.m_pathAfterLastSlash;
</span><span class="cx">         m_url.m_queryEnd = m_url.m_pathAfterLastSlash;
</span><span class="lines">@@ -1472,13 +1505,13 @@
</span><span class="cx">         RELEASE_ASSERT_NOT_REACHED();
</span><span class="cx">     case State::Path:
</span><span class="cx">         LOG_FINAL_STATE(&quot;Path&quot;);
</span><del>-        m_url.m_pathEnd = m_buffer.length();
</del><ins>+        m_url.m_pathEnd = m_asciiBuffer.size();
</ins><span class="cx">         m_url.m_queryEnd = m_url.m_pathEnd;
</span><span class="cx">         m_url.m_fragmentEnd = m_url.m_pathEnd;
</span><span class="cx">         break;
</span><span class="cx">     case State::CannotBeABaseURLPath:
</span><span class="cx">         LOG_FINAL_STATE(&quot;CannotBeABaseURLPath&quot;);
</span><del>-        m_url.m_pathEnd = m_buffer.length();
</del><ins>+        m_url.m_pathEnd = m_asciiBuffer.size();
</ins><span class="cx">         m_url.m_queryEnd = m_url.m_pathEnd;
</span><span class="cx">         m_url.m_fragmentEnd = m_url.m_pathEnd;
</span><span class="cx">         break;
</span><span class="lines">@@ -1485,17 +1518,27 @@
</span><span class="cx">     case State::Query:
</span><span class="cx">         LOG_FINAL_STATE(&quot;Query&quot;);
</span><span class="cx">         if (!isUTF8Encoding)
</span><del>-            encodeQuery(queryBuffer, m_buffer, encoding);
-        m_url.m_queryEnd = m_buffer.length();
</del><ins>+            encodeQuery(queryBuffer, m_asciiBuffer, encoding);
+        m_url.m_queryEnd = m_asciiBuffer.size();
</ins><span class="cx">         m_url.m_fragmentEnd = m_url.m_queryEnd;
</span><span class="cx">         break;
</span><span class="cx">     case State::Fragment:
</span><span class="cx">         LOG_FINAL_STATE(&quot;Fragment&quot;);
</span><del>-        m_url.m_fragmentEnd = m_buffer.length();
</del><ins>+        m_url.m_fragmentEnd = m_asciiBuffer.size() + m_unicodeFragmentBuffer.size();
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    m_url.m_string = m_buffer.toString();
</del><ins>+    if (m_unicodeFragmentBuffer.isEmpty()) {
+        // FIXME: String::adopt should require a WTFMove.
+        m_url.m_string = String::adopt(m_asciiBuffer);
+    } else {
+        StringBuilder builder;
+        builder.reserveCapacity(m_asciiBuffer.size() + m_unicodeFragmentBuffer.size());
+        builder.append(m_asciiBuffer.data(), m_asciiBuffer.size());
+        for (size_t i = 0; i &lt; m_unicodeFragmentBuffer.size(); ++i)
+            builder.append(m_unicodeFragmentBuffer[i]);
+        m_url.m_string = builder.toString();
+    }
</ins><span class="cx">     m_url.m_isValid = true;
</span><span class="cx">     LOG(URLParser, &quot;Parsed URL &lt;%s&gt;&quot;, m_url.m_string.utf8().data());
</span><span class="cx">     return m_url;
</span><span class="lines">@@ -1505,7 +1548,7 @@
</span><span class="cx"> void URLParser::parseAuthority(CodePointIterator&lt;CharacterType&gt; iterator)
</span><span class="cx"> {
</span><span class="cx">     if (iterator.atEnd()) {
</span><del>-        m_url.m_userEnd = m_buffer.length();
</del><ins>+        m_url.m_userEnd = m_asciiBuffer.size();
</ins><span class="cx">         m_url.m_passwordEnd = m_url.m_userEnd;
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="lines">@@ -1512,35 +1555,48 @@
</span><span class="cx">     for (; !iterator.atEnd(); ++iterator) {
</span><span class="cx">         if (*iterator == ':') {
</span><span class="cx">             ++iterator;
</span><del>-            m_url.m_userEnd = m_buffer.length();
</del><ins>+            m_url.m_userEnd = m_asciiBuffer.size();
</ins><span class="cx">             if (iterator.atEnd()) {
</span><span class="cx">                 m_url.m_passwordEnd = m_url.m_userEnd;
</span><span class="cx">                 if (m_url.m_userEnd &gt; m_url.m_userStart)
</span><del>-                    m_buffer.append('@');
</del><ins>+                    m_asciiBuffer.append('@');
</ins><span class="cx">                 return;
</span><span class="cx">             }
</span><del>-            m_buffer.append(':');
</del><ins>+            m_asciiBuffer.append(':');
</ins><span class="cx">             break;
</span><span class="cx">         }
</span><del>-        utf8PercentEncode(*iterator, m_buffer, isInUserInfoEncodeSet);
</del><ins>+        utf8PercentEncode(*iterator, m_asciiBuffer, isInUserInfoEncodeSet);
</ins><span class="cx">     }
</span><span class="cx">     for (; !iterator.atEnd(); ++iterator)
</span><del>-        utf8PercentEncode(*iterator, m_buffer, isInUserInfoEncodeSet);
-    m_url.m_passwordEnd = m_buffer.length();
</del><ins>+        utf8PercentEncode(*iterator, m_asciiBuffer, isInUserInfoEncodeSet);
+    m_url.m_passwordEnd = m_asciiBuffer.size();
</ins><span class="cx">     if (!m_url.m_userEnd)
</span><span class="cx">         m_url.m_userEnd = m_url.m_passwordEnd;
</span><del>-    m_buffer.append('@');
</del><ins>+    m_asciiBuffer.append('@');
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void serializeIPv4(uint32_t address, StringBuilder&amp; buffer)
</del><ins>+template&lt;typename UnsignedIntegerType&gt;
+void append(Vector&lt;LChar&gt;&amp; destination, UnsignedIntegerType number)
</ins><span class="cx"> {
</span><del>-    buffer.appendNumber(address &gt;&gt; 24);
</del><ins>+    LChar buf[sizeof(UnsignedIntegerType) * 3 + 1];
+    LChar* end = buf + WTF_ARRAY_LENGTH(buf);
+    LChar* p = end;
+    do {
+        *--p = (number % 10) + '0';
+        number /= 10;
+    } while (number);
+    destination.append(p, end - p);
+}
+
+static void serializeIPv4(uint32_t address, Vector&lt;LChar&gt;&amp; buffer)
+{
+    append&lt;uint8_t&gt;(buffer, address &gt;&gt; 24);
</ins><span class="cx">     buffer.append('.');
</span><del>-    buffer.appendNumber((address &gt;&gt; 16) &amp; 0xFF);
</del><ins>+    append&lt;uint8_t&gt;(buffer, address &gt;&gt; 16);
</ins><span class="cx">     buffer.append('.');
</span><del>-    buffer.appendNumber((address &gt;&gt; 8) &amp; 0xFF);
</del><ins>+    append&lt;uint8_t&gt;(buffer, address &gt;&gt; 8);
</ins><span class="cx">     buffer.append('.');
</span><del>-    buffer.appendNumber(address &amp; 0xFF);
</del><ins>+    append&lt;uint8_t&gt;(buffer, address);
</ins><span class="cx"> }
</span><span class="cx">     
</span><span class="cx"> static size_t zeroSequenceLength(const std::array&lt;uint16_t, 8&gt;&amp; address, size_t begin)
</span><span class="lines">@@ -1570,7 +1626,7 @@
</span><span class="cx">     return longest;
</span><span class="cx"> }
</span><span class="cx">     
</span><del>-static void serializeIPv6Piece(uint16_t piece, StringBuilder&amp; buffer)
</del><ins>+static void serializeIPv6Piece(uint16_t piece, Vector&lt;LChar&gt;&amp; buffer)
</ins><span class="cx"> {
</span><span class="cx">     bool printed = false;
</span><span class="cx">     if (auto nibble0 = piece &gt;&gt; 12) {
</span><span class="lines">@@ -1588,7 +1644,7 @@
</span><span class="cx">     buffer.append(lowerNibbleToLowercaseASCIIHexDigit(piece &amp; 0xF));
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void serializeIPv6(std::array&lt;uint16_t, 8&gt; address, StringBuilder&amp; buffer)
</del><ins>+static void serializeIPv6(std::array&lt;uint16_t, 8&gt; address, Vector&lt;LChar&gt;&amp; buffer)
</ins><span class="cx"> {
</span><span class="cx">     buffer.append('[');
</span><span class="cx">     auto compressPointer = findLongestZeroSequence(address);
</span><span class="lines">@@ -1598,7 +1654,7 @@
</span><span class="cx">             if (piece)
</span><span class="cx">                 buffer.append(':');
</span><span class="cx">             else
</span><del>-                buffer.append(&quot;::&quot;);
</del><ins>+                buffer.append(&quot;::&quot;, 2);
</ins><span class="cx">             while (piece &lt; 8 &amp;&amp; !address[piece])
</span><span class="cx">                 piece++;
</span><span class="cx">             if (piece == 8)
</span><span class="lines">@@ -1880,10 +1936,10 @@
</span><span class="cx"> {
</span><span class="cx">     uint32_t port = 0;
</span><span class="cx">     if (iterator.atEnd()) {
</span><del>-        m_url.m_portEnd = m_buffer.length();
</del><ins>+        m_url.m_portEnd = m_asciiBuffer.size();
</ins><span class="cx">         return true;
</span><span class="cx">     }
</span><del>-    m_buffer.append(':');
</del><ins>+    m_asciiBuffer.append(':');
</ins><span class="cx">     for (; !iterator.atEnd(); ++iterator) {
</span><span class="cx">         if (isTabOrNewline(*iterator))
</span><span class="cx">             continue;
</span><span class="lines">@@ -1894,14 +1950,14 @@
</span><span class="cx">         } else
</span><span class="cx">             return false;
</span><span class="cx">     }
</span><del>-    
-    if (isDefaultPort(bufferView(m_buffer, 0, m_url.m_schemeEnd), port)) {
-        ASSERT(m_buffer[m_buffer.length() - 1] == ':');
-        m_buffer.resize(m_buffer.length() - 1);
</del><ins>+
+    if (isDefaultPort(StringView(m_asciiBuffer.data(), m_url.m_schemeEnd), port)) {
+        ASSERT(m_asciiBuffer.last() == ':');
+        m_asciiBuffer.shrink(m_asciiBuffer.size() - 1);
</ins><span class="cx">     } else
</span><del>-        m_buffer.appendNumber(port);
</del><ins>+        append&lt;uint16_t&gt;(m_asciiBuffer, static_cast&lt;uint16_t&gt;(port));
</ins><span class="cx"> 
</span><del>-    m_url.m_portEnd = m_buffer.length();
</del><ins>+    m_url.m_portEnd = m_asciiBuffer.size();
</ins><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -1916,8 +1972,8 @@
</span><span class="cx">         while (!ipv6End.atEnd() &amp;&amp; *ipv6End != ']')
</span><span class="cx">             ++ipv6End;
</span><span class="cx">         if (auto address = parseIPv6Host(CodePointIterator&lt;CharacterType&gt;(iterator, ipv6End))) {
</span><del>-            serializeIPv6(address.value(), m_buffer);
-            m_url.m_hostEnd = m_buffer.length();
</del><ins>+            serializeIPv6(address.value(), m_asciiBuffer);
+            m_url.m_hostEnd = m_asciiBuffer.size();
</ins><span class="cx">             if (!ipv6End.atEnd()) {
</span><span class="cx">                 ++ipv6End;
</span><span class="cx">                 if (!ipv6End.atEnd() &amp;&amp; *ipv6End == ':') {
</span><span class="lines">@@ -1924,7 +1980,7 @@
</span><span class="cx">                     ++ipv6End;
</span><span class="cx">                     return parsePort(ipv6End);
</span><span class="cx">                 }
</span><del>-                m_url.m_portEnd = m_buffer.length();
</del><ins>+                m_url.m_portEnd = m_asciiBuffer.size();
</ins><span class="cx">                 return true;
</span><span class="cx">             }
</span><span class="cx">             return true;
</span><span class="lines">@@ -1942,10 +1998,10 @@
</span><span class="cx">                 return false;
</span><span class="cx">         }
</span><span class="cx">         if (auto address = parseIPv4Host(CodePointIterator&lt;CharacterType&gt;(hostIterator, iterator))) {
</span><del>-            serializeIPv4(address.value(), m_buffer);
-            m_url.m_hostEnd = m_buffer.length();
</del><ins>+            serializeIPv4(address.value(), m_asciiBuffer);
+            m_url.m_hostEnd = m_asciiBuffer.size();
</ins><span class="cx">             if (iterator.atEnd()) {
</span><del>-                m_url.m_portEnd = m_buffer.length();
</del><ins>+                m_url.m_portEnd = m_asciiBuffer.size();
</ins><span class="cx">                 return true;
</span><span class="cx">             }
</span><span class="cx">             ++iterator;
</span><span class="lines">@@ -1953,9 +2009,9 @@
</span><span class="cx">         }
</span><span class="cx">         for (; hostIterator != iterator; ++hostIterator) {
</span><span class="cx">             if (!isTabOrNewline(*hostIterator))
</span><del>-                m_buffer.append(toASCIILower(*hostIterator));
</del><ins>+                m_asciiBuffer.append(toASCIILower(*hostIterator));
</ins><span class="cx">         }
</span><del>-        m_url.m_hostEnd = m_buffer.length();
</del><ins>+        m_url.m_hostEnd = m_asciiBuffer.size();
</ins><span class="cx">         if (!hostIterator.atEnd()) {
</span><span class="cx">             ASSERT(*hostIterator == ':');
</span><span class="cx">             ++hostIterator;
</span><span class="lines">@@ -1963,7 +2019,7 @@
</span><span class="cx">                 ++hostIterator;
</span><span class="cx">             return parsePort(hostIterator);
</span><span class="cx">         }
</span><del>-        m_url.m_portEnd = m_buffer.length();
</del><ins>+        m_url.m_portEnd = m_asciiBuffer.size();
</ins><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -1992,20 +2048,20 @@
</span><span class="cx">     String&amp; asciiDomainValue = asciiDomain.value();
</span><span class="cx">     RELEASE_ASSERT(asciiDomainValue.is8Bit());
</span><span class="cx">     const LChar* asciiDomainCharacters = asciiDomainValue.characters8();
</span><del>-    
</del><ins>+
</ins><span class="cx">     if (auto address = parseIPv4Host(CodePointIterator&lt;LChar&gt;(asciiDomainCharacters, asciiDomainCharacters + asciiDomainValue.length()))) {
</span><del>-        serializeIPv4(address.value(), m_buffer);
-        m_url.m_hostEnd = m_buffer.length();
</del><ins>+        serializeIPv4(address.value(), m_asciiBuffer);
+        m_url.m_hostEnd = m_asciiBuffer.size();
</ins><span class="cx">         if (iterator.atEnd()) {
</span><del>-            m_url.m_portEnd = m_buffer.length();
</del><ins>+            m_url.m_portEnd = m_asciiBuffer.size();
</ins><span class="cx">             return true;
</span><span class="cx">         }
</span><span class="cx">         ++iterator;
</span><span class="cx">         return parsePort(iterator);
</span><span class="cx">     }
</span><del>-    
-    m_buffer.append(asciiDomain.value());
-    m_url.m_hostEnd = m_buffer.length();
</del><ins>+
+    m_asciiBuffer.append(asciiDomainCharacters, asciiDomainValue.length());
+    m_url.m_hostEnd = m_asciiBuffer.size();
</ins><span class="cx">     if (!iterator.atEnd()) {
</span><span class="cx">         ASSERT(*iterator == ':');
</span><span class="cx">         ++iterator;
</span><span class="lines">@@ -2013,7 +2069,7 @@
</span><span class="cx">             ++iterator;
</span><span class="cx">         return parsePort(iterator);
</span><span class="cx">     }
</span><del>-    m_url.m_portEnd = m_buffer.length();
</del><ins>+    m_url.m_portEnd = m_asciiBuffer.size();
</ins><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -2047,7 +2103,7 @@
</span><span class="cx">     return output;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void serializeURLEncodedForm(const String&amp; input, StringBuilder&amp; output)
</del><ins>+static void serializeURLEncodedForm(const String&amp; input, Vector&lt;LChar&gt;&amp; output)
</ins><span class="cx"> {
</span><span class="cx">     auto utf8 = input.utf8(StrictConversion);
</span><span class="cx">     const char* data = utf8.data();
</span><span class="lines">@@ -2070,7 +2126,7 @@
</span><span class="cx">     
</span><span class="cx"> String URLParser::serialize(const URLEncodedForm&amp; tuples)
</span><span class="cx"> {
</span><del>-    StringBuilder output;
</del><ins>+    Vector&lt;LChar&gt; output;
</ins><span class="cx">     for (auto&amp; tuple : tuples) {
</span><span class="cx">         if (!output.isEmpty())
</span><span class="cx">             output.append('&amp;');
</span><span class="lines">@@ -2078,7 +2134,7 @@
</span><span class="cx">         output.append('=');
</span><span class="cx">         serializeURLEncodedForm(tuple.second, output);
</span><span class="cx">     }
</span><del>-    return output.toString();
</del><ins>+    return String::adopt(output);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool URLParser::allValuesEqual(const URL&amp; a, const URL&amp; b)
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformURLParserh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/URLParser.h (206043 => 206044)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/URLParser.h        2016-09-16 20:24:23 UTC (rev 206043)
+++ trunk/Source/WebCore/platform/URLParser.h        2016-09-16 20:35:16 UTC (rev 206044)
</span><span class="lines">@@ -48,7 +48,8 @@
</span><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     URL m_url;
</span><del>-    StringBuilder m_buffer;
</del><ins>+    Vector&lt;LChar&gt; m_asciiBuffer;
+    Vector&lt;UChar32&gt; m_unicodeFragmentBuffer;
</ins><span class="cx">     bool m_urlIsSpecial { false };
</span><span class="cx">     bool m_hostHasPercentOrNonASCII { false };
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>