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