Bug 1869043 assert that graph set access is main thread only r=padenot
[gecko.git] / xpcom / string / nsTStringRepr.h
blob7e7fa5338404001b0e46322c41c57e6f5da81d98
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/. */
7 #ifndef nsTStringRepr_h
8 #define nsTStringRepr_h
10 #include <limits>
11 #include <string_view>
12 #include <type_traits> // std::enable_if
14 #include "mozilla/Char16.h"
15 #include "mozilla/CheckedInt.h"
16 #include "mozilla/fallible.h"
17 #include "nsStringBuffer.h"
18 #include "nsStringFlags.h"
19 #include "nsStringFwd.h"
20 #include "nsStringIterator.h"
21 #include "nsCharTraits.h"
23 template <typename T>
24 class nsTSubstringTuple;
26 namespace mozilla {
28 // This is mainly intended to be used in the context of nsTStrings where
29 // we want to enable a specific function only for a given character class. In
30 // order for this technique to work the member function needs to be templated
31 // on something other than `T`. We keep this in the `mozilla` namespace rather
32 // than `nsTStringRepr` as it's intentionally not dependent on `T`.
34 // The 'T' at the end of `Char[16]OnlyT` is refering to the `::type` portion
35 // which will only be defined if the character class is correct. This is similar
36 // to `std::enable_if_t` which is available in C++14, but not C++11.
38 // `CharType` is generally going to be a shadowed type of `T`.
40 // Example usage of a function that will only be defined if `T` == `char`:
42 // template <typename T>
43 // class nsTSubstring : public nsTStringRepr<T> {
44 // template <typename Q = T, typename EnableForChar = typename CharOnlyT<Q>>
45 // int Foo() { return 42; }
46 // };
48 // Please note that we had to use a separate type `Q` for this to work. You
49 // will get a semi-decent compiler error if you use `T` directly.
51 template <typename CharType>
52 using CharOnlyT =
53 typename std::enable_if<std::is_same<char, CharType>::value>::type;
55 template <typename CharType>
56 using Char16OnlyT =
57 typename std::enable_if<std::is_same<char16_t, CharType>::value>::type;
59 namespace detail {
61 // nsTStringLengthStorage is a helper class which holds the string's length and
62 // provides getters and setters for converting to and from `size_t`. This is
63 // done to allow the length to be stored in a `uint32_t` using assertions.
64 template <typename T>
65 class nsTStringLengthStorage {
66 public:
67 // The maximum byte capacity for a `nsTString` must fit within an `int32_t`,
68 // with enough room for a trailing null, as consumers often cast `Length()`
69 // and `Capacity()` to smaller types like `int32_t`.
70 static constexpr size_t kMax =
71 size_t{std::numeric_limits<int32_t>::max()} / sizeof(T) - 1;
72 static_assert(
73 (kMax + 1) * sizeof(T) <= std::numeric_limits<int32_t>::max(),
74 "nsTString's maximum length, including the trailing null, must fit "
75 "within `int32_t`, as callers will cast to `int32_t` occasionally");
76 static_assert(((CheckedInt<uint32_t>{kMax} + 1) * sizeof(T) +
77 sizeof(nsStringBuffer))
78 .isValid(),
79 "Math required to allocate a nsStringBuffer for a "
80 "maximum-capacity string must not overflow uint32_t");
82 // Implicit conversion and assignment from `size_t` which assert that the
83 // value is in-range.
84 MOZ_IMPLICIT constexpr nsTStringLengthStorage(size_t aLength)
85 : mLength(static_cast<uint32_t>(aLength)) {
86 MOZ_RELEASE_ASSERT(aLength <= kMax, "string is too large");
88 constexpr nsTStringLengthStorage& operator=(size_t aLength) {
89 MOZ_RELEASE_ASSERT(aLength <= kMax, "string is too large");
90 mLength = static_cast<uint32_t>(aLength);
91 return *this;
93 MOZ_IMPLICIT constexpr operator size_t() const { return mLength; }
95 private:
96 uint32_t mLength = 0;
99 // nsTStringRepr defines a string's memory layout and some accessor methods.
100 // This class exists so that nsTLiteralString can avoid inheriting
101 // nsTSubstring's destructor. All methods on this class must be const because
102 // literal strings are not writable.
104 // This class is an implementation detail and should not be instantiated
105 // directly, nor used in any way outside of the string code itself. It is
106 // buried in a namespace to discourage its use in function parameters.
107 // If you need to take a parameter, use [const] ns[C]Substring&.
108 // If you need to instantiate a string, use ns[C]String or descendents.
110 // NAMES:
111 // nsStringRepr for wide characters
112 // nsCStringRepr for narrow characters
113 template <typename T>
114 class nsTStringRepr {
115 public:
116 typedef mozilla::fallible_t fallible_t;
118 typedef T char_type;
120 typedef nsCharTraits<char_type> char_traits;
121 typedef typename char_traits::incompatible_char_type incompatible_char_type;
123 typedef nsTStringRepr<T> self_type;
124 typedef self_type base_string_type;
126 typedef nsTSubstring<T> substring_type;
127 typedef nsTSubstringTuple<T> substring_tuple_type;
129 typedef nsReadingIterator<char_type> const_iterator;
130 typedef char_type* iterator;
132 typedef nsTStringComparator<char_type> comparator_type;
134 typedef const char_type* const_char_iterator;
136 typedef std::basic_string_view<char_type> string_view;
138 typedef size_t index_type;
139 typedef size_t size_type;
141 // These are only for internal use within the string classes:
142 typedef StringDataFlags DataFlags;
143 typedef StringClassFlags ClassFlags;
144 typedef nsTStringLengthStorage<T> LengthStorage;
146 // Reading iterators.
147 constexpr const_char_iterator BeginReading() const { return mData; }
148 constexpr const_char_iterator EndReading() const { return mData + mLength; }
150 // Deprecated reading iterators.
151 const_iterator& BeginReading(const_iterator& aIter) const {
152 aIter.mStart = mData;
153 aIter.mEnd = mData + mLength;
154 aIter.mPosition = aIter.mStart;
155 return aIter;
158 const_iterator& EndReading(const_iterator& aIter) const {
159 aIter.mStart = mData;
160 aIter.mEnd = mData + mLength;
161 aIter.mPosition = aIter.mEnd;
162 return aIter;
165 const_char_iterator& BeginReading(const_char_iterator& aIter) const {
166 return aIter = mData;
169 const_char_iterator& EndReading(const_char_iterator& aIter) const {
170 return aIter = mData + mLength;
173 // Accessors.
174 template <typename U, typename Dummy>
175 struct raw_type {
176 typedef const U* type;
178 #if defined(MOZ_USE_CHAR16_WRAPPER)
179 template <typename Dummy>
180 struct raw_type<char16_t, Dummy> {
181 typedef char16ptr_t type;
183 #endif
185 // Returns pointer to string data (not necessarily null-terminated)
186 constexpr typename raw_type<T, int>::type Data() const { return mData; }
188 constexpr size_type Length() const { return static_cast<size_type>(mLength); }
190 constexpr string_view View() const { return string_view(Data(), Length()); }
192 constexpr operator string_view() const { return View(); }
194 constexpr DataFlags GetDataFlags() const { return mDataFlags; }
196 constexpr bool IsEmpty() const { return mLength == 0; }
198 constexpr bool IsLiteral() const {
199 return !!(mDataFlags & DataFlags::LITERAL);
202 constexpr bool IsVoid() const { return !!(mDataFlags & DataFlags::VOIDED); }
204 constexpr bool IsTerminated() const {
205 return !!(mDataFlags & DataFlags::TERMINATED);
208 constexpr char_type CharAt(index_type aIndex) const {
209 NS_ASSERTION(aIndex < Length(), "index exceeds allowable range");
210 return mData[aIndex];
213 constexpr char_type operator[](index_type aIndex) const {
214 return CharAt(aIndex);
217 char_type First() const;
219 char_type Last() const;
221 // Equality.
222 bool NS_FASTCALL Equals(const self_type&) const;
223 bool NS_FASTCALL Equals(const self_type&, comparator_type) const;
225 bool NS_FASTCALL Equals(const substring_tuple_type& aTuple) const;
226 bool NS_FASTCALL Equals(const substring_tuple_type& aTuple,
227 comparator_type) const;
229 bool NS_FASTCALL Equals(const char_type* aData) const;
230 bool NS_FASTCALL Equals(const char_type* aData, comparator_type) const;
233 * Compare this string and another ASCII-case-insensitively.
235 * This method is similar to `LowerCaseEqualsASCII` however both strings are
236 * lowercased, meaning that `aString` need not be all lowercase.
238 * @param aString is the string to check
239 * @return boolean
241 bool EqualsIgnoreCase(const std::string_view& aString) const;
243 #if defined(MOZ_USE_CHAR16_WRAPPER)
244 template <typename Q = T, typename EnableIfChar16 = Char16OnlyT<Q>>
245 bool NS_FASTCALL Equals(char16ptr_t aData) const {
246 return Equals(static_cast<const char16_t*>(aData));
248 template <typename Q = T, typename EnableIfChar16 = Char16OnlyT<Q>>
249 bool NS_FASTCALL Equals(char16ptr_t aData, comparator_type aComp) const {
250 return Equals(static_cast<const char16_t*>(aData), aComp);
252 #endif
254 // An efficient comparison with ASCII that can be used even
255 // for wide strings. Call this version when you know the
256 // length of 'data'.
257 bool NS_FASTCALL EqualsASCII(const char* aData, size_type aLen) const;
258 // An efficient comparison with ASCII that can be used even
259 // for wide strings. Call this version when 'data' is
260 // null-terminated.
261 bool NS_FASTCALL EqualsASCII(const char* aData) const;
263 // An efficient comparison with Latin1 characters that can be used even for
264 // wide strings.
265 bool EqualsLatin1(const char* aData, size_type aLength) const;
267 // EqualsLiteral must ONLY be called with an actual literal string, or
268 // a char array *constant* declared without an explicit size and with an
269 // initializer that is a string literal or is otherwise null-terminated.
270 // Use EqualsASCII for other char array variables.
271 // (Although this method may happen to produce expected results for other
272 // char arrays that have bound one greater than the sequence of interest,
273 // such use is discouraged for reasons of readability and maintainability.)
274 // The template trick to acquire the array bound at compile time without
275 // using a macro is due to Corey Kosak, with much thanks.
276 template <int N>
277 inline bool EqualsLiteral(const char (&aStr)[N]) const {
278 return EqualsASCII(aStr, N - 1);
281 // EqualsLiteral must ONLY be called with an actual literal string, or
282 // a char array *constant* declared without an explicit size and with an
283 // initializer that is a string literal or is otherwise null-terminated.
284 // Use EqualsASCII for other char array variables.
285 // (Although this method may happen to produce expected results for other
286 // char arrays that have bound one greater than the sequence of interest,
287 // such use is discouraged for reasons of readability and maintainability.)
288 // The template trick to acquire the array bound at compile time without
289 // using a macro is due to Corey Kosak, with much thanks.
290 template <size_t N, typename = std::enable_if_t<!std::is_same_v<
291 const char (&)[N], const char_type (&)[N]>>>
292 inline bool EqualsLiteral(const char_type (&aStr)[N]) const {
293 return *this == nsTLiteralString<char_type>(aStr);
296 // The LowerCaseEquals methods compare the ASCII-lowercase version of
297 // this string (lowercasing only ASCII uppercase characters) to some
298 // ASCII/Literal string. The ASCII string is *not* lowercased for
299 // you. If you compare to an ASCII or literal string that contains an
300 // uppercase character, it is guaranteed to return false. We will
301 // throw assertions too.
302 bool NS_FASTCALL LowerCaseEqualsASCII(const char* aData,
303 size_type aLen) const;
304 bool NS_FASTCALL LowerCaseEqualsASCII(const char* aData) const;
306 // LowerCaseEqualsLiteral must ONLY be called with an actual literal string,
307 // or a char array *constant* declared without an explicit size and with an
308 // initializer that is a string literal or is otherwise null-terminated.
309 // Use LowerCaseEqualsASCII for other char array variables.
310 // (Although this method may happen to produce expected results for other
311 // char arrays that have bound one greater than the sequence of interest,
312 // such use is discouraged for reasons of readability and maintainability.)
313 template <int N>
314 bool LowerCaseEqualsLiteral(const char (&aStr)[N]) const {
315 return LowerCaseEqualsASCII(aStr, N - 1);
318 // Returns true if this string overlaps with the given string fragment.
319 bool IsDependentOn(const char_type* aStart, const char_type* aEnd) const {
320 // If it _isn't_ the case that one fragment starts after the other ends,
321 // or ends before the other starts, then, they conflict:
323 // !(f2.begin >= f1.aEnd || f2.aEnd <= f1.begin)
325 // Simplified, that gives us (To avoid relying on Undefined Behavior
326 // from comparing pointers from different allocations (which in
327 // principle gives the optimizer the permission to assume elsewhere
328 // that the pointers are from the same allocation), the comparisons
329 // are done on integers, which merely relies on implementation-defined
330 // behavior of converting pointers to integers. std::less and
331 // std::greater implementations don't actually provide the guarantees
332 // that they should.):
333 return (reinterpret_cast<uintptr_t>(aStart) <
334 reinterpret_cast<uintptr_t>(mData + mLength) &&
335 reinterpret_cast<uintptr_t>(aEnd) >
336 reinterpret_cast<uintptr_t>(mData));
340 * Search for the given substring within this string.
342 * @param aString is substring to be sought in this
343 * @param aOffset tells us where in this string to start searching
344 * @return offset in string, or kNotFound
346 int32_t Find(const string_view& aString, index_type aOffset = 0) const;
348 // Previously there was an overload of `Find()` which took a bool second
349 // argument. Avoid issues by explicitly preventing that overload.
350 // TODO: Remove this at some point.
351 template <typename I,
352 typename = std::enable_if_t<!std::is_same_v<I, index_type> &&
353 std::is_convertible_v<I, index_type>>>
354 int32_t Find(const string_view& aString, I aOffset) const {
355 static_assert(!std::is_same_v<I, bool>, "offset must not be `bool`");
356 return Find(aString, static_cast<index_type>(aOffset));
360 * Search for the given ASCII substring within this string, ignoring case.
362 * @param aString is substring to be sought in this
363 * @param aOffset tells us where in this string to start searching
364 * @return offset in string, or kNotFound
366 int32_t LowerCaseFindASCII(const std::string_view& aString,
367 index_type aOffset = 0) const;
370 * Scan the string backwards, looking for the given substring.
372 * @param aString is substring to be sought in this
373 * @return offset in string, or kNotFound
375 int32_t RFind(const string_view& aString) const;
377 size_type CountChar(char_type) const;
379 bool Contains(char_type aChar) const { return FindChar(aChar) != kNotFound; }
382 * Search for the first instance of a given char within this string
384 * @param aChar is the character to search for
385 * @param aOffset tells us where in this string to start searching
386 * @return offset in string, or kNotFound
388 int32_t FindChar(char_type aChar, index_type aOffset = 0) const;
391 * Search for the last instance of a given char within this string
393 * @param aChar is the character to search for
394 * @param aOffset tells us where in this string to start searching
395 * @return offset in string, or kNotFound
397 int32_t RFindChar(char_type aChar, int32_t aOffset = -1) const;
400 * This method searches this string for the first character found in
401 * the given string.
403 * @param aSet contains set of chars to be found
404 * @param aOffset tells us where in this string to start searching
405 * (counting from left)
406 * @return offset in string, or kNotFound
409 int32_t FindCharInSet(const string_view& aSet, index_type aOffset = 0) const;
412 * This method searches this string for the last character found in
413 * the given string.
415 * @param aSet contains set of chars to be found
416 * @param aOffset tells us where in this string to start searching
417 * (counting from left)
418 * @return offset in string, or kNotFound
421 int32_t RFindCharInSet(const string_view& aSet, int32_t aOffset = -1) const;
424 * Perform locale-independent string to double-precision float conversion.
426 * Leading spaces in the string will be ignored. The returned value will be
427 * finite unless aErrorCode is set to a failed status.
429 * @param aErrorCode will contain error if one occurs
430 * @return double-precision float rep of string value
432 double ToDouble(nsresult* aErrorCode) const;
435 * Perform locale-independent string to single-precision float conversion.
437 * Leading spaces in the string will be ignored. The returned value will be
438 * finite unless aErrorCode is set to a failed status.
440 * @param aErrorCode will contain error if one occurs
441 * @return single-precision float rep of string value
443 float ToFloat(nsresult* aErrorCode) const;
446 * Similar to above ToDouble and ToFloat but allows trailing characters that
447 * are not converted.
449 double ToDoubleAllowTrailingChars(nsresult* aErrorCode) const;
450 float ToFloatAllowTrailingChars(nsresult* aErrorCode) const;
452 protected:
453 nsTStringRepr() = delete; // Never instantiate directly
455 constexpr nsTStringRepr(char_type* aData, size_type aLength,
456 DataFlags aDataFlags, ClassFlags aClassFlags)
457 : mData(aData),
458 mLength(aLength),
459 mDataFlags(aDataFlags),
460 mClassFlags(aClassFlags) {}
462 static constexpr size_type kMaxCapacity = LengthStorage::kMax;
465 * Checks if the given capacity is valid for this string type.
467 [[nodiscard]] static constexpr bool CheckCapacity(size_type aCapacity) {
468 return aCapacity <= kMaxCapacity;
471 char_type* mData;
472 LengthStorage mLength;
473 DataFlags mDataFlags;
474 ClassFlags const mClassFlags;
477 extern template class nsTStringRepr<char>;
478 extern template class nsTStringRepr<char16_t>;
480 } // namespace detail
481 } // namespace mozilla
483 template <typename T>
484 int NS_FASTCALL Compare(const mozilla::detail::nsTStringRepr<T>& aLhs,
485 const mozilla::detail::nsTStringRepr<T>& aRhs,
486 nsTStringComparator<T> = nsTDefaultStringComparator<T>);
488 extern template int NS_FASTCALL Compare<char>(
489 const mozilla::detail::nsTStringRepr<char>&,
490 const mozilla::detail::nsTStringRepr<char>&, nsTStringComparator<char>);
492 extern template int NS_FASTCALL
493 Compare<char16_t>(const mozilla::detail::nsTStringRepr<char16_t>&,
494 const mozilla::detail::nsTStringRepr<char16_t>&,
495 nsTStringComparator<char16_t>);
497 template <typename T>
498 inline constexpr bool operator!=(
499 const mozilla::detail::nsTStringRepr<T>& aLhs,
500 const mozilla::detail::nsTStringRepr<T>& aRhs) {
501 return !aLhs.Equals(aRhs);
504 template <typename T>
505 inline constexpr bool operator!=(const mozilla::detail::nsTStringRepr<T>& aLhs,
506 const T* aRhs) {
507 return !aLhs.Equals(aRhs);
510 template <typename T>
511 inline bool operator<(const mozilla::detail::nsTStringRepr<T>& aLhs,
512 const mozilla::detail::nsTStringRepr<T>& aRhs) {
513 return Compare(aLhs, aRhs) < 0;
516 template <typename T>
517 inline bool operator<=(const mozilla::detail::nsTStringRepr<T>& aLhs,
518 const mozilla::detail::nsTStringRepr<T>& aRhs) {
519 return Compare(aLhs, aRhs) <= 0;
522 template <typename T>
523 inline bool operator==(const mozilla::detail::nsTStringRepr<T>& aLhs,
524 const mozilla::detail::nsTStringRepr<T>& aRhs) {
525 return aLhs.Equals(aRhs);
528 template <typename T>
529 inline bool operator==(const mozilla::detail::nsTStringRepr<T>& aLhs,
530 const T* aRhs) {
531 return aLhs.Equals(aRhs);
534 template <typename T>
535 inline bool operator>=(const mozilla::detail::nsTStringRepr<T>& aLhs,
536 const mozilla::detail::nsTStringRepr<T>& aRhs) {
537 return Compare(aLhs, aRhs) >= 0;
540 template <typename T>
541 inline bool operator>(const mozilla::detail::nsTStringRepr<T>& aLhs,
542 const mozilla::detail::nsTStringRepr<T>& aRhs) {
543 return Compare(aLhs, aRhs) > 0;
546 #endif