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 "mozilla/StringBuffer.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(mozilla::StringBuffer
))
79 "Math required to allocate a mozilla::StringBuffer 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;
244 template <typename Q
= T
, typename EnableIfChar
= mozilla::CharOnlyT
<Q
>>
245 bool NS_FASTCALL
Equals(const char8_t
* aData
) const {
246 return Equals(reinterpret_cast<const char*>(aData
));
249 template <typename Q
= T
, typename EnableIfChar
= mozilla::CharOnlyT
<Q
>>
250 bool NS_FASTCALL
Equals(const char8_t
* aData
, comparator_type aComp
) const {
251 return Equals(reinterpret_cast<const char*>(aData
), aComp
);
255 #if defined(MOZ_USE_CHAR16_WRAPPER)
256 template <typename Q
= T
, typename EnableIfChar16
= Char16OnlyT
<Q
>>
257 bool NS_FASTCALL
Equals(char16ptr_t aData
) const {
258 return Equals(static_cast<const char16_t
*>(aData
));
260 template <typename Q
= T
, typename EnableIfChar16
= Char16OnlyT
<Q
>>
261 bool NS_FASTCALL
Equals(char16ptr_t aData
, comparator_type aComp
) const {
262 return Equals(static_cast<const char16_t
*>(aData
), aComp
);
266 // An efficient comparison with ASCII that can be used even
267 // for wide strings. Call this version when you know the
269 bool NS_FASTCALL
EqualsASCII(const char* aData
, size_type aLen
) const;
270 // An efficient comparison with ASCII that can be used even
271 // for wide strings. Call this version when 'data' is
273 bool NS_FASTCALL
EqualsASCII(const char* aData
) const;
275 // An efficient comparison with Latin1 characters that can be used even for
277 bool EqualsLatin1(const char* aData
, size_type aLength
) const;
279 // EqualsLiteral must ONLY be called with an actual literal string, or
280 // a char array *constant* declared without an explicit size and with an
281 // initializer that is a string literal or is otherwise null-terminated.
282 // Use EqualsASCII for other char array variables.
283 // (Although this method may happen to produce expected results for other
284 // char arrays that have bound one greater than the sequence of interest,
285 // such use is discouraged for reasons of readability and maintainability.)
286 // The template trick to acquire the array bound at compile time without
287 // using a macro is due to Corey Kosak, with much thanks.
289 inline bool EqualsLiteral(const char (&aStr
)[N
]) const {
290 return EqualsASCII(aStr
, N
- 1);
293 // EqualsLiteral must ONLY be called with an actual literal string, or
294 // a char array *constant* declared without an explicit size and with an
295 // initializer that is a string literal or is otherwise null-terminated.
296 // Use EqualsASCII for other char array variables.
297 // (Although this method may happen to produce expected results for other
298 // char arrays that have bound one greater than the sequence of interest,
299 // such use is discouraged for reasons of readability and maintainability.)
300 // The template trick to acquire the array bound at compile time without
301 // using a macro is due to Corey Kosak, with much thanks.
302 template <size_t N
, typename
= std::enable_if_t
<!std::is_same_v
<
303 const char (&)[N
], const char_type (&)[N
]>>>
304 inline bool EqualsLiteral(const char_type (&aStr
)[N
]) const {
305 return *this == nsTLiteralString
<char_type
>(aStr
);
308 // The LowerCaseEquals methods compare the ASCII-lowercase version of
309 // this string (lowercasing only ASCII uppercase characters) to some
310 // ASCII/Literal string. The ASCII string is *not* lowercased for
311 // you. If you compare to an ASCII or literal string that contains an
312 // uppercase character, it is guaranteed to return false. We will
313 // throw assertions too.
314 bool NS_FASTCALL
LowerCaseEqualsASCII(const char* aData
,
315 size_type aLen
) const;
316 bool NS_FASTCALL
LowerCaseEqualsASCII(const char* aData
) const;
318 // LowerCaseEqualsLiteral must ONLY be called with an actual literal string,
319 // or a char array *constant* declared without an explicit size and with an
320 // initializer that is a string literal or is otherwise null-terminated.
321 // Use LowerCaseEqualsASCII for other char array variables.
322 // (Although this method may happen to produce expected results for other
323 // char arrays that have bound one greater than the sequence of interest,
324 // such use is discouraged for reasons of readability and maintainability.)
326 bool LowerCaseEqualsLiteral(const char (&aStr
)[N
]) const {
327 return LowerCaseEqualsASCII(aStr
, N
- 1);
330 // Returns true if this string overlaps with the given string fragment.
331 bool IsDependentOn(const char_type
* aStart
, const char_type
* aEnd
) const {
332 // If it _isn't_ the case that one fragment starts after the other ends,
333 // or ends before the other starts, then, they conflict:
335 // !(f2.begin >= f1.aEnd || f2.aEnd <= f1.begin)
337 // Simplified, that gives us (To avoid relying on Undefined Behavior
338 // from comparing pointers from different allocations (which in
339 // principle gives the optimizer the permission to assume elsewhere
340 // that the pointers are from the same allocation), the comparisons
341 // are done on integers, which merely relies on implementation-defined
342 // behavior of converting pointers to integers. std::less and
343 // std::greater implementations don't actually provide the guarantees
344 // that they should.):
345 return (reinterpret_cast<uintptr_t>(aStart
) <
346 reinterpret_cast<uintptr_t>(mData
+ mLength
) &&
347 reinterpret_cast<uintptr_t>(aEnd
) >
348 reinterpret_cast<uintptr_t>(mData
));
352 * Search for the given substring within this string.
354 * @param aString is substring to be sought in this
355 * @param aOffset tells us where in this string to start searching
356 * @return offset in string, or kNotFound
358 int32_t Find(const string_view
& aString
, index_type aOffset
= 0) const;
360 // Previously there was an overload of `Find()` which took a bool second
361 // argument. Avoid issues by explicitly preventing that overload.
362 // TODO: Remove this at some point.
363 template <typename I
,
364 typename
= std::enable_if_t
<!std::is_same_v
<I
, index_type
> &&
365 std::is_convertible_v
<I
, index_type
>>>
366 int32_t Find(const string_view
& aString
, I aOffset
) const {
367 static_assert(!std::is_same_v
<I
, bool>, "offset must not be `bool`");
368 return Find(aString
, static_cast<index_type
>(aOffset
));
372 * Search for the given ASCII substring within this string, ignoring case.
374 * @param aString is substring to be sought in this
375 * @param aOffset tells us where in this string to start searching
376 * @return offset in string, or kNotFound
378 int32_t LowerCaseFindASCII(const std::string_view
& aString
,
379 index_type aOffset
= 0) const;
382 * Scan the string backwards, looking for the given substring.
384 * @param aString is substring to be sought in this
385 * @return offset in string, or kNotFound
387 int32_t RFind(const string_view
& aString
) const;
389 size_type
CountChar(char_type
) const;
391 bool Contains(char_type aChar
) const { return FindChar(aChar
) != kNotFound
; }
394 * Search for the first instance of a given char within this string
396 * @param aChar is the character to search for
397 * @param aOffset tells us where in this string to start searching
398 * @return offset in string, or kNotFound
400 int32_t FindChar(char_type aChar
, index_type aOffset
= 0) const;
403 * Search for the last instance of a given char within this string
405 * @param aChar is the character to search for
406 * @param aOffset tells us where in this string to start searching
407 * @return offset in string, or kNotFound
409 int32_t RFindChar(char_type aChar
, int32_t aOffset
= -1) const;
412 * This method searches this string for the first 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 FindCharInSet(const string_view
& aSet
, index_type aOffset
= 0) const;
424 * This method searches this string for the last character found in
427 * @param aSet contains set of chars to be found
428 * @param aOffset tells us where in this string to start searching
429 * (counting from left)
430 * @return offset in string, or kNotFound
433 int32_t RFindCharInSet(const string_view
& aSet
, int32_t aOffset
= -1) const;
436 * Perform locale-independent string to double-precision float conversion.
438 * Leading spaces in the string will be ignored. The returned value will be
439 * finite unless aErrorCode is set to a failed status.
441 * @param aErrorCode will contain error if one occurs
442 * @return double-precision float rep of string value
444 double ToDouble(nsresult
* aErrorCode
) const;
447 * Perform locale-independent string to single-precision float conversion.
449 * Leading spaces in the string will be ignored. The returned value will be
450 * finite unless aErrorCode is set to a failed status.
452 * @param aErrorCode will contain error if one occurs
453 * @return single-precision float rep of string value
455 float ToFloat(nsresult
* aErrorCode
) const;
458 * Similar to above ToDouble and ToFloat but allows trailing characters that
461 double ToDoubleAllowTrailingChars(nsresult
* aErrorCode
) const;
462 float ToFloatAllowTrailingChars(nsresult
* aErrorCode
) const;
465 nsTStringRepr() = delete; // Never instantiate directly
467 constexpr nsTStringRepr(char_type
* aData
, size_type aLength
,
468 DataFlags aDataFlags
, ClassFlags aClassFlags
)
471 mDataFlags(aDataFlags
),
472 mClassFlags(aClassFlags
) {}
474 static constexpr size_type kMaxCapacity
= LengthStorage::kMax
;
477 * Checks if the given capacity is valid for this string type.
479 [[nodiscard
]] static constexpr bool CheckCapacity(size_type aCapacity
) {
480 return aCapacity
<= kMaxCapacity
;
484 LengthStorage mLength
;
485 DataFlags mDataFlags
;
486 ClassFlags
const mClassFlags
;
489 extern template class nsTStringRepr
<char>;
490 extern template class nsTStringRepr
<char16_t
>;
492 } // namespace detail
493 } // namespace mozilla
495 template <typename T
>
496 int NS_FASTCALL
Compare(const mozilla::detail::nsTStringRepr
<T
>& aLhs
,
497 const mozilla::detail::nsTStringRepr
<T
>& aRhs
,
498 nsTStringComparator
<T
> = nsTDefaultStringComparator
<T
>);
500 extern template int NS_FASTCALL Compare
<char>(
501 const mozilla::detail::nsTStringRepr
<char>&,
502 const mozilla::detail::nsTStringRepr
<char>&, nsTStringComparator
<char>);
504 extern template int NS_FASTCALL
505 Compare
<char16_t
>(const mozilla::detail::nsTStringRepr
<char16_t
>&,
506 const mozilla::detail::nsTStringRepr
<char16_t
>&,
507 nsTStringComparator
<char16_t
>);
509 template <typename T
>
510 inline constexpr bool operator!=(
511 const mozilla::detail::nsTStringRepr
<T
>& aLhs
,
512 const mozilla::detail::nsTStringRepr
<T
>& aRhs
) {
513 return !aLhs
.Equals(aRhs
);
516 template <typename T
>
517 inline constexpr bool operator!=(const mozilla::detail::nsTStringRepr
<T
>& aLhs
,
519 return !aLhs
.Equals(aRhs
);
522 template <typename T
>
523 inline bool operator<(const mozilla::detail::nsTStringRepr
<T
>& aLhs
,
524 const mozilla::detail::nsTStringRepr
<T
>& aRhs
) {
525 return Compare(aLhs
, aRhs
) < 0;
528 template <typename T
>
529 inline bool operator<=(const mozilla::detail::nsTStringRepr
<T
>& aLhs
,
530 const mozilla::detail::nsTStringRepr
<T
>& aRhs
) {
531 return Compare(aLhs
, aRhs
) <= 0;
534 template <typename T
>
535 inline bool operator==(const mozilla::detail::nsTStringRepr
<T
>& aLhs
,
536 const mozilla::detail::nsTStringRepr
<T
>& aRhs
) {
537 return aLhs
.Equals(aRhs
);
540 template <typename T
>
541 inline bool operator==(const mozilla::detail::nsTStringRepr
<T
>& aLhs
,
543 return aLhs
.Equals(aRhs
);
546 template <typename T
>
547 inline bool operator>=(const mozilla::detail::nsTStringRepr
<T
>& aLhs
,
548 const mozilla::detail::nsTStringRepr
<T
>& aRhs
) {
549 return Compare(aLhs
, aRhs
) >= 0;
552 template <typename T
>
553 inline bool operator>(const mozilla::detail::nsTStringRepr
<T
>& aLhs
,
554 const mozilla::detail::nsTStringRepr
<T
>& aRhs
) {
555 return Compare(aLhs
, aRhs
) > 0;