Add a new templated string type to help write more idiomatic parsing code
authorweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 25 Jun 2020 21:13:18 +0000 (21:13 +0000)
committerweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 25 Jun 2020 21:13:18 +0000 (21:13 +0000)
https://bugs.webkit.org/show_bug.cgi?id=213588

Reviewed by Darin Adler.
Source/WTF:

Introduce StringParsingBuffer<CharType>, a new class in the String class family
designed to be used by parsing code. It's designed to be used with the helper
function, readCharactersForParsing, and for the user to write the parsing code
in a way that will work for both latin1 and UTF-16 inputs. Most usage will likely
follow something like the following form

{
    ...
    auto parsedResult = readCharactersForParsing(stringishObjectToParse, [](auto parsingBuffer) {
        while (parsingBuffer.hasCharactersRemaining()) {
            if (*parsingBuffer == foo) {
                ...
            }
        }

        ...

        return parsedResult
    });
    ...
}

API tests added.

* WTF.xcodeproj/project.pbxproj:
* wtf/CMakeLists.txt:
Add StringParsingBuffer.h

* wtf/Forward.h:
Forward declare StringParsingBuffer.

* wtf/text/StringParsingBuffer.h:
Added.

Tools:

* TestWebKitAPI/CMakeLists.txt:
* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WTF/StringParsingBuffer.cpp: Added.
(TestWebKitAPI::TEST):
Added new tests for new class.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@263529 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WTF/ChangeLog
Source/WTF/WTF.xcodeproj/project.pbxproj
Source/WTF/wtf/CMakeLists.txt
Source/WTF/wtf/Forward.h
Source/WTF/wtf/text/StringParsingBuffer.h [new file with mode: 0644]
Tools/ChangeLog
Tools/TestWebKitAPI/CMakeLists.txt
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WTF/StringParsingBuffer.cpp [new file with mode: 0644]

index 43130b0..427b016 100644 (file)
@@ -1,3 +1,44 @@
+2020-06-25  Sam Weinig  <weinig@apple.com>
+
+        Add a new templated string type to help write more idiomatic parsing code
+        https://bugs.webkit.org/show_bug.cgi?id=213588
+
+        Reviewed by Darin Adler.
+        
+        Introduce StringParsingBuffer<CharType>, a new class in the String class family
+        designed to be used by parsing code. It's designed to be used with the helper
+        function, readCharactersForParsing, and for the user to write the parsing code
+        in a way that will work for both latin1 and UTF-16 inputs. Most usage will likely
+        follow something like the following form
+        
+        {
+            ...
+            auto parsedResult = readCharactersForParsing(stringishObjectToParse, [](auto parsingBuffer) {
+                while (parsingBuffer.hasCharactersRemaining()) {
+                    if (*parsingBuffer == foo) {
+                        ...
+                    }
+                }
+
+                ...
+
+                return parsedResult
+            });
+            ...
+        }
+
+        API tests added.
+
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/CMakeLists.txt:
+        Add StringParsingBuffer.h
+
+        * wtf/Forward.h:
+        Forward declare StringParsingBuffer.
+
+        * wtf/text/StringParsingBuffer.h: 
+        Added.
+
 2020-06-25  Tadeu Zagallo  <tzagallo@apple.com>
 
         WTF::callOnMainThread should not require the main runloop to be initialized
index d96da78..71263b5 100644 (file)
                7C137942222326D500D7A824 /* ieee.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ieee.h; sourceTree = "<group>"; };
                7C137943222326D500D7A824 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
                7C3A45D023CFA883007DE3A6 /* PlatformHave.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformHave.h; sourceTree = "<group>"; };
+               7C3B06E824A116E600FD26C7 /* StringParsingBuffer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StringParsingBuffer.h; sourceTree = "<group>"; };
                7C3F72391D78811900674E26 /* Brigand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Brigand.h; sourceTree = "<group>"; };
                7C42307123CE2D8A006E54D0 /* PlatformEnable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformEnable.h; sourceTree = "<group>"; };
                7C42307223CE2D8A006E54D0 /* PlatformCPU.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformCPU.h; sourceTree = "<group>"; };
                                A8A47328151A825B004123FF /* StringImpl.cpp */,
                                A8A47329151A825B004123FF /* StringImpl.h */,
                                A8A4732A151A825B004123FF /* StringOperators.h */,
