summaryrefslogtreecommitdiff
path: root/Source/WTF/wtf/text/StringView.h
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WTF/wtf/text/StringView.h')
-rw-r--r--Source/WTF/wtf/text/StringView.h952
1 files changed, 868 insertions, 84 deletions
diff --git a/Source/WTF/wtf/text/StringView.h b/Source/WTF/wtf/text/StringView.h
index 70f4eb0cb..ef209f947 100644
--- a/Source/WTF/wtf/text/StringView.h
+++ b/Source/WTF/wtf/text/StringView.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,132 +26,916 @@
#ifndef StringView_h
#define StringView_h
-#include <wtf/text/WTFString.h>
+#include <limits.h>
+#include <unicode/utypes.h>
+#include <wtf/Forward.h>
+#include <wtf/RetainPtr.h>
+#include <wtf/Vector.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/ConversionMode.h>
+#include <wtf/text/LChar.h>
+#include <wtf/text/StringCommon.h>
+
+// FIXME: Enabling the StringView lifetime checking causes the MSVC build to fail. Figure out why.
+#if defined(NDEBUG) || COMPILER(MSVC)
+#define CHECK_STRINGVIEW_LIFETIME 0
+#else
+#define CHECK_STRINGVIEW_LIFETIME 1
+#endif
namespace WTF {
+using CharacterMatchFunction = bool (*)(UChar);
+
// StringView is a non-owning reference to a string, similar to the proposed std::string_view.
-// Whether the string is 8-bit or 16-bit is encoded in the upper bit of the length member.
-// This means that strings longer than 2 Gigabytes can not be represented. If that turns out to be
-// a problem we can investigate alternative solutions.
class StringView {
public:
- StringView()
- : m_characters(nullptr)
- , m_length(0)
- {
+ StringView();
+#if CHECK_STRINGVIEW_LIFETIME
+ ~StringView();
+ StringView(StringView&&);
+ StringView(const StringView&);
+ StringView& operator=(StringView&&);
+ StringView& operator=(const StringView&);
+#endif
+
+ StringView(const AtomicString&);
+ StringView(const String&);
+ StringView(const StringImpl&);
+ StringView(const StringImpl*);
+ StringView(const LChar*, unsigned length);
+ StringView(const UChar*, unsigned length);
+ StringView(const char*);
+
+ static StringView empty();
+
+ unsigned length() const;
+ bool isEmpty() const;
+
+ explicit operator bool() const;
+ bool isNull() const;
+
+ UChar operator[](unsigned index) const;
+
+ class CodeUnits;
+ CodeUnits codeUnits() const;
+
+ class CodePoints;
+ CodePoints codePoints() const;
+
+ class GraphemeClusters;
+ GraphemeClusters graphemeClusters() const;
+
+ bool is8Bit() const;
+ const LChar* characters8() const;
+ const UChar* characters16() const;
+
+ String toString() const;
+ String toStringWithoutCopying() const;
+ AtomicString toAtomicString() const;
+
+#if USE(CF)
+ // This function converts null strings to empty strings.
+ WTF_EXPORT_STRING_API RetainPtr<CFStringRef> createCFStringWithoutCopying() const;
+#endif
+
+#ifdef __OBJC__
+ // These functions convert null strings to empty strings.
+ WTF_EXPORT_STRING_API RetainPtr<NSString> createNSString() const;
+ WTF_EXPORT_STRING_API RetainPtr<NSString> createNSStringWithoutCopying() const;
+#endif
+
+ WTF_EXPORT_STRING_API CString utf8(ConversionMode = LenientConversion) const;
+
+ class UpconvertedCharacters;
+ UpconvertedCharacters upconvertedCharacters() const;
+
+ void getCharactersWithUpconvert(LChar*) const;
+ void getCharactersWithUpconvert(UChar*) const;
+
+ StringView substring(unsigned start, unsigned length = std::numeric_limits<unsigned>::max()) const;
+ StringView left(unsigned len) const { return substring(0, len); }
+ StringView right(unsigned len) const { return substring(length() - len, len); }
+
+ class SplitResult;
+ SplitResult split(UChar) const;
+
+ size_t find(UChar, unsigned start = 0) const;
+ size_t find(CharacterMatchFunction, unsigned start = 0) const;
+
+ WTF_EXPORT_STRING_API size_t find(StringView, unsigned start) const;
+
+ size_t reverseFind(UChar, unsigned index = UINT_MAX) const;
+
+ WTF_EXPORT_STRING_API size_t findIgnoringASCIICase(const StringView&) const;
+ WTF_EXPORT_STRING_API size_t findIgnoringASCIICase(const StringView&, unsigned startOffset) const;
+
+ bool contains(UChar) const;
+ WTF_EXPORT_STRING_API bool containsIgnoringASCIICase(const StringView&) const;
+ WTF_EXPORT_STRING_API bool containsIgnoringASCIICase(const StringView&, unsigned startOffset) const;
+
+ WTF_EXPORT_STRING_API bool startsWith(const StringView&) const;
+ WTF_EXPORT_STRING_API bool startsWithIgnoringASCIICase(const StringView&) const;
+
+ WTF_EXPORT_STRING_API bool endsWith(const StringView&) const;
+ WTF_EXPORT_STRING_API bool endsWithIgnoringASCIICase(const StringView&) const;
+
+ int toInt() const;
+ int toInt(bool& isValid) const;
+ int toIntStrict(bool& isValid) const;
+ float toFloat(bool& isValid) const;
+
+ static void invalidate(const StringImpl&);
+
+ struct UnderlyingString;
+
+private:
+ friend bool equal(StringView, StringView);
+
+ void initialize(const LChar*, unsigned length);
+ void initialize(const UChar*, unsigned length);
+
+#if CHECK_STRINGVIEW_LIFETIME
+ WTF_EXPORT_STRING_API bool underlyingStringIsValid() const;
+ WTF_EXPORT_STRING_API void setUnderlyingString(const StringImpl*);
+ WTF_EXPORT_STRING_API void setUnderlyingString(const StringView&);
+#else
+ bool underlyingStringIsValid() const { return true; }
+ void setUnderlyingString(const StringImpl*) { }
+ void setUnderlyingString(const StringView&) { }
+#endif
+ void clear();
+
+ const void* m_characters { nullptr };
+ unsigned m_length { 0 };
+ bool m_is8Bit { true };
+
+#if CHECK_STRINGVIEW_LIFETIME
+ void adoptUnderlyingString(UnderlyingString*);
+ UnderlyingString* m_underlyingString { nullptr };
+#endif
+};
+
+template<typename CharacterType, size_t inlineCapacity> void append(Vector<CharacterType, inlineCapacity>&, StringView);
+
+bool equal(StringView, StringView);
+bool equal(StringView, const LChar*);
+bool equal(StringView, const char*);
+
+bool equalIgnoringASCIICase(StringView, StringView);
+bool equalIgnoringASCIICase(StringView, const char*);
+
+template<unsigned length> bool equalLettersIgnoringASCIICase(StringView, const char (&lowercaseLetters)[length]);
+
+inline bool operator==(StringView a, StringView b) { return equal(a, b); }
+inline bool operator==(StringView a, const LChar* b) { return equal(a, b); }
+inline bool operator==(StringView a, const char* b) { return equal(a, b); }
+inline bool operator==(const LChar* a, StringView b) { return equal(b, a); }
+inline bool operator==(const char* a, StringView b) { return equal(b, a); }
+
+inline bool operator!=(StringView a, StringView b) { return !equal(a, b); }
+inline bool operator!=(StringView a, const LChar* b) { return !equal(a, b); }
+inline bool operator!=(StringView a, const char* b) { return !equal(a, b); }
+inline bool operator!=(const LChar* a, StringView b) { return !equal(b, a); }
+inline bool operator!=(const char* a, StringView b) { return !equal(b, a); }
+
+}
+
+#include <wtf/text/AtomicString.h>
+#include <wtf/text/WTFString.h>
+
+namespace WTF {
+
+inline StringView::StringView()
+{
+ // FIXME: It's peculiar that null strings are 16-bit and empty strings return 8-bit (according to the is8Bit function).
+}
+
+#if CHECK_STRINGVIEW_LIFETIME
+inline StringView::~StringView()
+{
+ setUnderlyingString(nullptr);
+}
+
+inline StringView::StringView(StringView&& other)
+ : m_characters(other.m_characters)
+ , m_length(other.m_length)
+ , m_is8Bit(other.m_is8Bit)
+{
+ ASSERT(other.underlyingStringIsValid());
+
+ other.clear();
+
+ setUnderlyingString(other);
+ other.setUnderlyingString(nullptr);
+}
+
+inline StringView::StringView(const StringView& other)
+ : m_characters(other.m_characters)
+ , m_length(other.m_length)
+ , m_is8Bit(other.m_is8Bit)
+{
+ ASSERT(other.underlyingStringIsValid());
+
+ setUnderlyingString(other);
+}
+
+inline StringView& StringView::operator=(StringView&& other)
+{
+ ASSERT(other.underlyingStringIsValid());
+
+ m_characters = other.m_characters;
+ m_length = other.m_length;
+ m_is8Bit = other.m_is8Bit;
+
+ other.clear();
+
+ setUnderlyingString(other);
+ other.setUnderlyingString(nullptr);
+
+ return *this;
+}
+
+inline StringView& StringView::operator=(const StringView& other)
+{
+ ASSERT(other.underlyingStringIsValid());
+
+ m_characters = other.m_characters;
+ m_length = other.m_length;
+ m_is8Bit = other.m_is8Bit;
+
+ setUnderlyingString(other);
+
+ return *this;
+}
+#endif // CHECK_STRINGVIEW_LIFETIME
+
+inline void StringView::initialize(const LChar* characters, unsigned length)
+{
+ m_characters = characters;
+ m_length = length;
+ m_is8Bit = true;
+}
+
+inline void StringView::initialize(const UChar* characters, unsigned length)
+{
+ m_characters = characters;
+ m_length = length;
+ m_is8Bit = false;
+}
+
+inline StringView::StringView(const LChar* characters, unsigned length)
+{
+ initialize(characters, length);
+}
+
+inline StringView::StringView(const UChar* characters, unsigned length)
+{
+ initialize(characters, length);
+}
+
+inline StringView::StringView(const char* characters)
+{
+ initialize(reinterpret_cast<const LChar*>(characters), strlen(characters));
+}
+
+inline StringView::StringView(const StringImpl& string)
+{
+ setUnderlyingString(&string);
+ if (string.is8Bit())
+ initialize(string.characters8(), string.length());
+ else
+ initialize(string.characters16(), string.length());
+}
+
+inline StringView::StringView(const StringImpl* string)
+{
+ if (!string)
+ return;
+
+ setUnderlyingString(string);
+ if (string->is8Bit())
+ initialize(string->characters8(), string->length());
+ else
+ initialize(string->characters16(), string->length());
+}
+
+inline StringView::StringView(const String& string)
+{
+ setUnderlyingString(string.impl());
+ if (!string.impl()) {
+ clear();
+ return;
+ }
+ if (string.is8Bit()) {
+ initialize(string.characters8(), string.length());
+ return;
}
+ initialize(string.characters16(), string.length());
+}
- StringView(const LChar* characters, unsigned length)
- {
- initialize(characters, length);
+inline StringView::StringView(const AtomicString& atomicString)
+ : StringView(atomicString.string())
+{
+}
+
+inline void StringView::clear()
+{
+ m_characters = nullptr;
+ m_length = 0;
+ m_is8Bit = true;
+}
+
+inline StringView StringView::empty()
+{
+ return StringView(reinterpret_cast<const LChar*>(""), 0);
+}
+
+inline const LChar* StringView::characters8() const
+{
+ ASSERT(is8Bit());
+ ASSERT(underlyingStringIsValid());
+ return static_cast<const LChar*>(m_characters);
+}
+
+inline const UChar* StringView::characters16() const
+{
+ ASSERT(!is8Bit());
+ ASSERT(underlyingStringIsValid());
+ return static_cast<const UChar*>(m_characters);
+}
+
+class StringView::UpconvertedCharacters {
+public:
+ explicit UpconvertedCharacters(const StringView&);
+ operator const UChar*() const { return m_characters; }
+ const UChar* get() const { return m_characters; }
+private:
+ Vector<UChar, 32> m_upconvertedCharacters;
+ const UChar* m_characters;
+};
+
+inline StringView::UpconvertedCharacters StringView::upconvertedCharacters() const
+{
+ return UpconvertedCharacters(*this);
+}
+
+inline bool StringView::isNull() const
+{
+ return !m_characters;
+}
+
+inline bool StringView::isEmpty() const
+{
+ return !length();
+}
+
+inline unsigned StringView::length() const
+{
+ return m_length;
+}
+
+inline StringView::operator bool() const
+{
+ return !isNull();
+}
+
+inline bool StringView::is8Bit() const
+{
+ return m_is8Bit;
+}
+
+inline StringView StringView::substring(unsigned start, unsigned length) const
+{
+ if (start >= this->length())
+ return empty();
+ unsigned maxLength = this->length() - start;
+
+ if (length >= maxLength) {
+ if (!start)
+ return *this;
+ length = maxLength;
}
- StringView(const UChar* characters, unsigned length)
- {
- initialize(characters, length);
+ if (is8Bit()) {
+ StringView result(characters8() + start, length);
+ result.setUnderlyingString(*this);
+ return result;
}
+ StringView result(characters16() + start, length);
+ result.setUnderlyingString(*this);
+ return result;
+}
- StringView(const String& string)
- : m_characters(nullptr)
- , m_length(0)
- {
- if (!string.impl())
- return;
-
- if (string.is8Bit())
- initialize(string.characters8(), string.length());
- else
- initialize(string.characters16(), string.length());
+inline UChar StringView::operator[](unsigned index) const
+{
+ ASSERT(index < length());
+ if (is8Bit())
+ return characters8()[index];
+ return characters16()[index];
+}
+
+inline bool StringView::contains(UChar character) const
+{
+ return find(character) != notFound;
+}
+
+inline void StringView::getCharactersWithUpconvert(LChar* destination) const
+{
+ ASSERT(is8Bit());
+ auto characters8 = this->characters8();
+ for (unsigned i = 0; i < m_length; ++i)
+ destination[i] = characters8[i];
+}
+
+inline void StringView::getCharactersWithUpconvert(UChar* destination) const
+{
+ if (is8Bit()) {
+ auto characters8 = this->characters8();
+ for (unsigned i = 0; i < m_length; ++i)
+ destination[i] = characters8[i];
+ return;
}
+ auto characters16 = this->characters16();
+ for (unsigned i = 0; i < m_length; ++i)
+ destination[i] = characters16[i];
+}
- static StringView empty()
- {
- return StringView(reinterpret_cast<const LChar*>(""), 0);
+inline StringView::UpconvertedCharacters::UpconvertedCharacters(const StringView& string)
+{
+ if (!string.is8Bit()) {
+ m_characters = string.characters16();
+ return;
}
+ const LChar* characters8 = string.characters8();
+ unsigned length = string.m_length;
+ m_upconvertedCharacters.reserveInitialCapacity(length);
+ for (unsigned i = 0; i < length; ++i)
+ m_upconvertedCharacters.uncheckedAppend(characters8[i]);
+ m_characters = m_upconvertedCharacters.data();
+}
- const LChar* characters8() const
- {
- ASSERT(is8Bit());
+inline String StringView::toString() const
+{
+ if (is8Bit())
+ return String(characters8(), m_length);
+ return String(characters16(), m_length);
+}
- return static_cast<const LChar*>(m_characters);
- }
+inline AtomicString StringView::toAtomicString() const
+{
+ if (is8Bit())
+ return AtomicString(characters8(), m_length);
+ return AtomicString(characters16(), m_length);
+}
- const UChar* characters16() const
- {
- ASSERT(!is8Bit());
+inline float StringView::toFloat(bool& isValid) const
+{
+ if (is8Bit())
+ return charactersToFloat(characters8(), m_length, &isValid);
+ return charactersToFloat(characters16(), m_length, &isValid);
+}
- return static_cast<const UChar*>(m_characters);
- }
+inline int StringView::toInt() const
+{
+ bool isValid;
+ return toInt(isValid);
+}
- bool isNull() const { return !m_characters; }
- bool isEmpty() const { return !length(); }
- unsigned length() const { return m_length & ~is16BitStringFlag; }
+inline int StringView::toInt(bool& isValid) const
+{
+ if (is8Bit())
+ return charactersToInt(characters8(), m_length, &isValid);
+ return charactersToInt(characters16(), m_length, &isValid);
+}
- explicit operator bool() const { return !isNull(); }
+inline int StringView::toIntStrict(bool& isValid) const
+{
+ if (is8Bit())
+ return charactersToIntStrict(characters8(), m_length, &isValid);
+ return charactersToIntStrict(characters16(), m_length, &isValid);
+}
- bool is8Bit() const { return !(m_length & is16BitStringFlag); }
+inline String StringView::toStringWithoutCopying() const
+{
+ if (is8Bit())
+ return StringImpl::createWithoutCopying(characters8(), m_length);
+ return StringImpl::createWithoutCopying(characters16(), m_length);
+}
- StringView substring(unsigned start, unsigned length = std::numeric_limits<unsigned>::max()) const
- {
- if (start >= this->length())
- return empty();
- unsigned maxLength = this->length() - start;
+inline size_t StringView::find(UChar character, unsigned start) const
+{
+ if (is8Bit())
+ return WTF::find(characters8(), m_length, character, start);
+ return WTF::find(characters16(), m_length, character, start);
+}
- if (length >= maxLength) {
- if (!start)
- return *this;
- length = maxLength;
- }
+inline size_t StringView::find(CharacterMatchFunction matchFunction, unsigned start) const
+{
+ if (is8Bit())
+ return WTF::find(characters8(), m_length, matchFunction, start);
+ return WTF::find(characters16(), m_length, matchFunction, start);
+}
- if (is8Bit())
- return StringView(characters8() + start, length);
+inline size_t StringView::reverseFind(UChar character, unsigned index) const
+{
+ if (is8Bit())
+ return WTF::reverseFind(characters8(), m_length, character, index);
+ return WTF::reverseFind(characters16(), m_length, character, index);
+}
- return StringView(characters16() + start, length);
- }
+#if !CHECK_STRINGVIEW_LIFETIME
+inline void StringView::invalidate(const StringImpl&)
+{
+}
+#endif
- String toString() const
- {
- if (is8Bit())
- return String(characters8(), length());
+template<typename StringType> class StringTypeAdapter;
- return String(characters16(), length());
+template<> class StringTypeAdapter<StringView> {
+public:
+ StringTypeAdapter<StringView>(StringView string)
+ : m_string(string)
+ {
}
- String toStringWithoutCopying() const
- {
- if (is8Bit())
- return StringImpl::createWithoutCopying(characters8(), length());
+ unsigned length() { return m_string.length(); }
+ bool is8Bit() { return m_string.is8Bit(); }
+ void writeTo(LChar* destination) { m_string.getCharactersWithUpconvert(destination); }
+ void writeTo(UChar* destination) { m_string.getCharactersWithUpconvert(destination); }
- return StringImpl::createWithoutCopying(characters16(), length());
- }
+ String toString() const { return m_string.toString(); }
private:
- void initialize(const LChar* characters, unsigned length)
- {
- ASSERT(!(length & is16BitStringFlag));
-
- m_characters = characters;
- m_length = length;
- }
+ StringView m_string;
+};
- void initialize(const UChar* characters, unsigned length)
- {
- ASSERT(!(length & is16BitStringFlag));
-
- m_characters = characters;
- m_length = is16BitStringFlag | length;
+template<typename CharacterType, size_t inlineCapacity> void append(Vector<CharacterType, inlineCapacity>& buffer, StringView string)
+{
+ unsigned oldSize = buffer.size();
+ buffer.grow(oldSize + string.length());
+ string.getCharactersWithUpconvert(buffer.data() + oldSize);
+}
+
+inline bool equal(StringView a, StringView b)
+{
+ if (a.m_characters == b.m_characters) {
+ ASSERT(a.is8Bit() == b.is8Bit());
+ return a.length() == b.length();
}
+
+ return equalCommon(a, b);
+}
+
+inline bool equal(StringView a, const LChar* b)
+{
+ if (!b)
+ return !a.isEmpty();
+ if (a.isEmpty())
+ return !b;
+ unsigned aLength = a.length();
+ if (a.is8Bit())
+ return equal(a.characters8(), b, aLength);
+ return equal(a.characters16(), b, aLength);
+}
- static const unsigned is16BitStringFlag = 1u << 31;
+inline bool equal(StringView a, const char* b)
+{
+ return equal(a, reinterpret_cast<const LChar*>(b));
+}
- const void* m_characters;
+inline bool equalIgnoringASCIICase(StringView a, StringView b)
+{
+ return equalIgnoringASCIICaseCommon(a, b);
+}
+
+inline bool equalIgnoringASCIICase(StringView a, const char* b)
+{
+ return equalIgnoringASCIICaseCommon(a, b);
+}
+
+class StringView::SplitResult {
+public:
+ explicit SplitResult(StringView, UChar separator);
+
+ class Iterator;
+ Iterator begin() const;
+ Iterator end() const;
+
+private:
+ StringView m_string;
+ UChar m_separator;
+};
+
+class StringView::GraphemeClusters {
+public:
+ explicit GraphemeClusters(const StringView&);
+
+ class Iterator;
+ Iterator begin() const;
+ Iterator end() const;
+
+private:
+ StringView m_stringView;
+};
+
+class StringView::CodePoints {
+public:
+ explicit CodePoints(const StringView&);
+
+ class Iterator;
+ Iterator begin() const;
+ Iterator end() const;
+
+private:
+ StringView m_stringView;
+};
+
+class StringView::CodeUnits {
+public:
+ explicit CodeUnits(const StringView&);
+
+ class Iterator;
+ Iterator begin() const;
+ Iterator end() const;
+
+private:
+ StringView m_stringView;
+};
+
+class StringView::SplitResult::Iterator {
+public:
+ StringView operator*() const;
+
+ WTF_EXPORT_PRIVATE Iterator& operator++();
+
+ bool operator==(const Iterator&) const;
+ bool operator!=(const Iterator&) const;
+
+private:
+ enum PositionTag { AtEnd };
+ Iterator(const SplitResult&);
+ Iterator(const SplitResult&, PositionTag);
+
+ WTF_EXPORT_PRIVATE void findNextSubstring();
+
+ friend SplitResult;
+
+ const SplitResult& m_result;
+ unsigned m_position { 0 };
unsigned m_length;
};
+class StringView::GraphemeClusters::Iterator {
+public:
+ WTF_EXPORT_PRIVATE Iterator() = delete;
+ WTF_EXPORT_PRIVATE Iterator(const StringView&, unsigned index);
+ WTF_EXPORT_PRIVATE ~Iterator();
+
+ Iterator(const Iterator&) = delete;
+ WTF_EXPORT_PRIVATE Iterator(Iterator&&);
+ Iterator& operator=(const Iterator&) = delete;
+ Iterator& operator=(Iterator&&) = delete;
+
+ WTF_EXPORT_PRIVATE StringView operator*() const;
+ WTF_EXPORT_PRIVATE Iterator& operator++();
+
+ WTF_EXPORT_PRIVATE bool operator==(const Iterator&) const;
+ WTF_EXPORT_PRIVATE bool operator!=(const Iterator&) const;
+
+private:
+ class Impl;
+
+ std::unique_ptr<Impl> m_impl;
+};
+
+class StringView::CodePoints::Iterator {
+public:
+ Iterator(const StringView&, unsigned index);
+
+ UChar32 operator*() const;
+ Iterator& operator++();
+
+ bool operator==(const Iterator&) const;
+ bool operator!=(const Iterator&) const;
+ Iterator& operator=(const Iterator&);
+
+private:
+ std::reference_wrapper<const StringView> m_stringView;
+ std::optional<unsigned> m_nextCodePointOffset;
+ UChar32 m_codePoint;
+};
+
+class StringView::CodeUnits::Iterator {
+public:
+ Iterator(const StringView&, unsigned index);
+
+ UChar operator*() const;
+ Iterator& operator++();
+
+ bool operator==(const Iterator&) const;
+ bool operator!=(const Iterator&) const;
+
+private:
+ const StringView& m_stringView;
+ unsigned m_index;
+};
+
+inline auto StringView::graphemeClusters() const -> GraphemeClusters
+{
+ return GraphemeClusters(*this);
+}
+
+inline auto StringView::codePoints() const -> CodePoints
+{
+ return CodePoints(*this);
+}
+
+inline auto StringView::codeUnits() const -> CodeUnits
+{
+ return CodeUnits(*this);
+}
+
+inline StringView::GraphemeClusters::GraphemeClusters(const StringView& stringView)
+ : m_stringView(stringView)
+{
+}
+
+inline auto StringView::GraphemeClusters::begin() const -> Iterator
+{
+ return Iterator(m_stringView, 0);
+}
+
+inline auto StringView::GraphemeClusters::end() const -> Iterator
+{
+ return Iterator(m_stringView, m_stringView.length());
+}
+
+inline StringView::CodePoints::CodePoints(const StringView& stringView)
+ : m_stringView(stringView)
+{
+}
+
+inline StringView::CodePoints::Iterator::Iterator(const StringView& stringView, unsigned index)
+ : m_stringView(stringView)
+ , m_nextCodePointOffset(index)
+{
+ operator++();
+}
+
+inline auto StringView::CodePoints::Iterator::operator++() -> Iterator&
+{
+ ASSERT(m_nextCodePointOffset);
+ if (m_nextCodePointOffset.value() == m_stringView.get().length()) {
+ m_nextCodePointOffset = std::nullopt;
+ return *this;
+ }
+ if (m_stringView.get().is8Bit())
+ m_codePoint = m_stringView.get().characters8()[m_nextCodePointOffset.value()++];
+ else
+ U16_NEXT(m_stringView.get().characters16(), m_nextCodePointOffset.value(), m_stringView.get().length(), m_codePoint);
+ ASSERT(m_nextCodePointOffset.value() <= m_stringView.get().length());
+ return *this;
+}
+
+inline auto StringView::CodePoints::Iterator::operator=(const Iterator& other) -> Iterator&
+{
+ m_stringView = other.m_stringView;
+ m_nextCodePointOffset = other.m_nextCodePointOffset;
+ m_codePoint = other.m_codePoint;
+ return *this;
+}
+
+inline UChar32 StringView::CodePoints::Iterator::operator*() const
+{
+ ASSERT(m_nextCodePointOffset);
+ return m_codePoint;
+}
+
+inline bool StringView::CodePoints::Iterator::operator==(const Iterator& other) const
+{
+ ASSERT(&m_stringView.get() == &other.m_stringView.get());
+ return m_nextCodePointOffset == other.m_nextCodePointOffset;
+}
+
+inline bool StringView::CodePoints::Iterator::operator!=(const Iterator& other) const
+{
+ return !(*this == other);
+}
+
+inline auto StringView::CodePoints::begin() const -> Iterator
+{
+ return Iterator(m_stringView, 0);
+}
+
+inline auto StringView::CodePoints::end() const -> Iterator
+{
+ return Iterator(m_stringView, m_stringView.length());
+}
+
+inline StringView::CodeUnits::CodeUnits(const StringView& stringView)
+ : m_stringView(stringView)
+{
+}
+
+inline StringView::CodeUnits::Iterator::Iterator(const StringView& stringView, unsigned index)
+ : m_stringView(stringView)
+ , m_index(index)
+{
+}
+
+inline auto StringView::CodeUnits::Iterator::operator++() -> Iterator&
+{
+ ++m_index;
+ return *this;
+}
+
+inline UChar StringView::CodeUnits::Iterator::operator*() const
+{
+ return m_stringView[m_index];
+}
+
+inline bool StringView::CodeUnits::Iterator::operator==(const Iterator& other) const
+{
+ ASSERT(&m_stringView == &other.m_stringView);
+ return m_index == other.m_index;
+}
+
+inline bool StringView::CodeUnits::Iterator::operator!=(const Iterator& other) const
+{
+ return !(*this == other);
+}
+
+inline auto StringView::CodeUnits::begin() const -> Iterator
+{
+ return Iterator(m_stringView, 0);
+}
+
+inline auto StringView::CodeUnits::end() const -> Iterator
+{
+ return Iterator(m_stringView, m_stringView.length());
+}
+
+inline auto StringView::split(UChar separator) const -> SplitResult
+{
+ return SplitResult { *this, separator };
+}
+
+inline StringView::SplitResult::SplitResult(StringView stringView, UChar separator)
+ : m_string { stringView }
+ , m_separator { separator }
+{
+}
+
+inline auto StringView::SplitResult::begin() const -> Iterator
+{
+ return Iterator { *this };
+}
+
+inline auto StringView::SplitResult::end() const -> Iterator
+{
+ return Iterator { *this, Iterator::AtEnd };
+}
+
+inline StringView::SplitResult::Iterator::Iterator(const SplitResult& result)
+ : m_result { result }
+{
+ findNextSubstring();
+}
+
+inline StringView::SplitResult::Iterator::Iterator(const SplitResult& result, PositionTag)
+ : m_result { result }
+ , m_position { result.m_string.length() }
+{
+}
+
+inline StringView StringView::SplitResult::Iterator::operator*() const
+{
+ ASSERT(m_position < m_result.m_string.length());
+ return m_result.m_string.substring(m_position, m_length);
+}
+
+inline bool StringView::SplitResult::Iterator::operator==(const Iterator& other) const
+{
+ ASSERT(&m_result == &other.m_result);
+ return m_position == other.m_position;
+}
+
+inline bool StringView::SplitResult::Iterator::operator!=(const Iterator& other) const
+{
+ return !(*this == other);
+}
+
+template<unsigned length> inline bool equalLettersIgnoringASCIICase(StringView string, const char (&lowercaseLetters)[length])
+{
+ return equalLettersIgnoringASCIICaseCommon(string, lowercaseLetters);
+}
+
} // namespace WTF
+using WTF::append;
+using WTF::equal;
using WTF::StringView;
#endif // StringView_h