1/*
2 * Copyright (C) 2015-2017 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 <algorithm>
29#include <unicode/uchar.h>
30#include <wtf/ASCIICType.h>
31#include <wtf/NotFound.h>
32#include <wtf/UnalignedAccess.h>
33
34namespace WTF {
35
36using CodeUnitMatchFunction = bool (*)(UChar);
37
38template<typename CharacterTypeA, typename CharacterTypeB> bool equalIgnoringASCIICase(const CharacterTypeA*, const CharacterTypeB*, unsigned length);
39template<typename CharacterTypeA, typename CharacterTypeB> bool equalIgnoringASCIICase(const CharacterTypeA*, unsigned lengthA, const CharacterTypeB*, unsigned lengthB);
40
41template<typename StringClassA, typename StringClassB> bool equalIgnoringASCIICaseCommon(const StringClassA&, const StringClassB&);
42
43template<typename CharacterType> bool equalLettersIgnoringASCIICase(const CharacterType*, const char* lowercaseLetters, unsigned length);
44template<typename CharacterType, unsigned lowercaseLettersLength> bool equalLettersIgnoringASCIICase(const CharacterType*, unsigned charactersLength, const char (&lowercaseLetters)[lowercaseLettersLength]);
45
46template<typename StringClass, unsigned length> bool equalLettersIgnoringASCIICaseCommon(const StringClass&, const char (&lowercaseLetters)[length]);
47
48bool equalIgnoringASCIICase(const char*, const char*);
49template<unsigned lowercaseLettersLength> bool equalLettersIgnoringASCIICase(const char*, const char (&lowercaseLetters)[lowercaseLettersLength]);
50
51// Do comparisons 8 or 4 bytes-at-a-time on architectures where it's safe.
52#if (CPU(X86_64) || CPU(ARM64)) && !ASAN_ENABLED
53ALWAYS_INLINE bool equal(const LChar* aLChar, const LChar* bLChar, unsigned length)
54{
55 unsigned dwordLength = length >> 3;
56
57 const char* a = reinterpret_cast<const char*>(aLChar);
58 const char* b = reinterpret_cast<const char*>(bLChar);
59
60 if (dwordLength) {
61 for (unsigned i = 0; i != dwordLength; ++i) {
62 if (unalignedLoad<uint64_t>(a) != unalignedLoad<uint64_t>(b))
63 return false;
64
65 a += sizeof(uint64_t);
66 b += sizeof(uint64_t);
67 }
68 }
69
70 if (length & 4) {
71 if (unalignedLoad<uint32_t>(a) != unalignedLoad<uint32_t>(b))
72 return false;
73
74 a += sizeof(uint32_t);
75 b += sizeof(uint32_t);
76 }
77
78 if (length & 2) {
79 if (unalignedLoad<uint16_t>(a) != unalignedLoad<uint16_t>(b))
80 return false;
81
82 a += sizeof(uint16_t);
83 b += sizeof(uint16_t);
84 }
85
86 if (length & 1 && (*reinterpret_cast<const LChar*>(a) != *reinterpret_cast<const LChar*>(b)))
87 return false;
88
89 return true;
90}
91
92ALWAYS_INLINE bool equal(const UChar* aUChar, const UChar* bUChar, unsigned length)
93{
94 unsigned dwordLength = length >> 2;
95
96 const char* a = reinterpret_cast<const char*>(aUChar);
97 const char* b = reinterpret_cast<const char*>(bUChar);
98
99 if (dwordLength) {
100 for (unsigned i = 0; i != dwordLength; ++i) {
101 if (unalignedLoad<uint64_t>(a) != unalignedLoad<uint64_t>(b))
102 return false;
103
104 a += sizeof(uint64_t);
105 b += sizeof(uint64_t);
106 }
107 }
108
109 if (length & 2) {
110 if (unalignedLoad<uint32_t>(a) != unalignedLoad<uint32_t>(b))
111 return false;
112
113 a += sizeof(uint32_t);
114 b += sizeof(uint32_t);
115 }
116
117 if (length & 1 && (*reinterpret_cast<const UChar*>(a) != *reinterpret_cast<const UChar*>(b)))
118 return false;
119
120 return true;
121}
122#elif CPU(X86) && !ASAN_ENABLED
123ALWAYS_INLINE bool equal(const LChar* aLChar, const LChar* bLChar, unsigned length)
124{
125 const char* a = reinterpret_cast<const char*>(aLChar);
126 const char* b = reinterpret_cast<const char*>(bLChar);
127
128 unsigned wordLength = length >> 2;
129 for (unsigned i = 0; i != wordLength; ++i) {
130 if (unalignedLoad<uint32_t>(a) != unalignedLoad<uint32_t>(b))
131 return false;
132 a += sizeof(uint32_t);
133 b += sizeof(uint32_t);
134 }
135
136 length &= 3;
137
138 if (length) {
139 const LChar* aRemainder = reinterpret_cast<const LChar*>(a);
140 const LChar* bRemainder = reinterpret_cast<const LChar*>(b);
141
142 for (unsigned i = 0; i < length; ++i) {
143 if (aRemainder[i] != bRemainder[i])
144 return false;
145 }
146 }
147
148 return true;
149}
150
151ALWAYS_INLINE bool equal(const UChar* aUChar, const UChar* bUChar, unsigned length)
152{
153 const char* a = reinterpret_cast<const char*>(aUChar);
154 const char* b = reinterpret_cast<const char*>(bUChar);
155
156 unsigned wordLength = length >> 1;
157 for (unsigned i = 0; i != wordLength; ++i) {
158 if (unalignedLoad<uint32_t>(a) != unalignedLoad<uint32_t>(b))
159 return false;
160 a += sizeof(uint32_t);
161 b += sizeof(uint32_t);
162 }
163
164 if (length & 1 && *reinterpret_cast<const UChar*>(a) != *reinterpret_cast<const UChar*>(b))
165 return false;
166
167 return true;
168}
169#elif PLATFORM(IOS_FAMILY) && WTF_ARM_ARCH_AT_LEAST(7) && !ASAN_ENABLED
170ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length)
171{
172 bool isEqual = false;
173 uint32_t aValue;
174 uint32_t bValue;
175 asm("subs %[length], #4\n"
176 "blo 2f\n"
177
178 "0:\n" // Label 0 = Start of loop over 32 bits.
179 "ldr %[aValue], [%[a]], #4\n"
180 "ldr %[bValue], [%[b]], #4\n"
181 "cmp %[aValue], %[bValue]\n"
182 "bne 66f\n"
183 "subs %[length], #4\n"
184 "bhs 0b\n"
185
186 // At this point, length can be:
187 // -0: 00000000000000000000000000000000 (0 bytes left)
188 // -1: 11111111111111111111111111111111 (3 bytes left)
189 // -2: 11111111111111111111111111111110 (2 bytes left)
190 // -3: 11111111111111111111111111111101 (1 byte left)
191 // -4: 11111111111111111111111111111100 (length was 0)
192 // The pointers are at the correct position.
193 "2:\n" // Label 2 = End of loop over 32 bits, check for pair of characters.
194 "tst %[length], #2\n"
195 "beq 1f\n"
196 "ldrh %[aValue], [%[a]], #2\n"
197 "ldrh %[bValue], [%[b]], #2\n"
198 "cmp %[aValue], %[bValue]\n"
199 "bne 66f\n"
200
201 "1:\n" // Label 1 = Check for a single character left.
202 "tst %[length], #1\n"
203 "beq 42f\n"
204 "ldrb %[aValue], [%[a]]\n"
205 "ldrb %[bValue], [%[b]]\n"
206 "cmp %[aValue], %[bValue]\n"
207 "bne 66f\n"
208
209 "42:\n" // Label 42 = Success.
210 "mov %[isEqual], #1\n"
211 "66:\n" // Label 66 = End without changing isEqual to 1.
212 : [length]"+r"(length), [isEqual]"+r"(isEqual), [a]"+r"(a), [b]"+r"(b), [aValue]"+r"(aValue), [bValue]"+r"(bValue)
213 :
214 :
215 );
216 return isEqual;
217}
218
219ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length)
220{
221 bool isEqual = false;
222 uint32_t aValue;
223 uint32_t bValue;
224 asm("subs %[length], #2\n"
225 "blo 1f\n"
226
227 "0:\n" // Label 0 = Start of loop over 32 bits.
228 "ldr %[aValue], [%[a]], #4\n"
229 "ldr %[bValue], [%[b]], #4\n"
230 "cmp %[aValue], %[bValue]\n"
231 "bne 66f\n"
232 "subs %[length], #2\n"
233 "bhs 0b\n"
234
235 // At this point, length can be:
236 // -0: 00000000000000000000000000000000 (0 bytes left)
237 // -1: 11111111111111111111111111111111 (1 character left, 2 bytes)
238 // -2: 11111111111111111111111111111110 (length was zero)
239 // The pointers are at the correct position.
240 "1:\n" // Label 1 = Check for a single character left.
241 "tst %[length], #1\n"
242 "beq 42f\n"
243 "ldrh %[aValue], [%[a]]\n"
244 "ldrh %[bValue], [%[b]]\n"
245 "cmp %[aValue], %[bValue]\n"
246 "bne 66f\n"
247
248 "42:\n" // Label 42 = Success.
249 "mov %[isEqual], #1\n"
250 "66:\n" // Label 66 = End without changing isEqual to 1.
251 : [length]"+r"(length), [isEqual]"+r"(isEqual), [a]"+r"(a), [b]"+r"(b), [aValue]"+r"(aValue), [bValue]"+r"(bValue)
252 :
253 :
254 );
255 return isEqual;
256}
257#elif !ASAN_ENABLED
258ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length) { return !memcmp(a, b, length); }
259ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length) { return !memcmp(a, b, length * sizeof(UChar)); }
260#else
261ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length)
262{
263 for (unsigned i = 0; i < length; ++i) {
264 if (a[i] != b[i])
265 return false;
266 }
267 return true;
268}
269ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length)
270{
271 for (unsigned i = 0; i < length; ++i) {
272 if (a[i] != b[i])
273 return false;
274 }
275 return true;
276}
277#endif
278
279ALWAYS_INLINE bool equal(const LChar* a, const UChar* b, unsigned length)
280{
281 for (unsigned i = 0; i < length; ++i) {
282 if (a[i] != b[i])
283 return false;
284 }
285 return true;
286}
287
288ALWAYS_INLINE bool equal(const UChar* a, const LChar* b, unsigned length) { return equal(b, a, length); }
289
290template<typename StringClassA, typename StringClassB>
291ALWAYS_INLINE bool equalCommon(const StringClassA& a, const StringClassB& b)
292{
293 unsigned length = a.length();
294 if (length != b.length())
295 return false;
296
297 if (a.is8Bit()) {
298 if (b.is8Bit())
299 return equal(a.characters8(), b.characters8(), length);
300
301 return equal(a.characters8(), b.characters16(), length);
302 }
303
304 if (b.is8Bit())
305 return equal(a.characters16(), b.characters8(), length);
306
307 return equal(a.characters16(), b.characters16(), length);
308}
309
310template<typename StringClassA, typename StringClassB>
311ALWAYS_INLINE bool equalCommon(const StringClassA* a, const StringClassB* b)
312{
313 if (a == b)
314 return true;
315 if (!a || !b)
316 return false;
317 return equal(*a, *b);
318}
319
320template<typename StringClass, unsigned length> bool equal(const StringClass& a, const UChar (&codeUnits)[length])
321{
322 if (a.length() != length)
323 return false;
324
325 if (a.is8Bit())
326 return equal(a.characters8(), codeUnits, length);
327
328 return equal(a.characters16(), codeUnits, length);
329}
330
331template<typename CharacterTypeA, typename CharacterTypeB>
332inline bool equalIgnoringASCIICase(const CharacterTypeA* a, const CharacterTypeB* b, unsigned length)
333{
334 for (unsigned i = 0; i < length; ++i) {
335 if (toASCIILower(a[i]) != toASCIILower(b[i]))
336 return false;
337 }
338 return true;
339}
340
341template<typename CharacterTypeA, typename CharacterTypeB> inline bool equalIgnoringASCIICase(const CharacterTypeA* a, unsigned lengthA, const CharacterTypeB* b, unsigned lengthB)
342{
343 return lengthA == lengthB && equalIgnoringASCIICase(a, b, lengthA);
344}
345
346template<typename StringClassA, typename StringClassB>
347bool equalIgnoringASCIICaseCommon(const StringClassA& a, const StringClassB& b)
348{
349 unsigned length = a.length();
350 if (length != b.length())
351 return false;
352
353 if (a.is8Bit()) {
354 if (b.is8Bit())
355 return equalIgnoringASCIICase(a.characters8(), b.characters8(), length);
356
357 return equalIgnoringASCIICase(a.characters8(), b.characters16(), length);
358 }
359
360 if (b.is8Bit())
361 return equalIgnoringASCIICase(a.characters16(), b.characters8(), length);
362
363 return equalIgnoringASCIICase(a.characters16(), b.characters16(), length);
364}
365
366template<typename StringClassA> bool equalIgnoringASCIICaseCommon(const StringClassA& a, const char* b)
367{
368 unsigned length = a.length();
369 if (length != strlen(b))
370 return false;
371
372 if (a.is8Bit())
373 return equalIgnoringASCIICase(a.characters8(), b, length);
374
375 return equalIgnoringASCIICase(a.characters16(), b, length);
376}
377
378template<typename StringClassA, typename StringClassB>
379bool startsWith(const StringClassA& reference, const StringClassB& prefix)
380{
381 unsigned prefixLength = prefix.length();
382 if (prefixLength > reference.length())
383 return false;
384
385 if (reference.is8Bit()) {
386 if (prefix.is8Bit())
387 return equal(reference.characters8(), prefix.characters8(), prefixLength);
388 return equal(reference.characters8(), prefix.characters16(), prefixLength);
389 }
390 if (prefix.is8Bit())
391 return equal(reference.characters16(), prefix.characters8(), prefixLength);
392 return equal(reference.characters16(), prefix.characters16(), prefixLength);
393}
394
395template<typename StringClassA, typename StringClassB>
396bool startsWithIgnoringASCIICase(const StringClassA& reference, const StringClassB& prefix)
397{
398 unsigned prefixLength = prefix.length();
399 if (prefixLength > reference.length())
400 return false;
401
402 if (reference.is8Bit()) {
403 if (prefix.is8Bit())
404 return equalIgnoringASCIICase(reference.characters8(), prefix.characters8(), prefixLength);
405 return equalIgnoringASCIICase(reference.characters8(), prefix.characters16(), prefixLength);
406 }
407 if (prefix.is8Bit())
408 return equalIgnoringASCIICase(reference.characters16(), prefix.characters8(), prefixLength);
409 return equalIgnoringASCIICase(reference.characters16(), prefix.characters16(), prefixLength);
410}
411
412template<typename StringClassA, typename StringClassB>
413bool endsWith(const StringClassA& reference, const StringClassB& suffix)
414{
415 unsigned suffixLength = suffix.length();
416 unsigned referenceLength = reference.length();
417 if (suffixLength > referenceLength)
418 return false;
419
420 unsigned startOffset = referenceLength - suffixLength;
421
422 if (reference.is8Bit()) {
423 if (suffix.is8Bit())
424 return equal(reference.characters8() + startOffset, suffix.characters8(), suffixLength);
425 return equal(reference.characters8() + startOffset, suffix.characters16(), suffixLength);
426 }
427 if (suffix.is8Bit())
428 return equal(reference.characters16() + startOffset, suffix.characters8(), suffixLength);
429 return equal(reference.characters16() + startOffset, suffix.characters16(), suffixLength);
430}
431
432template<typename StringClassA, typename StringClassB>
433bool endsWithIgnoringASCIICase(const StringClassA& reference, const StringClassB& suffix)
434{
435 unsigned suffixLength = suffix.length();
436 unsigned referenceLength = reference.length();
437 if (suffixLength > referenceLength)
438 return false;
439
440 unsigned startOffset = referenceLength - suffixLength;
441
442 if (reference.is8Bit()) {
443 if (suffix.is8Bit())
444 return equalIgnoringASCIICase(reference.characters8() + startOffset, suffix.characters8(), suffixLength);
445 return equalIgnoringASCIICase(reference.characters8() + startOffset, suffix.characters16(), suffixLength);
446 }
447 if (suffix.is8Bit())
448 return equalIgnoringASCIICase(reference.characters16() + startOffset, suffix.characters8(), suffixLength);
449 return equalIgnoringASCIICase(reference.characters16() + startOffset, suffix.characters16(), suffixLength);
450}
451
452template <typename SearchCharacterType, typename MatchCharacterType>
453size_t findIgnoringASCIICase(const SearchCharacterType* source, const MatchCharacterType* matchCharacters, unsigned startOffset, unsigned searchLength, unsigned matchLength)
454{
455 ASSERT(searchLength >= matchLength);
456
457 const SearchCharacterType* startSearchedCharacters = source + startOffset;
458
459 // delta is the number of additional times to test; delta == 0 means test only once.
460 unsigned delta = searchLength - matchLength;
461
462 for (unsigned i = 0; i <= delta; ++i) {
463 if (equalIgnoringASCIICase(startSearchedCharacters + i, matchCharacters, matchLength))
464 return startOffset + i;
465 }
466 return notFound;
467}
468
469template<typename StringClassA, typename StringClassB>
470size_t findIgnoringASCIICase(const StringClassA& source, const StringClassB& stringToFind, unsigned startOffset)
471{
472 unsigned sourceStringLength = source.length();
473 unsigned matchLength = stringToFind.length();
474 if (!matchLength)
475 return std::min(startOffset, sourceStringLength);
476
477 // Check startOffset & matchLength are in range.
478 if (startOffset > sourceStringLength)
479 return notFound;
480 unsigned searchLength = sourceStringLength - startOffset;
481 if (matchLength > searchLength)
482 return notFound;
483
484 if (source.is8Bit()) {
485 if (stringToFind.is8Bit())
486 return findIgnoringASCIICase(source.characters8(), stringToFind.characters8(), startOffset, searchLength, matchLength);
487 return findIgnoringASCIICase(source.characters8(), stringToFind.characters16(), startOffset, searchLength, matchLength);
488 }
489
490 if (stringToFind.is8Bit())
491 return findIgnoringASCIICase(source.characters16(), stringToFind.characters8(), startOffset, searchLength, matchLength);
492
493 return findIgnoringASCIICase(source.characters16(), stringToFind.characters16(), startOffset, searchLength, matchLength);
494}
495
496template <typename SearchCharacterType, typename MatchCharacterType>
497ALWAYS_INLINE static size_t findInner(const SearchCharacterType* searchCharacters, const MatchCharacterType* matchCharacters, unsigned index, unsigned searchLength, unsigned matchLength)
498{
499 // Optimization: keep a running hash of the strings,
500 // only call equal() if the hashes match.
501
502 // delta is the number of additional times to test; delta == 0 means test only once.
503 unsigned delta = searchLength - matchLength;
504
505 unsigned searchHash = 0;
506 unsigned matchHash = 0;
507
508 for (unsigned i = 0; i < matchLength; ++i) {
509 searchHash += searchCharacters[i];
510 matchHash += matchCharacters[i];
511 }
512
513 unsigned i = 0;
514 // keep looping until we match
515 while (searchHash != matchHash || !equal(searchCharacters + i, matchCharacters, matchLength)) {
516 if (i == delta)
517 return notFound;
518 searchHash += searchCharacters[i + matchLength];
519 searchHash -= searchCharacters[i];
520 ++i;
521 }
522 return index + i;
523}
524
525template<typename CharacterType>
526inline size_t find(const CharacterType* characters, unsigned length, CharacterType matchCharacter, unsigned index = 0)
527{
528 while (index < length) {
529 if (characters[index] == matchCharacter)
530 return index;
531 ++index;
532 }
533 return notFound;
534}
535
536ALWAYS_INLINE size_t find(const UChar* characters, unsigned length, LChar matchCharacter, unsigned index = 0)
537{
538 return find(characters, length, static_cast<UChar>(matchCharacter), index);
539}
540
541inline size_t find(const LChar* characters, unsigned length, UChar matchCharacter, unsigned index = 0)
542{
543 if (matchCharacter & ~0xFF)
544 return notFound;
545 return find(characters, length, static_cast<LChar>(matchCharacter), index);
546}
547
548template<typename StringClass>
549size_t findCommon(const StringClass& haystack, const StringClass& needle, unsigned start)
550{
551 unsigned needleLength = needle.length();
552
553 if (needleLength == 1) {
554 if (haystack.is8Bit())
555 return WTF::find(haystack.characters8(), haystack.length(), needle[0], start);
556 return WTF::find(haystack.characters16(), haystack.length(), needle[0], start);
557 }
558
559 if (!needleLength)
560 return std::min(start, haystack.length());
561
562 if (start > haystack.length())
563 return notFound;
564 unsigned searchLength = haystack.length() - start;
565 if (needleLength > searchLength)
566 return notFound;
567
568 if (haystack.is8Bit()) {
569 if (needle.is8Bit())
570 return findInner(haystack.characters8() + start, needle.characters8(), start, searchLength, needleLength);
571 return findInner(haystack.characters8() + start, needle.characters16(), start, searchLength, needleLength);
572 }
573
574 if (needle.is8Bit())
575 return findInner(haystack.characters16() + start, needle.characters8(), start, searchLength, needleLength);
576
577 return findInner(haystack.characters16() + start, needle.characters16(), start, searchLength, needleLength);
578}
579
580// This is marked inline since it's mostly used in non-inline functions for each string type.
581// When used directly in code it's probably OK to be inline; maybe the loop will be unrolled.
582template<typename CharacterType> inline bool equalLettersIgnoringASCIICase(const CharacterType* characters, const char* lowercaseLetters, unsigned length)
583{
584 for (unsigned i = 0; i < length; ++i) {
585 if (!isASCIIAlphaCaselessEqual(characters[i], lowercaseLetters[i]))
586 return false;
587 }
588 return true;
589}
590
591template<typename CharacterType, unsigned lowercaseLettersLength> inline bool equalLettersIgnoringASCIICase(const CharacterType* characters, unsigned charactersLength, const char (&lowercaseLetters)[lowercaseLettersLength])
592{
593 ASSERT(strlen(lowercaseLetters) == lowercaseLettersLength - 1);
594 unsigned lowercaseLettersStringLength = lowercaseLettersLength - 1;
595 return charactersLength == lowercaseLettersStringLength && equalLettersIgnoringASCIICase(characters, lowercaseLetters, lowercaseLettersStringLength);
596}
597
598template<typename StringClass> bool inline hasPrefixWithLettersIgnoringASCIICaseCommon(const StringClass& string, const char* lowercaseLetters, unsigned length)
599{
600#if !ASSERT_DISABLED
601 ASSERT(*lowercaseLetters);
602 for (const char* letter = lowercaseLetters; *letter; ++letter)
603 ASSERT(toASCIILowerUnchecked(*letter) == *letter);
604#endif
605 ASSERT(string.length() >= length);
606
607 if (string.is8Bit())
608 return equalLettersIgnoringASCIICase(string.characters8(), lowercaseLetters, length);
609 return equalLettersIgnoringASCIICase(string.characters16(), lowercaseLetters, length);
610}
611
612// This is intentionally not marked inline because it's used often and is not speed-critical enough to want it inlined everywhere.
613template<typename StringClass> bool equalLettersIgnoringASCIICaseCommonWithoutLength(const StringClass& string, const char* lowercaseLetters)
614{
615 unsigned length = string.length();
616 if (length != strlen(lowercaseLetters))
617 return false;
618 return hasPrefixWithLettersIgnoringASCIICaseCommon(string, lowercaseLetters, length);
619}
620
621template<typename StringClass> bool startsWithLettersIgnoringASCIICaseCommonWithoutLength(const StringClass& string, const char* lowercaseLetters)
622{
623 size_t prefixLength = strlen(lowercaseLetters);
624 if (!prefixLength)
625 return true;
626 if (string.length() < prefixLength)
627 return false;
628 return hasPrefixWithLettersIgnoringASCIICaseCommon(string, lowercaseLetters, prefixLength);
629}
630
631template<typename StringClass, unsigned length> inline bool equalLettersIgnoringASCIICaseCommon(const StringClass& string, const char (&lowercaseLetters)[length])
632{
633 // Don't actually use the length; we are choosing code size over speed.
634 ASSERT(strlen(lowercaseLetters) == length - 1);
635 const char* pointer = lowercaseLetters;
636 return equalLettersIgnoringASCIICaseCommonWithoutLength(string, pointer);
637}
638
639template<typename StringClass, unsigned length> inline bool startsWithLettersIgnoringASCIICaseCommon(const StringClass& string, const char (&lowercaseLetters)[length])
640{
641 const char* pointer = lowercaseLetters;
642 return startsWithLettersIgnoringASCIICaseCommonWithoutLength(string, pointer);
643}
644
645inline bool equalIgnoringASCIICase(const char* a, const char* b)
646{
647 auto length = strlen(a);
648 return length == strlen(b) && equalIgnoringASCIICase(a, b, length);
649}
650
651template<unsigned lowercaseLettersLength> inline bool equalLettersIgnoringASCIICase(const char* string, const char (&lowercaseLetters)[lowercaseLettersLength])
652{
653 auto length = strlen(lowercaseLetters);
654 return strlen(string) == length && equalLettersIgnoringASCIICase(string, lowercaseLetters, length);
655}
656
657}
658
659using WTF::equalIgnoringASCIICase;
660using WTF::equalLettersIgnoringASCIICase;
661