diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WTF/wtf/text/StringImpl.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WTF/wtf/text/StringImpl.cpp')
-rw-r--r-- | Source/WTF/wtf/text/StringImpl.cpp | 942 |
1 files changed, 473 insertions, 469 deletions
diff --git a/Source/WTF/wtf/text/StringImpl.cpp b/Source/WTF/wtf/text/StringImpl.cpp index 34794258c..ee66daf25 100644 --- a/Source/WTF/wtf/text/StringImpl.cpp +++ b/Source/WTF/wtf/text/StringImpl.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller ( mueller@kde.org ) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2003-2009, 2013-2016 Apple Inc. All rights reserved. * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) * * This library is free software; you can redistribute it and/or @@ -30,12 +30,14 @@ #include "StringHash.h" #include <wtf/ProcessID.h> #include <wtf/StdLibExtras.h> -#include <wtf/WTFThreadData.h> #include <wtf/text/CString.h> +#include <wtf/text/StringView.h> +#include <wtf/text/SymbolImpl.h> +#include <wtf/text/SymbolRegistry.h> #include <wtf/unicode/CharacterNames.h> #include <wtf/unicode/UTF8.h> -#ifdef STRING_STATS +#if STRING_STATS #include <unistd.h> #include <wtf/DataLog.h> #endif @@ -44,27 +46,21 @@ namespace WTF { using namespace Unicode; -COMPILE_ASSERT(sizeof(StringImpl) == 2 * sizeof(int) + 3 * sizeof(void*), StringImpl_should_stay_small); +static_assert(sizeof(StringImpl) == 2 * sizeof(int) + 2 * sizeof(void*), "StringImpl should stay small"); -#ifdef STRING_STATS +#if STRING_STATS StringStats StringImpl::m_stringStats; -unsigned StringStats::s_stringRemovesTillPrintStats = StringStats::s_printStringStatsFrequency; +std::atomic<unsigned> StringStats::s_stringRemovesTillPrintStats(s_printStringStatsFrequency); -void StringStats::removeString(StringImpl* string) +void StringStats::removeString(StringImpl& string) { - unsigned length = string->length(); - bool isSubString = string->isSubString(); + unsigned length = string.length(); + bool isSubString = string.isSubString(); --m_totalNumberStrings; - if (string->has16BitShadow()) { - --m_numberUpconvertedStrings; - if (!isSubString) - m_totalUpconvertedData -= length; - } - - if (string->is8Bit()) { + if (string.is8Bit()) { --m_number8BitStrings; if (!isSubString) m_total8BitData -= length; @@ -87,46 +83,46 @@ void StringStats::printStats() unsigned long long totalNumberCharacters = m_total8BitData + m_total16BitData; double percent8Bit = m_totalNumberStrings ? ((double)m_number8BitStrings * 100) / (double)m_totalNumberStrings : 0.0; double average8bitLength = m_number8BitStrings ? (double)m_total8BitData / (double)m_number8BitStrings : 0.0; - dataLogF("%8u (%5.2f%%) 8 bit %12llu chars %12llu bytes avg length %6.1f\n", m_number8BitStrings, percent8Bit, m_total8BitData, m_total8BitData, average8bitLength); + dataLogF("%8u (%5.2f%%) 8 bit %12llu chars %12llu bytes avg length %6.1f\n", m_number8BitStrings.load(), percent8Bit, m_total8BitData.load(), m_total8BitData.load(), average8bitLength); double percent16Bit = m_totalNumberStrings ? ((double)m_number16BitStrings * 100) / (double)m_totalNumberStrings : 0.0; double average16bitLength = m_number16BitStrings ? (double)m_total16BitData / (double)m_number16BitStrings : 0.0; - dataLogF("%8u (%5.2f%%) 16 bit %12llu chars %12llu bytes avg length %6.1f\n", m_number16BitStrings, percent16Bit, m_total16BitData, m_total16BitData * 2, average16bitLength); - - double percentUpconverted = m_totalNumberStrings ? ((double)m_numberUpconvertedStrings * 100) / (double)m_number8BitStrings : 0.0; - double averageUpconvertedLength = m_numberUpconvertedStrings ? (double)m_totalUpconvertedData / (double)m_numberUpconvertedStrings : 0.0; - dataLogF("%8u (%5.2f%%) upconverted %12llu chars %12llu bytes avg length %6.1f\n", m_numberUpconvertedStrings, percentUpconverted, m_totalUpconvertedData, m_totalUpconvertedData * 2, averageUpconvertedLength); + dataLogF("%8u (%5.2f%%) 16 bit %12llu chars %12llu bytes avg length %6.1f\n", m_number16BitStrings.load(), percent16Bit, m_total16BitData.load(), m_total16BitData * 2, average16bitLength); double averageLength = m_totalNumberStrings ? (double)totalNumberCharacters / (double)m_totalNumberStrings : 0.0; - unsigned long long totalDataBytes = m_total8BitData + (m_total16BitData + m_totalUpconvertedData) * 2; - dataLogF("%8u Total %12llu chars %12llu bytes avg length %6.1f\n", m_totalNumberStrings, totalNumberCharacters, totalDataBytes, averageLength); - unsigned long long totalSavedBytes = m_total8BitData - m_totalUpconvertedData; + unsigned long long totalDataBytes = m_total8BitData + m_total16BitData * 2; + dataLogF("%8u Total %12llu chars %12llu bytes avg length %6.1f\n", m_totalNumberStrings.load(), totalNumberCharacters, totalDataBytes, averageLength); + unsigned long long totalSavedBytes = m_total8BitData; double percentSavings = totalSavedBytes ? ((double)totalSavedBytes * 100) / (double)(totalDataBytes + totalSavedBytes) : 0.0; dataLogF(" Total savings %12llu bytes (%5.2f%%)\n", totalSavedBytes, percentSavings); + + dataLogF("%8u StringImpl::ref calls\n", m_refCalls.load()); + dataLogF("%8u StringImpl::deref calls\n", m_derefCalls.load()); } #endif +StringImpl::StaticStringImpl StringImpl::s_atomicEmptyString("", StringImpl::StringAtomic); StringImpl::~StringImpl() { ASSERT(!isStatic()); - STRING_STATS_REMOVE_STRING(this); + StringView::invalidate(*this); - if (isAtomic()) - AtomicString::remove(this); - if (isIdentifier()) { - if (!wtfThreadData().currentIdentifierTable()->remove(this)) - CRASH(); - } + STRING_STATS_REMOVE_STRING(*this); - BufferOwnership ownership = bufferOwnership(); + if (isAtomic() && length() && !isSymbol()) + AtomicStringImpl::remove(static_cast<AtomicStringImpl*>(this)); - if (has16BitShadow()) { - ASSERT(m_copyData16); - fastFree(m_copyData16); + if (isSymbol()) { + auto& symbol = static_cast<SymbolImpl&>(*this); + auto* symbolRegistry = symbol.symbolRegistry(); + if (symbolRegistry) + symbolRegistry->remove(symbol); } + BufferOwnership ownership = bufferOwnership(); + if (ownership == BufferInternal) return; if (ownership == BufferOwned) { @@ -137,8 +133,8 @@ StringImpl::~StringImpl() } ASSERT(ownership == BufferSubstring); - ASSERT(m_substringBuffer); - m_substringBuffer->deref(); + ASSERT(substringBuffer()); + substringBuffer()->deref(); } void StringImpl::destroy(StringImpl* stringImpl) @@ -147,19 +143,19 @@ void StringImpl::destroy(StringImpl* stringImpl) fastFree(stringImpl); } -PassRef<StringImpl> StringImpl::createFromLiteral(const char* characters, unsigned length) +Ref<StringImpl> StringImpl::createFromLiteral(const char* characters, unsigned length) { ASSERT_WITH_MESSAGE(length, "Use StringImpl::empty() to create an empty string"); ASSERT(charactersAreAllASCII<LChar>(reinterpret_cast<const LChar*>(characters), length)); return adoptRef(*new StringImpl(reinterpret_cast<const LChar*>(characters), length, ConstructWithoutCopying)); } -PassRef<StringImpl> StringImpl::createFromLiteral(const char* characters) +Ref<StringImpl> StringImpl::createFromLiteral(const char* characters) { return createFromLiteral(characters, strlen(characters)); } -PassRef<StringImpl> StringImpl::createWithoutCopying(const UChar* characters, unsigned length) +Ref<StringImpl> StringImpl::createWithoutCopying(const UChar* characters, unsigned length) { if (!length) return *empty(); @@ -167,7 +163,7 @@ PassRef<StringImpl> StringImpl::createWithoutCopying(const UChar* characters, un return adoptRef(*new StringImpl(characters, length, ConstructWithoutCopying)); } -PassRef<StringImpl> StringImpl::createWithoutCopying(const LChar* characters, unsigned length) +Ref<StringImpl> StringImpl::createWithoutCopying(const LChar* characters, unsigned length) { if (!length) return *empty(); @@ -176,7 +172,7 @@ PassRef<StringImpl> StringImpl::createWithoutCopying(const LChar* characters, un } template <typename CharType> -inline PassRef<StringImpl> StringImpl::createUninitializedInternal(unsigned length, CharType*& data) +inline Ref<StringImpl> StringImpl::createUninitializedInternal(unsigned length, CharType*& data) { if (!length) { data = 0; @@ -186,7 +182,7 @@ inline PassRef<StringImpl> StringImpl::createUninitializedInternal(unsigned leng } template <typename CharType> -inline PassRef<StringImpl> StringImpl::createUninitializedInternalNonEmpty(unsigned length, CharType*& data) +inline Ref<StringImpl> StringImpl::createUninitializedInternalNonEmpty(unsigned length, CharType*& data) { ASSERT(length); @@ -195,26 +191,25 @@ inline PassRef<StringImpl> StringImpl::createUninitializedInternalNonEmpty(unsig // heap allocation from this call. if (length > ((std::numeric_limits<unsigned>::max() - sizeof(StringImpl)) / sizeof(CharType))) CRASH(); - size_t size = sizeof(StringImpl) + length * sizeof(CharType); - StringImpl* string = static_cast<StringImpl*>(fastMalloc(size)); + StringImpl* string = static_cast<StringImpl*>(fastMalloc(allocationSize<CharType>(length))); - data = reinterpret_cast<CharType*>(string + 1); + data = string->tailPointer<CharType>(); return constructInternal<CharType>(string, length); } -PassRef<StringImpl> StringImpl::createUninitialized(unsigned length, LChar*& data) +Ref<StringImpl> StringImpl::createUninitialized(unsigned length, LChar*& data) { return createUninitializedInternal(length, data); } -PassRef<StringImpl> StringImpl::createUninitialized(unsigned length, UChar*& data) +Ref<StringImpl> StringImpl::createUninitialized(unsigned length, UChar*& data) { return createUninitializedInternal(length, data); } template <typename CharType> -inline PassRef<StringImpl> StringImpl::reallocateInternal(PassRefPtr<StringImpl> originalString, unsigned length, CharType*& data) -{ +inline Ref<StringImpl> StringImpl::reallocateInternal(Ref<StringImpl>&& originalString, unsigned length, CharType*& data) +{ ASSERT(originalString->hasOneRef()); ASSERT(originalString->bufferOwnership() == BufferInternal); @@ -226,28 +221,28 @@ inline PassRef<StringImpl> StringImpl::reallocateInternal(PassRefPtr<StringImpl> // Same as createUninitialized() except here we use fastRealloc. if (length > ((std::numeric_limits<unsigned>::max() - sizeof(StringImpl)) / sizeof(CharType))) CRASH(); - size_t size = sizeof(StringImpl) + length * sizeof(CharType); + originalString->~StringImpl(); - StringImpl* string = static_cast<StringImpl*>(fastRealloc(originalString.leakRef(), size)); + auto* string = static_cast<StringImpl*>(fastRealloc(&originalString.leakRef(), allocationSize<CharType>(length))); - data = reinterpret_cast<CharType*>(string + 1); + data = string->tailPointer<CharType>(); return constructInternal<CharType>(string, length); } -PassRef<StringImpl> StringImpl::reallocate(PassRefPtr<StringImpl> originalString, unsigned length, LChar*& data) +Ref<StringImpl> StringImpl::reallocate(Ref<StringImpl>&& originalString, unsigned length, LChar*& data) { ASSERT(originalString->is8Bit()); - return reallocateInternal(originalString, length, data); + return reallocateInternal(WTFMove(originalString), length, data); } -PassRef<StringImpl> StringImpl::reallocate(PassRefPtr<StringImpl> originalString, unsigned length, UChar*& data) +Ref<StringImpl> StringImpl::reallocate(Ref<StringImpl>&& originalString, unsigned length, UChar*& data) { ASSERT(!originalString->is8Bit()); - return reallocateInternal(originalString, length, data); + return reallocateInternal(WTFMove(originalString), length, data); } template <typename CharType> -inline PassRef<StringImpl> StringImpl::createInternal(const CharType* characters, unsigned length) +inline Ref<StringImpl> StringImpl::createInternal(const CharType* characters, unsigned length) { if (!characters || !length) return *empty(); @@ -258,23 +253,23 @@ inline PassRef<StringImpl> StringImpl::createInternal(const CharType* characters return string; } -PassRef<StringImpl> StringImpl::create(const UChar* characters, unsigned length) +Ref<StringImpl> StringImpl::create(const UChar* characters, unsigned length) { return createInternal(characters, length); } -PassRef<StringImpl> StringImpl::create(const LChar* characters, unsigned length) +Ref<StringImpl> StringImpl::create(const LChar* characters, unsigned length) { return createInternal(characters, length); } -PassRef<StringImpl> StringImpl::create8BitIfPossible(const UChar* characters, unsigned length) +Ref<StringImpl> StringImpl::create8BitIfPossible(const UChar* characters, unsigned length) { if (!characters || !length) return *empty(); LChar* data; - RefPtr<StringImpl> string = createUninitializedInternalNonEmpty(length, data); + auto string = createUninitializedInternalNonEmpty(length, data); for (size_t i = 0; i < length; ++i) { if (characters[i] & 0xff00) @@ -282,15 +277,15 @@ PassRef<StringImpl> StringImpl::create8BitIfPossible(const UChar* characters, un data[i] = static_cast<LChar>(characters[i]); } - return string.releaseNonNull(); + return string; } -PassRef<StringImpl> StringImpl::create8BitIfPossible(const UChar* string) +Ref<StringImpl> StringImpl::create8BitIfPossible(const UChar* string) { return StringImpl::create8BitIfPossible(string, lengthOfNullTerminatedString(string)); } -PassRef<StringImpl> StringImpl::create(const LChar* string) +Ref<StringImpl> StringImpl::create(const LChar* string) { if (!string) return *empty(); @@ -300,41 +295,6 @@ PassRef<StringImpl> StringImpl::create(const LChar* string) return create(string, length); } -const UChar* StringImpl::getData16SlowCase() const -{ - if (has16BitShadow()) - return m_copyData16; - - if (bufferOwnership() == BufferSubstring) { - // If this is a substring, return a pointer into the parent string. - // TODO: Consider severing this string from the parent string - unsigned offset = m_data8 - m_substringBuffer->characters8(); - return m_substringBuffer->deprecatedCharacters() + offset; - } - - STRING_STATS_ADD_UPCONVERTED_STRING(m_length); - - unsigned len = length(); - - m_copyData16 = static_cast<UChar*>(fastMalloc(len * sizeof(UChar))); - - m_hashAndFlags |= s_hashFlagHas16BitShadow; - - upconvertCharacters(0, len); - - return m_copyData16; -} - -void StringImpl::upconvertCharacters(unsigned start, unsigned end) const -{ - ASSERT(is8Bit()); - ASSERT(has16BitShadow()); - - for (size_t i = start; i < end; ++i) - m_copyData16[i] = m_data8[i]; -} - - bool StringImpl::containsOnlyWhitespace() { // FIXME: The definition of whitespace here includes a number of characters @@ -358,7 +318,7 @@ bool StringImpl::containsOnlyWhitespace() return true; } -PassRef<StringImpl> StringImpl::substring(unsigned start, unsigned length) +Ref<StringImpl> StringImpl::substring(unsigned start, unsigned length) { if (start >= m_length) return *empty(); @@ -385,42 +345,23 @@ UChar32 StringImpl::characterStartingAt(unsigned i) return 0; } -PassRef<StringImpl> StringImpl::lower() +Ref<StringImpl> StringImpl::convertToLowercaseWithoutLocale() { - // Note: This is a hot function in the Dromaeo benchmark, specifically the - // no-op code path up through the first 'return' statement. + // Note: At one time this was a hot function in the Dromaeo benchmark, specifically the + // no-op code path that may return ourself if we find no upper case letters and no invalid + // ASCII letters. // First scan the string for uppercase and non-ASCII characters: if (is8Bit()) { - unsigned failingIndex; for (unsigned i = 0; i < m_length; ++i) { LChar character = m_data8[i]; - if (UNLIKELY((character & ~0x7F) || isASCIIUpper(character))) { - failingIndex = i; - goto SlowPath8bitLower; - } - } - return *this; - -SlowPath8bitLower: - LChar* data8; - auto newImpl = createUninitializedInternalNonEmpty(m_length, data8); - - for (unsigned i = 0; i < failingIndex; ++i) - data8[i] = m_data8[i]; - - for (unsigned i = failingIndex; i < m_length; ++i) { - LChar character = m_data8[i]; - if (!(character & ~0x7F)) - data8[i] = toASCIILower(character); - else { - ASSERT(u_tolower(character) <= 0xFF); - data8[i] = static_cast<LChar>(u_tolower(character)); - } + if (UNLIKELY((character & ~0x7F) || isASCIIUpper(character))) + return convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit(i); } - return newImpl; + return *this; } + bool noUpper = true; unsigned ored = 0; @@ -451,26 +392,51 @@ SlowPath8bitLower: // Do a slower implementation for cases that include non-ASCII characters. UChar* data16; - RefPtr<StringImpl> newImpl = createUninitializedInternalNonEmpty(m_length, data16); + auto newImpl = createUninitializedInternalNonEmpty(m_length, data16); UErrorCode status = U_ZERO_ERROR; int32_t realLength = u_strToLower(data16, length, m_data16, m_length, "", &status); if (U_SUCCESS(status) && realLength == length) - return newImpl.releaseNonNull(); + return newImpl; newImpl = createUninitialized(realLength, data16); status = U_ZERO_ERROR; u_strToLower(data16, realLength, m_data16, m_length, "", &status); if (U_FAILURE(status)) return *this; - return newImpl.releaseNonNull(); + return newImpl; +} + +Ref<StringImpl> StringImpl::convertToLowercaseWithoutLocaleStartingAtFailingIndex8Bit(unsigned failingIndex) +{ + ASSERT(is8Bit()); + LChar* data8; + auto newImpl = createUninitializedInternalNonEmpty(m_length, data8); + + for (unsigned i = 0; i < failingIndex; ++i) { + ASSERT(!(m_data8[i] & ~0x7F) && !isASCIIUpper(m_data8[i])); + data8[i] = m_data8[i]; + } + + for (unsigned i = failingIndex; i < m_length; ++i) { + LChar character = m_data8[i]; + if (!(character & ~0x7F)) + data8[i] = toASCIILower(character); + else { + ASSERT(u_tolower(character) <= 0xFF); + data8[i] = static_cast<LChar>(u_tolower(character)); + } + } + + return newImpl; } -PassRef<StringImpl> StringImpl::upper() +Ref<StringImpl> StringImpl::convertToUppercaseWithoutLocale() { - // This function could be optimized for no-op cases the way lower() is, - // but in empirical testing, few actual calls to upper() are no-ops, so - // it wouldn't be worth the extra time for pre-scanning. + // This function could be optimized for no-op cases the way + // convertToLowercaseWithoutLocale() is, but in empirical testing, + // few actual calls to upper() are no-ops, so it wouldn't be worth + // the extra time for pre-scanning. if (m_length > static_cast<unsigned>(std::numeric_limits<int32_t>::max())) CRASH(); @@ -478,30 +444,23 @@ PassRef<StringImpl> StringImpl::upper() if (is8Bit()) { LChar* data8; - RefPtr<StringImpl> newImpl = createUninitialized(m_length, data8); + auto newImpl = createUninitialized(m_length, data8); // Do a faster loop for the case where all the characters are ASCII. unsigned ored = 0; for (int i = 0; i < length; ++i) { LChar c = m_data8[i]; ored |= c; -#if CPU(X86) && defined(_MSC_VER) && _MSC_VER >=1700 - // Workaround for an MSVC 2012 x86 optimizer bug. Remove once the bug is fixed. - // See https://connect.microsoft.com/VisualStudio/feedback/details/780362/optimization-bug-of-range-comparison - // for more details. - data8[i] = c >= 'a' && c <= 'z' ? c & ~0x20 : c; -#else data8[i] = toASCIIUpper(c); -#endif } if (!(ored & ~0x7F)) - return newImpl.releaseNonNull(); + return newImpl; // Do a slower implementation for cases that include non-ASCII Latin-1 characters. int numberSharpSCharacters = 0; // There are two special cases. - // 1. latin-1 characters when converted to upper case are 16 bit characters. + // 1. Some Latin-1 characters when converted to upper case are 16 bit characters. // 2. Lower case sharp-S converts to "SS" (two characters) for (int32_t i = 0; i < length; ++i) { LChar c = m_data8[i]; @@ -509,7 +468,7 @@ PassRef<StringImpl> StringImpl::upper() ++numberSharpSCharacters; ASSERT(u_toupper(c) <= 0xFFFF); UChar upper = u_toupper(c); - if (UNLIKELY(upper > 0xff)) { + if (UNLIKELY(upper > 0xFF)) { // Since this upper-cased character does not fit in an 8-bit string, we need to take the 16-bit path. goto upconvert; } @@ -517,7 +476,7 @@ PassRef<StringImpl> StringImpl::upper() } if (!numberSharpSCharacters) - return newImpl.releaseNonNull(); + return newImpl; // We have numberSSCharacters sharp-s characters, but none of the other special characters. newImpl = createUninitialized(m_length + numberSharpSCharacters, data8); @@ -535,14 +494,15 @@ PassRef<StringImpl> StringImpl::upper() } } - return newImpl.releaseNonNull(); + return newImpl; } upconvert: - const UChar* source16 = deprecatedCharacters(); + auto upconvertedCharacters = StringView(*this).upconvertedCharacters(); + const UChar* source16 = upconvertedCharacters; UChar* data16; - RefPtr<StringImpl> newImpl = createUninitialized(m_length, data16); + auto newImpl = createUninitialized(m_length, data16); // Do a faster loop for the case where all the characters are ASCII. unsigned ored = 0; @@ -552,19 +512,19 @@ upconvert: data16[i] = toASCIIUpper(c); } if (!(ored & ~0x7F)) - return newImpl.releaseNonNull(); + return newImpl; // Do a slower implementation for cases that include non-ASCII characters. UErrorCode status = U_ZERO_ERROR; int32_t realLength = u_strToUpper(data16, length, source16, m_length, "", &status); if (U_SUCCESS(status) && realLength == length) - return newImpl.releaseNonNull(); + return newImpl; newImpl = createUninitialized(realLength, data16); status = U_ZERO_ERROR; u_strToUpper(data16, realLength, source16, m_length, "", &status); if (U_FAILURE(status)) return *this; - return newImpl.releaseNonNull(); + return newImpl; } static inline bool needsTurkishCasingRules(const AtomicString& localeIdentifier) @@ -577,14 +537,14 @@ static inline bool needsTurkishCasingRules(const AtomicString& localeIdentifier) && (localeIdentifier.length() == 2 || localeIdentifier[2] == '-'); } -PassRef<StringImpl> StringImpl::lower(const AtomicString& localeIdentifier) +Ref<StringImpl> StringImpl::convertToLowercaseWithLocale(const AtomicString& localeIdentifier) { // Use the more-optimized code path most of the time. // Assuming here that the only locale-specific lowercasing is the Turkish casing rules. // FIXME: Could possibly optimize further by looking for the specific sequences // that have locale-specific lowercasing. There are only three of them. if (!needsTurkishCasingRules(localeIdentifier)) - return lower(); + return convertToLowercaseWithoutLocale(); // FIXME: Could share more code with the main StringImpl::lower by factoring out // this last part into a shared function that takes a locale string, since this is @@ -597,28 +557,29 @@ PassRef<StringImpl> StringImpl::lower(const AtomicString& localeIdentifier) // Below, we pass in the hardcoded locale "tr". Passing that is more efficient than // allocating memory just to turn localeIdentifier into a C string, and we assume // there is no difference between the uppercasing for "tr" and "az" locales. - const UChar* source16 = deprecatedCharacters(); + auto upconvertedCharacters = StringView(*this).upconvertedCharacters(); + const UChar* source16 = upconvertedCharacters; UChar* data16; - RefPtr<StringImpl> newString = createUninitialized(length, data16); + auto newString = createUninitialized(length, data16); UErrorCode status = U_ZERO_ERROR; int realLength = u_strToLower(data16, length, source16, length, "tr", &status); if (U_SUCCESS(status) && realLength == length) - return newString.releaseNonNull(); + return newString; newString = createUninitialized(realLength, data16); status = U_ZERO_ERROR; u_strToLower(data16, realLength, source16, length, "tr", &status); if (U_FAILURE(status)) return *this; - return newString.releaseNonNull(); + return newString; } -PassRef<StringImpl> StringImpl::upper(const AtomicString& localeIdentifier) +Ref<StringImpl> StringImpl::convertToUppercaseWithLocale(const AtomicString& localeIdentifier) { // Use the more-optimized code path most of the time. // Assuming here that the only locale-specific lowercasing is the Turkish casing rules, // and that the only affected character is lowercase "i". if (!needsTurkishCasingRules(localeIdentifier) || find('i') == notFound) - return upper(); + return convertToUppercaseWithoutLocale(); if (m_length > static_cast<unsigned>(std::numeric_limits<int32_t>::max())) CRASH(); @@ -627,95 +588,145 @@ PassRef<StringImpl> StringImpl::upper(const AtomicString& localeIdentifier) // Below, we pass in the hardcoded locale "tr". Passing that is more efficient than // allocating memory just to turn localeIdentifier into a C string, and we assume // there is no difference between the uppercasing for "tr" and "az" locales. - const UChar* source16 = deprecatedCharacters(); + auto upconvertedCharacters = StringView(*this).upconvertedCharacters(); + const UChar* source16 = upconvertedCharacters; UChar* data16; - RefPtr<StringImpl> newString = createUninitialized(length, data16); + auto newString = createUninitialized(length, data16); UErrorCode status = U_ZERO_ERROR; int realLength = u_strToUpper(data16, length, source16, length, "tr", &status); if (U_SUCCESS(status) && realLength == length) - return newString.releaseNonNull(); + return newString; newString = createUninitialized(realLength, data16); status = U_ZERO_ERROR; u_strToUpper(data16, realLength, source16, length, "tr", &status); if (U_FAILURE(status)) return *this; - return newString.releaseNonNull(); -} - -PassRef<StringImpl> StringImpl::fill(UChar character) -{ - if (!(character & ~0x7F)) { - LChar* data; - auto newImpl = createUninitialized(m_length, data); - for (unsigned i = 0; i < m_length; ++i) - data[i] = character; - return newImpl; - } - UChar* data; - auto newImpl = createUninitialized(m_length, data); - for (unsigned i = 0; i < m_length; ++i) - data[i] = character; - return newImpl; + return newString; } -PassRef<StringImpl> StringImpl::foldCase() +Ref<StringImpl> StringImpl::foldCase() { - if (m_length > static_cast<unsigned>(std::numeric_limits<int32_t>::max())) - CRASH(); - int32_t length = m_length; - if (is8Bit()) { - // Do a faster loop for the case where all the characters are ASCII. - LChar* data; - auto newImpl = createUninitialized(m_length, data); - LChar ored = 0; - - for (int32_t i = 0; i < length; ++i) { - LChar c = m_data8[i]; - data[i] = toASCIILower(c); - ored |= c; + unsigned failingIndex; + for (unsigned i = 0; i < m_length; ++i) { + auto character = m_data8[i]; + if (UNLIKELY(!isASCII(character) || isASCIIUpper(character))) { + failingIndex = i; + goto SlowPath; + } } + // String was all ASCII and no uppercase, so just return as-is. + return *this; - if (!(ored & ~0x7F)) - return newImpl; - - // Do a slower implementation for cases that include non-ASCII Latin-1 characters. - // FIXME: Shouldn't this use u_foldCase instead of u_tolower? - for (int32_t i = 0; i < length; ++i) { - ASSERT(u_tolower(m_data8[i]) <= 0xFF); - data[i] = static_cast<LChar>(u_tolower(m_data8[i])); +SlowPath: + bool need16BitCharacters = false; + for (unsigned i = failingIndex; i < m_length; ++i) { + auto character = m_data8[i]; + if (character == 0xB5 || character == 0xDF) { + need16BitCharacters = true; + break; + } } - return newImpl; + if (!need16BitCharacters) { + LChar* data8; + auto folded = createUninitializedInternalNonEmpty(m_length, data8); + for (unsigned i = 0; i < failingIndex; ++i) + data8[i] = m_data8[i]; + for (unsigned i = failingIndex; i < m_length; ++i) { + auto character = m_data8[i]; + if (isASCII(character)) + data8[i] = toASCIILower(character); + else { + ASSERT(u_foldCase(character, U_FOLD_CASE_DEFAULT) <= 0xFF); + data8[i] = static_cast<LChar>(u_foldCase(character, U_FOLD_CASE_DEFAULT)); + } + } + return folded; + } + } else { + // FIXME: Unclear why we use goto in the 8-bit case, and a different approach in the 16-bit case. + bool noUpper = true; + unsigned ored = 0; + for (unsigned i = 0; i < m_length; ++i) { + UChar character = m_data16[i]; + if (UNLIKELY(isASCIIUpper(character))) + noUpper = false; + ored |= character; + } + if (!(ored & ~0x7F)) { + if (noUpper) { + // String was all ASCII and no uppercase, so just return as-is. + return *this; + } + UChar* data16; + auto folded = createUninitializedInternalNonEmpty(m_length, data16); + for (unsigned i = 0; i < m_length; ++i) + data16[i] = toASCIILower(m_data16[i]); + return folded; + } } - // Do a faster loop for the case where all the characters are ASCII. - UChar* data; - RefPtr<StringImpl> newImpl = createUninitialized(m_length, data); - UChar ored = 0; - for (int32_t i = 0; i < length; ++i) { - UChar c = m_data16[i]; - ored |= c; - data[i] = toASCIILower(c); - } - if (!(ored & ~0x7F)) - return newImpl.releaseNonNull(); + if (m_length > static_cast<unsigned>(std::numeric_limits<int32_t>::max())) + CRASH(); - // Do a slower implementation for cases that include non-ASCII characters. + auto upconvertedCharacters = StringView(*this).upconvertedCharacters(); + + UChar* data; + auto folded = createUninitializedInternalNonEmpty(m_length, data); + int32_t length = m_length; UErrorCode status = U_ZERO_ERROR; - int32_t realLength = u_strFoldCase(data, length, m_data16, m_length, U_FOLD_CASE_DEFAULT, &status); + int32_t realLength = u_strFoldCase(data, length, upconvertedCharacters, length, U_FOLD_CASE_DEFAULT, &status); if (U_SUCCESS(status) && realLength == length) - return newImpl.releaseNonNull(); - newImpl = createUninitialized(realLength, data); + return folded; + ASSERT(realLength > length); + folded = createUninitializedInternalNonEmpty(realLength, data); status = U_ZERO_ERROR; - u_strFoldCase(data, realLength, m_data16, m_length, U_FOLD_CASE_DEFAULT, &status); + u_strFoldCase(data, realLength, upconvertedCharacters, length, U_FOLD_CASE_DEFAULT, &status); if (U_FAILURE(status)) return *this; - return newImpl.releaseNonNull(); + return folded; +} + +template<StringImpl::CaseConvertType type, typename CharacterType> +ALWAYS_INLINE Ref<StringImpl> StringImpl::convertASCIICase(StringImpl& impl, const CharacterType* data, unsigned length) +{ + unsigned failingIndex; + for (unsigned i = 0; i < length; ++i) { + CharacterType character = data[i]; + if (type == CaseConvertType::Lower ? UNLIKELY(isASCIIUpper(character)) : LIKELY(isASCIILower(character))) { + failingIndex = i; + goto SlowPath; + } + } + return impl; + +SlowPath: + CharacterType* newData; + auto newImpl = createUninitializedInternalNonEmpty(length, newData); + for (unsigned i = 0; i < failingIndex; ++i) + newData[i] = data[i]; + for (unsigned i = failingIndex; i < length; ++i) + newData[i] = type == CaseConvertType::Lower ? toASCIILower(data[i]) : toASCIIUpper(data[i]); + return newImpl; +} + +Ref<StringImpl> StringImpl::convertToASCIILowercase() +{ + if (is8Bit()) + return convertASCIICase<CaseConvertType::Lower>(*this, m_data8, m_length); + return convertASCIICase<CaseConvertType::Lower>(*this, m_data16, m_length); +} + +Ref<StringImpl> StringImpl::convertToASCIIUppercase() +{ + if (is8Bit()) + return convertASCIICase<CaseConvertType::Upper>(*this, m_data8, m_length); + return convertASCIICase<CaseConvertType::Upper>(*this, m_data16, m_length); } template <class UCharPredicate> -inline PassRef<StringImpl> StringImpl::stripMatchedCharacters(UCharPredicate predicate) +inline Ref<StringImpl> StringImpl::stripMatchedCharacters(UCharPredicate predicate) { if (!m_length) return *this; @@ -763,18 +774,18 @@ public: } }; -PassRef<StringImpl> StringImpl::stripWhiteSpace() +Ref<StringImpl> StringImpl::stripWhiteSpace() { return stripMatchedCharacters(SpaceOrNewlinePredicate()); } -PassRef<StringImpl> StringImpl::stripWhiteSpace(IsWhiteSpaceFunctionPtr isWhiteSpace) +Ref<StringImpl> StringImpl::stripWhiteSpace(IsWhiteSpaceFunctionPtr isWhiteSpace) { return stripMatchedCharacters(UCharPredicate(isWhiteSpace)); } template <typename CharType> -ALWAYS_INLINE PassRef<StringImpl> StringImpl::removeCharacters(const CharType* characters, CharacterMatchFunctionPtr findMatch) +ALWAYS_INLINE Ref<StringImpl> StringImpl::removeCharacters(const CharType* characters, CharacterMatchFunctionPtr findMatch) { const CharType* from = characters; const CharType* fromend = from + m_length; @@ -803,10 +814,10 @@ ALWAYS_INLINE PassRef<StringImpl> StringImpl::removeCharacters(const CharType* c data.shrink(outc); - return adopt(data); + return adopt(WTFMove(data)); } -PassRef<StringImpl> StringImpl::removeCharacters(CharacterMatchFunctionPtr findMatch) +Ref<StringImpl> StringImpl::removeCharacters(CharacterMatchFunctionPtr findMatch) { if (is8Bit()) return removeCharacters(characters8(), findMatch); @@ -814,11 +825,11 @@ PassRef<StringImpl> StringImpl::removeCharacters(CharacterMatchFunctionPtr findM } template <typename CharType, class UCharPredicate> -inline PassRef<StringImpl> StringImpl::simplifyMatchedCharactersToSpace(UCharPredicate predicate) +inline Ref<StringImpl> StringImpl::simplifyMatchedCharactersToSpace(UCharPredicate predicate) { StringBuffer<CharType> data(m_length); - const CharType* from = getCharacters<CharType>(); + const CharType* from = characters<CharType>(); const CharType* fromend = from + m_length; int outc = 0; bool changedToSpace = false; @@ -847,17 +858,17 @@ inline PassRef<StringImpl> StringImpl::simplifyMatchedCharactersToSpace(UCharPre data.shrink(outc); - return adopt(data); + return adopt(WTFMove(data)); } -PassRef<StringImpl> StringImpl::simplifyWhiteSpace() +Ref<StringImpl> StringImpl::simplifyWhiteSpace() { if (is8Bit()) return StringImpl::simplifyMatchedCharactersToSpace<LChar>(SpaceOrNewlinePredicate()); return StringImpl::simplifyMatchedCharactersToSpace<UChar>(SpaceOrNewlinePredicate()); } -PassRef<StringImpl> StringImpl::simplifyWhiteSpace(IsWhiteSpaceFunctionPtr isWhiteSpace) +Ref<StringImpl> StringImpl::simplifyWhiteSpace(IsWhiteSpaceFunctionPtr isWhiteSpace) { if (is8Bit()) return StringImpl::simplifyMatchedCharactersToSpace<LChar>(UCharPredicate(isWhiteSpace)); @@ -948,24 +959,54 @@ float StringImpl::toFloat(bool* ok) return charactersToFloat(characters16(), m_length, ok); } -bool equalIgnoringCase(const LChar* a, const LChar* b, unsigned length) +// Table is based on ftp://ftp.unicode.org/Public/UNIDATA/CaseFolding.txt +static const UChar latin1CaseFoldTable[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, + 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, + 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x03bc, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, + 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, + 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00d7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00df, + 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, + 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff, +}; + +static inline bool equalCompatibilityCaseless(const LChar* a, const LChar* b, unsigned length) { while (length--) { - if (StringImpl::latin1CaseFoldTable[*a++] != StringImpl::latin1CaseFoldTable[*b++]) + if (latin1CaseFoldTable[*a++] != latin1CaseFoldTable[*b++]) return false; } return true; } -bool equalIgnoringCase(const UChar* a, const LChar* b, unsigned length) +static inline bool equalCompatibilityCaseless(const UChar* a, const LChar* b, unsigned length) { while (length--) { - if (u_foldCase(*a++, U_FOLD_CASE_DEFAULT) != StringImpl::latin1CaseFoldTable[*b++]) + if (u_foldCase(*a++, U_FOLD_CASE_DEFAULT) != latin1CaseFoldTable[*b++]) return false; } return true; } +static inline bool equalCompatibilityCaseless(const LChar* a, const UChar* b, unsigned length) +{ + return equalCompatibilityCaseless(b, a, length); +} + +static inline bool equalCompatibilityCaseless(const UChar* a, const UChar* b, unsigned length) +{ + return !u_memcasecmp(a, b, length, U_FOLD_CASE_DEFAULT); +} + size_t StringImpl::find(CharacterMatchFunctionPtr matchFunction, unsigned start) { if (is8Bit()) @@ -986,8 +1027,11 @@ size_t StringImpl::find(const LChar* matchString, unsigned index) return std::min(index, length()); // Optimization 1: fast case for strings of length 1. - if (matchLength == 1) + if (matchLength == 1) { + if (is8Bit()) + return WTF::find(characters8(), length(), matchString[0], index); return WTF::find(characters16(), length(), *matchString, index); + } // Check index & matchLength are in range. if (index > length()) @@ -998,10 +1042,32 @@ size_t StringImpl::find(const LChar* matchString, unsigned index) // delta is the number of additional times to test; delta == 0 means test only once. unsigned delta = searchLength - matchLength; - const UChar* searchCharacters = deprecatedCharacters() + index; - // Optimization 2: keep a running hash of the strings, // only call equal if the hashes match. + + if (is8Bit()) { + const LChar* searchCharacters = characters8() + index; + + unsigned searchHash = 0; + unsigned matchHash = 0; + for (unsigned i = 0; i < matchLength; ++i) { + searchHash += searchCharacters[i]; + matchHash += matchString[i]; + } + + unsigned i = 0; + while (searchHash != matchHash || !equal(searchCharacters + i, matchString, matchLength)) { + if (i == delta) + return notFound; + searchHash += searchCharacters[i + matchLength]; + searchHash -= searchCharacters[i]; + ++i; + } + return index + i; + } + + const UChar* searchCharacters = characters16() + index; + unsigned searchHash = 0; unsigned matchHash = 0; for (unsigned i = 0; i < matchLength; ++i) { @@ -1010,7 +1076,6 @@ size_t StringImpl::find(const LChar* matchString, unsigned index) } unsigned i = 0; - // keep looping until we match while (searchHash != matchHash || !equal(searchCharacters + i, matchString, matchLength)) { if (i == delta) return notFound; @@ -1042,45 +1107,27 @@ size_t StringImpl::findIgnoringCase(const LChar* matchString, unsigned index) // delta is the number of additional times to test; delta == 0 means test only once. unsigned delta = searchLength - matchLength; - const UChar* searchCharacters = deprecatedCharacters() + index; + if (is8Bit()) { + const LChar* searchCharacters = characters8() + index; - unsigned i = 0; - // keep looping until we match - while (!equalIgnoringCase(searchCharacters + i, matchString, matchLength)) { - if (i == delta) - return notFound; - ++i; + unsigned i = 0; + while (!equalCompatibilityCaseless(searchCharacters + i, matchString, matchLength)) { + if (i == delta) + return notFound; + ++i; + } + return index + i; } - return index + i; -} - -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]; - } + const UChar* searchCharacters = characters16() + index; unsigned i = 0; - // keep looping until we match - while (searchHash != matchHash || !equal(searchCharacters + i, matchCharacters, matchLength)) { + while (!equalCompatibilityCaseless(searchCharacters + i, matchString, matchLength)) { if (i == delta) return notFound; - searchHash += searchCharacters[i + matchLength]; - searchHash -= searchCharacters[i]; ++i; } - return index + i; + return index + i; } size_t StringImpl::find(StringImpl* matchString) @@ -1128,35 +1175,7 @@ size_t StringImpl::find(StringImpl* matchString, unsigned index) if (UNLIKELY(!matchString)) return notFound; - unsigned matchLength = matchString->length(); - - // Optimization 1: fast case for strings of length 1. - if (matchLength == 1) { - if (is8Bit()) - return WTF::find(characters8(), length(), (*matchString)[0], index); - return WTF::find(characters16(), length(), (*matchString)[0], index); - } - - if (UNLIKELY(!matchLength)) - return std::min(index, length()); - - // Check index & matchLength are in range. - if (index > length()) - return notFound; - unsigned searchLength = length() - index; - if (matchLength > searchLength) - return notFound; - - if (is8Bit()) { - if (matchString->is8Bit()) - return findInner(characters8() + index, matchString->characters8(), index, searchLength, matchLength); - return findInner(characters8() + index, matchString->characters16(), index, searchLength, matchLength); - } - - if (matchString->is8Bit()) - return findInner(characters16() + index, matchString->characters8(), index, searchLength, matchLength); - - return findInner(characters16() + index, matchString->characters16(), index, searchLength, matchLength); + return findCommon(*this, *matchString, index); } template <typename SearchCharacterType, typename MatchCharacterType> @@ -1167,7 +1186,7 @@ ALWAYS_INLINE static size_t findIgnoringCaseInner(const SearchCharacterType* sea unsigned i = 0; // keep looping until we match - while (!equalIgnoringCase(searchCharacters + i, matchCharacters, matchLength)) { + while (!equalCompatibilityCaseless(searchCharacters + i, matchCharacters, matchLength)) { if (i == delta) return notFound; ++i; @@ -1203,11 +1222,28 @@ size_t StringImpl::findIgnoringCase(StringImpl* matchString, unsigned index) return findIgnoringCaseInner(characters16() + index, matchString->characters16(), index, searchLength, matchLength); } -size_t StringImpl::findNextLineStart(unsigned index) +size_t StringImpl::findIgnoringASCIICase(const StringImpl& matchString) const { - if (is8Bit()) - return WTF::findNextLineStart(characters8(), m_length, index); - return WTF::findNextLineStart(characters16(), m_length, index); + return ::WTF::findIgnoringASCIICase(*this, matchString, 0); +} + +size_t StringImpl::findIgnoringASCIICase(const StringImpl& matchString, unsigned startOffset) const +{ + return ::WTF::findIgnoringASCIICase(*this, matchString, startOffset); +} + +size_t StringImpl::findIgnoringASCIICase(const StringImpl* matchString) const +{ + if (!matchString) + return notFound; + return ::WTF::findIgnoringASCIICase(*this, *matchString, 0); +} + +size_t StringImpl::findIgnoringASCIICase(const StringImpl* matchString, unsigned startOffset) const +{ + if (!matchString) + return notFound; + return ::WTF::findIgnoringASCIICase(*this, *matchString, startOffset); } size_t StringImpl::reverseFind(UChar c, unsigned index) @@ -1284,7 +1320,7 @@ ALWAYS_INLINE static size_t reverseFindIgnoringCaseInner(const SearchCharacterTy unsigned delta = std::min(index, length - matchLength); // keep looping until we match - while (!equalIgnoringCase(searchCharacters + delta, matchCharacters, matchLength)) { + while (!equalCompatibilityCaseless(searchCharacters + delta, matchCharacters, matchLength)) { if (!delta) return notFound; --delta; @@ -1330,26 +1366,52 @@ ALWAYS_INLINE static bool equalInner(const StringImpl* stringImpl, unsigned star return equal(stringImpl->characters16() + startOffset, reinterpret_cast<const LChar*>(matchString), matchLength); } if (stringImpl->is8Bit()) - return equalIgnoringCase(stringImpl->characters8() + startOffset, reinterpret_cast<const LChar*>(matchString), matchLength); - return equalIgnoringCase(stringImpl->characters16() + startOffset, reinterpret_cast<const LChar*>(matchString), matchLength); + return equalCompatibilityCaseless(stringImpl->characters8() + startOffset, reinterpret_cast<const LChar*>(matchString), matchLength); + return equalCompatibilityCaseless(stringImpl->characters16() + startOffset, reinterpret_cast<const LChar*>(matchString), matchLength); +} + +ALWAYS_INLINE static bool equalInner(const StringImpl& stringImpl, unsigned startOffset, const StringImpl& matchString) +{ + if (startOffset > stringImpl.length()) + return false; + if (matchString.length() > stringImpl.length()) + return false; + if (matchString.length() + startOffset > stringImpl.length()) + return false; + + if (stringImpl.is8Bit()) { + if (matchString.is8Bit()) + return equal(stringImpl.characters8() + startOffset, matchString.characters8(), matchString.length()); + return equal(stringImpl.characters8() + startOffset, matchString.characters16(), matchString.length()); + } + if (matchString.is8Bit()) + return equal(stringImpl.characters16() + startOffset, matchString.characters8(), matchString.length()); + return equal(stringImpl.characters16() + startOffset, matchString.characters16(), matchString.length()); } bool StringImpl::startsWith(const StringImpl* str) const { if (!str) return false; + return ::WTF::startsWith(*this, *str); +} - if (str->length() > length()) +bool StringImpl::startsWith(const StringImpl& str) const +{ + return ::WTF::startsWith(*this, str); +} + +bool StringImpl::startsWithIgnoringASCIICase(const StringImpl* prefix) const +{ + if (!prefix) return false; - if (is8Bit()) { - if (str->is8Bit()) - return equal(characters8(), str->characters8(), str->length()); - return equal(characters8(), str->characters16(), str->length()); - } - if (str->is8Bit()) - return equal(characters16(), str->characters8(), str->length()); - return equal(characters16(), str->characters16(), str->length()); + return ::WTF::startsWithIgnoringASCIICase(*this, *prefix); +} + +bool StringImpl::startsWithIgnoringASCIICase(const StringImpl& prefix) const +{ + return ::WTF::startsWithIgnoringASCIICase(*this, prefix); } bool StringImpl::startsWith(UChar character) const @@ -1365,6 +1427,24 @@ bool StringImpl::startsWith(const char* matchString, unsigned matchLength, bool return equalInner(this, 0, matchString, matchLength, caseSensitive); } +bool StringImpl::hasInfixStartingAt(const StringImpl& matchString, unsigned startOffset) const +{ + return equalInner(*this, startOffset, matchString); +} + +bool StringImpl::endsWith(StringImpl* suffix) +{ + if (!suffix) + return false; + + return ::WTF::endsWith(*this, *suffix); +} + +bool StringImpl::endsWith(StringImpl& suffix) +{ + return ::WTF::endsWith(*this, suffix); +} + bool StringImpl::endsWith(StringImpl* matchString, bool caseSensitive) { ASSERT(matchString); @@ -1375,6 +1455,19 @@ bool StringImpl::endsWith(StringImpl* matchString, bool caseSensitive) return false; } +bool StringImpl::endsWithIgnoringASCIICase(const StringImpl* suffix) const +{ + if (!suffix) + return false; + + return ::WTF::endsWithIgnoringASCIICase(*this, *suffix); +} + +bool StringImpl::endsWithIgnoringASCIICase(const StringImpl& suffix) const +{ + return ::WTF::endsWithIgnoringASCIICase(*this, suffix); +} + bool StringImpl::endsWith(UChar character) const { return m_length && (*this)[m_length - 1] == character; @@ -1389,7 +1482,14 @@ bool StringImpl::endsWith(const char* matchString, unsigned matchLength, bool ca return equalInner(this, startOffset, matchString, matchLength, caseSensitive); } -PassRef<StringImpl> StringImpl::replace(UChar oldC, UChar newC) +bool StringImpl::hasInfixEndingAt(const StringImpl& matchString, unsigned endOffset) const +{ + if (endOffset < matchString.length()) + return false; + return equalInner(*this, endOffset - matchString.length(), matchString); +} + +Ref<StringImpl> StringImpl::replace(UChar oldC, UChar newC) { if (oldC == newC) return *this; @@ -1450,7 +1550,7 @@ PassRef<StringImpl> StringImpl::replace(UChar oldC, UChar newC) return newImpl; } -PassRef<StringImpl> StringImpl::replace(unsigned position, unsigned lengthToReplace, StringImpl* str) +Ref<StringImpl> StringImpl::replace(unsigned position, unsigned lengthToReplace, StringImpl* str) { position = std::min(position, length()); lengthToReplace = std::min(lengthToReplace, length() - position); @@ -1495,7 +1595,7 @@ PassRef<StringImpl> StringImpl::replace(unsigned position, unsigned lengthToRepl return newImpl; } -PassRef<StringImpl> StringImpl::replace(UChar pattern, StringImpl* replacement) +Ref<StringImpl> StringImpl::replace(UChar pattern, StringImpl* replacement) { if (!replacement) return *this; @@ -1506,7 +1606,7 @@ PassRef<StringImpl> StringImpl::replace(UChar pattern, StringImpl* replacement) return replace(pattern, replacement->m_data16, replacement->length()); } -PassRef<StringImpl> StringImpl::replace(UChar pattern, const LChar* replacement, unsigned repStrLength) +Ref<StringImpl> StringImpl::replace(UChar pattern, const LChar* replacement, unsigned repStrLength) { ASSERT(replacement); @@ -1583,7 +1683,7 @@ PassRef<StringImpl> StringImpl::replace(UChar pattern, const LChar* replacement, return newImpl; } -PassRef<StringImpl> StringImpl::replace(UChar pattern, const UChar* replacement, unsigned repStrLength) +Ref<StringImpl> StringImpl::replace(UChar pattern, const UChar* replacement, unsigned repStrLength) { ASSERT(replacement); @@ -1663,7 +1763,7 @@ PassRef<StringImpl> StringImpl::replace(UChar pattern, const UChar* replacement, return newImpl; } -PassRef<StringImpl> StringImpl::replace(StringImpl* pattern, StringImpl* replacement) +Ref<StringImpl> StringImpl::replace(StringImpl* pattern, StringImpl* replacement) { if (!pattern || !replacement) return *this; @@ -1770,34 +1870,9 @@ PassRef<StringImpl> StringImpl::replace(StringImpl* pattern, StringImpl* replace return newImpl; } -static inline bool stringImplContentEqual(const StringImpl* a, const StringImpl* b) -{ - unsigned aLength = a->length(); - unsigned bLength = b->length(); - if (aLength != bLength) - return false; - - if (a->is8Bit()) { - if (b->is8Bit()) - return equal(a->characters8(), b->characters8(), aLength); - - return equal(a->characters8(), b->characters16(), aLength); - } - - if (b->is8Bit()) - return equal(a->characters16(), b->characters8(), aLength); - - return equal(a->characters16(), b->characters16(), aLength); -} - bool equal(const StringImpl* a, const StringImpl* b) { - if (a == b) - return true; - if (!a || !b) - return false; - - return stringImplContentEqual(a, b); + return equalCommon(a, b); } template <typename CharType> @@ -1860,109 +1935,34 @@ bool equal(const StringImpl* a, const LChar* b) return !b[length]; } -bool equalNonNull(const StringImpl* a, const StringImpl* b) +bool equal(const StringImpl& a, const StringImpl& b) { - ASSERT(a && b); - if (a == b) - return true; - - return stringImplContentEqual(a, b); + return equalCommon(a, b); } -bool equalIgnoringCase(const StringImpl* a, const StringImpl* b) +bool equalIgnoringNullity(StringImpl* a, StringImpl* b) { - if (a == b) + if (!a && b && !b->length()) return true; - if (!a || !b) - return false; - - return CaseFoldingHash::equal(a, b); -} - -bool equalIgnoringCase(const StringImpl* a, const LChar* b) -{ - if (!a) - return !b; - if (!b) - return !a; - - unsigned length = a->length(); - - // Do a faster loop for the case where all the characters are ASCII. - UChar ored = 0; - bool equal = true; - if (a->is8Bit()) { - const LChar* as = a->characters8(); - for (unsigned i = 0; i != length; ++i) { - LChar bc = b[i]; - if (!bc) - return false; - UChar ac = as[i]; - ored |= ac; - equal = equal && (toASCIILower(ac) == toASCIILower(bc)); - } - - // Do a slower implementation for cases that include non-ASCII characters. - if (ored & ~0x7F) { - equal = true; - for (unsigned i = 0; i != length; ++i) - equal = equal && u_foldCase(as[i], U_FOLD_CASE_DEFAULT) == u_foldCase(b[i], U_FOLD_CASE_DEFAULT); - } - - return equal && !b[length]; - } - - const UChar* as = a->characters16(); - for (unsigned i = 0; i != length; ++i) { - LChar bc = b[i]; - if (!bc) - return false; - UChar ac = as[i]; - ored |= ac; - equal = equal && (toASCIILower(ac) == toASCIILower(bc)); - } - - // Do a slower implementation for cases that include non-ASCII characters. - if (ored & ~0x7F) { - equal = true; - for (unsigned i = 0; i != length; ++i) { - equal = equal && u_foldCase(as[i], U_FOLD_CASE_DEFAULT) == u_foldCase(b[i], U_FOLD_CASE_DEFAULT); - } - } - - return equal && !b[length]; + if (!b && a && !a->length()) + return true; + return equal(a, b); } -bool equalIgnoringCaseNonNull(const StringImpl* a, const StringImpl* b) +bool equalIgnoringASCIICase(const StringImpl* a, const StringImpl* b) { - ASSERT(a && b); if (a == b) return true; - - unsigned length = a->length(); - if (length != b->length()) + if (!a || !b) return false; - - if (a->is8Bit()) { - if (b->is8Bit()) - return equalIgnoringCase(a->characters8(), b->characters8(), length); - - return equalIgnoringCase(b->characters16(), a->characters8(), length); - } - - if (b->is8Bit()) - return equalIgnoringCase(a->characters16(), b->characters8(), length); - - return equalIgnoringCase(a->characters16(), b->characters16(), length); + return equalIgnoringASCIICaseCommon(*a, *b); } -bool equalIgnoringNullity(StringImpl* a, StringImpl* b) +bool equalIgnoringASCIICaseNonNull(const StringImpl* a, const StringImpl* b) { - if (!a && b && !b->length()) - return true; - if (!b && a && !a->length()) - return true; - return equal(a, b); + ASSERT(a); + ASSERT(b); + return equalIgnoringASCIICase(*a, *b); } UCharDirection StringImpl::defaultWritingDirection(bool* hasStrongDirectionality) @@ -1985,7 +1985,7 @@ UCharDirection StringImpl::defaultWritingDirection(bool* hasStrongDirectionality return U_LEFT_TO_RIGHT; } -PassRef<StringImpl> StringImpl::adopt(StringBuffer<LChar>& buffer) +Ref<StringImpl> StringImpl::adopt(StringBuffer<LChar>&& buffer) { unsigned length = buffer.length(); if (!length) @@ -1993,7 +1993,7 @@ PassRef<StringImpl> StringImpl::adopt(StringBuffer<LChar>& buffer) return adoptRef(*new StringImpl(buffer.release(), length)); } -PassRef<StringImpl> StringImpl::adopt(StringBuffer<UChar>& buffer) +Ref<StringImpl> StringImpl::adopt(StringBuffer<UChar>&& buffer) { unsigned length = buffer.length(); if (!length) @@ -2005,11 +2005,7 @@ size_t StringImpl::sizeInBytes() const { // FIXME: support substrings size_t size = length(); - if (is8Bit()) { - if (has16BitShadow()) { - size += 2 * size; - } - } else + if (!is8Bit()) size *= 2; return size + sizeof(*this); } @@ -2023,8 +2019,7 @@ static inline void putUTF8Triple(char*& buffer, UChar ch) *buffer++ = static_cast<char>((ch & 0x3F) | 0x80); } -bool StringImpl::utf8Impl( - const UChar* characters, unsigned length, char*& buffer, size_t bufferSize, ConversionMode mode) +bool StringImpl::utf8Impl(const UChar* characters, unsigned length, char*& buffer, size_t bufferSize, ConversionMode mode) { if (mode == StrictConversionReplacingUnpairedSurrogatesWithFFFD) { const UChar* charactersEnd = characters + length; @@ -2075,8 +2070,21 @@ bool StringImpl::utf8Impl( return true; } -CString StringImpl::utf8ForCharacters( - const UChar* characters, unsigned length, ConversionMode mode) +CString StringImpl::utf8ForCharacters(const LChar* characters, unsigned length) +{ + if (!length) + return CString("", 0); + if (length > std::numeric_limits<unsigned>::max() / 3) + return CString(); + Vector<char, 1024> bufferVector(length * 3); + char* buffer = bufferVector.data(); + const LChar* source = characters; + ConversionResult result = convertLatin1ToUTF8(&source, source + length, &buffer, buffer + bufferVector.size()); + ASSERT_UNUSED(result, result != targetExhausted); // (length * 3) should be sufficient for any conversion + return CString(bufferVector.data(), buffer - bufferVector.data()); +} + +CString StringImpl::utf8ForCharacters(const UChar* characters, unsigned length, ConversionMode mode) { if (!length) return CString("", 0); @@ -2131,25 +2139,21 @@ CString StringImpl::utf8(ConversionMode mode) const return utf8ForRange(0, length(), mode); } -// Table is based on ftp://ftp.unicode.org/Public/UNIDATA/CaseFolding.txt -const UChar StringImpl::latin1CaseFoldTable[256] = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, - 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, - 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, - 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, - 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, - 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x03bc, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, - 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, - 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00d7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00df, - 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, - 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff, -}; - +bool equalIgnoringNullity(const UChar* a, size_t aLength, StringImpl* b) +{ + if (!b) + return !aLength; + if (aLength != b->length()) + return false; + if (b->is8Bit()) { + const LChar* bCharacters = b->characters8(); + for (unsigned i = 0; i < aLength; ++i) { + if (a[i] != bCharacters[i]) + return false; + } + return true; + } + return !memcmp(a, b->characters16(), b->length() * sizeof(UChar)); +} } // namespace WTF |