1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef nsTStringRepr_h
8 #define nsTStringRepr_h
9 
10 #include <type_traits>  // std::enable_if
11 
12 #include "mozilla/Char16.h"
13 #include "mozilla/fallible.h"
14 #include "nsStringFlags.h"
15 #include "nsCharTraits.h"
16 
17 template <typename T>
18 class nsTSubstringTuple;
19 template <typename T>
20 class nsTLiteralString;
21 
22 // The base for string comparators
23 template <typename T>
24 class nsTStringComparator {
25  public:
26   typedef T char_type;
27 
nsTStringComparator()28   nsTStringComparator() {}
29 
30   virtual int operator()(const char_type*, const char_type*, uint32_t,
31                          uint32_t) const = 0;
32 };
33 
34 // The default string comparator (case-sensitive comparision)
35 template <typename T>
36 class nsTDefaultStringComparator : public nsTStringComparator<T> {
37  public:
38   typedef T char_type;
39 
nsTDefaultStringComparator()40   nsTDefaultStringComparator() {}
41 
42   virtual int operator()(const char_type*, const char_type*, uint32_t,
43                          uint32_t) const override;
44 };
45 
46 extern template class nsTDefaultStringComparator<char>;
47 extern template class nsTDefaultStringComparator<char16_t>;
48 
49 namespace mozilla {
50 
51 // This is mainly intended to be used in the context of nsTStrings where
52 // we want to enable a specific function only for a given character class. In
53 // order for this technique to work the member function needs to be templated
54 // on something other than `T`. We keep this in the `mozilla` namespace rather
55 // than `nsTStringRepr` as it's intentionally not dependent on `T`.
56 //
57 // The 'T' at the end of `Char[16]OnlyT` is refering to the `::type` portion
58 // which will only be defined if the character class is correct. This is similar
59 // to `std::enable_if_t` which is available in C++14, but not C++11.
60 //
61 // `CharType` is generally going to be a shadowed type of `T`.
62 //
63 // Example usage of a function that will only be defined if `T` == `char`:
64 //
65 // template <typename T>
66 // class nsTSubstring : public nsTStringRepr<T> {
67 //   template <typename Q = T, typename EnableForChar = typename CharOnlyT<Q>>
68 //   int Foo() { return 42; }
69 // };
70 //
71 // Please note that we had to use a separate type `Q` for this to work. You
72 // will get a semi-decent compiler error if you use `T` directly.
73 
74 template <typename CharType>
75 using CharOnlyT =
76     typename std::enable_if<std::is_same<char, CharType>::value>::type;
77 
78 template <typename CharType>
79 using Char16OnlyT =
80     typename std::enable_if<std::is_same<char16_t, CharType>::value>::type;
81 
82 namespace detail {
83 
84 // nsTStringRepr defines a string's memory layout and some accessor methods.
85 // This class exists so that nsTLiteralString can avoid inheriting
86 // nsTSubstring's destructor. All methods on this class must be const because
87 // literal strings are not writable.
88 //
89 // This class is an implementation detail and should not be instantiated
90 // directly, nor used in any way outside of the string code itself. It is
91 // buried in a namespace to discourage its use in function parameters.
92 // If you need to take a parameter, use [const] ns[C]Substring&.
93 // If you need to instantiate a string, use ns[C]String or descendents.
94 //
95 // NAMES:
96 //   nsStringRepr for wide characters
97 //   nsCStringRepr for narrow characters
98 template <typename T>
99 class nsTStringRepr {
100  public:
101   typedef mozilla::fallible_t fallible_t;
102 
103   typedef T char_type;
104 
105   typedef nsCharTraits<char_type> char_traits;
106   typedef typename char_traits::incompatible_char_type incompatible_char_type;
107 
108   typedef nsTStringRepr<T> self_type;
109   typedef self_type base_string_type;
110 
111   typedef nsTSubstring<T> substring_type;
112   typedef nsTSubstringTuple<T> substring_tuple_type;
113   typedef nsTLiteralString<T> literalstring_type;
114 
115   typedef nsReadingIterator<char_type> const_iterator;
116   typedef nsWritingIterator<char_type> iterator;
117 
118   typedef nsTStringComparator<char_type> comparator_type;
119 
120   typedef char_type* char_iterator;
121   typedef const char_type* const_char_iterator;
122 
123   typedef uint32_t index_type;
124   typedef uint32_t size_type;
125 
126   // These are only for internal use within the string classes:
127   typedef StringDataFlags DataFlags;
128   typedef StringClassFlags ClassFlags;
129 
130   // Reading iterators.
BeginReading()131   const_char_iterator BeginReading() const { return mData; }
EndReading()132   const_char_iterator EndReading() const { return mData + mLength; }
133 
134   // Deprecated reading iterators.
BeginReading(const_iterator & aIter)135   const_iterator& BeginReading(const_iterator& aIter) const {
136     aIter.mStart = mData;
137     aIter.mEnd = mData + mLength;
138     aIter.mPosition = aIter.mStart;
139     return aIter;
140   }
141 
EndReading(const_iterator & aIter)142   const_iterator& EndReading(const_iterator& aIter) const {
143     aIter.mStart = mData;
144     aIter.mEnd = mData + mLength;
145     aIter.mPosition = aIter.mEnd;
146     return aIter;
147   }
148 
BeginReading(const_char_iterator & aIter)149   const_char_iterator& BeginReading(const_char_iterator& aIter) const {
150     return aIter = mData;
151   }
152 
EndReading(const_char_iterator & aIter)153   const_char_iterator& EndReading(const_char_iterator& aIter) const {
154     return aIter = mData + mLength;
155   }
156 
157   // Accessors.
158   template <typename U, typename Dummy>
159   struct raw_type {
160     typedef const U* type;
161   };
162 #if defined(MOZ_USE_CHAR16_WRAPPER)
163   template <typename Dummy>
164   struct raw_type<char16_t, Dummy> {
165     typedef char16ptr_t type;
166   };
167 #endif
168 
169   // Returns pointer to string data (not necessarily null-terminated)
170   const typename raw_type<T, int>::type Data() const { return mData; }
171 
172   size_type Length() const { return mLength; }
173 
174   DataFlags GetDataFlags() const { return mDataFlags; }
175 
176   bool IsEmpty() const { return mLength == 0; }
177 
178   bool IsLiteral() const { return !!(mDataFlags & DataFlags::LITERAL); }
179 
180   bool IsVoid() const { return !!(mDataFlags & DataFlags::VOIDED); }
181 
182   bool IsTerminated() const { return !!(mDataFlags & DataFlags::TERMINATED); }
183 
184   char_type CharAt(index_type aIndex) const {
185     NS_ASSERTION(aIndex < mLength, "index exceeds allowable range");
186     return mData[aIndex];
187   }
188 
189   char_type operator[](index_type aIndex) const { return CharAt(aIndex); }
190 
191   char_type First() const;
192 
193   char_type Last() const;
194 
195   size_type NS_FASTCALL CountChar(char_type) const;
196   int32_t NS_FASTCALL FindChar(char_type, index_type aOffset = 0) const;
197 
198   inline bool Contains(char_type aChar) const {
199     return FindChar(aChar) != kNotFound;
200   }
201 
202   // Equality.
203   bool NS_FASTCALL Equals(const self_type&) const;
204   bool NS_FASTCALL Equals(const self_type&, const comparator_type&) const;
205 
206   bool NS_FASTCALL Equals(const substring_tuple_type& aTuple) const;
207   bool NS_FASTCALL Equals(const substring_tuple_type& aTuple,
208                           const comparator_type& aComp) const;
209 
210   bool NS_FASTCALL Equals(const char_type* aData) const;
211   bool NS_FASTCALL Equals(const char_type* aData,
212                           const comparator_type& aComp) const;
213 
214 #if defined(MOZ_USE_CHAR16_WRAPPER)
215   template <typename Q = T, typename EnableIfChar16 = Char16OnlyT<Q>>
216   bool NS_FASTCALL Equals(char16ptr_t aData) const {
217     return Equals(static_cast<const char16_t*>(aData));
218   }
219   template <typename Q = T, typename EnableIfChar16 = Char16OnlyT<Q>>
220   bool NS_FASTCALL Equals(char16ptr_t aData,
221                           const comparator_type& aComp) const {
222     return Equals(static_cast<const char16_t*>(aData), aComp);
223   }
224 #endif
225 
226   // An efficient comparison with ASCII that can be used even
227   // for wide strings. Call this version when you know the
228   // length of 'data'.
229   bool NS_FASTCALL EqualsASCII(const char* aData, size_type aLen) const;
230   // An efficient comparison with ASCII that can be used even
231   // for wide strings. Call this version when 'data' is
232   // null-terminated.
233   bool NS_FASTCALL EqualsASCII(const char* aData) const;
234 
235   // EqualsLiteral must ONLY be applied to an actual literal string, or
236   // a char array *constant* declared without an explicit size.
237   // Do not attempt to use it with a regular char* pointer, or with a
238   // non-constant char array variable. Use EqualsASCII for them.
239   // The template trick to acquire the array length at compile time without
240   // using a macro is due to Corey Kosak, with much thanks.
241   template <int N>
242   inline bool EqualsLiteral(const char (&aStr)[N]) const {
243     return EqualsASCII(aStr, N - 1);
244   }
245 
246   // The LowerCaseEquals methods compare the ASCII-lowercase version of
247   // this string (lowercasing only ASCII uppercase characters) to some
248   // ASCII/Literal string. The ASCII string is *not* lowercased for
249   // you. If you compare to an ASCII or literal string that contains an
250   // uppercase character, it is guaranteed to return false. We will
251   // throw assertions too.
252   bool NS_FASTCALL LowerCaseEqualsASCII(const char* aData,
253                                         size_type aLen) const;
254   bool NS_FASTCALL LowerCaseEqualsASCII(const char* aData) const;
255 
256   // LowerCaseEqualsLiteral must ONLY be applied to an actual
257   // literal string, or a char array *constant* declared without an
258   // explicit size.  Do not attempt to use it with a regular char*
259   // pointer, or with a non-constant char array variable. Use
260   // LowerCaseEqualsASCII for them.
261   template <int N>
262   bool LowerCaseEqualsLiteral(const char (&aStr)[N]) const {
263     return LowerCaseEqualsASCII(aStr, N - 1);
264   }
265 
266   // Returns true if this string overlaps with the given string fragment.
267   bool IsDependentOn(const char_type* aStart, const char_type* aEnd) const {
268     // If it _isn't_ the case that one fragment starts after the other ends,
269     // or ends before the other starts, then, they conflict:
270     //
271     //   !(f2.begin >= f1.aEnd || f2.aEnd <= f1.begin)
272     //
273     // Simplified, that gives us:
274     return (aStart < (mData + mLength) && aEnd > mData);
275   }
276 
277  protected:
278   nsTStringRepr() = delete;  // Never instantiate directly
279 
280   constexpr nsTStringRepr(char_type* aData, size_type aLength,
281                           DataFlags aDataFlags, ClassFlags aClassFlags)
282       : mData(aData),
283         mLength(aLength),
284         mDataFlags(aDataFlags),
285         mClassFlags(aClassFlags) {}
286 
287   char_type* mData;
288   size_type mLength;
289   DataFlags mDataFlags;
290   ClassFlags const mClassFlags;
291 };
292 
293 extern template class nsTStringRepr<char>;
294 extern template class nsTStringRepr<char16_t>;
295 
296 }  // namespace detail
297 }  // namespace mozilla
298 
299 template <typename T>
300 int NS_FASTCALL
301 Compare(const mozilla::detail::nsTStringRepr<T>& aLhs,
302         const mozilla::detail::nsTStringRepr<T>& aRhs,
303         const nsTStringComparator<T>& = nsTDefaultStringComparator<T>());
304 
305 template <typename T>
306 inline bool operator!=(const mozilla::detail::nsTStringRepr<T>& aLhs,
307                        const mozilla::detail::nsTStringRepr<T>& aRhs) {
308   return !aLhs.Equals(aRhs);
309 }
310 
311 template <typename T>
312 inline bool operator!=(const mozilla::detail::nsTStringRepr<T>& aLhs,
313                        const T* aRhs) {
314   return !aLhs.Equals(aRhs);
315 }
316 
317 template <typename T>
318 inline bool operator<(const mozilla::detail::nsTStringRepr<T>& aLhs,
319                       const mozilla::detail::nsTStringRepr<T>& aRhs) {
320   return Compare(aLhs, aRhs) < 0;
321 }
322 
323 template <typename T>
324 inline bool operator<=(const mozilla::detail::nsTStringRepr<T>& aLhs,
325                        const mozilla::detail::nsTStringRepr<T>& aRhs) {
326   return Compare(aLhs, aRhs) <= 0;
327 }
328 
329 template <typename T>
330 inline bool operator==(const mozilla::detail::nsTStringRepr<T>& aLhs,
331                        const mozilla::detail::nsTStringRepr<T>& aRhs) {
332   return aLhs.Equals(aRhs);
333 }
334 
335 template <typename T>
336 inline bool operator==(const mozilla::detail::nsTStringRepr<T>& aLhs,
337                        const T* aRhs) {
338   return aLhs.Equals(aRhs);
339 }
340 
341 template <typename T>
342 inline bool operator>=(const mozilla::detail::nsTStringRepr<T>& aLhs,
343                        const mozilla::detail::nsTStringRepr<T>& aRhs) {
344   return Compare(aLhs, aRhs) >= 0;
345 }
346 
347 template <typename T>
348 inline bool operator>(const mozilla::detail::nsTStringRepr<T>& aLhs,
349                       const mozilla::detail::nsTStringRepr<T>& aRhs) {
350   return Compare(aLhs, aRhs) > 0;
351 }
352 
353 #endif
354