<!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>[205986] 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/205986">205986</a></dd>
<dt>Author</dt> <dd>achristensen@apple.com</dd>
<dt>Date</dt> <dd>2016-09-15 11:12:09 -0700 (Thu, 15 Sep 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Use efficient iterators in URLParser
https://bugs.webkit.org/show_bug.cgi?id=162007

Reviewed by Tim Horton.

URLParser used to use StringView::CodePoints::Iterator, which needs to check if
the StringView is 8-bit or 16-bit every time it does anything.
I wrote a new CodePointIterator template which already knows whether it is iterating
8-bit or 16-bit characters, so it does not need to do the checks each time it gets a
code point or advances to the next code point.

No change in behavior except a performance increase.
Covered by existing tests.

* platform/URLParser.cpp:
(WebCore::CodePointIterator::CodePointIterator):
(WebCore::CodePointIterator::operator==):
(WebCore::CodePointIterator::operator!=):
(WebCore::CodePointIterator::operator=):
(WebCore::CodePointIterator::atEnd):
(WebCore::CodePointIterator&lt;LChar&gt;::operator):
(WebCore::CodePointIterator&lt;UChar&gt;::operator):
(WebCore::isWindowsDriveLetter):
(WebCore::shouldCopyFileURL):
(WebCore::isPercentEncodedDot):
(WebCore::isSingleDotPathSegment):
(WebCore::isDoubleDotPathSegment):
(WebCore::consumeSingleDotPathSegment):
(WebCore::consumeDoubleDotPathSegment):
(WebCore::URLParser::failure):
(WebCore::URLParser::parse):
(WebCore::URLParser::parseAuthority):
(WebCore::parseIPv4Number):
(WebCore::parseIPv4Host):
(WebCore::parseIPv6Host):
(WebCore::URLParser::parsePort):
(WebCore::URLParser::parseHost):
* 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 (205985 => 205986)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-09-15 18:11:48 UTC (rev 205985)
+++ trunk/Source/WebCore/ChangeLog        2016-09-15 18:12:09 UTC (rev 205986)
</span><span class="lines">@@ -1,3 +1,44 @@
</span><ins>+2016-09-15  Alex Christensen  &lt;achristensen@webkit.org&gt;
+
+        Use efficient iterators in URLParser
+        https://bugs.webkit.org/show_bug.cgi?id=162007
+
+        Reviewed by Tim Horton.
+
+        URLParser used to use StringView::CodePoints::Iterator, which needs to check if
+        the StringView is 8-bit or 16-bit every time it does anything.
+        I wrote a new CodePointIterator template which already knows whether it is iterating
+        8-bit or 16-bit characters, so it does not need to do the checks each time it gets a
+        code point or advances to the next code point.
+
+        No change in behavior except a performance increase.
+        Covered by existing tests.
+
+        * platform/URLParser.cpp:
+        (WebCore::CodePointIterator::CodePointIterator):
+        (WebCore::CodePointIterator::operator==):
+        (WebCore::CodePointIterator::operator!=):
+        (WebCore::CodePointIterator::operator=):
+        (WebCore::CodePointIterator::atEnd):
+        (WebCore::CodePointIterator&lt;LChar&gt;::operator):
+        (WebCore::CodePointIterator&lt;UChar&gt;::operator):
+        (WebCore::isWindowsDriveLetter):
+        (WebCore::shouldCopyFileURL):
+        (WebCore::isPercentEncodedDot):
+        (WebCore::isSingleDotPathSegment):
+        (WebCore::isDoubleDotPathSegment):
+        (WebCore::consumeSingleDotPathSegment):
+        (WebCore::consumeDoubleDotPathSegment):
+        (WebCore::URLParser::failure):
+        (WebCore::URLParser::parse):
+        (WebCore::URLParser::parseAuthority):
+        (WebCore::parseIPv4Number):
+        (WebCore::parseIPv4Host):
+        (WebCore::parseIPv6Host):
+        (WebCore::URLParser::parsePort):
+        (WebCore::URLParser::parseHost):
+        * platform/URLParser.h:
+
</ins><span class="cx"> 2016-09-14  Antti Koivisto  &lt;antti@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Move text decoration style computation from RenderObject to TextDecorationPainter
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformURLParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/URLParser.cpp (205985 => 205986)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/URLParser.cpp        2016-09-15 18:11:48 UTC (rev 205985)
+++ trunk/Source/WebCore/platform/URLParser.cpp        2016-09-15 18:12:09 UTC (rev 205986)
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx"> #include &quot;Logging.h&quot;
</span><span class="cx"> #include &lt;array&gt;
</span><span class="cx"> #include &lt;unicode/uidna.h&gt;
</span><ins>+#include &lt;unicode/utypes.h&gt;
</ins><span class="cx"> #include &lt;wtf/HashMap.h&gt;
</span><span class="cx"> #include &lt;wtf/NeverDestroyed.h&gt;
</span><span class="cx"> #include &lt;wtf/text/StringBuilder.h&gt;
</span><span class="lines">@@ -36,6 +37,85 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><ins>+template&lt;typename CharacterType&gt;
+class CodePointIterator {
+public:
+    CodePointIterator() { }
+    CodePointIterator(const CharacterType* begin, const CharacterType* end)
+        : m_begin(begin)
+        , m_end(end)
+    {
+    }
+    
+    CodePointIterator(const CodePointIterator&amp; begin, const CodePointIterator&amp; end)
+        : CodePointIterator(begin.m_begin, end.m_begin)
+    {
+        ASSERT(end.m_begin &gt;= begin.m_begin);
+    }
+    
+    UChar32 operator*() const;
+    CodePointIterator&amp; operator++();
+
+    bool operator==(const CodePointIterator&amp; other) const
+    {
+        return m_begin == other.m_begin
+            &amp;&amp; m_end == other.m_end;
+    }
+    bool operator!=(const CodePointIterator&amp; other) const { return !(*this == other); }
+    
+    CodePointIterator&amp; operator=(const CodePointIterator&amp; other)
+    {
+        m_begin = other.m_begin;
+        m_end = other.m_end;
+        return *this;
+    }
+
+    bool atEnd() const
+    {
+        ASSERT(m_begin &lt;= m_end);
+        return m_begin &gt;= m_end;
+    }
+    
+private:
+    const CharacterType* m_begin { nullptr };
+    const CharacterType* m_end { nullptr };
+};
+
+template&lt;&gt;
+UChar32 CodePointIterator&lt;LChar&gt;::operator*() const
+{
+    ASSERT(!atEnd());
+    return *m_begin;
+}
+
+template&lt;&gt;
+auto CodePointIterator&lt;LChar&gt;::operator++() -&gt; CodePointIterator&amp;
+{
+    ASSERT(!atEnd());
+    m_begin++;
+    return *this;
+}
+
+template&lt;&gt;
+UChar32 CodePointIterator&lt;UChar&gt;::operator*() const
+{
+    ASSERT(!atEnd());
+    UChar32 c;
+    U16_GET(m_begin, 0, 0, m_end - m_begin, c);
+    return c;
+}
+
+template&lt;&gt;
+auto CodePointIterator&lt;UChar&gt;::operator++() -&gt; CodePointIterator&amp;
+{
+    ASSERT(!atEnd());
+    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++;
+    return *this;
+}
+
</ins><span class="cx"> template&lt;typename CharacterType&gt; static bool isC0Control(CharacterType character) { return character &lt;= 0x0001F; }
</span><span class="cx"> template&lt;typename CharacterType&gt; static bool isC0ControlOrSpace(CharacterType character) { return isC0Control(character) || character == 0x0020; }
</span><span class="cx"> template&lt;typename CharacterType&gt; static bool isTabOrNewline(CharacterType character) { return character == 0x0009 || character == 0x000A || character == 0x000D; }
</span><span class="lines">@@ -45,12 +125,13 @@
</span><span class="cx"> template&lt;typename CharacterType&gt; static bool isInvalidDomainCharacter(CharacterType character) { return character == 0x0000 || character == 0x0009 || character == 0x000A || character == 0x000D || character == 0x0020 || character == '#' || character == '%' || character == '/' || character == ':' || character == '?' || character == '@' || character == '[' || character == '\\' || character == ']'; }
</span><span class="cx"> template&lt;typename CharacterType&gt; static bool isPercentOrNonASCII(CharacterType character) { return !isASCII(character) || character == '%'; }
</span><span class="cx">     
</span><del>-static bool isWindowsDriveLetter(StringView::CodePoints::Iterator iterator, const StringView::CodePoints::Iterator&amp; end)
</del><ins>+template&lt;typename CharacterType&gt;
+static bool isWindowsDriveLetter(CodePointIterator&lt;CharacterType&gt; iterator)
</ins><span class="cx"> {
</span><del>-    if (iterator == end || !isASCIIAlpha(*iterator))
</del><ins>+    if (iterator.atEnd() || !isASCIIAlpha(*iterator))
</ins><span class="cx">         return false;
</span><span class="cx">     ++iterator;
</span><del>-    if (iterator == end)
</del><ins>+    if (iterator.atEnd())
</ins><span class="cx">         return false;
</span><span class="cx">     return *iterator == ':' || *iterator == '|';
</span><span class="cx"> }
</span><span class="lines">@@ -62,17 +143,18 @@
</span><span class="cx">     return isASCIIAlpha(builder[index]) &amp;&amp; (builder[index + 1] == ':' || builder[index + 1] == '|');
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static bool shouldCopyFileURL(StringView::CodePoints::Iterator iterator, const StringView::CodePoints::Iterator end)
</del><ins>+template&lt;typename CharacterType&gt;
+static bool shouldCopyFileURL(CodePointIterator&lt;CharacterType&gt; iterator)
</ins><span class="cx"> {
</span><del>-    if (isWindowsDriveLetter(iterator, end))
</del><ins>+    if (isWindowsDriveLetter(iterator))
</ins><span class="cx">         return true;
</span><del>-    if (iterator == end)
</del><ins>+    if (iterator.atEnd())
</ins><span class="cx">         return false;
</span><span class="cx">     ++iterator;
</span><del>-    if (iterator == end)
</del><ins>+    if (iterator.atEnd())
</ins><span class="cx">         return true;
</span><span class="cx">     ++iterator;
</span><del>-    if (iterator == end)
</del><ins>+    if (iterator.atEnd())
</ins><span class="cx">         return true;
</span><span class="cx">     return *iterator != '/' &amp;&amp; *iterator != '\\' &amp;&amp; *iterator != '?' &amp;&amp; *iterator != '#';
</span><span class="cx"> }
</span><span class="lines">@@ -351,75 +433,79 @@
</span><span class="cx"> 
</span><span class="cx"> static const char* dotASCIICode = &quot;2e&quot;;
</span><span class="cx"> 
</span><del>-static bool isPercentEncodedDot(StringView::CodePoints::Iterator c, const StringView::CodePoints::Iterator&amp; end)
</del><ins>+template&lt;typename CharacterType&gt;
+static bool isPercentEncodedDot(CodePointIterator&lt;CharacterType&gt; c)
</ins><span class="cx"> {
</span><del>-    if (c == end)
</del><ins>+    if (c.atEnd())
</ins><span class="cx">         return false;
</span><span class="cx">     if (*c != '%')
</span><span class="cx">         return false;
</span><span class="cx">     ++c;
</span><del>-    if (c == end)
</del><ins>+    if (c.atEnd())
</ins><span class="cx">         return false;
</span><span class="cx">     if (*c != dotASCIICode[0])
</span><span class="cx">         return false;
</span><span class="cx">     ++c;
</span><del>-    if (c == end)
</del><ins>+    if (c.atEnd())
</ins><span class="cx">         return false;
</span><span class="cx">     return toASCIILower(*c) == dotASCIICode[1];
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static bool isSingleDotPathSegment(StringView::CodePoints::Iterator c, const StringView::CodePoints::Iterator&amp; end)
</del><ins>+template&lt;typename CharacterType&gt;
+static bool isSingleDotPathSegment(CodePointIterator&lt;CharacterType&gt; c)
</ins><span class="cx"> {
</span><del>-    if (c == end)
</del><ins>+    if (c.atEnd())
</ins><span class="cx">         return false;
</span><span class="cx">     if (*c == '.') {
</span><span class="cx">         ++c;
</span><del>-        return c == end || *c == '/' || *c == '\\' || *c == '?' || *c == '#';
</del><ins>+        return c.atEnd() || *c == '/' || *c == '\\' || *c == '?' || *c == '#';
</ins><span class="cx">     }
</span><span class="cx">     if (*c != '%')
</span><span class="cx">         return false;
</span><span class="cx">     ++c;
</span><del>-    if (c == end || *c != dotASCIICode[0])
</del><ins>+    if (c.atEnd() || *c != dotASCIICode[0])
</ins><span class="cx">         return false;
</span><span class="cx">     ++c;
</span><del>-    if (c == end)
</del><ins>+    if (c.atEnd())
</ins><span class="cx">         return false;
</span><span class="cx">     if (toASCIILower(*c) == dotASCIICode[1]) {
</span><span class="cx">         ++c;
</span><del>-        return c == end || *c == '/' || *c == '\\' || *c == '?' || *c == '#';
</del><ins>+        return c.atEnd() || *c == '/' || *c == '\\' || *c == '?' || *c == '#';
</ins><span class="cx">     }
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><del>-    
-static bool isDoubleDotPathSegment(StringView::CodePoints::Iterator c, const StringView::CodePoints::Iterator&amp; end)
</del><ins>+
+template&lt;typename CharacterType&gt;
+static bool isDoubleDotPathSegment(CodePointIterator&lt;CharacterType&gt; c)
</ins><span class="cx"> {
</span><del>-    if (c == end)
</del><ins>+    if (c.atEnd())
</ins><span class="cx">         return false;
</span><span class="cx">     if (*c == '.') {
</span><span class="cx">         ++c;
</span><del>-        return isSingleDotPathSegment(c, end);
</del><ins>+        return isSingleDotPathSegment(c);
</ins><span class="cx">     }
</span><span class="cx">     if (*c != '%')
</span><span class="cx">         return false;
</span><span class="cx">     ++c;
</span><del>-    if (c == end || *c != dotASCIICode[0])
</del><ins>+    if (c.atEnd() || *c != dotASCIICode[0])
</ins><span class="cx">         return false;
</span><span class="cx">     ++c;
</span><del>-    if (c == end)
</del><ins>+    if (c.atEnd())
</ins><span class="cx">         return false;
</span><span class="cx">     if (toASCIILower(*c) == dotASCIICode[1]) {
</span><span class="cx">         ++c;
</span><del>-        return isSingleDotPathSegment(c, end);
</del><ins>+        return isSingleDotPathSegment(c);
</ins><span class="cx">     }
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void consumeSingleDotPathSegment(StringView::CodePoints::Iterator&amp; c, const StringView::CodePoints::Iterator end)
</del><ins>+template&lt;typename CharacterType&gt;
+static void consumeSingleDotPathSegment(CodePointIterator&lt;CharacterType&gt;&amp; c)
</ins><span class="cx"> {
</span><del>-    ASSERT(isSingleDotPathSegment(c, end));
</del><ins>+    ASSERT(isSingleDotPathSegment(c));
</ins><span class="cx">     if (*c == '.') {
</span><span class="cx">         ++c;
</span><del>-        if (c != end) {
</del><ins>+        if (!c.atEnd()) {
</ins><span class="cx">             if (*c == '/' || *c == '\\')
</span><span class="cx">                 ++c;
</span><span class="cx">             else
</span><span class="lines">@@ -432,7 +518,7 @@
</span><span class="cx">         ++c;
</span><span class="cx">         ASSERT(toASCIILower(*c) == dotASCIICode[1]);
</span><span class="cx">         ++c;
</span><del>-        if (c != end) {
</del><ins>+        if (!c.atEnd()) {
</ins><span class="cx">             if (*c == '/' || *c == '\\')
</span><span class="cx">                 ++c;
</span><span class="cx">             else
</span><span class="lines">@@ -441,9 +527,10 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void consumeDoubleDotPathSegment(StringView::CodePoints::Iterator&amp; c, const StringView::CodePoints::Iterator end)
</del><ins>+template&lt;typename CharacterType&gt;
+static void consumeDoubleDotPathSegment(CodePointIterator&lt;CharacterType&gt;&amp; c)
</ins><span class="cx"> {
</span><del>-    ASSERT(isDoubleDotPathSegment(c, end));
</del><ins>+    ASSERT(isDoubleDotPathSegment(c));
</ins><span class="cx">     if (*c == '.')
</span><span class="cx">         ++c;
</span><span class="cx">     else {
</span><span class="lines">@@ -454,7 +541,7 @@
</span><span class="cx">         ASSERT(toASCIILower(*c) == dotASCIICode[1]);
</span><span class="cx">         ++c;
</span><span class="cx">     }
</span><del>-    consumeSingleDotPathSegment(c, end);
</del><ins>+    consumeSingleDotPathSegment(c);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void URLParser::popPath()
</span><span class="lines">@@ -470,11 +557,13 @@
</span><span class="cx">     m_buffer.resize(m_url.m_pathAfterLastSlash);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-URL URLParser::failure(const String&amp; input)
</del><ins>+template&lt;typename CharacterType&gt;
+URL URLParser::failure(const CharacterType* input, unsigned length)
</ins><span class="cx"> {
</span><span class="cx">     URL url;
</span><span class="cx">     url.m_isValid = false;
</span><span class="cx">     url.m_protocolIsInHTTPFamily = false;
</span><ins>+    url.m_cannotBeABaseURL = false;
</ins><span class="cx">     url.m_schemeEnd = 0;
</span><span class="cx">     url.m_userStart = 0;
</span><span class="cx">     url.m_userEnd = 0;
</span><span class="lines">@@ -485,31 +574,37 @@
</span><span class="cx">     url.m_pathEnd = 0;
</span><span class="cx">     url.m_queryEnd = 0;
</span><span class="cx">     url.m_fragmentEnd = 0;
</span><del>-    url.m_string = input;
</del><ins>+    url.m_string = String(input, length);
</ins><span class="cx">     return url;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> URL URLParser::parse(const String&amp; input, const URL&amp; base, const TextEncoding&amp; encoding)
</span><span class="cx"> {
</span><del>-    LOG(URLParser, &quot;Parsing URL &lt;%s&gt; base &lt;%s&gt;&quot;, input.utf8().data(), base.string().utf8().data());
</del><ins>+    if (input.is8Bit())
+        return parse(input.characters8(), input.length(), base, encoding);
+    return parse(input.characters16(), input.length(), base, encoding);
+}
+
+template&lt;typename CharacterType&gt;
+URL URLParser::parse(const CharacterType* input, const unsigned length, const URL&amp; base, const TextEncoding&amp; encoding)
+{
+    LOG(URLParser, &quot;Parsing URL &lt;%s&gt; base &lt;%s&gt;&quot;, String(input, length).utf8().data(), base.string().utf8().data());
</ins><span class="cx">     m_url = { };
</span><span class="cx">     m_buffer.clear();
</span><del>-    m_buffer.reserveCapacity(input.length());
</del><ins>+    m_buffer.reserveCapacity(length);
</ins><span class="cx">     
</span><span class="cx">     bool isUTF8Encoding = encoding == UTF8Encoding();
</span><span class="cx">     StringBuilder queryBuffer;
</span><span class="cx"> 
</span><del>-    unsigned endIndex = input.length();
</del><ins>+    unsigned endIndex = length;
</ins><span class="cx">     while (endIndex &amp;&amp; isC0ControlOrSpace(input[endIndex - 1]))
</span><span class="cx">         endIndex--;
</span><del>-    auto codePoints = bufferView(input, 0, endIndex).codePoints();
-    auto c = codePoints.begin();
-    auto end = codePoints.end();
-    auto authorityOrHostBegin = codePoints.begin();
-    while (c != end &amp;&amp; isC0ControlOrSpace(*c))
</del><ins>+    CodePointIterator&lt;CharacterType&gt; c(input, input + endIndex);
+    CodePointIterator&lt;CharacterType&gt; authorityOrHostBegin;
+    while (!c.atEnd() &amp;&amp; isC0ControlOrSpace(*c))
</ins><span class="cx">         ++c;
</span><span class="cx">     auto beginAfterControlAndSpace = c;
</span><del>-    
</del><ins>+
</ins><span class="cx">     enum class State : uint8_t {
</span><span class="cx">         SchemeStart,
</span><span class="cx">         Scheme,
</span><span class="lines">@@ -536,7 +631,7 @@
</span><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><del>-    while (c != end) {
</del><ins>+    while (!c.atEnd()) {
</ins><span class="cx">         if (isTabOrNewline(*c)) {
</span><span class="cx">             ++c;
</span><span class="cx">             continue;
</span><span class="lines">@@ -582,9 +677,9 @@
</span><span class="cx">                     m_url.m_portEnd = m_url.m_userStart;
</span><span class="cx">                     auto maybeSlash = c;
</span><span class="cx">                     ++maybeSlash;
</span><del>-                    while (maybeSlash != end &amp;&amp; isTabOrNewline(*maybeSlash))
</del><ins>+                    while (!maybeSlash.atEnd() &amp;&amp; isTabOrNewline(*maybeSlash))
</ins><span class="cx">                         ++maybeSlash;
</span><del>-                    if (maybeSlash != end &amp;&amp; *maybeSlash == '/') {
</del><ins>+                    if (!maybeSlash.atEnd() &amp;&amp; *maybeSlash == '/') {
</ins><span class="cx">                         m_buffer.append('/');
</span><span class="cx">                         m_url.m_pathAfterLastSlash = m_url.m_userStart + 1;
</span><span class="cx">                         state = State::PathOrAuthority;
</span><span class="lines">@@ -605,9 +700,9 @@
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="cx">             ++c;
</span><del>-            while (c != end &amp;&amp; isTabOrNewline(*c))
</del><ins>+            while (!c.atEnd() &amp;&amp; isTabOrNewline(*c))
</ins><span class="cx">                 ++c;
</span><del>-            if (c == end) {
</del><ins>+            if (c.atEnd()) {
</ins><span class="cx">                 m_buffer.clear();
</span><span class="cx">                 state = State::NoScheme;
</span><span class="cx">                 c = beginAfterControlAndSpace;
</span><span class="lines">@@ -616,7 +711,7 @@
</span><span class="cx">         case State::NoScheme:
</span><span class="cx">             LOG_STATE(&quot;NoScheme&quot;);
</span><span class="cx">             if (base.isNull() || (base.m_cannotBeABaseURL &amp;&amp; *c != '#'))
</span><del>-                return failure(input);
</del><ins>+                return failure(input, length);
</ins><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><span class="lines">@@ -637,10 +732,10 @@
</span><span class="cx">             if (*c == '/') {
</span><span class="cx">                 m_buffer.append('/');
</span><span class="cx">                 ++c;
</span><del>-                while (c != end &amp;&amp; isTabOrNewline(*c))
</del><ins>+                while (!c.atEnd() &amp;&amp; isTabOrNewline(*c))
</ins><span class="cx">                     ++c;
</span><del>-                if (c == end)
-                    return failure(input);
</del><ins>+                if (c.atEnd())
+                    return failure(input, length);
</ins><span class="cx">                 if (*c == '/') {
</span><span class="cx">                     m_buffer.append('/');
</span><span class="cx">                     state = State::SpecialAuthorityIgnoreSlashes;
</span><span class="lines">@@ -705,10 +800,10 @@
</span><span class="cx">             m_buffer.append(&quot;//&quot;);
</span><span class="cx">             if (*c == '/' || *c == '\\') {
</span><span class="cx">                 ++c;
</span><del>-                while (c != end &amp;&amp; isTabOrNewline(*c))
</del><ins>+                while (!c.atEnd() &amp;&amp; isTabOrNewline(*c))
</ins><span class="cx">                     ++c;
</span><del>-                if (c == end)
-                    return failure(input);
</del><ins>+                if (c.atEnd())
+                    return failure(input, length);
</ins><span class="cx">                 if (*c == '/' || *c == '\\')
</span><span class="cx">                     ++c;
</span><span class="cx">             }
</span><span class="lines">@@ -728,9 +823,9 @@
</span><span class="cx">             LOG_STATE(&quot;AuthorityOrHost&quot;);
</span><span class="cx">             {
</span><span class="cx">                 if (*c == '@') {
</span><del>-                    parseAuthority(authorityOrHostBegin, c);
</del><ins>+                    parseAuthority(CodePointIterator&lt;CharacterType&gt;(authorityOrHostBegin, c));
</ins><span class="cx">                     ++c;
</span><del>-                    while (c != end &amp;&amp; isTabOrNewline(*c))
</del><ins>+                    while (!c.atEnd() &amp;&amp; isTabOrNewline(*c))
</ins><span class="cx">                         ++c;
</span><span class="cx">                     authorityOrHostBegin = c;
</span><span class="cx">                     state = State::Host;
</span><span class="lines">@@ -741,8 +836,8 @@
</span><span class="cx">                 if (isSlash || *c == '?' || *c == '#') {
</span><span class="cx">                     m_url.m_userEnd = m_buffer.length();
</span><span class="cx">                     m_url.m_passwordEnd = m_url.m_userEnd;
</span><del>-                    if (!parseHost(authorityOrHostBegin, c))
-                        return failure(input);
</del><ins>+                    if (!parseHost(CodePointIterator&lt;CharacterType&gt;(authorityOrHostBegin, c)))
+                        return failure(input, length);
</ins><span class="cx">                     if (!isSlash) {
</span><span class="cx">                         m_buffer.append('/');
</span><span class="cx">                         m_url.m_pathAfterLastSlash = m_buffer.length();
</span><span class="lines">@@ -758,8 +853,8 @@
</span><span class="cx">         case State::Host:
</span><span class="cx">             LOG_STATE(&quot;Host&quot;);
</span><span class="cx">             if (*c == '/' || *c == '?' || *c == '#') {
</span><del>-                if (!parseHost(authorityOrHostBegin, c))
-                    return failure(input);
</del><ins>+                if (!parseHost(CodePointIterator&lt;CharacterType&gt;(authorityOrHostBegin, c)))
+                    return failure(input, length);
</ins><span class="cx">                 state = State::Path;
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="lines">@@ -806,7 +901,7 @@
</span><span class="cx">                 ++c;
</span><span class="cx">                 break;
</span><span class="cx">             default:
</span><del>-                if (!base.isNull() &amp;&amp; base.protocolIs(&quot;file&quot;) &amp;&amp; shouldCopyFileURL(c, end))
</del><ins>+                if (!base.isNull() &amp;&amp; base.protocolIs(&quot;file&quot;) &amp;&amp; shouldCopyFileURL(c))
</ins><span class="cx">                     copyURLPartsUntil(base, URLPart::PathAfterLastSlash);
</span><span class="cx">                 else {
</span><span class="cx">                     m_buffer.append(&quot;///&quot;);
</span><span class="lines">@@ -835,12 +930,17 @@
</span><span class="cx">                 state = State::FileHost;
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><del>-            if (!base.isNull() &amp;&amp; base.protocol() == &quot;file&quot;) {
</del><ins>+            if (!base.isNull() &amp;&amp; base.protocolIs(&quot;file&quot;)) {
+                // FIXME: This String copy is unnecessary.
</ins><span class="cx">                 String basePath = base.path();
</span><del>-                auto basePathCodePoints = StringView(basePath).codePoints();
-                if (basePath.length() &gt;= 2 &amp;&amp; isWindowsDriveLetter(basePathCodePoints.begin(), basePathCodePoints.end())) {
-                    m_buffer.append(basePath[0]);
-                    m_buffer.append(basePath[1]);
</del><ins>+                if (basePath.length() &gt;= 2) {
+                    bool windowsQuirk = basePath.is8Bit()
+                        ? isWindowsDriveLetter(CodePointIterator&lt;LChar&gt;(basePath.characters8(), basePath.characters8() + basePath.length()))
+                        : isWindowsDriveLetter(CodePointIterator&lt;UChar&gt;(basePath.characters16(), basePath.characters16() + basePath.length()));
+                    if (windowsQuirk) {
+                        m_buffer.append(basePath[0]);
+                        m_buffer.append(basePath[1]);
+                    }
</ins><span class="cx">                 }
</span><span class="cx">                 state = State::Path;
</span><span class="cx">                 break;
</span><span class="lines">@@ -883,8 +983,8 @@
</span><span class="cx">                     state = State::Path;
</span><span class="cx">                     break;
</span><span class="cx">                 }
</span><del>-                if (!parseHost(authorityOrHostBegin, c))
-                    return failure(input);
</del><ins>+                if (!parseHost(CodePointIterator&lt;CharacterType&gt;(authorityOrHostBegin, c)))
+                    return failure(input, length);
</ins><span class="cx">                 
</span><span class="cx">                 if (bufferView(m_buffer, m_url.m_passwordEnd, m_buffer.length() - m_url.m_passwordEnd) == &quot;localhost&quot;)  {
</span><span class="cx">                     m_buffer.resize(m_url.m_passwordEnd);
</span><span class="lines">@@ -914,13 +1014,13 @@
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="cx">             if (m_buffer.length() &amp;&amp; m_buffer[m_buffer.length() - 1] == '/') {
</span><del>-                if (isDoubleDotPathSegment(c, end)) {
-                    consumeDoubleDotPathSegment(c, end);
</del><ins>+                if (isDoubleDotPathSegment(c)) {
+                    consumeDoubleDotPathSegment(c);
</ins><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, end)) {
-                    consumeSingleDotPathSegment(c, end);
</del><ins>+                if (m_buffer[m_buffer.length() - 1] == '/' &amp;&amp; isSingleDotPathSegment(c)) {
+                    consumeSingleDotPathSegment(c);
</ins><span class="cx">                     break;
</span><span class="cx">                 }
</span><span class="cx">             }
</span><span class="lines">@@ -935,7 +1035,7 @@
</span><span class="cx">                 state = State::Fragment;
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><del>-            if (isPercentEncodedDot(c, end)) {
</del><ins>+            if (isPercentEncodedDot(c)) {
</ins><span class="cx">                 m_buffer.append('.');
</span><span class="cx">                 ASSERT(*c == '%');
</span><span class="cx">                 ++c;
</span><span class="lines">@@ -990,7 +1090,7 @@
</span><span class="cx">         LOG_FINAL_STATE(&quot;SchemeStart&quot;);
</span><span class="cx">         if (!m_buffer.length() &amp;&amp; !base.isNull())
</span><span class="cx">             return base;
</span><del>-        return failure(input);
</del><ins>+        return failure(input, length);
</ins><span class="cx">     case State::Scheme:
</span><span class="cx">         LOG_FINAL_STATE(&quot;Scheme&quot;);
</span><span class="cx">         break;
</span><span class="lines">@@ -1032,7 +1132,7 @@
</span><span class="cx">         break;
</span><span class="cx">     case State::SpecialAuthorityIgnoreSlashes:
</span><span class="cx">         LOG_FINAL_STATE(&quot;SpecialAuthorityIgnoreSlashes&quot;);
</span><del>-        return failure(input);
</del><ins>+        return failure(input, length);
</ins><span class="cx">     case State::AuthorityOrHost:
</span><span class="cx">         LOG_FINAL_STATE(&quot;AuthorityOrHost&quot;);
</span><span class="cx">         m_url.m_userEnd = m_buffer.length();
</span><span class="lines">@@ -1041,8 +1141,8 @@
</span><span class="cx">     case State::Host:
</span><span class="cx">         if (state == State::Host)
</span><span class="cx">             LOG_FINAL_STATE(&quot;Host&quot;);
</span><del>-        if (!parseHost(authorityOrHostBegin, end))
-            return failure(input);
</del><ins>+        if (!parseHost(authorityOrHostBegin))
+            return failure(input, length);
</ins><span class="cx">         m_buffer.append('/');
</span><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="lines">@@ -1095,8 +1195,8 @@
</span><span class="cx">             break;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        if (!parseHost(authorityOrHostBegin, c))
-            return failure(input);
</del><ins>+        if (!parseHost(CodePointIterator&lt;CharacterType&gt;(authorityOrHostBegin, c)))
+            return failure(input, length);
</ins><span class="cx">         
</span><span class="cx">         if (bufferView(m_buffer, m_url.m_passwordEnd, m_buffer.length() - m_url.m_passwordEnd) == &quot;localhost&quot;)  {
</span><span class="cx">             m_buffer.resize(m_url.m_passwordEnd);
</span><span class="lines">@@ -1143,18 +1243,19 @@
</span><span class="cx">     return m_url;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void URLParser::parseAuthority(StringView::CodePoints::Iterator&amp; iterator, const StringView::CodePoints::Iterator&amp; end)
</del><ins>+template&lt;typename CharacterType&gt;
+void URLParser::parseAuthority(CodePointIterator&lt;CharacterType&gt; iterator)
</ins><span class="cx"> {
</span><del>-    if (iterator == end) {
</del><ins>+    if (iterator.atEnd()) {
</ins><span class="cx">         m_url.m_userEnd = m_buffer.length();
</span><span class="cx">         m_url.m_passwordEnd = m_url.m_userEnd;
</span><span class="cx">         return;
</span><span class="cx">     }
</span><del>-    for (; iterator != end; ++iterator) {
</del><ins>+    for (; !iterator.atEnd(); ++iterator) {
</ins><span class="cx">         if (*iterator == ':') {
</span><span class="cx">             ++iterator;
</span><span class="cx">             m_url.m_userEnd = m_buffer.length();
</span><del>-            if (iterator == end) {
</del><ins>+            if (iterator.atEnd()) {
</ins><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><span class="cx">                     m_buffer.append('@');
</span><span class="lines">@@ -1165,7 +1266,7 @@
</span><span class="cx">         }
</span><span class="cx">         m_buffer.append(*iterator);
</span><span class="cx">     }
</span><del>-    for (; iterator != end; ++iterator)
</del><ins>+    for (; !iterator.atEnd(); ++iterator)
</ins><span class="cx">         m_buffer.append(*iterator);
</span><span class="cx">     m_url.m_passwordEnd = m_buffer.length();
</span><span class="cx">     if (!m_url.m_userEnd)
</span><span class="lines">@@ -1252,7 +1353,8 @@
</span><span class="cx">     buffer.append(']');
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static Optional&lt;uint32_t&gt; parseIPv4Number(StringView::CodePoints::Iterator&amp; iterator, const StringView::CodePoints::Iterator&amp; end)
</del><ins>+template&lt;typename CharacterType&gt;
+static Optional&lt;uint32_t&gt; parseIPv4Number(CodePointIterator&lt;CharacterType&gt;&amp; iterator)
</ins><span class="cx"> {
</span><span class="cx">     // FIXME: Check for overflow.
</span><span class="cx">     enum class State : uint8_t {
</span><span class="lines">@@ -1264,7 +1366,7 @@
</span><span class="cx">     };
</span><span class="cx">     State state = State::UnknownBase;
</span><span class="cx">     uint32_t value = 0;
</span><del>-    while (iterator != end) {
</del><ins>+    while (!iterator.atEnd()) {
</ins><span class="cx">         if (*iterator == '.') {
</span><span class="cx">             ++iterator;
</span><span class="cx">             return value;
</span><span class="lines">@@ -1319,14 +1421,15 @@
</span><span class="cx">     return values[exponent];
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static Optional&lt;uint32_t&gt; parseIPv4Host(StringView::CodePoints::Iterator iterator, const StringView::CodePoints::Iterator&amp; end)
</del><ins>+template&lt;typename CharacterType&gt;
+static Optional&lt;uint32_t&gt; parseIPv4Host(CodePointIterator&lt;CharacterType&gt; iterator)
</ins><span class="cx"> {
</span><span class="cx">     Vector&lt;uint32_t, 4&gt; items;
</span><span class="cx">     items.reserveInitialCapacity(4);
</span><del>-    while (iterator != end) {
</del><ins>+    while (!iterator.atEnd()) {
</ins><span class="cx">         if (items.size() &gt;= 4)
</span><span class="cx">             return Nullopt;
</span><del>-        if (auto item = parseIPv4Number(iterator, end))
</del><ins>+        if (auto item = parseIPv4Number(iterator))
</ins><span class="cx">             items.append(item.value());
</span><span class="cx">         else
</span><span class="cx">             return Nullopt;
</span><span class="lines">@@ -1348,10 +1451,11 @@
</span><span class="cx">         ipv4 += items[counter] * pow256(3 - counter);
</span><span class="cx">     return ipv4;
</span><span class="cx"> }
</span><del>-
-static Optional&lt;std::array&lt;uint16_t, 8&gt;&gt; parseIPv6Host(StringView::CodePoints::Iterator c, StringView::CodePoints::Iterator end)
</del><ins>+    
+template&lt;typename CharacterType&gt;
+static Optional&lt;std::array&lt;uint16_t, 8&gt;&gt; parseIPv6Host(CodePointIterator&lt;CharacterType&gt; c)
</ins><span class="cx"> {
</span><del>-    if (c == end)
</del><ins>+    if (c.atEnd())
</ins><span class="cx">         return Nullopt;
</span><span class="cx"> 
</span><span class="cx">     std::array&lt;uint16_t, 8&gt; address = {{0, 0, 0, 0, 0, 0, 0, 0}};
</span><span class="lines">@@ -1360,7 +1464,7 @@
</span><span class="cx"> 
</span><span class="cx">     if (*c == ':') {
</span><span class="cx">         ++c;
</span><del>-        if (c == end)
</del><ins>+        if (c.atEnd())
</ins><span class="cx">             return Nullopt;
</span><span class="cx">         if (*c != ':')
</span><span class="cx">             return Nullopt;
</span><span class="lines">@@ -1369,7 +1473,7 @@
</span><span class="cx">         compressPointer = piecePointer;
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    while (c != end) {
</del><ins>+    while (!c.atEnd()) {
</ins><span class="cx">         if (piecePointer == 8)
</span><span class="cx">             return Nullopt;
</span><span class="cx">         if (*c == ':') {
</span><span class="lines">@@ -1382,7 +1486,7 @@
</span><span class="cx">         }
</span><span class="cx">         uint16_t value = 0;
</span><span class="cx">         for (size_t length = 0; length &lt; 4; length++) {
</span><del>-            if (c == end)
</del><ins>+            if (c.atEnd())
</ins><span class="cx">                 break;
</span><span class="cx">             if (!isASCIIHexDigit(*c))
</span><span class="cx">                 break;
</span><span class="lines">@@ -1390,7 +1494,7 @@
</span><span class="cx">             ++c;
</span><span class="cx">         }
</span><span class="cx">         address[piecePointer++] = value;
</span><del>-        if (c == end)
</del><ins>+        if (c.atEnd())
</ins><span class="cx">             break;
</span><span class="cx">         if (*c != ':')
</span><span class="cx">             return Nullopt;
</span><span class="lines">@@ -1397,11 +1501,11 @@
</span><span class="cx">         ++c;
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    if (c != end) {
</del><ins>+    if (!c.atEnd()) {
</ins><span class="cx">         if (piecePointer &gt; 6)
</span><span class="cx">             return Nullopt;
</span><span class="cx">         size_t dotsSeen = 0;
</span><del>-        while (c != end) {
</del><ins>+        while (!c.atEnd()) {
</ins><span class="cx">             Optional&lt;uint16_t&gt; value;
</span><span class="cx">             if (!isASCIIDigit(*c))
</span><span class="cx">                 return Nullopt;
</span><span class="lines">@@ -1414,7 +1518,7 @@
</span><span class="cx">                 else
</span><span class="cx">                     value = value.value() * 10 + number;
</span><span class="cx">                 ++c;
</span><del>-                if (c == end)
</del><ins>+                if (c.atEnd())
</ins><span class="cx">                     return Nullopt;
</span><span class="cx">                 if (value.value() &gt; 255)
</span><span class="cx">                     return Nullopt;
</span><span class="lines">@@ -1424,9 +1528,9 @@
</span><span class="cx">             address[piecePointer] = address[piecePointer] * 0x100 + value.valueOr(0);
</span><span class="cx">             if (dotsSeen == 1 || dotsSeen == 3)
</span><span class="cx">                 piecePointer++;
</span><del>-            if (c != end)
</del><ins>+            if (!c.atEnd())
</ins><span class="cx">                 ++c;
</span><del>-            if (dotsSeen == 3 &amp;&amp; c != end)
</del><ins>+            if (dotsSeen == 3 &amp;&amp; !c.atEnd())
</ins><span class="cx">                 return Nullopt;
</span><span class="cx">             dotsSeen++;
</span><span class="cx">         }
</span><span class="lines">@@ -1513,15 +1617,16 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool URLParser::parsePort(StringView::CodePoints::Iterator&amp; iterator, const StringView::CodePoints::Iterator&amp; end)
</del><ins>+template&lt;typename CharacterType&gt;
+bool URLParser::parsePort(CodePointIterator&lt;CharacterType&gt;&amp; iterator)
</ins><span class="cx"> {
</span><span class="cx">     uint32_t port = 0;
</span><del>-    if (iterator == end) {
</del><ins>+    if (iterator.atEnd()) {
</ins><span class="cx">         m_url.m_portEnd = m_buffer.length();
</span><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx">     m_buffer.append(':');
</span><del>-    for (; iterator != end; ++iterator) {
</del><ins>+    for (; !iterator.atEnd(); ++iterator) {
</ins><span class="cx">         if (isTabOrNewline(*iterator))
</span><span class="cx">             continue;
</span><span class="cx">         if (isASCIIDigit(*iterator)) {
</span><span class="lines">@@ -1542,23 +1647,24 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool URLParser::parseHost(StringView::CodePoints::Iterator&amp; iterator, const StringView::CodePoints::Iterator&amp; end)
</del><ins>+template&lt;typename CharacterType&gt;
+bool URLParser::parseHost(CodePointIterator&lt;CharacterType&gt; iterator)
</ins><span class="cx"> {
</span><del>-    if (iterator == end)
</del><ins>+    if (iterator.atEnd())
</ins><span class="cx">         return false;
</span><span class="cx">     if (*iterator == '[') {
</span><span class="cx">         ++iterator;
</span><span class="cx">         auto ipv6End = iterator;
</span><del>-        while (ipv6End != end &amp;&amp; *ipv6End != ']')
</del><ins>+        while (!ipv6End.atEnd() &amp;&amp; *ipv6End != ']')
</ins><span class="cx">             ++ipv6End;
</span><del>-        if (auto address = parseIPv6Host(iterator, ipv6End)) {
</del><ins>+        if (auto address = parseIPv6Host(CodePointIterator&lt;CharacterType&gt;(iterator, ipv6End))) {
</ins><span class="cx">             serializeIPv6(address.value(), m_buffer);
</span><span class="cx">             m_url.m_hostEnd = m_buffer.length();
</span><del>-            if (ipv6End != end) {
</del><ins>+            if (!ipv6End.atEnd()) {
</ins><span class="cx">                 ++ipv6End;
</span><del>-                if (ipv6End != end &amp;&amp; *ipv6End == ':') {
</del><ins>+                if (!ipv6End.atEnd() &amp;&amp; *ipv6End == ':') {
</ins><span class="cx">                     ++ipv6End;
</span><del>-                    return parsePort(ipv6End, end);
</del><ins>+                    return parsePort(ipv6End);
</ins><span class="cx">                 }
</span><span class="cx">                 m_url.m_portEnd = m_buffer.length();
</span><span class="cx">                 return true;
</span><span class="lines">@@ -1569,21 +1675,21 @@
</span><span class="cx">     
</span><span class="cx">     if (!m_hostHasPercentOrNonASCII) {
</span><span class="cx">         auto hostIterator = iterator;
</span><del>-        for (; iterator != end; ++iterator) {
</del><ins>+        for (; !iterator.atEnd(); ++iterator) {
</ins><span class="cx">             if (isTabOrNewline(*iterator))
</span><span class="cx">                 continue;
</span><span class="cx">             if (*iterator == ':')
</span><span class="cx">                 break;
</span><span class="cx">         }
</span><del>-        if (auto address = parseIPv4Host(hostIterator, iterator)) {
</del><ins>+        if (auto address = parseIPv4Host(CodePointIterator&lt;CharacterType&gt;(hostIterator, iterator))) {
</ins><span class="cx">             serializeIPv4(address.value(), m_buffer);
</span><span class="cx">             m_url.m_hostEnd = m_buffer.length();
</span><del>-            if (iterator == end) {
</del><ins>+            if (iterator.atEnd()) {
</ins><span class="cx">                 m_url.m_portEnd = m_buffer.length();
</span><span class="cx">                 return true;
</span><span class="cx">             }
</span><span class="cx">             ++iterator;
</span><del>-            return parsePort(iterator, end);
</del><ins>+            return parsePort(iterator);
</ins><span class="cx">         }
</span><span class="cx">         for (; hostIterator != iterator; ++hostIterator) {
</span><span class="cx">             if (!isTabOrNewline(*hostIterator))
</span><span class="lines">@@ -1590,12 +1696,12 @@
</span><span class="cx">                 m_buffer.append(toASCIILower(*hostIterator));
</span><span class="cx">         }
</span><span class="cx">         m_url.m_hostEnd = m_buffer.length();
</span><del>-        if (hostIterator != end) {
</del><ins>+        if (!hostIterator.atEnd()) {
</ins><span class="cx">             ASSERT(*hostIterator == ':');
</span><span class="cx">             ++hostIterator;
</span><del>-            while (hostIterator != end &amp;&amp; isTabOrNewline(*hostIterator))
</del><ins>+            while (!hostIterator.atEnd() &amp;&amp; isTabOrNewline(*hostIterator))
</ins><span class="cx">                 ++hostIterator;
</span><del>-            return parsePort(hostIterator, end);
</del><ins>+            return parsePort(hostIterator);
</ins><span class="cx">         }
</span><span class="cx">         m_url.m_portEnd = m_buffer.length();
</span><span class="cx">         return true;
</span><span class="lines">@@ -1603,7 +1709,7 @@
</span><span class="cx"> 
</span><span class="cx">     // FIXME: We probably don't need to make so many buffers and String copies.
</span><span class="cx">     StringBuilder utf8Encoded;
</span><del>-    for (; iterator != end; ++iterator) {
</del><ins>+    for (; !iterator.atEnd(); ++iterator) {
</ins><span class="cx">         if (isTabOrNewline(*iterator))
</span><span class="cx">             continue;
</span><span class="cx">         if (*iterator == ':')
</span><span class="lines">@@ -1623,27 +1729,29 @@
</span><span class="cx">     auto asciiDomain = domainToASCII(domain);
</span><span class="cx">     if (!asciiDomain || hasInvalidDomainCharacter(asciiDomain.value()))
</span><span class="cx">         return false;
</span><ins>+    String&amp; asciiDomainValue = asciiDomain.value();
+    RELEASE_ASSERT(asciiDomainValue.is8Bit());
+    const LChar* asciiDomainCharacters = asciiDomainValue.characters8();
</ins><span class="cx">     
</span><del>-    auto asciiDomainCodePoints = StringView(asciiDomain.value()).codePoints();
-    if (auto address = parseIPv4Host(asciiDomainCodePoints.begin(), asciiDomainCodePoints.end())) {
</del><ins>+    if (auto address = parseIPv4Host(CodePointIterator&lt;LChar&gt;(asciiDomainCharacters, asciiDomainCharacters + asciiDomainValue.length()))) {
</ins><span class="cx">         serializeIPv4(address.value(), m_buffer);
</span><span class="cx">         m_url.m_hostEnd = m_buffer.length();
</span><del>-        if (iterator == end) {
</del><ins>+        if (iterator.atEnd()) {
</ins><span class="cx">             m_url.m_portEnd = m_buffer.length();
</span><span class="cx">             return true;
</span><span class="cx">         }
</span><span class="cx">         ++iterator;
</span><del>-        return parsePort(iterator, end);
</del><ins>+        return parsePort(iterator);
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     m_buffer.append(asciiDomain.value());
</span><span class="cx">     m_url.m_hostEnd = m_buffer.length();
</span><del>-    if (iterator != end) {
</del><ins>+    if (!iterator.atEnd()) {
</ins><span class="cx">         ASSERT(*iterator == ':');
</span><span class="cx">         ++iterator;
</span><del>-        while (iterator != end &amp;&amp; isTabOrNewline(*iterator))
</del><ins>+        while (!iterator.atEnd() &amp;&amp; isTabOrNewline(*iterator))
</ins><span class="cx">             ++iterator;
</span><del>-        return parsePort(iterator, end);
</del><ins>+        return parsePort(iterator);
</ins><span class="cx">     }
</span><span class="cx">     m_url.m_portEnd = m_buffer.length();
</span><span class="cx">     return true;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformURLParserh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/URLParser.h (205985 => 205986)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/URLParser.h        2016-09-15 18:11:48 UTC (rev 205985)
+++ trunk/Source/WebCore/platform/URLParser.h        2016-09-15 18:12:09 UTC (rev 205986)
</span><span class="lines">@@ -32,6 +32,8 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><ins>+template&lt;typename CharacterType&gt; class CodePointIterator;
+
</ins><span class="cx"> class URLParser {
</span><span class="cx"> public:
</span><span class="cx">     WEBCORE_EXPORT URL parse(const String&amp;, const URL&amp; = { }, const TextEncoding&amp; = UTF8Encoding());
</span><span class="lines">@@ -49,11 +51,13 @@
</span><span class="cx">     StringBuilder m_buffer;
</span><span class="cx">     bool m_urlIsSpecial { false };
</span><span class="cx">     bool m_hostHasPercentOrNonASCII { false };
</span><del>-    void parseAuthority(StringView::CodePoints::Iterator&amp;, const StringView::CodePoints::Iterator&amp; end);
-    bool parseHost(StringView::CodePoints::Iterator&amp;, const StringView::CodePoints::Iterator&amp; end);
-    bool parsePort(StringView::CodePoints::Iterator&amp;, const StringView::CodePoints::Iterator&amp; end);
-    URL failure(const String&amp; input);
</del><span class="cx"> 
</span><ins>+    template&lt;typename CharacterType&gt; URL parse(const CharacterType*, const unsigned length, const URL&amp;, const TextEncoding&amp;);
+    template&lt;typename CharacterType&gt; void parseAuthority(CodePointIterator&lt;CharacterType&gt;);
+    template&lt;typename CharacterType&gt; bool parseHost(CodePointIterator&lt;CharacterType&gt;);
+    template&lt;typename CharacterType&gt; bool parsePort(CodePointIterator&lt;CharacterType&gt;&amp;);
+    template&lt;typename CharacterType&gt; URL failure(const CharacterType*, unsigned length);
+
</ins><span class="cx">     enum class URLPart;
</span><span class="cx">     void copyURLPartsUntil(const URL&amp; base, URLPart);
</span><span class="cx">     static size_t urlLengthUntilPart(const URL&amp;, URLPart);
</span></span></pre>
</div>
</div>

</body>
</html>