+                               7C3B06E824A116E600FD26C7 /* StringParsingBuffer.h */,
                                CD00360D21501F7800F4ED4C /* StringToIntegerConversion.h */,
                                93F1993D19D7958D00C2390B /* StringView.cpp */,
                                1A6EB1DF187D0BD30030126F /* StringView.h */,
index 5313207..ed71eb2 100644 (file)
@@ -339,6 +339,7 @@ set(WTF_PUBLIC_HEADERS
     text/StringHasher.h
     text/StringImpl.h
     text/StringOperators.h
+    text/StringParsingBuffer.h
     text/StringToIntegerConversion.h
     text/StringView.h
     text/SymbolImpl.h
index 76cebd3..7b42072 100644 (file)
@@ -71,6 +71,7 @@ template<typename T, size_t = alignof(T)> class PackedAlignedPtr;
 template<typename T, typename = DumbPtrTraits<T>> class Ref;
 template<typename T, typename = DumbPtrTraits<T>> class RefPtr;
 template<typename> class StringBuffer;
+template<typename> class StringParsingBuffer;
 template<typename, typename = void> class StringTypeAdapter;
 template<typename> class UniqueRef;
 template<typename...> class Variant;
@@ -122,6 +123,7 @@ using WTF::String;
 using WTF::StringBuffer;
 using WTF::StringBuilder;
 using WTF::StringImpl;
+using WTF::StringParsingBuffer;
 using WTF::StringView;
 using WTF::TextPosition;
 using WTF::TextStream;
diff --git a/Source/WTF/wtf/text/StringParsingBuffer.h b/Source/WTF/wtf/text/StringParsingBuffer.h
new file mode 100644 (file)
index 0000000..218c346
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/Forward.h>
+#include <wtf/text/LChar.h>
+#include <wtf/text/StringCommon.h>
+
+namespace WTF {
+
+template<typename T>
+class StringParsingBuffer final {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    using CharacterType = T;
+
+    constexpr StringParsingBuffer() = default;
+
+    constexpr StringParsingBuffer(const CharacterType* characters, unsigned length)
+        : m_position { characters }
+        , m_end { characters + length }
+    {
+        ASSERT(characters || !length);
+    }
+
+    constexpr StringParsingBuffer(const CharacterType* characters, const CharacterType* end)
+        : m_position { characters }
+        , m_end { end }
+    {
+        ASSERT(m_end >= m_position);
+        ASSERT(!characters == !end);
+    }
+
+    constexpr auto position() const { return m_position; }
+    constexpr auto end() const { return m_end; }
+
+    constexpr bool hasCharactersRemaining() const { return m_position < m_end; }
+    constexpr bool atEnd() const { return m_position == m_end; }
+
+    constexpr unsigned lengthRemaining() const { return m_end - m_position; }
+
+    StringView stringViewOfCharactersRemaining() { return { m_position, lengthRemaining() }; }
+
+    CharacterType operator[](unsigned i)
+    {
+        ASSERT(m_position + i < m_end);
+        return m_position[i];
+    }
+
+    constexpr CharacterType operator*() const
+    {
+        ASSERT(m_position < m_end);
+        return *m_position;
+    }
+
+    constexpr void advance()
+    {
+        ASSERT(m_position < m_end);
+        ++m_position;
+    }
+
+    constexpr void advanceBy(unsigned places)
+    {
+        ASSERT(m_position <= m_end);
+        ASSERT(m_position + places <= m_end);
+        m_position += places;
+    }
+
+    constexpr StringParsingBuffer& operator++()
+    {
+        advance();
+        return *this;
+    }
+
+    constexpr StringParsingBuffer operator++(int)
+    {
+        auto result = *this;
+        ++*this;
+        return result;
+    }
+
+    constexpr StringParsingBuffer& operator+=(int places)
+    {
+        advanceBy(places);
+        return *this;
+    }
+
+private:
+    const CharacterType* m_position { nullptr };
+    const CharacterType* m_end { nullptr };
+};
+
+template<typename StringType, typename Function> decltype(auto) readCharactersForParsing(StringType&& string, Function&& functor)
+{
+    if (string.is8Bit())
+        return functor(StringParsingBuffer { string.characters8(), string.length() });
+    return functor(StringParsingBuffer { string.characters16(), string.length() });
+}
+
+} // namespace WTF
+
+using WTF::StringParsingBuffer;
+using WTF::readCharactersForParsing;
index e79c28a..fc76ed3 100644 (file)
@@ -1,3 +1,16 @@
+2020-06-25  Sam Weinig  <weinig@apple.com>
+
+        Add a new templated string type to help write more idiomatic parsing code
+        https://bugs.webkit.org/show_bug.cgi?id=213588
+
+        Reviewed by Darin Adler.
+
+        * TestWebKitAPI/CMakeLists.txt:
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WTF/StringParsingBuffer.cpp: Added.
+        (TestWebKitAPI::TEST):
+        Added new tests for new class.
+
 2020-06-25  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Text manipulation should exclude text rendered using icon-only fonts
index 3602b0b..78faf9d 100644 (file)
@@ -89,6 +89,7 @@ set(TestWTF_SOURCES
     Tests/WTF/StringHasher.cpp
     Tests/WTF/StringImpl.cpp
     Tests/WTF/StringOperators.cpp
+    Tests/WTF/StringParsingBuffer.cpp
     Tests/WTF/StringView.cpp
     Tests/WTF/SynchronizedFixedQueue.cpp
     Tests/WTF/TextBreakIterator.cpp
index 5e0ca0a..08d8e54 100644 (file)
                7CCE7F2E1A411B1000447C4C /* WKBrowsingContextGroupTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC3C4C7D14587AA60025FB62 /* WKBrowsingContextGroupTest.mm */; };
                7CCE7F2F1A411B1000447C4C /* WKBrowsingContextLoadDelegateTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC3C4C7014575B6A0025FB62 /* WKBrowsingContextLoadDelegateTest.mm */; };
                7CD4C26E1E2C0E6E00929470 /* StringConcatenate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CD4C26C1E2C0E6E00929470 /* StringConcatenate.cpp */; };
