xref: /aosp_15_r20/external/pdfium/core/fxcrt/string_view_template.h (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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