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 // These functions convert 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 length) const { return substring(0, length); }
121 StringView right(unsigned length) const { return substring(this->length() - length, length); }
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 = std::numeric_limits<unsigned>::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 bool contains(CodeUnitMatchFunction) const;
145 WTF_EXPORT_PRIVATE bool containsIgnoringASCIICase(const StringView&) const;
146 WTF_EXPORT_PRIVATE bool containsIgnoringASCIICase(const StringView&, unsigned startOffset) const;
147
148 WTF_EXPORT_PRIVATE bool startsWith(UChar) const;
149 WTF_EXPORT_PRIVATE bool startsWith(const StringView&) const;
150 WTF_EXPORT_PRIVATE bool startsWithIgnoringASCIICase(const StringView&) const;
151
152 WTF_EXPORT_PRIVATE bool endsWith(const StringView&) const;
153 WTF_EXPORT_PRIVATE bool endsWithIgnoringASCIICase(const StringView&) const;
154
155 int toInt() const;
156 int toInt(bool& isValid) const;
157 int toIntStrict(bool& isValid) const;
158 Optional<uint64_t> toUInt64Strict() const;
159 float toFloat(bool& isValid) const;
160
161 static void invalidate(const StringImpl&);
162
163 struct UnderlyingString;
164
165private:
166 friend bool equal(StringView, StringView);
167
168 void initialize(const LChar*, unsigned length);
169 void initialize(const UChar*, unsigned length);
170
171 template<typename CharacterType, typename MatchedCharacterPredicate>
172 StringView stripLeadingAndTrailingMatchedCharacters(const CharacterType*, const MatchedCharacterPredicate&);
173
174#if CHECK_STRINGVIEW_LIFETIME
175 WTF_EXPORT_PRIVATE bool underlyingStringIsValid() const;
176 WTF_EXPORT_PRIVATE void setUnderlyingString(const StringImpl*);
177 WTF_EXPORT_PRIVATE void setUnderlyingString(const StringView&);
178 void adoptUnderlyingString(UnderlyingString*);
179#else
180 bool underlyingStringIsValid() const { return true; }
181 void setUnderlyingString(const StringImpl*) { }
182 void setUnderlyingString(const StringView&) { }
183#endif
184 void clear();
185
186 const void* m_characters { nullptr };
187 unsigned m_length { 0 };
188 bool m_is8Bit { true };
189
190#if CHECK_STRINGVIEW_LIFETIME
191 UnderlyingString* m_underlyingString { nullptr };
192#endif
193};
194
195template<typename CharacterType, size_t inlineCapacity> void append(Vector<CharacterType, inlineCapacity>&, StringView);
196
197bool equal(StringView, StringView);
198bool equal(StringView, const LChar* b);
199
200bool equalIgnoringASCIICase(StringView, StringView);
201bool equalIgnoringASCIICase(StringView, const char*);
202
203template<unsigned length> bool equalLettersIgnoringASCIICase(StringView, const char (&lowercaseLetters)[length]);
204
205inline bool operator==(StringView a, StringView b) { return equal(a, b); }
206inline bool operator==(StringView a, const LChar *b);
207inline bool operator==(StringView a, const char *b) { return equal(a, reinterpret_cast<const LChar*>(b)); }
208inline bool operator==(const char* a, StringView b) { return equal(b, a); }
209
210inline bool operator!=(StringView a, StringView b) { return !equal(a, b); }
211inline bool operator!=(StringView a, const LChar* b) { return !equal(a, b); }
212inline bool operator!=(StringView a, const char* b) { return !equal(a, b); }
213inline bool operator!=(const LChar*a, StringView b) { return !equal(b, a); }
214inline bool operator!=(const char*a, StringView b) { return !equal(b, a); }
215
216struct StringViewWithUnderlyingString;
217
218// This returns a StringView of the normalized result, and a String that is either
219// null, if the input was already normalized, or contains the normalized result
220// and needs to be kept around so the StringView remains valid. Typically the
221// easiest way to use it correctly is to put it into a local and use the StringView.
222WTF_EXPORT_PRIVATE StringViewWithUnderlyingString normalizedNFC(StringView);
223
224WTF_EXPORT_PRIVATE String normalizedNFC(const String&);
225
226}
227
228#include <wtf/text/AtomString.h>
229#include <wtf/text/WTFString.h>
230
231namespace WTF {
232
233struct StringViewWithUnderlyingString {
234 StringView view;
235 String underlyingString;
236};
237
238inline StringView::StringView()
239{
240}
241
242#if CHECK_STRINGVIEW_LIFETIME
243
244inline StringView::~StringView()
245{
246 setUnderlyingString(nullptr);
247}
248
249inline StringView::StringView(StringView&& other)
250 : m_characters(other.m_characters)
251 , m_length(other.m_length)
252 , m_is8Bit(other.m_is8Bit)
253{
254 ASSERT(other.underlyingStringIsValid());
255
256 other.clear();
257
258 setUnderlyingString(other);
259 other.setUnderlyingString(nullptr);
260}
261
262inline StringView::StringView(const StringView& other)
263 : m_characters(other.m_characters)
264 , m_length(other.m_length)
265 , m_is8Bit(other.m_is8Bit)
266{
267 ASSERT(other.underlyingStringIsValid());
268
269 setUnderlyingString(other);
270}
271
272inline StringView& StringView::operator=(StringView&& other)
273{
274 ASSERT(other.underlyingStringIsValid());
275
276 m_characters = other.m_characters;
277 m_length = other.m_length;
278 m_is8Bit = other.m_is8Bit;
279
280 other.clear();
281
282 setUnderlyingString(other);
283 other.setUnderlyingString(nullptr);
284
285 return *this;
286}
287
288inline StringView& StringView::operator=(const StringView& other)
289{
290 ASSERT(other.underlyingStringIsValid());
291
292 m_characters = other.m_characters;
293 m_length = other.m_length;
294 m_is8Bit = other.m_is8Bit;
295
296 setUnderlyingString(other);
297
298 return *this;
299}
300
301#endif // CHECK_STRINGVIEW_LIFETIME
302
303inline void StringView::initialize(const LChar* characters, unsigned length)
304{
305 m_characters = characters;
306 m_length = length;
307 m_is8Bit = true;
308}
309
310inline void StringView::initialize(const UChar* characters, unsigned length)
311{
312 m_characters = characters;
313 m_length = length;
314 m_is8Bit = false;
315}
316
317inline StringView::StringView(const LChar* characters, unsigned length)
318{
319 initialize(characters, length);
320}
321
322inline StringView::StringView(const UChar* characters, unsigned length)
323{
324 initialize(characters, length);
325}
326
327inline StringView::StringView(const char* characters)
328{
329 initialize(reinterpret_cast<const LChar*>(characters), strlen(characters));
330}
331
332inline StringView::StringView(const StringImpl& string)
333{
334 setUnderlyingString(&string);
335 if (string.is8Bit())
336 initialize(string.characters8(), string.length());
337 else
338 initialize(string.characters16(), string.length());
339}
340
341inline StringView::StringView(const StringImpl* string)
342{
343 if (!string)
344 return;
345
346 setUnderlyingString(string);
347 if (string->is8Bit())
348 initialize(string->characters8(), string->length());
349 else
350 initialize(string->characters16(), string->length());
351}
352
353inline StringView::StringView(const String& string)
354{
355 setUnderlyingString(string.impl());
356 if (!string.impl()) {
357 clear();
358 return;
359 }
360 if (string.is8Bit()) {
361 initialize(string.characters8(), string.length());
362 return;
363 }
364 initialize(string.characters16(), string.length());
365}
366
367inline StringView::StringView(const AtomString& atomString)
368 : StringView(atomString.string())
369{
370}
371
372inline void StringView::clear()
373{
374 m_characters = nullptr;
375 m_length = 0;
376 m_is8Bit = true;
377}
378
379inline StringView StringView::empty()
380{
381 return StringView(reinterpret_cast<const LChar*>(""), 0);
382}
383
384inline const LChar* StringView::characters8() const
385{
386 ASSERT(is8Bit());
387 ASSERT(underlyingStringIsValid());
388 return static_cast<const LChar*>(m_characters);
389}
390
391inline const UChar* StringView::characters16() const
392{
393 ASSERT(!is8Bit());
394 ASSERT(underlyingStringIsValid());
395 return static_cast<const UChar*>(m_characters);
396}
397
398class StringView::UpconvertedCharacters {
399public:
400 explicit UpconvertedCharacters(const StringView&);
401 operator const UChar*() const { return m_characters; }
402 const UChar* get() const { return m_characters; }
403private:
404 Vector<UChar, 32> m_upconvertedCharacters;
405 const UChar* m_characters;
406};
407
408inline StringView::UpconvertedCharacters StringView::upconvertedCharacters() const
409{
410 return UpconvertedCharacters(*this);
411}
412
413inline bool StringView::isNull() const
414{
415 return !m_characters;
416}
417
418inline bool StringView::isEmpty() const
419{
420 return !length();
421}
422
423inline unsigned StringView::length() const
424{
425 return m_length;
426}
427
428inline StringView::operator bool() const
429{
430 return !isNull();
431}
432
433inline bool StringView::is8Bit() const
434{
435 return m_is8Bit;
436}
437
438inline StringView StringView::substring(unsigned start, unsigned length) const
439{
440 if (start >= this->length())
441 return empty();
442 unsigned maxLength = this->length() - start;
443
444 if (length >= maxLength) {
445 if (!start)
446 return *this;
447 length = maxLength;
448 }
449
450 if (is8Bit()) {
451 StringView result(characters8() + start, length);
452 result.setUnderlyingString(*this);
453 return result;
454 }
455 StringView result(characters16() + start, length);
456 result.setUnderlyingString(*this);
457 return result;
458}
459
460inline UChar StringView::operator[](unsigned index) const
461{
462 ASSERT(index < length());
463 if (is8Bit())
464 return characters8()[index];
465 return characters16()[index];
466}
467
468inline bool StringView::contains(UChar character) const
469{
470 return find(character) != notFound;
471}
472
473inline bool StringView::contains(CodeUnitMatchFunction function) const
474{
475 return find(function) != notFound;
476}
477
478inline void StringView::getCharactersWithUpconvert(LChar* destination) const
479{
480 ASSERT(is8Bit());
481 StringImpl::copyCharacters(destination, characters8(), m_length);
482}
483
484inline void StringView::getCharactersWithUpconvert(UChar* destination) const
485{
486 if (is8Bit()) {
487 StringImpl::copyCharacters(destination, characters8(), m_length);
488 return;
489 }
490 StringImpl::copyCharacters(destination, characters16(), m_length);
491}
492
493inline StringView::UpconvertedCharacters::UpconvertedCharacters(const StringView& string)
494{
495 if (!string.is8Bit()) {
496 m_characters = string.characters16();
497 return;
498 }
499 const LChar* characters8 = string.characters8();
500 unsigned length = string.m_length;
501 m_upconvertedCharacters.reserveInitialCapacity(length);
502 for (unsigned i = 0; i < length; ++i)
503 m_upconvertedCharacters.uncheckedAppend(characters8[i]);
504 m_characters = m_upconvertedCharacters.data();
505}
506
507inline String StringView::toString() const
508{
509 if (is8Bit())
510 return String(characters8(), m_length);
511 return String(characters16(), m_length);
512}
513
514inline AtomString StringView::toAtomString() const
515{
516 if (is8Bit())
517 return AtomString(characters8(), m_length);
518 return AtomString(characters16(), m_length);
519}
520
521inline RefPtr<AtomStringImpl> StringView::toExistingAtomString() const
522{
523 if (is8Bit())
524 return AtomStringImpl::lookUp(characters8(), m_length);
525 return AtomStringImpl::lookUp(characters16(), m_length);
526}
527
528inline float StringView::toFloat(bool& isValid) const
529{
530 if (is8Bit())
531 return charactersToFloat(characters8(), m_length, &isValid);
532 return charactersToFloat(characters16(), m_length, &isValid);
533}
534
535inline int StringView::toInt() const
536{
537 bool isValid;
538 return toInt(isValid);
539}
540
541inline int StringView::toInt(bool& isValid) const
542{
543 if (is8Bit())
544 return charactersToInt(characters8(), m_length, &isValid);
545 return charactersToInt(characters16(), m_length, &isValid);
546}
547
548inline int StringView::toIntStrict(bool& isValid) const
549{
550 if (is8Bit())
551 return charactersToIntStrict(characters8(), m_length, &isValid);
552 return charactersToIntStrict(characters16(), m_length, &isValid);
553}
554
555inline Optional<uint64_t> StringView::toUInt64Strict() const
556{
557 bool isValid;
558 uint64_t result = is8Bit() ? charactersToUInt64Strict(characters8(), m_length, &isValid) : charactersToUInt64Strict(characters16(), m_length, &isValid);
559 return isValid ? makeOptional(result) : WTF::nullopt;
560}
561
562inline String StringView::toStringWithoutCopying() const
563{
564 if (is8Bit())
565 return StringImpl::createWithoutCopying(characters8(), m_length);
566 return StringImpl::createWithoutCopying(characters16(), m_length);
567}
568
569inline size_t StringView::find(UChar character, unsigned start) const
570{
571 if (is8Bit())
572 return WTF::find(characters8(), m_length, character, start);
573 return WTF::find(characters16(), m_length, character, start);
574}
575
576inline size_t StringView::find(CodeUnitMatchFunction matchFunction, unsigned start) const
577{
578 if (is8Bit())
579 return WTF::find(characters8(), m_length, matchFunction, start);
580 return WTF::find(characters16(), m_length, matchFunction, start);
581}
582
583inline size_t StringView::reverseFind(UChar character, unsigned index) const
584{
585 if (is8Bit())
586 return WTF::reverseFind(characters8(), m_length, character, index);
587 return WTF::reverseFind(characters16(), m_length, character, index);
588}
589
590#if !CHECK_STRINGVIEW_LIFETIME
591
592inline void StringView::invalidate(const StringImpl&)
593{
594}
595
596#endif
597
598template<> class StringTypeAdapter<StringView, void> {
599public:
600 StringTypeAdapter(StringView string)
601 : m_string { string }
602 {
603 }
604
605 unsigned length() { return m_string.length(); }
606 bool is8Bit() { return m_string.is8Bit(); }
607 template<typename CharacterType> void writeTo(CharacterType* destination) { m_string.getCharactersWithUpconvert(destination); }
608
609private:
610 StringView m_string;
611};
612
613template<typename CharacterType, size_t inlineCapacity> void append(Vector<CharacterType, inlineCapacity>& buffer, StringView string)
614{
615 unsigned oldSize = buffer.size();
616 buffer.grow(oldSize + string.length());
617 string.getCharactersWithUpconvert(buffer.data() + oldSize);
618}
619
620inline bool equal(StringView a, StringView b)
621{
622 if (a.m_characters == b.m_characters) {
623 ASSERT(a.is8Bit() == b.is8Bit());
624 return a.length() == b.length();
625 }
626
627 return equalCommon(a, b);
628}
629
630inline bool equal(StringView a, const LChar* b)
631{
632 if (!b)
633 return !a.isEmpty();
634 if (a.isEmpty())
635 return !b;
636
637 unsigned aLength = a.length();
638 if (aLength != strlen(reinterpret_cast<const char*>(b)))
639 return false;
640
641 if (a.is8Bit())
642 return equal(a.characters8(), b, aLength);
643 return equal(a.characters16(), b, aLength);
644}
645
646inline bool equalIgnoringASCIICase(StringView a, StringView b)
647{
648 return equalIgnoringASCIICaseCommon(a, b);
649}
650
651inline bool equalIgnoringASCIICase(StringView a, const char* b)
652{
653 return equalIgnoringASCIICaseCommon(a, b);
654}
655
656class StringView::SplitResult {
657public:
658 SplitResult(StringView, UChar separator, bool allowEmptyEntries);
659
660 class Iterator;
661 Iterator begin() const;
662 Iterator end() const;
663
664private:
665 StringView m_string;
666 UChar m_separator;
667 bool m_allowEmptyEntries;
668};
669
670class StringView::GraphemeClusters {
671public:
672 explicit GraphemeClusters(const StringView&);
673
674 class Iterator;
675 Iterator begin() const;
676 Iterator end() const;
677
678private:
679 StringView m_stringView;
680};
681
682class StringView::CodePoints {
683public:
684 explicit CodePoints(const StringView&);
685
686 class Iterator;
687 Iterator begin() const;
688 Iterator end() const;
689
690private:
691 StringView m_stringView;
692};
693
694class StringView::CodeUnits {
695public:
696 explicit CodeUnits(const StringView&);
697
698 class Iterator;
699 Iterator begin() const;
700 Iterator end() const;
701
702private:
703 StringView m_stringView;
704};
705
706class StringView::SplitResult::Iterator {
707public:
708 StringView operator*() const;
709
710 WTF_EXPORT_PRIVATE Iterator& operator++();
711
712 bool operator==(const Iterator&) const;
713 bool operator!=(const Iterator&) const;
714
715private:
716 enum PositionTag { AtEnd };
717 Iterator(const SplitResult&);
718 Iterator(const SplitResult&, PositionTag);
719
720 WTF_EXPORT_PRIVATE void findNextSubstring();
721
722 friend SplitResult;
723
724 const SplitResult& m_result;
725 unsigned m_position { 0 };
726 unsigned m_length;
727 bool m_isDone;
728};
729
730class StringView::GraphemeClusters::Iterator {
731public:
732 Iterator() = delete;
733 WTF_EXPORT_PRIVATE Iterator(const StringView&, unsigned index);
734 WTF_EXPORT_PRIVATE ~Iterator();
735
736 Iterator(const Iterator&) = delete;
737 WTF_EXPORT_PRIVATE Iterator(Iterator&&);
738 Iterator& operator=(const Iterator&) = delete;
739 Iterator& operator=(Iterator&&) = delete;
740
741 WTF_EXPORT_PRIVATE StringView operator*() const;
742 WTF_EXPORT_PRIVATE Iterator& operator++();
743
744 WTF_EXPORT_PRIVATE bool operator==(const Iterator&) const;
745 WTF_EXPORT_PRIVATE bool operator!=(const Iterator&) const;
746
747private:
748 class Impl;
749
750 std::unique_ptr<Impl> m_impl;
751};
752
753class StringView::CodePoints::Iterator {
754public:
755 Iterator(const StringView&, unsigned index);
756
757 UChar32 operator*() const;
758 Iterator& operator++();
759
760 bool operator==(const Iterator&) const;
761 bool operator!=(const Iterator&) const;
762
763private:
764 std::reference_wrapper<const StringView> m_stringView;
765 Optional<unsigned> m_nextCodePointOffset;
766 UChar32 m_codePoint;
767};
768
769class StringView::CodeUnits::Iterator {
770public:
771 Iterator(const StringView&, unsigned index);
772
773 UChar operator*() const;
774 Iterator& operator++();
775
776 bool operator==(const Iterator&) const;
777 bool operator!=(const Iterator&) const;
778
779private:
780 const StringView& m_stringView;
781 unsigned m_index;
782};
783
784inline auto StringView::graphemeClusters() const -> GraphemeClusters
785{
786 return GraphemeClusters(*this);
787}
788
789inline auto StringView::codePoints() const -> CodePoints
790{
791 return CodePoints(*this);
792}
793
794inline auto StringView::codeUnits() const -> CodeUnits
795{
796 return CodeUnits(*this);
797}
798
799inline StringView::GraphemeClusters::GraphemeClusters(const StringView& stringView)
800 : m_stringView(stringView)
801{
802}
803
804inline auto StringView::GraphemeClusters::begin() const -> Iterator
805{
806 return Iterator(m_stringView, 0);
807}
808
809inline auto StringView::GraphemeClusters::end() const -> Iterator
810{
811 return Iterator(m_stringView, m_stringView.length());
812}
813
814inline StringView::CodePoints::CodePoints(const StringView& stringView)
815 : m_stringView(stringView)
816{
817}
818
819inline StringView::CodePoints::Iterator::Iterator(const StringView& stringView, unsigned index)
820 : m_stringView(stringView)
821 , m_nextCodePointOffset(index)
822{
823 operator++();
824}
825
826inline auto StringView::CodePoints::Iterator::operator++() -> Iterator&
827{
828 ASSERT(m_nextCodePointOffset);
829 if (m_nextCodePointOffset.value() == m_stringView.get().length()) {
830 m_nextCodePointOffset = WTF::nullopt;
831 return *this;
832 }
833 if (m_stringView.get().is8Bit())
834 m_codePoint = m_stringView.get().characters8()[m_nextCodePointOffset.value()++];
835 else
836 U16_NEXT(m_stringView.get().characters16(), m_nextCodePointOffset.value(), m_stringView.get().length(), m_codePoint);
837 ASSERT(m_nextCodePointOffset.value() <= m_stringView.get().length());
838 return *this;
839}
840
841inline UChar32 StringView::CodePoints::Iterator::operator*() const
842{
843 ASSERT(m_nextCodePointOffset);
844 return m_codePoint;
845}
846
847inline bool StringView::CodePoints::Iterator::operator==(const Iterator& other) const
848{
849 ASSERT(&m_stringView.get() == &other.m_stringView.get());
850 return m_nextCodePointOffset == other.m_nextCodePointOffset;
851}
852
853inline bool StringView::CodePoints::Iterator::operator!=(const Iterator& other) const
854{
855 return !(*this == other);
856}
857
858inline auto StringView::CodePoints::begin() const -> Iterator
859{
860 return Iterator(m_stringView, 0);
861}
862
863inline auto StringView::CodePoints::end() const -> Iterator
864{
865 return Iterator(m_stringView, m_stringView.length());
866}
867
868inline StringView::CodeUnits::CodeUnits(const StringView& stringView)
869 : m_stringView(stringView)
870{
871}
872
873inline StringView::CodeUnits::Iterator::Iterator(const StringView& stringView, unsigned index)
874 : m_stringView(stringView)
875 , m_index(index)
876{
877}
878
879inline auto StringView::CodeUnits::Iterator::operator++() -> Iterator&
880{
881 ++m_index;
882 return *this;
883}
884
885inline UChar StringView::CodeUnits::Iterator::operator*() const
886{
887 return m_stringView[m_index];
888}
889
890inline bool StringView::CodeUnits::Iterator::operator==(const Iterator& other) const
891{
892 ASSERT(&m_stringView == &other.m_stringView);
893 return m_index == other.m_index;
894}
895
896inline bool StringView::CodeUnits::Iterator::operator!=(const Iterator& other) const
897{
898 return !(*this == other);
899}
900
901inline auto StringView::CodeUnits::begin() const -> Iterator
902{
903 return Iterator(m_stringView, 0);
904}
905
906inline auto StringView::CodeUnits::end() const -> Iterator
907{
908 return Iterator(m_stringView, m_stringView.length());
909}
910
911inline auto StringView::split(UChar separator) const -> SplitResult
912{
913 return SplitResult { *this, separator, false };
914}
915
916inline auto StringView::splitAllowingEmptyEntries(UChar separator) const -> SplitResult
917{
918 return SplitResult { *this, separator, true };
919}
920
921inline StringView::SplitResult::SplitResult(StringView stringView, UChar separator, bool allowEmptyEntries)
922 : m_string { stringView }
923 , m_separator { separator }
924 , m_allowEmptyEntries { allowEmptyEntries }
925{
926}
927
928inline auto StringView::SplitResult::begin() const -> Iterator
929{
930 return Iterator { *this };
931}
932
933inline auto StringView::SplitResult::end() const -> Iterator
934{
935 return Iterator { *this, Iterator::AtEnd };
936}
937
938inline StringView::SplitResult::Iterator::Iterator(const SplitResult& result)
939 : m_result { result }
940 , m_isDone { result.m_string.isEmpty() && !result.m_allowEmptyEntries }
941{
942 findNextSubstring();
943}
944
945inline StringView::SplitResult::Iterator::Iterator(const SplitResult& result, PositionTag)
946 : m_result { result }
947 , m_position { result.m_string.length() }
948 , m_isDone { true }
949{
950}
951
952inline StringView StringView::SplitResult::Iterator::operator*() const
953{
954 ASSERT(m_position <= m_result.m_string.length() && !m_isDone);
955 return m_result.m_string.substring(m_position, m_length);
956}
957
958inline bool StringView::SplitResult::Iterator::operator==(const Iterator& other) const
959{
960 ASSERT(&m_result == &other.m_result);
961 return m_position == other.m_position && m_isDone == other.m_isDone;
962}
963
964inline bool StringView::SplitResult::Iterator::operator!=(const Iterator& other) const
965{
966 return !(*this == other);
967}
968
969template<typename CharacterType, typename MatchedCharacterPredicate>
970inline StringView StringView::stripLeadingAndTrailingMatchedCharacters(const CharacterType* characters, const MatchedCharacterPredicate& predicate)
971{
972 if (!m_length)
973 return *this;
974
975 unsigned start = 0;
976 unsigned end = m_length - 1;
977
978 while (start <= end && predicate(characters[start]))
979 ++start;
980
981 if (start > end)
982 return StringView::empty();
983
984 while (end && predicate(characters[end]))
985 --end;
986
987 if (!start && end == m_length - 1)
988 return *this;
989
990 StringView result(characters + start, end + 1 - start);
991 result.setUnderlyingString(*this);
992 return result;
993}
994
995template<typename MatchedCharacterPredicate>
996StringView StringView::stripLeadingAndTrailingMatchedCharacters(const MatchedCharacterPredicate& predicate)
997{
998 if (is8Bit())
999 return stripLeadingAndTrailingMatchedCharacters<LChar>(characters8(), predicate);
1000 return stripLeadingAndTrailingMatchedCharacters<UChar>(characters16(), predicate);
1001}
1002
1003template<unsigned length> inline bool equalLettersIgnoringASCIICase(StringView string, const char (&lowercaseLetters)[length])
1004{
1005 return equalLettersIgnoringASCIICaseCommon(string, lowercaseLetters);
1006}
1007
1008} // namespace WTF
1009
1010using WTF::append;
1011using WTF::equal;
1012using WTF::StringView;
1013using WTF::StringViewWithUnderlyingString;
1014