+               7CD70C4F24A436EE00E61040 /* StringParsingBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CD70C4E24A436EE00E61040 /* StringParsingBuffer.cpp */; };
                7CEB62AB223609DE0069CBB0 /* IteratorRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CEB62A92236086C0069CBB0 /* IteratorRange.cpp */; };
                7CEFA9661AC0B9E200B910FD /* _WKUserContentExtensionStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7CEFA9641AC0B9E200B910FD /* _WKUserContentExtensionStore.mm */; };
                7CFBCAE51743238F00B2BFCF /* WillLoad_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CFBCAE31743238E00B2BFCF /* WillLoad_Bundle.cpp */; };
                7CCE7EA31A4115CB00447C4C /* TestWebKitAPILibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = TestWebKitAPILibrary.xcconfig; sourceTree = "<group>"; };
                7CD0D5AA1D5534DE000CC9E1 /* Variant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Variant.cpp; sourceTree = "<group>"; };
                7CD4C26C1E2C0E6E00929470 /* StringConcatenate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StringConcatenate.cpp; sourceTree = "<group>"; };
+               7CD70C4E24A436EE00E61040 /* StringParsingBuffer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = StringParsingBuffer.cpp; sourceTree = "<group>"; };
                7CEB62A92236086C0069CBB0 /* IteratorRange.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = IteratorRange.cpp; sourceTree = "<group>"; };
                7CEFA9641AC0B9E200B910FD /* _WKUserContentExtensionStore.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _WKUserContentExtensionStore.mm; sourceTree = "<group>"; };
                7CFBCADD1743234F00B2BFCF /* WillLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WillLoad.cpp; sourceTree = "<group>"; };
                                93ABA80816DDAB91002DB2FA /* StringHasher.cpp */,
                                26F1B44315CA434F00D1E4BF /* StringImpl.cpp */,
                                C01363C713C3997300EF3964 /* StringOperators.cpp */,
+                               7CD70C4E24A436EE00E61040 /* StringParsingBuffer.cpp */,
                                7C74D42D188228F300E5ED57 /* StringView.cpp */,
                                5597F8341D9596C80066BC21 /* SynchronizedFixedQueue.cpp */,
                                9329AA281DE3F81E003ABD07 /* TextBreakIterator.cpp */,
                                7C83DF361D0A590C00FEBCF3 /* StringHasher.cpp in Sources */,
                                7C83DF371D0A590C00FEBCF3 /* StringImpl.cpp in Sources */,
                                7C83DF381D0A590C00FEBCF3 /* StringOperators.cpp in Sources */,
