diff options
Diffstat (limited to 'Source/WTF/wtf/text/StringView.cpp')
-rw-r--r-- | Source/WTF/wtf/text/StringView.cpp | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/Source/WTF/wtf/text/StringView.cpp b/Source/WTF/wtf/text/StringView.cpp new file mode 100644 index 000000000..580799765 --- /dev/null +++ b/Source/WTF/wtf/text/StringView.cpp @@ -0,0 +1,285 @@ +/* + +Copyright (C) 2014-2017 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. + +*/ + +#include "config.h" +#include "StringView.h" + +#include <mutex> +#include <unicode/ubrk.h> +#include <wtf/HashMap.h> +#include <wtf/Lock.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/Optional.h> +#include <wtf/text/TextBreakIterator.h> +#include <wtf/unicode/UTF8.h> + +namespace WTF { + +using namespace Unicode; + +bool StringView::containsIgnoringASCIICase(const StringView& matchString) const +{ + return findIgnoringASCIICase(matchString) != notFound; +} + +bool StringView::containsIgnoringASCIICase(const StringView& matchString, unsigned startOffset) const +{ + return findIgnoringASCIICase(matchString, startOffset) != notFound; +} + +size_t StringView::findIgnoringASCIICase(const StringView& matchString) const +{ + return ::WTF::findIgnoringASCIICase(*this, matchString, 0); +} + +size_t StringView::findIgnoringASCIICase(const StringView& matchString, unsigned startOffset) const +{ + return ::WTF::findIgnoringASCIICase(*this, matchString, startOffset); +} + +bool StringView::startsWith(const StringView& prefix) const +{ + return ::WTF::startsWith(*this, prefix); +} + +bool StringView::startsWithIgnoringASCIICase(const StringView& prefix) const +{ + return ::WTF::startsWithIgnoringASCIICase(*this, prefix); +} + +bool StringView::endsWith(const StringView& suffix) const +{ + return ::WTF::endsWith(*this, suffix); +} + +bool StringView::endsWithIgnoringASCIICase(const StringView& suffix) const +{ + return ::WTF::endsWithIgnoringASCIICase(*this, suffix); +} + +CString StringView::utf8(ConversionMode mode) const +{ + if (isNull()) + return CString("", 0); + if (is8Bit()) + return StringImpl::utf8ForCharacters(characters8(), length()); + return StringImpl::utf8ForCharacters(characters16(), length(), mode); +} + +size_t StringView::find(StringView matchString, unsigned start) const +{ + return findCommon(*this, matchString, start); +} + +void StringView::SplitResult::Iterator::findNextSubstring() +{ + for (size_t separatorPosition; (separatorPosition = m_result.m_string.find(m_result.m_separator, m_position)) != notFound; ++m_position) { + if (separatorPosition > m_position) { + m_length = separatorPosition - m_position; + return; + } + } + m_length = m_result.m_string.length() - m_position; +} + +auto StringView::SplitResult::Iterator::operator++() -> Iterator& +{ + ASSERT(m_position < m_result.m_string.length()); + m_position += m_length; + if (m_position < m_result.m_string.length()) { + ++m_position; + findNextSubstring(); + } + return *this; +} + +class StringView::GraphemeClusters::Iterator::Impl { +public: + Impl(const StringView& stringView, std::optional<NonSharedCharacterBreakIterator>&& iterator, unsigned index) + : m_stringView(stringView) + , m_iterator(WTFMove(iterator)) + , m_index(index) + , m_indexEnd(computeIndexEnd()) + { + } + + void operator++() + { + ASSERT(m_indexEnd > m_index); + m_index = m_indexEnd; + m_indexEnd = computeIndexEnd(); + } + + StringView operator*() const + { + if (m_stringView.is8Bit()) + return StringView(m_stringView.characters8() + m_index, m_indexEnd - m_index); + return StringView(m_stringView.characters16() + m_index, m_indexEnd - m_index); + } + + bool operator==(const Impl& other) const + { + ASSERT(&m_stringView == &other.m_stringView); + auto result = m_index == other.m_index; + ASSERT(!result || m_indexEnd == other.m_indexEnd); + return result; + } + + unsigned computeIndexEnd() + { + if (!m_iterator) + return 0; + if (m_index == m_stringView.length()) + return m_index; + return ubrk_following(m_iterator.value(), m_index); + } + +private: + const StringView& m_stringView; + std::optional<NonSharedCharacterBreakIterator> m_iterator; + unsigned m_index; + unsigned m_indexEnd; +}; + +StringView::GraphemeClusters::Iterator::Iterator(const StringView& stringView, unsigned index) + : m_impl(std::make_unique<Impl>(stringView, stringView.isNull() ? std::nullopt : std::optional<NonSharedCharacterBreakIterator>(NonSharedCharacterBreakIterator(stringView)), index)) +{ +} + +StringView::GraphemeClusters::Iterator::~Iterator() +{ +} + +StringView::GraphemeClusters::Iterator::Iterator(Iterator&& other) + : m_impl(WTFMove(other.m_impl)) +{ +} + +auto StringView::GraphemeClusters::Iterator::operator++() -> Iterator& +{ + ++(*m_impl); + return *this; +} + +StringView StringView::GraphemeClusters::Iterator::operator*() const +{ + return **m_impl; +} + +bool StringView::GraphemeClusters::Iterator::operator==(const Iterator& other) const +{ + return *m_impl == *(other.m_impl); +} + +bool StringView::GraphemeClusters::Iterator::operator!=(const Iterator& other) const +{ + return !(*this == other); +} + +#if CHECK_STRINGVIEW_LIFETIME + +// Manage reference count manually so UnderlyingString does not need to be defined in the header. + +struct StringView::UnderlyingString { + std::atomic_uint refCount { 1u }; + bool isValid { true }; + const StringImpl& string; + explicit UnderlyingString(const StringImpl&); +}; + +StringView::UnderlyingString::UnderlyingString(const StringImpl& string) + : string(string) +{ +} + +static StaticLock underlyingStringsMutex; + +static HashMap<const StringImpl*, StringView::UnderlyingString*>& underlyingStrings() +{ + static NeverDestroyed<HashMap<const StringImpl*, StringView::UnderlyingString*>> map; + return map; +} + +void StringView::invalidate(const StringImpl& stringToBeDestroyed) +{ + UnderlyingString* underlyingString; + { + std::lock_guard<StaticLock> lock(underlyingStringsMutex); + underlyingString = underlyingStrings().take(&stringToBeDestroyed); + if (!underlyingString) + return; + } + ASSERT(underlyingString->isValid); + underlyingString->isValid = false; +} + +bool StringView::underlyingStringIsValid() const +{ + return !m_underlyingString || m_underlyingString->isValid; +} + +void StringView::adoptUnderlyingString(UnderlyingString* underlyingString) +{ + if (m_underlyingString) { + std::lock_guard<StaticLock> lock(underlyingStringsMutex); + if (!--m_underlyingString->refCount) { + if (m_underlyingString->isValid) { + underlyingStrings().remove(&m_underlyingString->string); + } + delete m_underlyingString; + } + } + m_underlyingString = underlyingString; +} + +void StringView::setUnderlyingString(const StringImpl* string) +{ + UnderlyingString* underlyingString; + if (!string) + underlyingString = nullptr; + else { + std::lock_guard<StaticLock> lock(underlyingStringsMutex); + auto result = underlyingStrings().add(string, nullptr); + if (result.isNewEntry) + result.iterator->value = new UnderlyingString(*string); + else + ++result.iterator->value->refCount; + underlyingString = result.iterator->value; + } + adoptUnderlyingString(underlyingString); +} + +void StringView::setUnderlyingString(const StringView& otherString) +{ + UnderlyingString* underlyingString = otherString.m_underlyingString; + if (underlyingString) + ++underlyingString->refCount; + adoptUnderlyingString(underlyingString); +} + +#endif // CHECK_STRINGVIEW_LIFETIME + +} // namespace WTF |