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