// 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 #include #include #include #include #include #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 class StringViewTemplate { public: using CharType = T; using UnsignedType = typename std::make_unsigned::type; using const_iterator = const CharType*; using const_reverse_iterator = std::reverse_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(ptr), ptr ? std::char_traits::length(ptr) : 0))) {} explicit constexpr StringViewTemplate( const pdfium::span& other) noexcept { if (!other.empty()) { m_Span = reinterpret_span(other); } } template ::value>::type> explicit constexpr StringViewTemplate( const pdfium::span& 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(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(ptr), size))) {} template ::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(src), src ? std::char_traits::length(src) : 0)); return *this; } StringViewTemplate& operator=(const StringViewTemplate& src) { m_Span = src.m_Span; return *this; } const_iterator begin() const { return reinterpret_cast(m_Span.begin()); } const_iterator end() const { return reinterpret_cast(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& 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& 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(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 unsigned_span() const { return m_Span; } pdfium::span span() const { return reinterpret_span(m_Span); } const UnsignedType* unterminated_unsigned_str() const { return m_Span.data(); } const CharType* unterminated_c_str() const { return reinterpret_cast(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(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 Find(CharType ch) const { const auto* found = reinterpret_cast(std::char_traits::find( reinterpret_cast(m_Span.data()), m_Span.size(), ch)); return found ? std::optional(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::compare( reinterpret_cast(m_Span.data()), reinterpret_cast(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::compare( reinterpret_cast(m_Span.data()), reinterpret_cast(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 m_Span; private: void* operator new(size_t) throw() { return nullptr; } }; template inline bool operator==(const T* lhs, const StringViewTemplate& rhs) { return rhs == lhs; } template inline bool operator!=(const T* lhs, const StringViewTemplate& rhs) { return rhs != lhs; } template inline bool operator<(const T* lhs, const StringViewTemplate& rhs) { return rhs > lhs; } extern template class StringViewTemplate; extern template class StringViewTemplate; using ByteStringView = StringViewTemplate; using WideStringView = StringViewTemplate; } // namespace fxcrt using ByteStringView = fxcrt::ByteStringView; using WideStringView = fxcrt::WideStringView; #endif // CORE_FXCRT_STRING_VIEW_TEMPLATE_H_