| // 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_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) |
| constexpr StringViewTemplate(const CharType* ptr) noexcept |
| // SAFETY: caller ensures `ptr` is nul-terminated so `length()` is |
| // correct. |
| : span_(UNSAFE_BUFFERS( |
| pdfium::span(ptr, |
| ptr ? std::char_traits<CharType>::length(ptr) : 0))) {} |
| |
| explicit constexpr StringViewTemplate( |
| const pdfium::span<const CharType>& other) noexcept |
| : span_(other.empty() ? pdfium::span<const CharType>() : other) {} |
| |
| explicit StringViewTemplate( |
| const pdfium::span<const UnsignedType>& other) noexcept |
| requires(!std::is_same<UnsignedType, CharType>::value) |
| : span_(other.empty() |
| ? pdfium::span<const CharType>() |
| : UNSAFE_BUFFERS(pdfium::span( |
| reinterpret_cast<const CharType*>(other.data()), |
| other.size()))) {} |
| |
| // 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 |
| : span_(pdfium::span_from_ref(ch)) {} |
| |
| // PRECONDITIONS: `ptr` must point to `size` valid elements. |
| UNSAFE_BUFFER_USAGE |
| constexpr StringViewTemplate(const CharType* ptr, size_t size) noexcept |
| // SAFETY: propagated to caller via UNSAFE_BUFFER_USAGE. |
| : span_(UNSAFE_BUFFERS(pdfium::span(ptr, size))) {} |
| |
| // PRECONDITIONS: `ptr` must point to `size` valid elements. |
| UNSAFE_BUFFER_USAGE StringViewTemplate(const UnsignedType* ptr, |
| size_t size) noexcept |
| requires(!std::is_same<UnsignedType, CharType>::value) |
| // SAFETY: propagated to caller via UNSAFE_BUFFER_USAGE. |
| : span_(UNSAFE_BUFFERS( |
| pdfium::span(reinterpret_cast<const CharType*>(ptr), size))) {} |
| |
| constexpr StringViewTemplate& operator=(const CharType* src) { |
| // SAFETY: caller ensures `src` is nul-terminated so `length()` is correct. |
| span_ = UNSAFE_BUFFERS( |
| pdfium::span(src, src ? std::char_traits<CharType>::length(src) : 0)); |
| return *this; |
| } |
| |
| StringViewTemplate& operator=(const StringViewTemplate& src) { |
| span_ = src.span_; |
| return *this; |
| } |
| |
| constexpr const_iterator begin() const { return span_.begin(); } |
| constexpr const_iterator end() const { return span_.end(); } |
| const_reverse_iterator rbegin() const { |
| return const_reverse_iterator(end()); |
| } |
| const_reverse_iterator rend() const { |
| return const_reverse_iterator(begin()); |
| } |
| |
| friend bool operator==(const StringViewTemplate& lhs, |
| const StringViewTemplate& rhs) { |
| return lhs.span_ == rhs.span_; |
| } |
| friend bool operator==(const StringViewTemplate& lhs, const CharType* ptr) { |
| return lhs == StringViewTemplate(ptr); |
| } |
| |
| constexpr bool IsASCII() const { |
| for (auto c : *this) { |
| if (static_cast<UnsignedType>(c) > 127) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| constexpr 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 > 127 || |
| static_cast<CharType>(c) != static_cast<CharType>(that[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| constexpr 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 > 127 || ToLowerASCII(static_cast<CharType>(c)) != |
| ToLowerASCII(static_cast<CharType>(that[i]))) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| uint32_t GetID() const { |
| if (span_.empty()) { |
| return 0; |
| } |
| |
| uint32_t strid = 0; |
| size_t size = std::min(static_cast<size_t>(4), span_.size()); |
| for (size_t i = 0; i < size; i++) { |
| strid = strid * 256 + static_cast<UnsignedType>(span_[i]); |
| } |
| |
| return strid << ((4 - size) * 8); |
| } |
| |
| pdfium::span<const UnsignedType> unsigned_span() const { |
| return reinterpret_span<const UnsignedType>(span_); |
| } |
| constexpr pdfium::span<const CharType> span() const { return span_; } |
| const UnsignedType* unterminated_unsigned_str() const { |
| return reinterpret_cast<const UnsignedType*>(span_.data()); |
| } |
| constexpr const CharType* unterminated_c_str() const { return span_.data(); } |
| |
| constexpr size_t GetLength() const { return span_.size(); } |
| constexpr bool IsEmpty() const { return span_.empty(); } |
| constexpr bool IsValidIndex(size_t index) const { |
| return index < span_.size(); |
| } |
| constexpr bool IsValidLength(size_t length) const { |
| return length <= span_.size(); |
| } |
| |
| // CHECK() if index is out of range (via span's operator[]). |
| constexpr UnsignedType operator[](const size_t index) const { |
| return static_cast<UnsignedType>(span_[index]); |
| } |
| |
| // CHECK() if index is out of range (via span's operator[]). |
| constexpr CharType CharAt(const size_t index) const { return span_[index]; } |
| |
| // Unlike std::string_view::front(), this is always safe and returns a |
| // NUL char when the string is empty. |
| constexpr UnsignedType Front() const { |
| return !span_.empty() ? static_cast<UnsignedType>(span_.front()) : 0; |
| } |
| |
| // Unlike std::string_view::back(), this is always safe and returns a |
| // NUL char when the string is empty. |
| constexpr UnsignedType Back() const { |
| return !span_.empty() ? static_cast<UnsignedType>(span_.back()) : 0; |
| } |
| |
| constexpr std::optional<size_t> Find(CharType ch) const { |
| const auto* found = |
| std::char_traits<CharType>::find(span_.data(), span_.size(), ch); |
| return found ? std::optional<size_t>(found - span_.data()) : std::nullopt; |
| } |
| |
| constexpr bool Contains(CharType ch) const { return Find(ch).has_value(); } |
| |
| constexpr StringViewTemplate Substr(size_t offset) const { |
| // Unsigned underflow is well-defined and out-of-range is handled by |
| // Substr(). |
| return Substr(offset, GetLength() - offset); |
| } |
| |
| constexpr StringViewTemplate Substr(size_t first, size_t count) const { |
| if (!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(span_.data() + first, count)); |
| } |
| |
| constexpr StringViewTemplate First(size_t count) const { |
| return Substr(0, count); |
| } |
| |
| constexpr StringViewTemplate Last(size_t count) const { |
| // Unsigned underflow is well-defined and out-of-range is handled by |
| // Substr(). |
| return Substr(GetLength() - count, count); |
| } |
| |
| constexpr 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(span_.data(), pos)); |
| } |
| |
| constexpr bool operator<(const StringViewTemplate& that) const { |
| const size_t common_size = std::min(span_.size(), that.span_.size()); |
| int result = common_size ? std::char_traits<CharType>::compare( |
| span_.data(), that.span_.data(), common_size) |
| : 0; |
| return result < 0 || (result == 0 && span_.size() < that.span_.size()); |
| } |
| |
| constexpr bool operator>(const StringViewTemplate& that) const { |
| const size_t common_size = std::min(span_.size(), that.span_.size()); |
| int result = common_size ? std::char_traits<CharType>::compare( |
| span_.data(), that.span_.data(), common_size) |
| : 0; |
| return result > 0 || (result == 0 && span_.size() > that.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 CharType> span_; |
| |
| private: |
| // Need a constexpr version here rather than the non-constexpr versions |
| // in fx_extension.h. |
| static constexpr CharType ToLowerASCII(CharType c) { |
| return c >= 'A' && c <= 'Z' ? static_cast<CharType>(c + ('a' - 'A')) : c; |
| } |
| |
| void* operator new(size_t) = delete; |
| }; |
| |
| 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_ |