summaryrefslogtreecommitdiff
path: root/Source/WTF/wtf/text/StringCommon.h
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WTF/wtf/text/StringCommon.h
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WTF/wtf/text/StringCommon.h')
-rw-r--r--Source/WTF/wtf/text/StringCommon.h656
1 files changed, 656 insertions, 0 deletions
diff --git a/Source/WTF/wtf/text/StringCommon.h b/Source/WTF/wtf/text/StringCommon.h
new file mode 100644
index 000000000..d35d8905d
--- /dev/null
+++ b/Source/WTF/wtf/text/StringCommon.h
@@ -0,0 +1,656 @@
+/*
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef StringCommon_h
+#define StringCommon_h
+
+#include <unicode/uchar.h>
+#include <wtf/ASCIICType.h>
+
+namespace WTF {
+
+template<typename CharacterTypeA, typename CharacterTypeB> bool equalIgnoringASCIICase(const CharacterTypeA*, const CharacterTypeB*, unsigned length);
+template<typename CharacterTypeA, typename CharacterTypeB> bool equalIgnoringASCIICase(const CharacterTypeA*, unsigned lengthA, const CharacterTypeB*, unsigned lengthB);
+
+template<typename StringClassA, typename StringClassB> bool equalIgnoringASCIICaseCommon(const StringClassA&, const StringClassB&);
+
+template<typename CharacterType> bool equalLettersIgnoringASCIICase(const CharacterType*, const char* lowercaseLetters, unsigned length);
+template<typename CharacterType, unsigned lowercaseLettersLength> bool equalLettersIgnoringASCIICase(const CharacterType*, unsigned charactersLength, const char (&lowercaseLetters)[lowercaseLettersLength]);
+
+template<typename StringClass, unsigned length> bool equalLettersIgnoringASCIICaseCommon(const StringClass&, const char (&lowercaseLetters)[length]);
+
+template<typename T>
+inline T loadUnaligned(const char* s)
+{
+#if COMPILER(CLANG)
+ T tmp;
+ memcpy(&tmp, s, sizeof(T));
+ return tmp;
+#else
+ // This may result in undefined behavior due to unaligned access.
+ return *reinterpret_cast<const T*>(s);
+#endif
+}
+
+// Do comparisons 8 or 4 bytes-at-a-time on architectures where it's safe.
+#if (CPU(X86_64) || CPU(ARM64)) && !ASAN_ENABLED
+ALWAYS_INLINE bool equal(const LChar* aLChar, const LChar* bLChar, unsigned length)
+{
+ unsigned dwordLength = length >> 3;
+
+ const char* a = reinterpret_cast<const char*>(aLChar);
+ const char* b = reinterpret_cast<const char*>(bLChar);
+
+ if (dwordLength) {
+ for (unsigned i = 0; i != dwordLength; ++i) {
+ if (loadUnaligned<uint64_t>(a) != loadUnaligned<uint64_t>(b))
+ return false;
+
+ a += sizeof(uint64_t);
+ b += sizeof(uint64_t);
+ }
+ }
+
+ if (length & 4) {
+ if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
+ return false;
+
+ a += sizeof(uint32_t);
+ b += sizeof(uint32_t);
+ }
+
+ if (length & 2) {
+ if (loadUnaligned<uint16_t>(a) != loadUnaligned<uint16_t>(b))
+ return false;
+
+ a += sizeof(uint16_t);
+ b += sizeof(uint16_t);
+ }
+
+ if (length & 1 && (*reinterpret_cast<const LChar*>(a) != *reinterpret_cast<const LChar*>(b)))
+ return false;
+
+ return true;
+}
+
+ALWAYS_INLINE bool equal(const UChar* aUChar, const UChar* bUChar, unsigned length)
+{
+ unsigned dwordLength = length >> 2;
+
+ const char* a = reinterpret_cast<const char*>(aUChar);
+ const char* b = reinterpret_cast<const char*>(bUChar);
+
+ if (dwordLength) {
+ for (unsigned i = 0; i != dwordLength; ++i) {
+ if (loadUnaligned<uint64_t>(a) != loadUnaligned<uint64_t>(b))
+ return false;
+
+ a += sizeof(uint64_t);
+ b += sizeof(uint64_t);
+ }
+ }
+
+ if (length & 2) {
+ if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
+ return false;
+
+ a += sizeof(uint32_t);
+ b += sizeof(uint32_t);
+ }
+
+ if (length & 1 && (*reinterpret_cast<const UChar*>(a) != *reinterpret_cast<const UChar*>(b)))
+ return false;
+
+ return true;
+}
+#elif CPU(X86) && !ASAN_ENABLED
+ALWAYS_INLINE bool equal(const LChar* aLChar, const LChar* bLChar, unsigned length)
+{
+ const char* a = reinterpret_cast<const char*>(aLChar);
+ const char* b = reinterpret_cast<const char*>(bLChar);
+
+ unsigned wordLength = length >> 2;
+ for (unsigned i = 0; i != wordLength; ++i) {
+ if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
+ return false;
+ a += sizeof(uint32_t);
+ b += sizeof(uint32_t);
+ }
+
+ length &= 3;
+
+ if (length) {
+ const LChar* aRemainder = reinterpret_cast<const LChar*>(a);
+ const LChar* bRemainder = reinterpret_cast<const LChar*>(b);
+
+ for (unsigned i = 0; i < length; ++i) {
+ if (aRemainder[i] != bRemainder[i])
+ return false;
+ }
+ }
+
+ return true;
+}
+
+ALWAYS_INLINE bool equal(const UChar* aUChar, const UChar* bUChar, unsigned length)
+{
+ const char* a = reinterpret_cast<const char*>(aUChar);
+ const char* b = reinterpret_cast<const char*>(bUChar);
+
+ unsigned wordLength = length >> 1;
+ for (unsigned i = 0; i != wordLength; ++i) {
+ if (loadUnaligned<uint32_t>(a) != loadUnaligned<uint32_t>(b))
+ return false;
+ a += sizeof(uint32_t);
+ b += sizeof(uint32_t);
+ }
+
+ if (length & 1 && *reinterpret_cast<const UChar*>(a) != *reinterpret_cast<const UChar*>(b))
+ return false;
+
+ return true;
+}
+#elif PLATFORM(IOS) && WTF_ARM_ARCH_AT_LEAST(7) && !ASAN_ENABLED
+ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length)
+{
+ bool isEqual = false;
+ uint32_t aValue;
+ uint32_t bValue;
+ asm("subs %[length], #4\n"
+ "blo 2f\n"
+
+ "0:\n" // Label 0 = Start of loop over 32 bits.
+ "ldr %[aValue], [%[a]], #4\n"
+ "ldr %[bValue], [%[b]], #4\n"
+ "cmp %[aValue], %[bValue]\n"
+ "bne 66f\n"
+ "subs %[length], #4\n"
+ "bhs 0b\n"
+
+ // At this point, length can be:
+ // -0: 00000000000000000000000000000000 (0 bytes left)
+ // -1: 11111111111111111111111111111111 (3 bytes left)
+ // -2: 11111111111111111111111111111110 (2 bytes left)
+ // -3: 11111111111111111111111111111101 (1 byte left)
+ // -4: 11111111111111111111111111111100 (length was 0)
+ // The pointers are at the correct position.
+ "2:\n" // Label 2 = End of loop over 32 bits, check for pair of characters.
+ "tst %[length], #2\n"
+ "beq 1f\n"
+ "ldrh %[aValue], [%[a]], #2\n"
+ "ldrh %[bValue], [%[b]], #2\n"
+ "cmp %[aValue], %[bValue]\n"
+ "bne 66f\n"
+
+ "1:\n" // Label 1 = Check for a single character left.
+ "tst %[length], #1\n"
+ "beq 42f\n"
+ "ldrb %[aValue], [%[a]]\n"
+ "ldrb %[bValue], [%[b]]\n"
+ "cmp %[aValue], %[bValue]\n"
+ "bne 66f\n"
+
+ "42:\n" // Label 42 = Success.
+ "mov %[isEqual], #1\n"
+ "66:\n" // Label 66 = End without changing isEqual to 1.
+ : [length]"+r"(length), [isEqual]"+r"(isEqual), [a]"+r"(a), [b]"+r"(b), [aValue]"+r"(aValue), [bValue]"+r"(bValue)
+ :
+ :
+ );
+ return isEqual;
+}
+
+ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length)
+{
+ bool isEqual = false;
+ uint32_t aValue;
+ uint32_t bValue;
+ asm("subs %[length], #2\n"
+ "blo 1f\n"
+
+ "0:\n" // Label 0 = Start of loop over 32 bits.
+ "ldr %[aValue], [%[a]], #4\n"
+ "ldr %[bValue], [%[b]], #4\n"
+ "cmp %[aValue], %[bValue]\n"
+ "bne 66f\n"
+ "subs %[length], #2\n"
+ "bhs 0b\n"
+
+ // At this point, length can be:
+ // -0: 00000000000000000000000000000000 (0 bytes left)
+ // -1: 11111111111111111111111111111111 (1 character left, 2 bytes)
+ // -2: 11111111111111111111111111111110 (length was zero)
+ // The pointers are at the correct position.
+ "1:\n" // Label 1 = Check for a single character left.
+ "tst %[length], #1\n"
+ "beq 42f\n"
+ "ldrh %[aValue], [%[a]]\n"
+ "ldrh %[bValue], [%[b]]\n"
+ "cmp %[aValue], %[bValue]\n"
+ "bne 66f\n"
+
+ "42:\n" // Label 42 = Success.
+ "mov %[isEqual], #1\n"
+ "66:\n" // Label 66 = End without changing isEqual to 1.
+ : [length]"+r"(length), [isEqual]"+r"(isEqual), [a]"+r"(a), [b]"+r"(b), [aValue]"+r"(aValue), [bValue]"+r"(bValue)
+ :
+ :
+ );
+ return isEqual;
+}
+#elif !ASAN_ENABLED
+ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length) { return !memcmp(a, b, length); }
+ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length) { return !memcmp(a, b, length * sizeof(UChar)); }
+#else
+ALWAYS_INLINE bool equal(const LChar* a, const LChar* b, unsigned length)
+{
+ for (unsigned i = 0; i < length; ++i) {
+ if (a[i] != b[i])
+ return false;
+ }
+ return true;
+}
+ALWAYS_INLINE bool equal(const UChar* a, const UChar* b, unsigned length)
+{
+ for (unsigned i = 0; i < length; ++i) {
+ if (a[i] != b[i])
+ return false;
+ }
+ return true;
+}
+#endif
+
+ALWAYS_INLINE bool equal(const LChar* a, const UChar* b, unsigned length)
+{
+ for (unsigned i = 0; i < length; ++i) {
+ if (a[i] != b[i])
+ return false;
+ }
+ return true;
+}
+
+ALWAYS_INLINE bool equal(const UChar* a, const LChar* b, unsigned length) { return equal(b, a, length); }
+
+template<typename StringClassA, typename StringClassB>
+ALWAYS_INLINE bool equalCommon(const StringClassA& a, const StringClassB& b)
+{
+ unsigned length = a.length();
+ if (length != b.length())
+ return false;
+
+ if (a.is8Bit()) {
+ if (b.is8Bit())
+ return equal(a.characters8(), b.characters8(), length);
+
+ return equal(a.characters8(), b.characters16(), length);
+ }
+
+ if (b.is8Bit())
+ return equal(a.characters16(), b.characters8(), length);
+
+ return equal(a.characters16(), b.characters16(), length);
+}
+
+template<typename StringClassA, typename StringClassB>
+ALWAYS_INLINE bool equalCommon(const StringClassA* a, const StringClassB* b)
+{
+ if (a == b)
+ return true;
+ if (!a || !b)
+ return false;
+ return equal(*a, *b);
+}
+
+template<typename StringClass, unsigned length> bool equal(const StringClass& a, const UChar (&codeUnits)[length])
+{
+ if (a.length() != length)
+ return false;
+
+ if (a.is8Bit())
+ return equal(a.characters8(), codeUnits, length);
+
+ return equal(a.characters16(), codeUnits, length);
+}
+
+template<typename CharacterTypeA, typename CharacterTypeB>
+inline bool equalIgnoringASCIICase(const CharacterTypeA* a, const CharacterTypeB* b, unsigned length)
+{
+ for (unsigned i = 0; i < length; ++i) {
+ if (toASCIILower(a[i]) != toASCIILower(b[i]))
+ return false;
+ }
+ return true;
+}
+
+template<typename CharacterTypeA, typename CharacterTypeB> inline bool equalIgnoringASCIICase(const CharacterTypeA* a, unsigned lengthA, const CharacterTypeB* b, unsigned lengthB)
+{
+ return lengthA == lengthB && equalIgnoringASCIICase(a, b, lengthA);
+}
+
+template<typename StringClassA, typename StringClassB>
+bool equalIgnoringASCIICaseCommon(const StringClassA& a, const StringClassB& b)
+{
+ unsigned length = a.length();
+ if (length != b.length())
+ return false;
+
+ if (a.is8Bit()) {
+ if (b.is8Bit())
+ return equalIgnoringASCIICase(a.characters8(), b.characters8(), length);
+
+ return equalIgnoringASCIICase(a.characters8(), b.characters16(), length);
+ }
+
+ if (b.is8Bit())
+ return equalIgnoringASCIICase(a.characters16(), b.characters8(), length);
+
+ return equalIgnoringASCIICase(a.characters16(), b.characters16(), length);
+}
+
+template<typename StringClassA> bool equalIgnoringASCIICaseCommon(const StringClassA& a, const char* b)
+{
+ unsigned length = a.length();
+ if (length != strlen(b))
+ return false;
+
+ if (a.is8Bit())
+ return equalIgnoringASCIICase(a.characters8(), b, length);
+
+ return equalIgnoringASCIICase(a.characters16(), b, length);
+}
+
+template<typename StringClassA, typename StringClassB>
+bool startsWith(const StringClassA& reference, const StringClassB& prefix)
+{
+ unsigned prefixLength = prefix.length();
+ if (prefixLength > reference.length())
+ return false;
+
+ if (reference.is8Bit()) {
+ if (prefix.is8Bit())
+ return equal(reference.characters8(), prefix.characters8(), prefixLength);
+ return equal(reference.characters8(), prefix.characters16(), prefixLength);
+ }
+ if (prefix.is8Bit())
+ return equal(reference.characters16(), prefix.characters8(), prefixLength);
+ return equal(reference.characters16(), prefix.characters16(), prefixLength);
+}
+
+template<typename StringClassA, typename StringClassB>
+bool startsWithIgnoringASCIICase(const StringClassA& reference, const StringClassB& prefix)
+{
+ unsigned prefixLength = prefix.length();
+ if (prefixLength > reference.length())
+ return false;
+
+ if (reference.is8Bit()) {
+ if (prefix.is8Bit())
+ return equalIgnoringASCIICase(reference.characters8(), prefix.characters8(), prefixLength);
+ return equalIgnoringASCIICase(reference.characters8(), prefix.characters16(), prefixLength);
+ }
+ if (prefix.is8Bit())
+ return equalIgnoringASCIICase(reference.characters16(), prefix.characters8(), prefixLength);
+ return equalIgnoringASCIICase(reference.characters16(), prefix.characters16(), prefixLength);
+}
+
+template<typename StringClassA, typename StringClassB>
+bool endsWith(const StringClassA& reference, const StringClassB& suffix)
+{
+ unsigned suffixLength = suffix.length();
+ unsigned referenceLength = reference.length();
+ if (suffixLength > referenceLength)
+ return false;
+
+ unsigned startOffset = referenceLength - suffixLength;
+
+ if (reference.is8Bit()) {
+ if (suffix.is8Bit())
+ return equal(reference.characters8() + startOffset, suffix.characters8(), suffixLength);
+ return equal(reference.characters8() + startOffset, suffix.characters16(), suffixLength);
+ }
+ if (suffix.is8Bit())
+ return equal(reference.characters16() + startOffset, suffix.characters8(), suffixLength);
+ return equal(reference.characters16() + startOffset, suffix.characters16(), suffixLength);
+}
+
+template<typename StringClassA, typename StringClassB>
+bool endsWithIgnoringASCIICase(const StringClassA& reference, const StringClassB& suffix)
+{
+ unsigned suffixLength = suffix.length();
+ unsigned referenceLength = reference.length();
+ if (suffixLength > referenceLength)
+ return false;
+
+ unsigned startOffset = referenceLength - suffixLength;
+
+ if (reference.is8Bit()) {
+ if (suffix.is8Bit())
+ return equalIgnoringASCIICase(reference.characters8() + startOffset, suffix.characters8(), suffixLength);
+ return equalIgnoringASCIICase(reference.characters8() + startOffset, suffix.characters16(), suffixLength);
+ }
+ if (suffix.is8Bit())
+ return equalIgnoringASCIICase(reference.characters16() + startOffset, suffix.characters8(), suffixLength);
+ return equalIgnoringASCIICase(reference.characters16() + startOffset, suffix.characters16(), suffixLength);
+}
+
+template <typename SearchCharacterType, typename MatchCharacterType>
+size_t findIgnoringASCIICase(const SearchCharacterType* source, const MatchCharacterType* matchCharacters, unsigned startOffset, unsigned searchLength, unsigned matchLength)
+{
+ ASSERT(searchLength >= matchLength);
+
+ const SearchCharacterType* startSearchedCharacters = source + startOffset;
+
+ // delta is the number of additional times to test; delta == 0 means test only once.
+ unsigned delta = searchLength - matchLength;
+
+ for (unsigned i = 0; i <= delta; ++i) {
+ if (equalIgnoringASCIICase(startSearchedCharacters + i, matchCharacters, matchLength))
+ return startOffset + i;
+ }
+ return notFound;
+}
+
+template<typename StringClassA, typename StringClassB>
+size_t findIgnoringASCIICase(const StringClassA& source, const StringClassB& stringToFind, unsigned startOffset)
+{
+ unsigned sourceStringLength = source.length();
+ unsigned matchLength = stringToFind.length();
+ if (!matchLength)
+ return std::min(startOffset, sourceStringLength);
+
+ // Check startOffset & matchLength are in range.
+ if (startOffset > sourceStringLength)
+ return notFound;
+ unsigned searchLength = sourceStringLength - startOffset;
+ if (matchLength > searchLength)
+ return notFound;
+
+ if (source.is8Bit()) {
+ if (stringToFind.is8Bit())
+ return findIgnoringASCIICase(source.characters8(), stringToFind.characters8(), startOffset, searchLength, matchLength);
+ return findIgnoringASCIICase(source.characters8(), stringToFind.characters16(), startOffset, searchLength, matchLength);
+ }
+
+ if (stringToFind.is8Bit())
+ return findIgnoringASCIICase(source.characters16(), stringToFind.characters8(), startOffset, searchLength, matchLength);
+
+ return findIgnoringASCIICase(source.characters16(), stringToFind.characters16(), startOffset, searchLength, matchLength);
+}
+
+template <typename SearchCharacterType, typename MatchCharacterType>
+ALWAYS_INLINE static size_t findInner(const SearchCharacterType* searchCharacters, const MatchCharacterType* matchCharacters, unsigned index, unsigned searchLength, unsigned matchLength)
+{
+ // Optimization: keep a running hash of the strings,
+ // only call equal() if the hashes match.
+
+ // delta is the number of additional times to test; delta == 0 means test only once.
+ unsigned delta = searchLength - matchLength;
+
+ unsigned searchHash = 0;
+ unsigned matchHash = 0;
+
+ for (unsigned i = 0; i < matchLength; ++i) {
+ searchHash += searchCharacters[i];
+ matchHash += matchCharacters[i];
+ }
+
+ unsigned i = 0;
+ // keep looping until we match
+ while (searchHash != matchHash || !equal(searchCharacters + i, matchCharacters, matchLength)) {
+ if (i == delta)
+ return notFound;
+ searchHash += searchCharacters[i + matchLength];
+ searchHash -= searchCharacters[i];
+ ++i;
+ }
+ return index + i;
+}
+
+template<typename CharacterType>
+inline size_t find(const CharacterType* characters, unsigned length, CharacterType matchCharacter, unsigned index = 0)
+{
+ while (index < length) {
+ if (characters[index] == matchCharacter)
+ return index;
+ ++index;
+ }
+ return notFound;
+}
+
+ALWAYS_INLINE size_t find(const UChar* characters, unsigned length, LChar matchCharacter, unsigned index = 0)
+{
+ return find(characters, length, static_cast<UChar>(matchCharacter), index);
+}
+
+inline size_t find(const LChar* characters, unsigned length, UChar matchCharacter, unsigned index = 0)
+{
+ if (matchCharacter & ~0xFF)
+ return notFound;
+ return find(characters, length, static_cast<LChar>(matchCharacter), index);
+}
+
+template<typename StringClass>
+size_t findCommon(const StringClass& haystack, const StringClass& needle, unsigned start)
+{
+ unsigned needleLength = needle.length();
+
+ if (needleLength == 1) {
+ if (haystack.is8Bit())
+ return WTF::find(haystack.characters8(), haystack.length(), needle[0], start);
+ return WTF::find(haystack.characters16(), haystack.length(), needle[0], start);
+ }
+
+ if (!needleLength)
+ return std::min(start, haystack.length());
+
+ if (start > haystack.length())
+ return notFound;
+ unsigned searchLength = haystack.length() - start;
+ if (needleLength > searchLength)
+ return notFound;
+
+ if (haystack.is8Bit()) {
+ if (needle.is8Bit())
+ return findInner(haystack.characters8() + start, needle.characters8(), start, searchLength, needleLength);
+ return findInner(haystack.characters8() + start, needle.characters16(), start, searchLength, needleLength);
+ }
+
+ if (needle.is8Bit())
+ return findInner(haystack.characters16() + start, needle.characters8(), start, searchLength, needleLength);
+
+ return findInner(haystack.characters16() + start, needle.characters16(), start, searchLength, needleLength);
+}
+
+// This is marked inline since it's mostly used in non-inline functions for each string type.
+// When used directly in code it's probably OK to be inline; maybe the loop will be unrolled.
+template<typename CharacterType> inline bool equalLettersIgnoringASCIICase(const CharacterType* characters, const char* lowercaseLetters, unsigned length)
+{
+ for (unsigned i = 0; i < length; ++i) {
+ if (!isASCIIAlphaCaselessEqual(characters[i], lowercaseLetters[i]))
+ return false;
+ }
+ return true;
+}
+
+template<typename CharacterType, unsigned lowercaseLettersLength> inline bool equalLettersIgnoringASCIICase(const CharacterType* characters, unsigned charactersLength, const char (&lowercaseLetters)[lowercaseLettersLength])
+{
+ ASSERT(strlen(lowercaseLetters) == lowercaseLettersLength - 1);
+ unsigned lowercaseLettersStringLength = lowercaseLettersLength - 1;
+ return charactersLength == lowercaseLettersStringLength && equalLettersIgnoringASCIICase(characters, lowercaseLetters, lowercaseLettersStringLength);
+}
+
+template<typename StringClass> bool inline hasPrefixWithLettersIgnoringASCIICaseCommon(const StringClass& string, const char* lowercaseLetters, unsigned length)
+{
+#if !ASSERT_DISABLED
+ ASSERT(*lowercaseLetters);
+ for (const char* letter = lowercaseLetters; *letter; ++letter)
+ ASSERT(toASCIILowerUnchecked(*letter) == *letter);
+#endif
+ ASSERT(string.length() >= length);
+
+ if (string.is8Bit())
+ return equalLettersIgnoringASCIICase(string.characters8(), lowercaseLetters, length);
+ return equalLettersIgnoringASCIICase(string.characters16(), lowercaseLetters, length);
+}
+
+// This is intentionally not marked inline because it's used often and is not speed-critical enough to want it inlined everywhere.
+template<typename StringClass> bool equalLettersIgnoringASCIICaseCommonWithoutLength(const StringClass& string, const char* lowercaseLetters)
+{
+ unsigned length = string.length();
+ if (length != strlen(lowercaseLetters))
+ return false;
+ return hasPrefixWithLettersIgnoringASCIICaseCommon(string, lowercaseLetters, length);
+}
+
+template<typename StringClass> bool startsWithLettersIgnoringASCIICaseCommonWithoutLength(const StringClass& string, const char* lowercaseLetters)
+{
+ size_t prefixLength = strlen(lowercaseLetters);
+ if (!prefixLength)
+ return true;
+ if (string.length() < prefixLength)
+ return false;
+ return hasPrefixWithLettersIgnoringASCIICaseCommon(string, lowercaseLetters, prefixLength);
+}
+
+template<typename StringClass, unsigned length> inline bool equalLettersIgnoringASCIICaseCommon(const StringClass& string, const char (&lowercaseLetters)[length])
+{
+ // Don't actually use the length; we are choosing code size over speed.
+ ASSERT(strlen(lowercaseLetters) == length - 1);
+ const char* pointer = lowercaseLetters;
+ return equalLettersIgnoringASCIICaseCommonWithoutLength(string, pointer);
+}
+
+template<typename StringClass, unsigned length> inline bool startsWithLettersIgnoringASCIICaseCommon(const StringClass& string, const char (&lowercaseLetters)[length])
+{
+ const char* pointer = lowercaseLetters;
+ return startsWithLettersIgnoringASCIICaseCommonWithoutLength(string, pointer);
+}
+
+}
+
+using WTF::equalIgnoringASCIICase;
+using WTF::equalLettersIgnoringASCIICase;
+
+#endif // StringCommon_h