1/*
2 * Copyright (C) 2014-2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include <limits.h>
29#include <unicode/utypes.h>
30#include <wtf/Forward.h>
31#include <wtf/Optional.h>
32#include <wtf/RetainPtr.h>
33#include <wtf/Vector.h>
34#include <wtf/text/CString.h>
35#include <wtf/text/ConversionMode.h>
36#include <wtf/text/LChar.h>
37#include <wtf/text/StringCommon.h>
38#include <wtf/text/UTF8ConversionError.h>
39
40// FIXME: Enabling the StringView lifetime checking causes the MSVC build to fail. Figure out why.
41#if defined(NDEBUG) || COMPILER(MSVC)
42#define CHECK_STRINGVIEW_LIFETIME 0
43#else
44#define CHECK_STRINGVIEW_LIFETIME 1
45#endif
46
47namespace WTF {
48
49// StringView is a non-owning reference to a string, similar to the proposed std::string_view.
50
51class StringView {
52public:
53 StringView();
54#if CHECK_STRINGVIEW_LIFETIME
55 ~StringView();
56 StringView(StringView&&);
57 StringView(const StringView&);
58 StringView& operator=(StringView&&);
59 StringView& operator=(const StringView&);
60#endif
61
62 StringView(const AtomString&);
63 StringView(const String&);
64 StringView(const StringImpl&);
65 StringView(const StringImpl*);
66 StringView(const LChar*, unsigned length);
67 StringView(const UChar*, unsigned length);
68 StringView(const char*);
69
70 static StringView empty();
71
72 unsigned length() const;
73 bool isEmpty() const;
74
75 explicit operator bool() const;
76 bool isNull() const;
77
78 UChar operator[](unsigned index) const;
79
80 class CodeUnits;
81 CodeUnits codeUnits() const;
82
83 class CodePoints;
84 CodePoints codePoints() const;
85
86 class GraphemeClusters;
87 GraphemeClusters graphemeClusters() const;
88
89 bool is8Bit() const;
90 const LChar* characters8() const;
91 const UChar* characters16() const;
92
93 String toString() const;
94 String toStringWithoutCopying() const;
95 AtomString toAtomString() const;
96 RefPtr<AtomStringImpl> toExistingAtomString() const;
97
98#if USE(CF)
99 // This function converts null strings to empty strings.
100 WTF_EXPORT_PRIVATE RetainPtr<CFStringRef> createCFString() const;
101 WTF_EXPORT_PRIVATE RetainPtr<CFStringRef> createCFStringWithoutCopying() const;
102#endif
103
104#ifdef __OBJC__
105 // These functions convert null strings to empty strings.
106 WTF_EXPORT_PRIVATE RetainPtr<NSString> createNSString() const;
107 WTF_EXPORT_PRIVATE RetainPtr<NSString> createNSStringWithoutCopying() const;
108#endif
109
110 WTF_EXPORT_PRIVATE Expected<CString, UTF8ConversionError> tryGetUtf8(ConversionMode = LenientConversion) const;
111 WTF_EXPORT_PRIVATE CString utf8(ConversionMode = LenientConversion) const;
112
113 class UpconvertedCharacters;
114 UpconvertedCharacters upconvertedCharacters() const;
115
116 void getCharactersWithUpconvert(LChar*) const;
117 void getCharactersWithUpconvert(UChar*) const;
118
119 StringView substring(unsigned start, unsigned length = std::numeric_limits<unsigned>::max()) const;
120 StringView left(unsigned len) const { return substring(0, len); }
121 StringView right(unsigned len) const { return substring(length() - len, len); }
122
123 template<typename MatchedCharacterPredicate>
124 StringView stripLeadingAndTrailingMatchedCharacters(const MatchedCharacterPredicate&);
125
126 class SplitResult;
127 SplitResult split(UChar) const;
128 SplitResult splitAllowingEmptyEntries(UChar) const;
129
130 size_t find(UChar, unsigned start = 0) const;
131 size_t find(CodeUnitMatchFunction, unsigned start = 0) const;
132
133 WTF_EXPORT_PRIVATE size_t find(StringView, unsigned start) const;
134
135 size_t reverseFind(UChar, unsigned index = UINT_MAX) const;
136
137 WTF_EXPORT_PRIVATE size_t findIgnoringASCIICase(const StringView&) const;
138 WTF_EXPORT_PRIVATE size_t findIgnoringASCIICase(const StringView&, unsigned startOffset) const;
139
140 WTF_EXPORT_PRIVATE String convertToASCIILowercase() const;
141 WTF_EXPORT_PRIVATE String convertToASCIIUppercase() const;
142
143 bool contains(UChar) const;
144 WTF_EXPORT_PRIVATE bool containsIgnoringASCIICase(const StringView&) const;
145 WTF_EXPORT_PRIVATE bool containsIgnoringASCIICase(const StringView&, unsigned startOffset) const;
146
147 WTF_EXPORT_PRIVATE bool startsWith(const StringView&) const;
148 WTF_EXPORT_PRIVATE bool startsWithIgnoringASCIICase(const StringView&) const;
149
150 WTF_EXPORT_PRIVATE bool endsWith(const StringView&) const;
151 WTF_EXPORT_PRIVATE bool endsWithIgnoringASCIICase(const StringView&) const;
152
153 int toInt() const;
154 int toInt(bool& isValid) const;
155 int toIntStrict(bool& isValid) const;
156 Optional<uint64_t> toUInt64Strict() const;
157 float toFloat(bool& isValid) const;
158
159 static void invalidate(const StringImpl&);
160
161 struct UnderlyingString;
162
163private:
164 friend bool equal(StringView, StringView);
165
166 void initialize(const LChar*, unsigned length);
167 void initialize(const UChar*, unsigned length);
168
169 template<typename CharacterType, typename MatchedCharacterPredicate>
170 StringView stripLeadingAndTrailingMatchedCharacters(const CharacterType*, const MatchedCharacterPredicate&);
171
172#if CHECK_STRINGVIEW_LIFETIME
173 WTF_EXPORT_PRIVATE bool underlyingStringIsValid() const;
174 WTF_EXPORT_PRIVATE void setUnderlyingString(const StringImpl*);
175 WTF_EXPORT_PRIVATE void setUnderlyingString(const StringView&);
176#else
177 bool underlyingStringIsValid() const { return true; }
178 void setUnderlyingString(const StringImpl*) { }
179 void setUnderlyingString(const StringView&) { }
180#endif
181 void clear();
182
183 const void* m_characters { nullptr };
184 unsigned m_length { 0 };
185 bool m_is8Bit { true };
186
187#if CHECK_STRINGVIEW_LIFETIME
188 void adoptUnderlyingString(UnderlyingString*);
189 UnderlyingString* m_underlyingString { nullptr };
190#endif
191};
192
193template<typename CharacterType, size_t inlineCapacity> void append(Vector<CharacterType, inlineCapacity>&, StringView);
194
195bool equal(StringView, StringView);
196bool equal(StringView, const LChar* b);
197
198bool equalIgnoringASCIICase(StringView, StringView);
199bool equalIgnoringASCIICase(StringView, const char*);
200
201template<unsigned length> bool equalLettersIgnoringASCIICase(StringView, const char (&lowercaseLetters)[length]);
202
203inline bool operator==(StringView a, StringView b) { return equal(a, b); }
204inline bool operator==(StringView a, const LChar *b);
205inline bool operator==(StringView a, const char *b) { return equal(a, reinterpret_cast<const LChar*>(b)); }
206inline bool operator==(const char* a, StringView b) { return equal(b, a); }
207
208inline bool operator!=(StringView a, StringView b) { return !equal(a, b); }
209inline bool operator!=(StringView a, const LChar* b) { return !equal(a, b); }
210inline bool operator!=(StringView a, const char* b) { return !equal(a, b); }
211inline bool operator!=(const LChar*a, StringView b) { return !equal(b, a); }
212inline bool operator!=(const char*a, StringView b) { return !equal(b, a); }
213
214struct StringViewWithUnderlyingString;
215
216// This returns a StringView of the normalized result, and a String that is either
217// null, if the input was already normalized, or contains the normalized result
218// and needs to be kept around so the StringView remains valid. Typically the
219// easiest way to use it correctly is to put it into a local and use the StringView.
220WTF_EXPORT_PRIVATE StringViewWithUnderlyingString normalizedNFC(StringView);
221
222WTF_EXPORT_PRIVATE String normalizedNFC(const String&);
223
224}
225
226#include <wtf/text/AtomString.h>
227#include <wtf/text/WTFString.h>
228
229namespace WTF {
230
231struct StringViewWithUnderlyingString {
232 StringView view;
233 String underlyingString;
234};
235
236inline StringView::StringView()
237{
238}
239
240#if CHECK_STRINGVIEW_LIFETIME
241
242inline StringView::~StringView()
243{
244 setUnderlyingString(nullptr);
245}
246
247inline StringView::StringView(StringView&& other)
248 : m_characters(other.m_characters)
249 , m_length(other.m_length)
250 , m_is8Bit(other.m_is8Bit)
251{
252 ASSERT(other.underlyingStringIsValid());
253
254 other.clear();
255
256 setUnderlyingString(other);
257 other.setUnderlyingString(nullptr);
258}
259
260inline StringView::StringView(const StringView& other)
261 : m_characters(other.m_characters)
262 , m_length(other.m_length)
263 , m_is8Bit(other.m_is8Bit)
264{
265 ASSERT(other.underlyingStringIsValid());
266
267 setUnderlyingString(other);
268}
269
270inline StringView& StringView::operator=(StringView&& other)
271{
272 ASSERT(other.underlyingStringIsValid());
273
274 m_characters = other.m_characters;
275 m_length = other.m_length;
276 m_is8Bit = other.m_is8Bit;
277
278 other.clear();
279
280 setUnderlyingString(other);
281 other.setUnderlyingString(nullptr);
282
283 return *this;
284}
285
286inline StringView& StringView::operator=(const StringView& other)
287{
288 ASSERT(other.underlyingStringIsValid());
289
290 m_characters = other.m_characters;
291 m_length = other.m_length;
292 m_is8Bit = other.m_is8Bit;
293
294 setUnderlyingString(other);
295
296 return *this;
297}
298
299#endif // CHECK_STRINGVIEW_LIFETIME
300
301inline void StringView::initialize(const LChar* characters, unsigned length)
302{
303 m_characters = characters;
304 m_length = length;
305 m_is8Bit = true;
306}
307
308inline void StringView::initialize(const UChar* characters, unsigned length)
309{
310 m_characters = characters;
311 m_length = length;
312 m_is8Bit = false;
313}
314
315inline StringView::StringView(const LChar* characters, unsigned length)
316{
317 initialize(characters, length);
318}
319
320inline StringView::StringView(const UChar* characters, unsigned length)
321{
322 initialize(characters, length);
323}
324
325inline StringView::StringView(const char* characters)
326{
327 initialize(reinterpret_cast<const LChar*>(characters), strlen(characters));
328}
329
330inline StringView::StringView(const StringImpl& string)
331{
332 setUnderlyingString(&string);
333 if (string.is8Bit())
334 initialize(string.characters8(), string.length());
335 else
336 initialize(string.characters16(), string.length());
337}
338
339inline StringView::StringView(const StringImpl* string)
340{
341 if (!string)
342 return;
343
344 setUnderlyingString(string);
345 if (string->is8Bit())
346 initialize(string->characters8(), string->length());
347 else
348 initialize(string->characters16(), string->length());
349}
350
351inline StringView::StringView(const String& string)
352{
353 setUnderlyingString(string.impl());
354 if (!string.impl()) {
355 clear();
356 return;
357 }
358 if (string.is8Bit()) {
359 initialize(string.characters8(), string.length());
360 return;
361 }
362 initialize(string.characters16(), string.length());
363}
364
365inline StringView::StringView(const AtomString& atomString)
366 : StringView(atomString.string())
367{
368}
369
370inline void StringView::clear()
371{
372 m_characters = nullptr;
373 m_length = 0;
374 m_is8Bit = true;
375}
376
377inline StringView StringView::empty()
378{
379 return StringView(reinterpret_cast<const LChar*>(""), 0);
380}
381
382inline const LChar* StringView::characters8() const
383{
384 ASSERT(is8Bit());
385 ASSERT(underlyingStringIsValid());
386 return static_cast<const LChar*>(m_characters);
387}
388
389inline const UChar* StringView::characters16() const
390{
391 ASSERT(!is8Bit());
392 ASSERT(underlyingStringIsValid());
393 return static_cast<const UChar*>(m_characters);
394}
395
396class StringView::UpconvertedCharacters {
397public:
398 explicit UpconvertedCharacters(const StringView&);
399 operator const UChar*() const { return m_characters; }
400 const UChar* get() const { return m_characters; }
401private:
402 Vector<UChar, 32> m_upconvertedCharacters;
403 const UChar* m_characters;
404};
405
406inline StringView::UpconvertedCharacters StringView::upconvertedCharacters() const
407{
408 return UpconvertedCharacters(*this);
409}
410
411inline bool StringView::isNull() const
412{
413 return !m_characters;
414}
415
416inline bool StringView::isEmpty() const
417{
418 return !length();
419}
420
421inline unsigned StringView::length() const
422{
423 return m_length;
424}
425
426inline StringView::operator bool() const
427{
428 return !isNull();
429}
430
431inline bool StringView::is8Bit() const
432{
433 return m_is8Bit;
434}
435
436inline StringView StringView::substring(unsigned start, unsigned length) const
437{
438 if (start >= this->length())
439 return empty();
440 unsigned maxLength = this->length() - start;
441
442 if (length >= maxLength) {
443 if (!start)
444 return *this;
445 length = maxLength;
446 }
447
448 if (is8Bit()) {
449 StringView result(characters8() + start, length);
450 result.setUnderlyingString(*this);
451 return result;
452 }
453 StringView result(characters16() + start, length);
454 result.setUnderlyingString(*this);
455 return result;
456}
457
458inline UChar StringView::operator[](unsigned index) const
459{
460 ASSERT(index < length());
461 if (is8Bit())
462 return characters8()[index];
463 return characters16()[index];
464}
465
466inline bool StringView::contains(UChar character) const
467{
468 return find(character) != notFound;
469}
470
471inline void StringView::getCharactersWithUpconvert(LChar* destination) const
472{
473 ASSERT(is8Bit());
474 StringImpl::copyCharacters(destination, characters8(), m_length);
475}
476
477inline void StringView::getCharactersWithUpconvert(UChar* destination) const
478{
479 if (is8Bit()) {
480 StringImpl::copyCharacters(destination, characters8(), m_length);
481 return;
482 }
483 StringImpl::copyCharacters(destination, characters16(), m_length);
484}
485
486inline StringView::UpconvertedCharacters::UpconvertedCharacters(const StringView& string)
487{
488 if (!string.is8Bit()) {
489 m_characters = string.characters16();
490 return;
491 }
492 const LChar* characters8 = string.characters8();
493 unsigned length = string.m_length;
494 m_upconvertedCharacters.reserveInitialCapacity(length);
495 for (unsigned i = 0; i < length; ++i)
496 m_upconvertedCharacters.uncheckedAppend(characters8[i]);
497 m_characters = m_upconvertedCharacters.data();
498}
499
500inline String StringView::toString() const
501{
502 if (is8Bit())
503 return String(characters8(), m_length);
504 return String(characters16(), m_length);
505}
506
507inline AtomString StringView::toAtomString() const
508{
509 if (is8Bit())
510 return AtomString(characters8(), m_length);
511 return AtomString(characters16(), m_length);
512}
513
514inline RefPtr<AtomStringImpl> StringView::toExistingAtomString() const
515{
516 if (is8Bit())
517 return AtomStringImpl::lookUp(characters8(), m_length);
518 return AtomStringImpl::lookUp(characters16(), m_length);
519}
520
521inline float StringView::toFloat(bool& isValid) const
522{
523 if (is8Bit())
524 return charactersToFloat(characters8(), m_length, &isValid);
525 return charactersToFloat(characters16(), m_length, &isValid);
526}
527
528inline int StringView::toInt() const
529{
530 bool isValid;
531 return toInt(isValid);
532}
533
534inline int StringView::toInt(bool& isValid) const
535{
536 if (is8Bit())
537 return charactersToInt(characters8(), m_length, &isValid);
538 return charactersToInt(characters16(), m_length, &isValid);
539}
540
541inline int StringView::toIntStrict(bool& isValid) const
542{
543 if (is8Bit())
544 return charactersToIntStrict(characters8(), m_length, &isValid);
545 return charactersToIntStrict(characters16(), m_length, &isValid);
546}
547
548inline Optional<uint64_t> StringView::toUInt64Strict() const
549{
550 bool isValid;
551 uint64_t result = is8Bit() ? charactersToUInt64Strict(characters8(), m_length, &isValid) : charactersToUInt64Strict(characters16(), m_length, &isValid);
552 return isValid ? makeOptional(result) : WTF::nullopt;
553}
554
555inline String StringView::toStringWithoutCopying() const
556{
557 if (is8Bit())
558 return StringImpl::createWithoutCopying(characters8(), m_length);
559 return StringImpl::createWithoutCopying(characters16(), m_length);
560}
561
562inline size_t StringView::find(UChar character, unsigned start) const
563{
564 if (is8Bit())
565 return WTF::find(characters8(), m_length, character, start);
566 return WTF::find(characters16(), m_length, character, start);
567}
568
569inline size_t StringView::find(CodeUnitMatchFunction matchFunction, unsigned start) const
570{
571 if (is8Bit())
572 return WTF::find(characters8(), m_length, matchFunction, start);
573 return WTF::find(characters16(), m_length, matchFunction, start);
574}
575
576inline size_t StringView::reverseFind(UChar character, unsigned index) const
577{
578 if (is8Bit())
579 return WTF::reverseFind(characters8(), m_length, character, index);
580 return WTF::reverseFind(characters16(), m_length, character, index);
581}
582
583#if !CHECK_STRINGVIEW_LIFETIME
584
585inline void StringView::invalidate(const StringImpl&)
586{
587}
588
589#endif
590
591template<> class StringTypeAdapter<StringView, void> {
592public:
593 StringTypeAdapter(StringView string)
594 : m_string { string }
595 {
596 }
597
598 unsigned length() { return m_string.length(); }
599 bool is8Bit() { return m_string.is8Bit(); }
600 template<typename CharacterType> void writeTo(CharacterType* destination) { m_string.getCharactersWithUpconvert(destination); }
601
602private:
603 StringView m_string;
604};
605
606template<typename CharacterType, size_t inlineCapacity> void append(Vector<CharacterType, inlineCapacity>& buffer, StringView string)
607{
608 unsigned oldSize = buffer.size();
609 buffer.grow(oldSize + string.length());
610 string.getCharactersWithUpconvert(buffer.data() + oldSize);
611}
612
613inline bool equal(StringView a, StringView b)
614{
615 if (a.m_characters == b.m_characters) {
616 ASSERT(a.is8Bit() == b.is8Bit());
617 return a.length() == b.length();
618 }
619
620 return equalCommon(a, b);
621}
622
623inline bool equal(StringView a, const LChar* b)
624{
625 if (!b)
626 return !a.isEmpty();
627 if (a.isEmpty())
628 return !b;
629
630 unsigned aLength = a.length();
631 if (aLength != strlen(reinterpret_cast<const char*>(b)))
632 return false;
633
634 if (a.is8Bit())
635 return equal(a.characters8(), b, aLength);
636 return equal(a.characters16(), b, aLength);
637}
638
639inline bool equalIgnoringASCIICase(StringView a, StringView b)
640{
641 return equalIgnoringASCIICaseCommon(a, b);
642}
643
644inline bool equalIgnoringASCIICase(StringView a, const char* b)
645{
646 return equalIgnoringASCIICaseCommon(a, b);
647}
648
649class StringView::SplitResult {
650public:
651 SplitResult(StringView, UChar separator, bool allowEmptyEntries);
652
653 class Iterator;
654 Iterator begin() const;
655 Iterator end() const;
656
657private:
658 StringView m_string;
659 UChar m_separator;
660 bool m_allowEmptyEntries;
661};
662
663class StringView::GraphemeClusters {
664public:
665 explicit GraphemeClusters(const StringView&);
666
667 class Iterator;
668 Iterator begin() const;
669 Iterator end() const;
670
671private:
672 StringView m_stringView;
673};
674
675class StringView::CodePoints {
676public:
677 explicit CodePoints(const StringView&);
678
679 class Iterator;
680 Iterator begin() const;
681 Iterator end() const;
682
683private:
684 StringView m_stringView;
685};
686
687class StringView::CodeUnits {
688public:
689 explicit CodeUnits(const StringView&);
690
691 class Iterator;
692 Iterator begin() const;
693 Iterator end() const;
694
695private:
696 StringView m_stringView;
697};
698
699class StringView::SplitResult::Iterator {
700public:
701 StringView operator*() const;
702
703 WTF_EXPORT_PRIVATE Iterator& operator++();
704
705 bool operator==(const Iterator&) const;
706 bool operator!=(const Iterator&) const;
707
708private:
709 enum PositionTag { AtEnd };
710 Iterator(const SplitResult&);
711 Iterator(const SplitResult&, PositionTag);
712
713 WTF_EXPORT_PRIVATE void findNextSubstring();
714
715 friend SplitResult;
716
717 const SplitResult& m_result;
718 unsigned m_position { 0 };
719 unsigned m_length;
720 bool m_isDone;
721};
722
723class StringView::GraphemeClusters::Iterator {
724public:
725 Iterator() = delete;
726 WTF_EXPORT_PRIVATE Iterator(const StringView&, unsigned index);
727 WTF_EXPORT_PRIVATE ~Iterator();
728
729 Iterator(const Iterator&) = delete;
730 WTF_EXPORT_PRIVATE Iterator(Iterator&&);
731 Iterator& operator=(const Iterator&) = delete;
732 Iterator& operator=(Iterator&&) = delete;
733
734 WTF_EXPORT_PRIVATE StringView operator*() const;
735 WTF_EXPORT_PRIVATE Iterator& operator++();
736
737 WTF_EXPORT_PRIVATE bool operator==(const Iterator&) const;
738 WTF_EXPORT_PRIVATE bool operator!=(const Iterator&) const;
739
740private:
741 class Impl;
742
743 std::unique_ptr<Impl> m_impl;
744};
745
746class StringView::CodePoints::Iterator {
747public:
748 Iterator(const StringView&, unsigned index);
749
750 UChar32 operator*() const;
751 Iterator& operator++();
752
753 bool operator==(const Iterator&) const;
754 bool operator!=(const Iterator&) const;
755
756private:
757 std::reference_wrapper<const StringView> m_stringView;
758 Optional<unsigned> m_nextCodePointOffset;
759 UChar32 m_codePoint;
760};
761
762class StringView::CodeUnits::Iterator {
763public:
764 Iterator(const StringView&, unsigned index);
765
766 UChar operator*() const;
767 Iterator& operator++();
768
769 bool operator==(const Iterator&) const;
770 bool operator!=(const Iterator&) const;
771
772private:
773 const StringView& m_stringView;
774 unsigned m_index;
775};
776
777inline auto StringView::graphemeClusters() const -> GraphemeClusters
778{
779 return GraphemeClusters(*this);
780}
781
782inline auto StringView::codePoints() const -> CodePoints
783{
784 return CodePoints(*this);
785}
786
787inline auto StringView::codeUnits() const -> CodeUnits
788{
789 return CodeUnits(*this);
790}
791
792inline StringView::GraphemeClusters::GraphemeClusters(const StringView& stringView)
793 : m_stringView(stringView)
794{
795}
796
797inline auto StringView::GraphemeClusters::begin() const -> Iterator
798{
799 return Iterator(m_stringView, 0);
800}
801
802inline auto StringView::GraphemeClusters::end() const -> Iterator
803{
804 return Iterator(m_stringView, m_stringView.length());
805}
806
807inline StringView::CodePoints::CodePoints(const StringView& stringView)
808 : m_stringView(stringView)
809{
810}
811
812inline StringView::CodePoints::Iterator::Iterator(const StringView& stringView, unsigned index)
813 : m_stringView(stringView)
814 , m_nextCodePointOffset(index)
815{
816 operator++();
817}
818
819inline auto StringView::CodePoints::Iterator::operator++() -> Iterator&
820{
821 ASSERT(m_nextCodePointOffset);
822 if (m_nextCodePointOffset.value() == m_stringView.get().length()) {
823 m_nextCodePointOffset = WTF::nullopt;
824 return *this;
825 }
826 if (m_stringView.get().is8Bit())
827 m_codePoint = m_stringView.get().characters8()[m_nextCodePointOffset.value()++];
828 else
829 U16_NEXT(m_stringView.get().characters16(), m_nextCodePointOffset.value(), m_stringView.get().length(), m_codePoint);
830 ASSERT(m_nextCodePointOffset.value() <= m_stringView.get().length());
831 return *this;
832}
833
834inline UChar32 StringView::CodePoints::Iterator::operator*() const
835{
836 ASSERT(m_nextCodePointOffset);
837 return m_codePoint;
838}
839
840inline bool StringView::CodePoints::Iterator::operator==(const Iterator& other) const
841{
842 ASSERT(&m_stringView.get() == &other.m_stringView.get());
843 return m_nextCodePointOffset == other.m_nextCodePointOffset;
844}
845
846inline bool StringView::CodePoints::Iterator::operator!=(const Iterator& other) const
847{
848 return !(*this == other);
849}
850
851inline auto StringView::CodePoints::begin() const -> Iterator
852{
853 return Iterator(m_stringView, 0);
854}
855
856inline auto StringView::CodePoints::end() const -> Iterator
857{
858 return Iterator(m_stringView, m_stringView.length());
859}
860
861inline StringView::CodeUnits::CodeUnits(const StringView& stringView)
862 : m_stringView(stringView)
863{
864}
865
866inline StringView::CodeUnits::Iterator::Iterator(const StringView& stringView, unsigned index)
867 : m_stringView(stringView)
868 , m_index(index)
869{
870}
871
872inline auto StringView::CodeUnits::Iterator::operator++() -> Iterator&
873{
874 ++m_index;
875 return *this;
876}
877
878inline UChar StringView::CodeUnits::Iterator::operator*() const
879{
880 return m_stringView[m_index];
881}
882
883inline bool StringView::CodeUnits::Iterator::operator==(const Iterator& other) const
884{
885 ASSERT(&m_stringView == &other.m_stringView);
886 return m_index == other.m_index;
887}
888
889inline bool StringView::CodeUnits::Iterator::operator!=(const Iterator& other) const
890{
891 return !(*this == other);
892}
893
894inline auto StringView::CodeUnits::begin() const -> Iterator
895{
896 return Iterator(m_stringView, 0);
897}
898
899inline auto StringView::CodeUnits::end() const -> Iterator
900{
901 return Iterator(m_stringView, m_stringView.length());
902}
903
904inline auto StringView::split(UChar separator) const -> SplitResult
905{
906 return SplitResult { *this, separator, false };
907}
908
909inline auto StringView::splitAllowingEmptyEntries(UChar separator) const -> SplitResult
910{
911 return SplitResult { *this, separator, true };
912}
913
914inline StringView::SplitResult::SplitResult(StringView stringView, UChar separator, bool allowEmptyEntries)
915 : m_string { stringView }
916 , m_separator { separator }
917 , m_allowEmptyEntries { allowEmptyEntries }
918{
919}
920
921inline auto StringView::SplitResult::begin() const -> Iterator
922{
923 return Iterator { *this };
924}
925
926inline auto StringView::SplitResult::end() const -> Iterator
927{
928 return Iterator { *this, Iterator::AtEnd };
929}
930
931inline StringView::SplitResult::Iterator::Iterator(const SplitResult& result)
932 : m_result { result }
933 , m_isDone { result.m_string.isEmpty() && !result.m_allowEmptyEntries }
934{
935 findNextSubstring();
936}
937
938inline StringView::SplitResult::Iterator::Iterator(const SplitResult& result, PositionTag)
939 : m_result { result }
940 , m_position { result.m_string.length() }
941 , m_isDone { true }
942{
943}
944
945inline StringView StringView::SplitResult::Iterator::operator*() const
946{
947 ASSERT(m_position <= m_result.m_string.length() && !m_isDone);
948 return m_result.m_string.substring(m_position, m_length);
949}
950
951inline bool StringView::SplitResult::Iterator::operator==(const Iterator& other) const
952{
953 ASSERT(&m_result == &other.m_result);
954 return m_position == other.m_position && m_isDone == other.m_isDone;
955}
956
957inline bool StringView::SplitResult::Iterator::operator!=(const Iterator& other) const
958{
959 return !(*this == other);
960}
961
962template<typename CharacterType, typename MatchedCharacterPredicate>
963inline StringView StringView::stripLeadingAndTrailingMatchedCharacters(const CharacterType* characters, const MatchedCharacterPredicate& predicate)
964{
965 if (!m_length)
966 return *this;
967
968 unsigned start = 0;
969 unsigned end = m_length - 1;
970
971 while (start <= end && predicate(characters[start]))
972 ++start;
973
974 if (start > end)
975 return StringView::empty();
976
977 while (end && predicate(characters[end]))
978 --end;
979
980 if (!start && end == m_length - 1)
981 return *this;
982
983 StringView result(characters + start, end + 1 - start);
984 result.setUnderlyingString(*this);
985 return result;
986}
987
988template<typename MatchedCharacterPredicate>
989StringView StringView::stripLeadingAndTrailingMatchedCharacters(const MatchedCharacterPredicate& predicate)
990{
991 if (is8Bit())
992 return stripLeadingAndTrailingMatchedCharacters<LChar>(characters8(), predicate);
993 return stripLeadingAndTrailingMatchedCharacters<UChar>(characters16(), predicate);
994}
995
996template<unsigned length> inline bool equalLettersIgnoringASCIICase(StringView string, const char (&lowercaseLetters)[length])
997{
998 return equalLettersIgnoringASCIICaseCommon(string, lowercaseLetters);
999}
1000
1001} // namespace WTF
1002
1003using WTF::append;
1004using WTF::equal;
1005using WTF::StringView;
1006using WTF::StringViewWithUnderlyingString;
1007