<!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>[174271] trunk</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/174271">174271</a></dd>
<dt>Author</dt> <dd>mmaxfield@apple.com</dd>
<dt>Date</dt> <dd>2014-10-03 10:43:32 -0700 (Fri, 03 Oct 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Support modern for loops over StringViews
https://bugs.webkit.org/show_bug.cgi?id=137165

Patch by Myles C. Maxfield &lt;mmaxfield@apple.com&gt; on 2014-10-03
Reviewed by Darin Adler.

This patch adds two functions, codePoints() and codeUnits(), on StringView.
These two functions return small objects which have begin() and end() functions,
which means it can be used by the modern for loop style. This small class also
has an inner iterator class which can be incremented, dereferenced, and
compared.

Using these new objects looks like this:
for (UChar codeunit : stringView.codeUnits()) { } and
for (UChar32 codepoint : stringView.codePoints()) { }.

* wtf/text/StringView.h:
(WTF::StringView::codepoints):
(WTF::StringView::codeunits):
(WTF::StringViewCodePointIterator::StringViewCodePointIterator):
(WTF::StringViewCodePointIterator::Iterator::Iterator):
(WTF::StringViewCodePointIterator::Iterator::operator*):
(WTF::StringViewCodePointIterator::Iterator::operator!=):
(WTF::StringViewCodeUnitIterator::StringViewCodeUnitIterator):
(WTF::StringViewCodeUnitIterator::Iterator::Iterator):
(WTF::StringViewCodeUnitIterator::Iterator::operator*):
(WTF::StringViewCodeUnitIterator::Iterator::operator!=):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWTFChangeLog">trunk/Source/WTF/ChangeLog</a></li>
<li><a href="#trunkSourceWTFwtftextStringViewh">trunk/Source/WTF/wtf/text/StringView.h</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWTFStringViewcpp">trunk/Tools/TestWebKitAPI/Tests/WTF/StringView.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWTFChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/ChangeLog (174270 => 174271)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/ChangeLog        2014-10-03 17:15:51 UTC (rev 174270)
+++ trunk/Source/WTF/ChangeLog        2014-10-03 17:43:32 UTC (rev 174271)
</span><span class="lines">@@ -1,3 +1,32 @@
</span><ins>+2014-10-03  Myles C. Maxfield  &lt;mmaxfield@apple.com&gt;
+
+        Support modern for loops over StringViews
+        https://bugs.webkit.org/show_bug.cgi?id=137165
+
+        Reviewed by Darin Adler.
+
+        This patch adds two functions, codePoints() and codeUnits(), on StringView.
+        These two functions return small objects which have begin() and end() functions,
+        which means it can be used by the modern for loop style. This small class also
+        has an inner iterator class which can be incremented, dereferenced, and
+        compared.
+
+        Using these new objects looks like this:
+        for (UChar codeunit : stringView.codeUnits()) { } and
+        for (UChar32 codepoint : stringView.codePoints()) { }.
+
+        * wtf/text/StringView.h:
+        (WTF::StringView::codepoints):
+        (WTF::StringView::codeunits):
+        (WTF::StringViewCodePointIterator::StringViewCodePointIterator):
+        (WTF::StringViewCodePointIterator::Iterator::Iterator):
+        (WTF::StringViewCodePointIterator::Iterator::operator*):
+        (WTF::StringViewCodePointIterator::Iterator::operator!=):
+        (WTF::StringViewCodeUnitIterator::StringViewCodeUnitIterator):
+        (WTF::StringViewCodeUnitIterator::Iterator::Iterator):
+        (WTF::StringViewCodeUnitIterator::Iterator::operator*):
+        (WTF::StringViewCodeUnitIterator::Iterator::operator!=):
+
</ins><span class="cx"> 2014-09-28  Sam Weinig  &lt;sam@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         Remove RefPtrHashMap
</span></span></pre></div>
<a id="trunkSourceWTFwtftextStringViewh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/text/StringView.h (174270 => 174271)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/text/StringView.h        2014-10-03 17:15:51 UTC (rev 174270)
+++ trunk/Source/WTF/wtf/text/StringView.h        2014-10-03 17:43:32 UTC (rev 174271)
</span><span class="lines">@@ -138,6 +138,12 @@
</span><span class="cx"> 
</span><span class="cx">     bool contains(UChar c) const { return find(c) != notFound; }
</span><span class="cx"> 
</span><ins>+    class CodePoints;
+    class CodeUnits;
+
+    CodePoints codePoints() const;
+    CodeUnits codeUnits() const;
+
</ins><span class="cx"> #if USE(CF)
</span><span class="cx">     // This function converts null strings to empty strings.
</span><span class="cx">     WTF_EXPORT_STRING_API RetainPtr&lt;CFStringRef&gt; createCFStringWithoutCopying() const;
</span><span class="lines">@@ -200,6 +206,55 @@
</span><span class="cx">     initialize(string.characters16(), string.length());
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+class StringView::CodePoints {
+public:
+    class Iterator {
+    public:
+        Iterator(const StringView&amp;, unsigned index);
+        Iterator&amp; operator++();
+        UChar32 operator*() const;
+        bool operator==(const Iterator&amp;) const;
+        bool operator!=(const Iterator&amp;) const;
+
+    private:
+        const StringView&amp; m_stringView;
+        mutable unsigned m_index;
+#if !ASSERT_DISABLED
+        mutable bool m_alreadyIncremented;
+#endif
+    };
+
+    explicit CodePoints(const StringView&amp;);
+    Iterator begin() const;
+    Iterator end() const;
+
+private:
+    StringView m_stringView;
+};
+
+class StringView::CodeUnits {
+public:
+    class Iterator {
+    public:
+        Iterator(const StringView&amp;, unsigned index);
+        Iterator&amp; operator++();
+        UChar operator*() const;
+        bool operator==(const Iterator&amp;) const;
+        bool operator!=(const Iterator&amp;) const;
+
+    private:
+        const StringView&amp; m_stringView;
+        unsigned m_index;
+    };
+
+    explicit CodeUnits(const StringView&amp;);
+    Iterator begin() const;
+    Iterator end() const;
+
+private:
+    StringView m_stringView;
+};
+
</ins><span class="cx"> inline void StringView::getCharactersWithUpconvert(LChar* destination) const
</span><span class="cx"> {
</span><span class="cx">     ASSERT(is8Bit());
</span><span class="lines">@@ -294,6 +349,119 @@
</span><span class="cx">     string.getCharactersWithUpconvert(buffer.data() + oldSize);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+inline auto StringView::codePoints() const -&gt; CodePoints
+{
+    return CodePoints(*this);
+}
+
+inline auto StringView::codeUnits() const -&gt; CodeUnits
+{
+    return CodeUnits(*this);
+}
+
+inline StringView::CodePoints::CodePoints(const StringView&amp; stringView)
+    : m_stringView(stringView)
+{
+}
+
+inline StringView::CodePoints::Iterator::Iterator(const StringView&amp; stringView, unsigned index)
+    : m_stringView(stringView)
+    , m_index(index)
+#if !ASSERT_DISABLED
+    , m_alreadyIncremented(false)
+#endif
+{
+}
+
+inline auto StringView::CodePoints::Iterator::operator++() -&gt; Iterator&amp;
+{
+#if !ASSERT_DISABLED
+    ASSERT(m_alreadyIncremented);
+    m_alreadyIncremented = false;
+#endif
+    return *this;
+}
+
+inline UChar32 StringView::CodePoints::Iterator::operator*() const
+{
+#if !ASSERT_DISABLED
+    ASSERT(!m_alreadyIncremented);
+    m_alreadyIncremented = true;
+#endif
+
+    if (m_stringView.is8Bit())
+        return m_stringView.characters8()[m_index++];
+
+    UChar32 codePoint;
+    U16_NEXT(m_stringView.characters16(), m_index, m_stringView.length(), codePoint);
+    return codePoint;
+}
+
+inline bool StringView::CodePoints::Iterator::operator==(const Iterator&amp; other) const
+{
+    ASSERT(&amp;m_stringView == &amp;other.m_stringView);
+    ASSERT(!m_alreadyIncremented);
+    return m_index == other.m_index;
+}
+
+inline bool StringView::CodePoints::Iterator::operator!=(const Iterator&amp; other) const
+{
+    return !(*this == other);
+}
+
+inline auto StringView::CodePoints::begin() const -&gt; Iterator
+{
+    return Iterator(m_stringView, 0);
+}
+
+inline auto StringView::CodePoints::end() const -&gt; Iterator
+{
+    return Iterator(m_stringView, m_stringView.length());
+}
+
+inline StringView::CodeUnits::CodeUnits(const StringView&amp; stringView)
+    : m_stringView(stringView)
+{
+}
+
+inline StringView::CodeUnits::Iterator::Iterator(const StringView&amp; stringView, unsigned index)
+    : m_stringView(stringView)
+    , m_index(index)
+{
+}
+
+inline auto StringView::CodeUnits::Iterator::operator++() -&gt; Iterator&amp;
+{
+    ++m_index;
+    return *this;
+}
+
+inline UChar StringView::CodeUnits::Iterator::operator*() const
+{
+    return m_stringView[m_index];
+}
+
+inline bool StringView::CodeUnits::Iterator::operator==(const Iterator&amp; other) const
+{
+    ASSERT(&amp;m_stringView == &amp;other.m_stringView);
+    return m_index == other.m_index;
+}
+
+inline bool StringView::CodeUnits::Iterator::operator!=(const Iterator&amp; other) const
+{
+    return !(*this == other);
+}
+
+inline auto StringView::CodeUnits::begin() const -&gt; Iterator
+{
+    return Iterator(m_stringView, 0);
+}
+
+inline auto StringView::CodeUnits::end() const -&gt; Iterator
+{
+    return Iterator(m_stringView, m_stringView.length());
+}
+
</ins><span class="cx"> } // namespace WTF
</span><span class="cx"> 
</span><span class="cx"> using WTF::append;
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWTFStringViewcpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WTF/StringView.cpp (174270 => 174271)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WTF/StringView.cpp        2014-10-03 17:15:51 UTC (rev 174270)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/StringView.cpp        2014-10-03 17:43:32 UTC (rev 174271)
</span><span class="lines">@@ -25,6 +25,7 @@
</span><span class="cx"> 
</span><span class="cx"> #include &quot;config.h&quot;
</span><span class="cx"> 
</span><ins>+#include &lt;wtf/text/StringBuilder.h&gt;
</ins><span class="cx"> #include &lt;wtf/text/StringView.h&gt;
</span><span class="cx"> 
</span><span class="cx"> namespace TestWebKitAPI {
</span><span class="lines">@@ -77,4 +78,68 @@
</span><span class="cx">         SUCCEED();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool compareLoopIterations(StringView::CodePoints codePoints, std::vector&lt;UChar32&gt; expected)
+{
+    std::vector&lt;UChar32&gt; actual;
+    for (auto codePoint : codePoints)
+        actual.push_back(codePoint);
+    return actual == expected;
+}
+
+static bool compareLoopIterations(StringView::CodeUnits codeUnits, std::vector&lt;UChar&gt; expected)
+{
+    std::vector&lt;UChar&gt; actual;
+    for (auto codeUnit : codeUnits)
+        actual.push_back(codeUnit);
+    return actual == expected;
+}
+
+static void build(StringBuilder&amp; builder, std::vector&lt;UChar&gt; input)
+{
+    builder.clear();
+    for (auto codeUnit : input)
+        builder.append(codeUnit);
+}
+
+TEST(WTF, StringViewIterators)
+{
+    compareLoopIterations(StringView().codePoints(), { });
+    compareLoopIterations(StringView().codeUnits(), { });
+
+    compareLoopIterations(StringView::empty().codePoints(), { });
+    compareLoopIterations(StringView::empty().codeUnits(), { });
+
+    compareLoopIterations(StringView(String(&quot;hello&quot;)).codePoints(), {'h', 'e', 'l', 'l', 'o'});
+    compareLoopIterations(StringView(String(&quot;hello&quot;)).codeUnits(), {'h', 'e', 'l', 'l', 'o'});
+
+    StringBuilder b;
+    build(b, {0xD800, 0xDD55}); // Surrogates for unicode code point U+10155
+    EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0x10155}));
+    EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xD800, 0xDD55}));
+
+    build(b, {0xD800}); // Leading surrogate only
+    EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0xD800}));
+    EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xD800}));
+
+    build(b, {0xD800, 0xD801}); // Two leading surrogates
+    EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0xD800, 0xD801}));
+    EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xD800, 0xD801}));
+
+    build(b, {0xDD55}); // Trailing surrogate only
+    EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0xDD55}));
+    EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xDD55}));
+
+    build(b, {0xD800, 'h'}); // Leading surrogate followed by non-surrogate
+    EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0xD800, 'h'}));
+    EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xD800, 'h'}));
+
+    build(b, {0x0306}); // &quot;COMBINING BREVE&quot;
+    EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0x0306}));
+    EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0x0306}));
+
+    build(b, {0x0306, 0xD800, 0xDD55, 'h', 'e', 'l', 'o'}); // Mix of single code unit and multi code unit code points
+    EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0x0306, 0x10155, 'h', 'e', 'l', 'o'}));
+    EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0x0306, 0xD800, 0xDD55, 'h', 'e', 'l', 'o'}));
+}
+
</ins><span class="cx"> } // namespace TestWebKitAPI
</span></span></pre>
</div>
</div>

</body>
</html>