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