1 // Copyright 2016 The PDFium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com 6 7 #ifndef CORE_FXCRT_STRING_VIEW_TEMPLATE_H_ 8 #define CORE_FXCRT_STRING_VIEW_TEMPLATE_H_ 9 10 #include <ctype.h> 11 12 #include <algorithm> 13 #include <iterator> 14 #include <type_traits> 15 16 #include "core/fxcrt/fx_memcpy_wrappers.h" 17 #include "core/fxcrt/fx_system.h" 18 #include "third_party/abseil-cpp/absl/types/optional.h" 19 #include "third_party/base/containers/span.h" 20 21 namespace fxcrt { 22 23 // An immutable string with caller-provided storage which must outlive the 24 // string itself. These are not necessarily nul-terminated, so that substring 25 // extraction (via the Substr(), First(), and Last() methods) is copy-free. 26 // 27 // String view arguments should be passed by value, since they are small, 28 // rather than const-ref, even if they are not modified. 29 // 30 // Front() and Back() tolerate empty strings and must return NUL in those 31 // cases. Substr(), First(), and Last() tolerate out-of-range indices and 32 // must return an empty string view in those cases. The aim here is allowing 33 // callers to avoid range-checking first. 34 template <typename T> 35 class StringViewTemplate { 36 public: 37 using CharType = T; 38 using UnsignedType = typename std::make_unsigned<CharType>::type; 39 using const_iterator = const CharType*; 40 using const_reverse_iterator = std::reverse_iterator<const_iterator>; 41 42 constexpr StringViewTemplate() noexcept = default; 43 constexpr StringViewTemplate(const StringViewTemplate& src) noexcept = 44 default; 45 46 // Deliberately implicit to avoid calling on every string literal. 47 // NOLINTNEXTLINE(runtime/explicit) StringViewTemplate(const CharType * ptr)48 StringViewTemplate(const CharType* ptr) noexcept 49 : m_Span(reinterpret_cast<const UnsignedType*>(ptr), 50 ptr ? FXSYS_len(ptr) : 0) {} 51 StringViewTemplate(const CharType * ptr,size_t size)52 constexpr StringViewTemplate(const CharType* ptr, size_t size) noexcept 53 : m_Span(reinterpret_cast<const UnsignedType*>(ptr), size) {} 54 55 template <typename E = typename std::enable_if< 56 !std::is_same<UnsignedType, CharType>::value>::type> StringViewTemplate(const UnsignedType * ptr,size_t size)57 constexpr StringViewTemplate(const UnsignedType* ptr, size_t size) noexcept 58 : m_Span(ptr, size) {} 59 StringViewTemplate(const pdfium::span<const CharType> & other)60 explicit constexpr StringViewTemplate( 61 const pdfium::span<const CharType>& other) noexcept 62 : m_Span(!other.empty() 63 ? reinterpret_cast<const UnsignedType*>(other.data()) 64 : nullptr, 65 other.size()) {} 66 67 template <typename E = typename std::enable_if< 68 !std::is_same<UnsignedType, CharType>::value>::type> StringViewTemplate(const pdfium::span<const UnsignedType> & other)69 constexpr StringViewTemplate( 70 const pdfium::span<const UnsignedType>& other) noexcept 71 : m_Span(!other.empty() ? other.data() : nullptr, other.size()) {} 72 73 // Deliberately implicit to avoid calling on every char literal. 74 // |ch| must be an lvalue that outlives the StringViewTemplate. 75 // NOLINTNEXTLINE(runtime/explicit) StringViewTemplate(const CharType & ch)76 constexpr StringViewTemplate(const CharType& ch) noexcept 77 : m_Span(reinterpret_cast<const UnsignedType*>(&ch), 1) {} 78 79 StringViewTemplate& operator=(const CharType* src) { 80 m_Span = pdfium::span<const UnsignedType>( 81 reinterpret_cast<const UnsignedType*>(src), src ? FXSYS_len(src) : 0); 82 return *this; 83 } 84 85 StringViewTemplate& operator=(const StringViewTemplate& src) { 86 m_Span = src.m_Span; 87 return *this; 88 } 89 begin()90 const_iterator begin() const { 91 return reinterpret_cast<const_iterator>(m_Span.begin()); 92 } end()93 const_iterator end() const { 94 return reinterpret_cast<const_iterator>(m_Span.end()); 95 } rbegin()96 const_reverse_iterator rbegin() const { 97 return const_reverse_iterator(end()); 98 } rend()99 const_reverse_iterator rend() const { 100 return const_reverse_iterator(begin()); 101 } 102 103 bool operator==(const StringViewTemplate& other) const { 104 return m_Span == other.m_Span; 105 } 106 bool operator==(const CharType* ptr) const { 107 StringViewTemplate other(ptr); 108 return *this == other; 109 } 110 bool operator!=(const CharType* ptr) const { return !(*this == ptr); } 111 bool operator!=(const StringViewTemplate& other) const { 112 return !(*this == other); 113 } 114 IsASCII()115 bool IsASCII() const { 116 for (auto c : *this) { 117 if (c <= 0 || c > 127) // Questionable signedness of |c|. 118 return false; 119 } 120 return true; 121 } 122 EqualsASCII(const StringViewTemplate<char> & that)123 bool EqualsASCII(const StringViewTemplate<char>& that) const { 124 size_t length = GetLength(); 125 if (length != that.GetLength()) 126 return false; 127 128 for (size_t i = 0; i < length; ++i) { 129 auto c = (*this)[i]; 130 if (c <= 0 || c > 127 || c != that[i]) // Questionable signedness of |c|. 131 return false; 132 } 133 return true; 134 } 135 EqualsASCIINoCase(const StringViewTemplate<char> & that)136 bool EqualsASCIINoCase(const StringViewTemplate<char>& that) const { 137 size_t length = GetLength(); 138 if (length != that.GetLength()) 139 return false; 140 141 for (size_t i = 0; i < length; ++i) { 142 auto c = (*this)[i]; 143 if (c <= 0 || c > 127 || tolower(c) != tolower(that[i])) 144 return false; 145 } 146 return true; 147 } 148 GetID()149 uint32_t GetID() const { 150 if (m_Span.empty()) 151 return 0; 152 153 uint32_t strid = 0; 154 size_t size = std::min(static_cast<size_t>(4), m_Span.size()); 155 for (size_t i = 0; i < size; i++) 156 strid = strid * 256 + m_Span[i]; 157 158 return strid << ((4 - size) * 8); 159 } 160 raw_span()161 pdfium::span<const UnsignedType> raw_span() const { return m_Span; } span()162 pdfium::span<const CharType> span() const { 163 return pdfium::make_span(reinterpret_cast<const CharType*>(m_Span.data()), 164 m_Span.size()); 165 } raw_str()166 const UnsignedType* raw_str() const { return m_Span.data(); } unterminated_c_str()167 const CharType* unterminated_c_str() const { 168 return reinterpret_cast<const CharType*>(m_Span.data()); 169 } 170 GetLength()171 size_t GetLength() const { return m_Span.size(); } IsEmpty()172 bool IsEmpty() const { return m_Span.empty(); } IsValidIndex(size_t index)173 bool IsValidIndex(size_t index) const { return index < m_Span.size(); } IsValidLength(size_t length)174 bool IsValidLength(size_t length) const { return length <= m_Span.size(); } 175 176 const UnsignedType& operator[](const size_t index) const { 177 return m_Span[index]; 178 } 179 Front()180 UnsignedType Front() const { return !m_Span.empty() ? m_Span[0] : 0; } Back()181 UnsignedType Back() const { 182 return !m_Span.empty() ? m_Span[m_Span.size() - 1] : 0; 183 } 184 CharAt(const size_t index)185 CharType CharAt(const size_t index) const { 186 return static_cast<CharType>(m_Span[index]); 187 } 188 Find(CharType ch)189 absl::optional<size_t> Find(CharType ch) const { 190 const auto* found = reinterpret_cast<const UnsignedType*>(FXSYS_chr( 191 reinterpret_cast<const CharType*>(m_Span.data()), ch, m_Span.size())); 192 193 return found ? absl::optional<size_t>(found - m_Span.data()) 194 : absl::nullopt; 195 } 196 Contains(CharType ch)197 bool Contains(CharType ch) const { return Find(ch).has_value(); } 198 Substr(size_t offset)199 StringViewTemplate Substr(size_t offset) const { 200 // Unsigned underflow is well-defined and out-of-range is handled by 201 // Substr(). 202 return Substr(offset, GetLength() - offset); 203 } 204 Substr(size_t first,size_t count)205 StringViewTemplate Substr(size_t first, size_t count) const { 206 if (!m_Span.data()) 207 return StringViewTemplate(); 208 209 if (!IsValidIndex(first)) 210 return StringViewTemplate(); 211 212 if (count == 0 || !IsValidLength(count)) 213 return StringViewTemplate(); 214 215 if (!IsValidIndex(first + count - 1)) 216 return StringViewTemplate(); 217 218 return StringViewTemplate(m_Span.subspan(first, count)); 219 } 220 First(size_t count)221 StringViewTemplate First(size_t count) const { 222 return Substr(0, count); 223 } 224 Last(size_t count)225 StringViewTemplate Last(size_t count) const { 226 // Unsigned underflow is well-defined and out-of-range is handled by 227 // Substr(). 228 return Substr(GetLength() - count, count); 229 } 230 TrimmedRight(CharType ch)231 StringViewTemplate TrimmedRight(CharType ch) const { 232 if (IsEmpty()) 233 return StringViewTemplate(); 234 235 size_t pos = GetLength(); 236 while (pos && CharAt(pos - 1) == ch) 237 pos--; 238 239 if (pos == 0) 240 return StringViewTemplate(); 241 242 return StringViewTemplate(m_Span.data(), pos); 243 } 244 245 bool operator<(const StringViewTemplate& that) const { 246 int result = 247 FXSYS_cmp(reinterpret_cast<const CharType*>(m_Span.data()), 248 reinterpret_cast<const CharType*>(that.m_Span.data()), 249 std::min(m_Span.size(), that.m_Span.size())); 250 return result < 0 || (result == 0 && m_Span.size() < that.m_Span.size()); 251 } 252 253 bool operator>(const StringViewTemplate& that) const { 254 int result = 255 FXSYS_cmp(reinterpret_cast<const CharType*>(m_Span.data()), 256 reinterpret_cast<const CharType*>(that.m_Span.data()), 257 std::min(m_Span.size(), that.m_Span.size())); 258 return result > 0 || (result == 0 && m_Span.size() > that.m_Span.size()); 259 } 260 261 protected: 262 pdfium::span<const UnsignedType> m_Span; 263 264 private: new(size_t)265 void* operator new(size_t) throw() { return nullptr; } 266 }; 267 268 template <typename T> 269 inline bool operator==(const T* lhs, const StringViewTemplate<T>& rhs) { 270 return rhs == lhs; 271 } 272 template <typename T> 273 inline bool operator!=(const T* lhs, const StringViewTemplate<T>& rhs) { 274 return rhs != lhs; 275 } 276 template <typename T> 277 inline bool operator<(const T* lhs, const StringViewTemplate<T>& rhs) { 278 return rhs > lhs; 279 } 280 281 extern template class StringViewTemplate<char>; 282 extern template class StringViewTemplate<wchar_t>; 283 284 using ByteStringView = StringViewTemplate<char>; 285 using WideStringView = StringViewTemplate<wchar_t>; 286 287 } // namespace fxcrt 288 289 using ByteStringView = fxcrt::ByteStringView; 290 using WideStringView = fxcrt::WideStringView; 291 292 #endif // CORE_FXCRT_STRING_VIEW_TEMPLATE_H_ 293