+                               7CD70C4F24A436EE00E61040 /* StringParsingBuffer.cpp in Sources */,
                                7C83DF3A1D0A590C00FEBCF3 /* StringView.cpp in Sources */,
                                5597F8361D9596C80066BC21 /* SynchronizedFixedQueue.cpp in Sources */,
                                7C83DF401D0A590C00FEBCF3 /* TestsController.cpp in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WTF/StringParsingBuffer.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringParsingBuffer.cpp
new file mode 100644 (file)
index 0000000..c4b2620
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "WTFStringUtilities.h"
+
+#include <wtf/text/StringParsingBuffer.h>
+#include <wtf/text/StringView.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF, StringParsingBufferEmpty)
+{
+    StringParsingBuffer<LChar> parsingBuffer;
+
+    EXPECT_TRUE(parsingBuffer.atEnd());
+    EXPECT_FALSE(parsingBuffer.hasCharactersRemaining());
+    EXPECT_EQ(parsingBuffer.position(), nullptr);
+    EXPECT_EQ(parsingBuffer.end(), nullptr);
+    EXPECT_EQ(parsingBuffer.lengthRemaining(), 0u);
+
+}
+
+TEST(WTF, StringParsingBufferInitial)
+{
+    StringView string { "abc" };
+    StringParsingBuffer<LChar> parsingBuffer { string.characters8(), string.length() };
+
+    EXPECT_FALSE(parsingBuffer.atEnd());
+    EXPECT_TRUE(parsingBuffer.hasCharactersRemaining());
+    EXPECT_EQ(parsingBuffer.position(), string.characters8());
+    EXPECT_EQ(parsingBuffer.end(), string.characters8() + string.length());
+    EXPECT_EQ(parsingBuffer.lengthRemaining(), 3u);
+    EXPECT_EQ(*parsingBuffer, 'a');
+}
+
+TEST(WTF, StringParsingBufferAdvance)
+{
+    StringView string { "abc" };
+    StringParsingBuffer<LChar> parsingBuffer { string.characters8(), string.length() };
+
+    parsingBuffer.advance();
+    EXPECT_FALSE(parsingBuffer.atEnd());
+    EXPECT_TRUE(parsingBuffer.hasCharactersRemaining());
+    EXPECT_EQ(parsingBuffer.lengthRemaining(), 2u);
+    EXPECT_EQ(*parsingBuffer, 'b');
+}
+
+TEST(WTF, StringParsingBufferAdvanceBy)
+{
+    StringView string { "abc" };
+    StringParsingBuffer<LChar> parsingBuffer { string.characters8(), string.length() };
+
+    parsingBuffer.advanceBy(2);
+    EXPECT_FALSE(parsingBuffer.atEnd());
+    EXPECT_TRUE(parsingBuffer.hasCharactersRemaining());
+    EXPECT_EQ(parsingBuffer.lengthRemaining(), 1u);
+    EXPECT_EQ(*parsingBuffer, 'c');
+}
+
+TEST(WTF, StringParsingBufferPreIncrement)
+{
+    StringView string { "abc" };
+    StringParsingBuffer<LChar> parsingBuffer { string.characters8(), string.length() };
+
+    auto preIncrementedParsingBuffer = ++parsingBuffer;
+    EXPECT_FALSE(parsingBuffer.atEnd());
+    EXPECT_TRUE(parsingBuffer.hasCharactersRemaining());
+    EXPECT_EQ(parsingBuffer.lengthRemaining(), 2u);
+    EXPECT_EQ(*parsingBuffer, 'b');
+    EXPECT_FALSE(preIncrementedParsingBuffer.atEnd());
+    EXPECT_TRUE(preIncrementedParsingBuffer.hasCharactersRemaining());
+    EXPECT_EQ(preIncrementedParsingBuffer.lengthRemaining(), 2u);
+    EXPECT_EQ(*preIncrementedParsingBuffer, 'b');
+}
+
+TEST(WTF, StringParsingBufferPostIncrement)
+{
+    StringView string { "abc" };
+    StringParsingBuffer<LChar> parsingBuffer { string.characters8(), string.length() };
+
+    auto postIncrementedParsingBuffer = parsingBuffer++;
+    EXPECT_FALSE(parsingBuffer.atEnd());
+    EXPECT_TRUE(parsingBuffer.hasCharactersRemaining());
+    EXPECT_EQ(parsingBuffer.lengthRemaining(), 2u);
+    EXPECT_EQ(*parsingBuffer, 'b');
+    EXPECT_FALSE(postIncrementedParsingBuffer.atEnd());
+    EXPECT_TRUE(postIncrementedParsingBuffer.hasCharactersRemaining());
+    EXPECT_EQ(postIncrementedParsingBuffer.lengthRemaining(), 3u);
+    EXPECT_EQ(*postIncrementedParsingBuffer, 'a');
+}
+
+TEST(WTF, StringParsingBufferPlusEquals)
+{
+    StringView string { "abc" };
+    StringParsingBuffer<LChar> parsingBuffer { string.characters8(), string.length() };
+
+    parsingBuffer += 2;
+    EXPECT_FALSE(parsingBuffer.atEnd());
+    EXPECT_TRUE(parsingBuffer.hasCharactersRemaining());
+    EXPECT_EQ(parsingBuffer.lengthRemaining(), 1u);
+    EXPECT_EQ(*parsingBuffer, 'c');
+}
+
+TEST(WTF, StringParsingBufferEnd)
+{
+    StringView string { "abc" };
+    StringParsingBuffer<LChar> parsingBuffer { string.characters8(), string.length() };
+
+    ++parsingBuffer;
+    EXPECT_FALSE(parsingBuffer.atEnd());
+    EXPECT_TRUE(parsingBuffer.hasCharactersRemaining());
+    EXPECT_EQ(*parsingBuffer, 'b');
+
+    ++parsingBuffer;
+    EXPECT_FALSE(parsingBuffer.atEnd());
+    EXPECT_TRUE(parsingBuffer.hasCharactersRemaining());
+    EXPECT_EQ(*parsingBuffer, 'c');
+
+    ++parsingBuffer;
+    EXPECT_TRUE(parsingBuffer.atEnd());
+    EXPECT_FALSE(parsingBuffer.hasCharactersRemaining());
+}
+
+TEST(WTF, StringParsingBufferSubscript)
+{
+    StringView string { "abc" };
+    StringParsingBuffer<LChar> parsingBuffer { string.characters8(), string.length() };
+    
+    ++parsingBuffer;
+    EXPECT_EQ(parsingBuffer[0], 'b');
+    EXPECT_EQ(parsingBuffer[1], 'c');
+}
+
+TEST(WTF, StringParsingBufferStringView)
+{
+    StringView string { "abc" };
+    StringParsingBuffer<LChar> parsingBuffer { string.characters8(), string.length() };
+
+    ++parsingBuffer;
+    auto viewRemaining = parsingBuffer.stringViewOfCharactersRemaining();
+    EXPECT_EQ(viewRemaining.length(), 2u);
+    EXPECT_EQ(viewRemaining[0], 'b');
+    EXPECT_EQ(viewRemaining[1], 'c');
+}
+
+TEST(WTF, StringParsingBufferReadCharactersForParsing)
+{
+    auto latin1 = StringView { "abc" };
+    auto result1 = WTF::readCharactersForParsing(latin1, [](auto parsingBuffer) {
+        EXPECT_FALSE(parsingBuffer.atEnd());
+        EXPECT_TRUE(parsingBuffer.hasCharactersRemaining());
+        EXPECT_EQ(parsingBuffer.lengthRemaining(), 3u);
+        EXPECT_EQ(*parsingBuffer, 'a');
+    
+        ++parsingBuffer;
+        return parsingBuffer.lengthRemaining();
+    });
+    EXPECT_EQ(result1, 2u);
+
+    auto utf16 = utf16String(u"😍😉🙃");
+    auto result2 = WTF::readCharactersForParsing(utf16, [](auto parsingBuffer) {
+        EXPECT_FALSE(parsingBuffer.atEnd());
+        EXPECT_TRUE(parsingBuffer.hasCharactersRemaining());
+        EXPECT_EQ(parsingBuffer.lengthRemaining(), 6u);
+        EXPECT_EQ(*parsingBuffer, 0xD83D);
+
+        ++parsingBuffer;
+        return parsingBuffer.lengthRemaining();
+    });
+    EXPECT_EQ(result2, 5u);
+}
+
+}
+