| // Copyright 2016 The PDFium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com |
| |
| #ifndef CORE_FXCRT_STRING_VIEW_TEMPLATE_H_ |
| #define CORE_FXCRT_STRING_VIEW_TEMPLATE_H_ |
| |
| #include <ctype.h> |
| |
| #include <algorithm> |
| #include <iterator> |
| #include <optional> |
| #include <string> |
| #include <type_traits> |
| |
| #include "core/fxcrt/compiler_specific.h" |
| #include "core/fxcrt/fx_memcpy_wrappers.h" |
| #include "core/fxcrt/fx_system.h" |
| #include "core/fxcrt/span.h" |
| #include "core/fxcrt/span_util.h" |
| |
| namespace fxcrt { |
| |
| // An immutable string with caller-provided storage which must outlive the |
| // string itself. These are not necessarily nul-terminated, so that substring |
| // extraction (via the Substr(), First(), and Last() methods) is copy-free. |
| // |
| // String view arguments should be passed by value, since they are small, |
| // rather than const-ref, even if they are not modified. |
| // |
| // Front() and Back() tolerate empty strings and must return NUL in those |
| // cases. Substr(), First(), and Last() tolerate out-of-range indices and |
| // must return an empty string view in those cases. The aim here is allowing |
| // callers to avoid range-checking first. |
| template <typename T> |
| class StringViewTemplate { |
| public: |
| using CharType = T; |
| using UnsignedType = typename std::make_unsigned<CharType>::type; |
| using const_iterator = const CharType*; |
| using const_reverse_iterator = std::reverse_iterator<const_iterator>; |
| |
| constexpr StringViewTemplate() noexcept = default; |
| constexpr StringViewTemplate(const StringViewTemplate& src) noexcept = |
| default; |
| |
| // Deliberately implicit to avoid calling on every string literal. |
| // NOLINTNEXTLINE(runtime/explicit) |
| StringViewTemplate(const CharType* ptr) noexcept |
| // SAFETY: from length() function. |
| : m_Span(UNSAFE_BUFFERS(pdfium::make_span( |
| reinterpret_cast<const UnsignedType*>(ptr), |
| ptr ? std::char_traits<CharType>::length(ptr) : 0))) {} |
| |
| explicit constexpr StringViewTemplate( |
| const pdfium::span<const CharType>& other) noexcept { |
| if (!other.empty()) { |
| m_Span = reinterpret_span<const UnsignedType>(other); |
| } |
| } |
| |
| template <typename E = typename std::enable_if< |
| !std::is_same<UnsignedType, CharType>::value>::type> |
| explicit constexpr StringViewTemplate( |
| const pdfium::span<const UnsignedType>& other) noexcept { |
| if (!other.empty()) { |
| m_Span = other; |
| } |
| } |
| |
| // Deliberately implicit to avoid calling on every char literal. |
| // |ch| must be an lvalue that outlives the StringViewTemplate. |
| // NOLINTNEXTLINE(runtime/explicit) |
| constexpr StringViewTemplate(const CharType& ch) noexcept |
| : m_Span( |
| reinterpret_span<const UnsignedType>(pdfium::span_from_ref(ch))) {} |
| |
| UNSAFE_BUFFER_USAGE |
| constexpr StringViewTemplate(const CharType* ptr, size_t size) noexcept |
| // SAFETY: propagated to caller via UNSAFE_BUFFER_USAGE. |
| : m_Span(UNSAFE_BUFFERS( |
| pdfium::make_span(reinterpret_cast<const UnsignedType*>(ptr), |
| size))) {} |
| |
| template <typename E = typename std::enable_if< |
| !std::is_same<UnsignedType, CharType>::value>::type> |
| UNSAFE_BUFFER_USAGE constexpr StringViewTemplate(const UnsignedType* ptr, |
| size_t size) noexcept |
| // SAFETY: propagated to caller via UNSAFE_BUFFER_USAGE. |
| : m_Span(UNSAFE_BUFFERS(pdfium::make_span(ptr, size))) {} |
| |
| StringViewTemplate& operator=(const CharType* src) { |
| // SAFETY: caller ensures `src` is nul-terminated so `length()` is correct. |
| m_Span = UNSAFE_BUFFERS( |
| pdfium::make_span(reinterpret_cast<const UnsignedType*>(src), |
| src ? std::char_traits<CharType>::length(src) : 0)); |
| return *this; |
| } |
| |
| StringViewTemplate& operator=(const StringViewTemplate& src) { |
| m_Span = src.m_Span; |
| return *this; |
| } |
| |
| const_iterator begin() const { |
| return reinterpret_cast<const_iterator>(m_Span.begin()); |
| } |
| const_iterator end() const { |
| return reinterpret_cast<const_iterator>(m_Span.end()); |
| } |
| const_reverse_iterator rbegin() const { |
| return const_reverse_iterator(end()); |
| } |
| const_reverse_iterator rend() const { |
| return const_reverse_iterator(begin()); |
| } |
| |
| bool operator==(const StringViewTemplate& other) const { |
| return std::equal(m_Span.begin(), m_Span.end(), other.m_Span.begin(), |
| other.m_Span.end()); |
| } |
| bool operator==(const CharType* ptr) const { |
| StringViewTemplate other(ptr); |
| return *this == other; |
| } |
| bool operator!=(const CharType* ptr) const { return !(*this == ptr); } |
| bool operator!=(const StringViewTemplate& other) const { |
| return !(*this == other); |
| } |
| |
| bool IsASCII() const { |
| for (auto c : *this) { |
| if (c <= 0 || c > 127) // Questionable signedness of |c|. |
| return false; |
| } |
| return true; |
| } |
| |
| bool EqualsASCII(const StringViewTemplate<char>& that) const { |
| size_t length = GetLength(); |
| if (length != that.GetLength()) |
| return false; |
| |
| for (size_t i = 0; i < length; ++i) { |
| auto c = (*this)[i]; |
| if (c <= 0 || c > 127 || c != that[i]) // Questionable signedness of |c|. |
| return false; |
| } |
| return true; |
| } |
| |
| bool EqualsASCIINoCase(const StringViewTemplate<char>& that) const { |
| size_t length = GetLength(); |
| if (length != that.GetLength()) |
| return false; |
| |
| for (size_t i = 0; i < length; ++i) { |
| auto c = (*this)[i]; |
| if (c <= 0 || c > 127 || tolower(c) != tolower(that[i])) |
| return false; |
| } |
| return true; |
| } |
| |
| uint32_t GetID() const { |
| if (m_Span.empty()) |
| return 0; |
| |
| uint32_t strid = 0; |
| size_t size = std::min(static_cast<size_t>(4), m_Span.size()); |
| for (size_t i = 0; i < size; i++) |
| strid = strid * 256 + m_Span[i]; |
| |
| return strid << ((4 - size) * 8); |
| } |
| |
| pdfium::span<const UnsignedType> unsigned_span() const { return m_Span; } |
| pdfium::span<const CharType> span() const { |
| return reinterpret_span<const CharType>(m_Span); |
| } |
| const UnsignedType* unterminated_unsigned_str() const { |
| return m_Span.data(); |
| } |
| const CharType* unterminated_c_str() const { |
| return reinterpret_cast<const CharType*>(m_Span.data()); |
| } |
| |
| size_t GetLength() const { return m_Span.size(); } |
| bool IsEmpty() const { return m_Span.empty(); } |
| bool IsValidIndex(size_t index) const { return index < m_Span.size(); } |
| bool IsValidLength(size_t length) const { return length <= m_Span.size(); } |
| |
| // CHECK() if index is out of range (via span's operator[]). |
| const UnsignedType& operator[](const size_t index) const { |
| return m_Span[index]; |
| } |
| |
| // CHECK() if index is out of range (via span's operator[]). |
| CharType CharAt(const size_t index) const { |
| return static_cast<CharType>(m_Span[index]); |
| } |
| |
| // Unlike std::string_view::front(), this is always safe and returns a |
| // NUL char when the string is empty. |
| UnsignedType Front() const { return !m_Span.empty() ? m_Span.front() : 0; } |
| |
| // Unlike std::string_view::back(), this is always safe and returns a |
| // NUL char when the string is empty. |
| UnsignedType Back() const { return !m_Span.empty() ? m_Span.back() : 0; } |
| |
| std::optional<size_t> Find(CharType ch) const { |
| const auto* found = |
| reinterpret_cast<const UnsignedType*>(std::char_traits<CharType>::find( |
| reinterpret_cast<const CharType*>(m_Span.data()), m_Span.size(), |
| ch)); |
| |
| return found ? std::optional<size_t>(found - m_Span.data()) : std::nullopt; |
| } |
| |
| bool Contains(CharType ch) const { return Find(ch).has_value(); } |
| |
| StringViewTemplate Substr(size_t offset) const { |
| // Unsigned underflow is well-defined and out-of-range is handled by |
| // Substr(). |
| return Substr(offset, GetLength() - offset); |
| } |
| |
| StringViewTemplate Substr(size_t first, size_t count) const { |
| if (!m_Span.data()) |
| return StringViewTemplate(); |
| |
| if (!IsValidIndex(first)) |
| return StringViewTemplate(); |
| |
| if (count == 0 || !IsValidLength(count)) |
| return StringViewTemplate(); |
| |
| if (!IsValidIndex(first + count - 1)) |
| return StringViewTemplate(); |
| |
| // SAFETY: performance-sensitive, checks above equivalent to subspan()'s. |
| return UNSAFE_BUFFERS(StringViewTemplate(m_Span.data() + first, count)); |
| } |
| |
| StringViewTemplate First(size_t count) const { |
| return Substr(0, count); |
| } |
| |
| StringViewTemplate Last(size_t count) const { |
| // Unsigned underflow is well-defined and out-of-range is handled by |
| // Substr(). |
| return Substr(GetLength() - count, count); |
| } |
| |
| StringViewTemplate TrimmedRight(CharType ch) const { |
| if (IsEmpty()) |
| return StringViewTemplate(); |
| |
| size_t pos = GetLength(); |
| while (pos && CharAt(pos - 1) == ch) |
| pos--; |
| |
| if (pos == 0) |
| return StringViewTemplate(); |
| |
| // SAFETY: Loop above keeps `pos` at length of string or less. |
| return UNSAFE_BUFFERS(StringViewTemplate(m_Span.data(), pos)); |
| } |
| |
| bool operator<(const StringViewTemplate& that) const { |
| const size_t common_size = std::min(m_Span.size(), that.m_Span.size()); |
| int result = |
| common_size ? std::char_traits<CharType>::compare( |
| reinterpret_cast<const CharType*>(m_Span.data()), |
| reinterpret_cast<const CharType*>(that.m_Span.data()), |
| common_size) |
| : 0; |
| return result < 0 || (result == 0 && m_Span.size() < that.m_Span.size()); |
| } |
| |
| bool operator>(const StringViewTemplate& that) const { |
| const size_t common_size = std::min(m_Span.size(), that.m_Span.size()); |
| int result = |
| common_size ? std::char_traits<CharType>::compare( |
| reinterpret_cast<const CharType*>(m_Span.data()), |
| reinterpret_cast<const CharType*>(that.m_Span.data()), |
| common_size) |
| : 0; |
| return result > 0 || (result == 0 && m_Span.size() > that.m_Span.size()); |
| } |
| |
| protected: |
| // This is not a raw_span<> because StringViewTemplates must be passed by |
| // value without introducing BackupRefPtr churn. Also, repeated re-assignment |
| // of substrings of a StringViewTemplate to itself must avoid the same issue. |
| pdfium::span<const UnsignedType> m_Span; |
| |
| private: |
| void* operator new(size_t) throw() { return nullptr; } |
| }; |
| |
| template <typename T> |
| inline bool operator==(const T* lhs, const StringViewTemplate<T>& rhs) { |
| return rhs == lhs; |
| } |
| template <typename T> |
| inline bool operator!=(const T* lhs, const StringViewTemplate<T>& rhs) { |
| return rhs != lhs; |
| } |
| template <typename T> |
| inline bool operator<(const T* lhs, const StringViewTemplate<T>& rhs) { |
| return rhs > lhs; |
| } |
| |
| extern template class StringViewTemplate<char>; |
| extern template class StringViewTemplate<wchar_t>; |
| |
| using ByteStringView = StringViewTemplate<char>; |
| using WideStringView = StringViewTemplate<wchar_t>; |
| |
| } // namespace fxcrt |
| |
| using ByteStringView = fxcrt::ByteStringView; |
| using WideStringView = fxcrt::WideStringView; |
| |
| #endif // CORE_FXCRT_STRING_VIEW_TEMPLATE_H_ |