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
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"
24 class nsTSubstringTuple
;
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; }
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
>
53 typename
std::enable_if
<std::is_same
<char, CharType
>::value
>::type
;
55 template <typename CharType
>
57 typename
std::enable_if
<std::is_same
<char16_t
, CharType
>::value
>::type
;
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.
65 class nsTStringLengthStorage
{
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;
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
))
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
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
);
93 MOZ_IMPLICIT
constexpr operator size_t() const { return mLength
; }
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.
111 // nsStringRepr for wide characters
112 // nsCStringRepr for narrow characters
113 template <typename T
>
114 class nsTStringRepr
{
116 typedef mozilla::fallible_t fallible_t
;
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
;
158 const_iterator
& EndReading(const_iterator
& aIter
) const {
159 aIter
.mStart
= mData
;
160 aIter
.mEnd
= mData
+ mLength
;
161 aIter
.mPosition
= aIter
.mEnd
;
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
;
174 template <typename U
, typename Dummy
>
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
;
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;
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
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
);
254 // An efficient comparison with ASCII that can be used even
255 // for wide strings. Call this version when you know the
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
261 bool NS_FASTCALL
EqualsASCII(const char* aData
) const;
263 // An efficient comparison with Latin1 characters that can be used even for
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.
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.)
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
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
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
449 double ToDoubleAllowTrailingChars(nsresult
* aErrorCode
) const;
450 float ToFloatAllowTrailingChars(nsresult
* aErrorCode
) const;
453 nsTStringRepr() = delete; // Never instantiate directly
455 constexpr nsTStringRepr(char_type
* aData
, size_type aLength
,
456 DataFlags aDataFlags
, ClassFlags aClassFlags
)
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
;
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
,
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
,
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;