gecko-dev/xpcom/string/nsTString.h

695 строки
22 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// IWYU pragma: private, include "nsString.h"
#ifndef nsTString_h
#define nsTString_h
#include "nsTSubstring.h"
/**
* This is the canonical null-terminated string class. All subclasses
* promise null-terminated storage. Instances of this class allocate
* strings on the heap.
*
* NAMES:
* nsString for wide characters
* nsCString for narrow characters
*
* This class is also known as nsAFlat[C]String, where "flat" is used
* to denote a null-terminated string.
*/
template <typename T>
class nsTString : public nsTSubstring<T> {
public:
typedef nsTString<T> self_type;
#ifdef __clang__
// bindgen w/ clang 3.9 at least chokes on a typedef, but using is okay.
using typename nsTSubstring<T>::substring_type;
#else
// On the other hand msvc chokes on the using statement. It seems others
// don't care either way so we lump them in here.
typedef typename nsTSubstring<T>::substring_type substring_type;
#endif
typedef typename substring_type::fallible_t fallible_t;
typedef typename substring_type::char_type char_type;
typedef typename substring_type::char_traits char_traits;
typedef
typename substring_type::incompatible_char_type incompatible_char_type;
typedef typename substring_type::substring_tuple_type substring_tuple_type;
typedef typename substring_type::const_iterator const_iterator;
typedef typename substring_type::iterator iterator;
typedef typename substring_type::comparator_type comparator_type;
typedef typename substring_type::const_char_iterator const_char_iterator;
typedef typename substring_type::index_type index_type;
typedef typename substring_type::size_type size_type;
// These are only for internal use within the string classes:
typedef typename substring_type::DataFlags DataFlags;
typedef typename substring_type::ClassFlags ClassFlags;
public:
/**
* constructors
*/
nsTString() : substring_type(ClassFlags::NULL_TERMINATED) {}
explicit nsTString(const char_type* aData, size_type aLength = size_type(-1))
: substring_type(ClassFlags::NULL_TERMINATED) {
this->Assign(aData, aLength);
}
explicit nsTString(mozilla::Span<const char_type> aData)
: nsTString(aData.Elements(), aData.Length()) {}
#if defined(MOZ_USE_CHAR16_WRAPPER)
template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
explicit nsTString(char16ptr_t aStr, size_type aLength = size_type(-1))
: substring_type(ClassFlags::NULL_TERMINATED) {
this->Assign(static_cast<const char16_t*>(aStr), aLength);
}
#endif
nsTString(const self_type& aStr)
: substring_type(ClassFlags::NULL_TERMINATED) {
this->Assign(aStr);
}
nsTString(self_type&& aStr) : substring_type(ClassFlags::NULL_TERMINATED) {
this->Assign(std::move(aStr));
}
MOZ_IMPLICIT nsTString(const substring_tuple_type& aTuple)
: substring_type(ClassFlags::NULL_TERMINATED) {
this->Assign(aTuple);
}
explicit nsTString(const substring_type& aReadable)
: substring_type(ClassFlags::NULL_TERMINATED) {
this->Assign(aReadable);
}
explicit nsTString(substring_type&& aReadable)
: substring_type(ClassFlags::NULL_TERMINATED) {
this->Assign(std::move(aReadable));
}
// |operator=| does not inherit, so we must define our own
self_type& operator=(char_type aChar) {
this->Assign(aChar);
return *this;
}
self_type& operator=(const char_type* aData) {
this->Assign(aData);
return *this;
}
self_type& operator=(const self_type& aStr) {
this->Assign(aStr);
return *this;
}
self_type& operator=(self_type&& aStr) {
this->Assign(std::move(aStr));
return *this;
}
#if defined(MOZ_USE_CHAR16_WRAPPER)
template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
self_type& operator=(const char16ptr_t aStr) {
this->Assign(static_cast<const char16_t*>(aStr));
return *this;
}
#endif
self_type& operator=(const substring_type& aStr) {
this->Assign(aStr);
return *this;
}
self_type& operator=(substring_type&& aStr) {
this->Assign(std::move(aStr));
return *this;
}
self_type& operator=(const substring_tuple_type& aTuple) {
this->Assign(aTuple);
return *this;
}
/**
* returns the null-terminated string
*/
template <typename U, typename Dummy>
struct raw_type {
typedef const U* type;
};
#if defined(MOZ_USE_CHAR16_WRAPPER)
template <typename Dummy>
struct raw_type<char16_t, Dummy> {
typedef char16ptr_t type;
};
#endif
MOZ_NO_DANGLING_ON_TEMPORARIES typename raw_type<T, int>::type get() const {
return this->mData;
}
/**
* returns character at specified index.
*
* NOTE: unlike nsTSubstring::CharAt, this function allows you to index
* the null terminator character.
*/
char_type CharAt(index_type aIndex) const {
NS_ASSERTION(aIndex <= this->mLength, "index exceeds allowable range");
return this->mData[aIndex];
}
char_type operator[](index_type aIndex) const { return CharAt(aIndex); }
#if MOZ_STRING_WITH_OBSOLETE_API
/**
* Search for the given substring within this string.
*
* @param aString is substring to be sought in this
* @param aIgnoreCase selects case sensitivity
* @param aOffset tells us where in this string to start searching
* @param aCount tells us how far from the offset we are to search. Use
* -1 to search the whole string.
* @return offset in string, or kNotFound
*/
int32_t Find(const nsTString<char>& aString, bool aIgnoreCase = false,
int32_t aOffset = 0, int32_t aCount = -1) const;
int32_t Find(const char* aString, bool aIgnoreCase = false,
int32_t aOffset = 0, int32_t aCount = -1) const;
template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
int32_t Find(const self_type& aString, int32_t aOffset = 0,
int32_t aCount = -1) const;
template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
int32_t Find(const char_type* aString, int32_t aOffset = 0,
int32_t aCount = -1) const;
# ifdef MOZ_USE_CHAR16_WRAPPER
template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
int32_t Find(char16ptr_t aString, int32_t aOffset = 0,
int32_t aCount = -1) const {
return Find(static_cast<const char16_t*>(aString), aOffset, aCount);
}
# endif
/**
* This methods scans the string backwards, looking for the given string
*
* @param aString is substring to be sought in this
* @param aIgnoreCase tells us whether or not to do caseless compare
* @param aOffset tells us where in this string to start searching.
* Use -1 to search from the end of the string.
* @param aCount tells us how many iterations to make starting at the
* given offset.
* @return offset in string, or kNotFound
*/
// Case aIgnoreCase option only with char versions
int32_t RFind(const nsTString<char>& aString, bool aIgnoreCase = false,
int32_t aOffset = -1, int32_t aCount = -1) const;
int32_t RFind(const char* aCString, bool aIgnoreCase = false,
int32_t aOffset = -1, int32_t aCount = -1) const;
template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
int32_t RFind(const self_type& aString, int32_t aOffset = -1,
int32_t aCount = -1) const;
template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
int32_t RFind(const char_type* aString, int32_t aOffset = -1,
int32_t aCount = -1) const;
/**
* Search for given char within this string
*
* @param aChar is the character to search for
* @param aOffset tells us where in this string to start searching
* @param aCount tells us how far from the offset we are to search.
* Use -1 to search the whole string.
* @return offset in string, or kNotFound
*/
// int32_t FindChar( char16_t aChar, int32_t aOffset=0,
// int32_t aCount=-1 ) const;
int32_t RFindChar(char16_t aChar, int32_t aOffset = -1,
int32_t aCount = -1) const;
/**
* This method searches this string for the first character found in
* the given string.
*
* @param aString contains set of chars to be found
* @param aOffset tells us where in this string to start searching
* (counting from left)
* @return offset in string, or kNotFound
*/
int32_t FindCharInSet(const char_type* aString, int32_t aOffset = 0) const;
int32_t FindCharInSet(const self_type& aString, int32_t aOffset = 0) const {
return FindCharInSet(aString.get(), aOffset);
}
template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
int32_t FindCharInSet(const char* aSet, int32_t aOffset = 0) const;
/**
* This method searches this string for the last character found in
* the given string.
*
* @param aString contains set of chars to be found
* @param aOffset tells us where in this string to start searching
* (counting from left)
* @return offset in string, or kNotFound
*/
int32_t RFindCharInSet(const char_type* aString, int32_t aOffset = -1) const;
int32_t RFindCharInSet(const self_type& aString, int32_t aOffset = -1) const {
return RFindCharInSet(aString.get(), aOffset);
}
/**
* Perform string to double-precision float conversion.
*
* @param aErrorCode will contain error if one occurs
* @return double-precision float rep of string value
*/
double ToDouble(nsresult* aErrorCode) const;
/**
* Perform string to single-precision float conversion.
*
* @param aErrorCode will contain error if one occurs
* @return single-precision float rep of string value
*/
float ToFloat(nsresult* aErrorCode) const;
/**
* Similar to above ToDouble and ToFloat but allows trailing characters that
* are not converted.
*/
double ToDoubleAllowTrailingChars(nsresult* aErrorCode) const;
float ToFloatAllowTrailingChars(nsresult* aErrorCode) const;
/**
* |Left|, |Mid|, and |Right| are annoying signatures that seem better almost
* any _other_ way than they are now. Consider these alternatives
*
* // ...a member function that returns a |Substring|
* aWritable = aReadable.Left(17);
* // ...a global function that returns a |Substring|
* aWritable = Left(aReadable, 17);
* // ...a global function that does the assignment
* Left(aReadable, 17, aWritable);
*
* as opposed to the current signature
*
* // ...a member function that does the assignment
* aReadable.Left(aWritable, 17);
*
* or maybe just stamping them out in favor of |Substring|, they are just
* duplicate functionality
*
* aWritable = Substring(aReadable, 0, 17);
*/
size_type Mid(self_type& aResult, index_type aStartPos,
size_type aCount) const;
size_type Left(self_type& aResult, size_type aCount) const {
return Mid(aResult, 0, aCount);
}
size_type Right(self_type& aResult, size_type aCount) const {
aCount = XPCOM_MIN(this->mLength, aCount);
return Mid(aResult, this->mLength - aCount, aCount);
}
/**
* Set a char inside this string at given index
*
* @param aChar is the char you want to write into this string
* @param anIndex is the ofs where you want to write the given char
* @return TRUE if successful
*/
bool SetCharAt(char16_t aChar, uint32_t aIndex);
/**
* These methods are used to remove all occurrences of the
* characters found in aSet from this string.
*
* @param aSet -- characters to be cut from this
*/
void StripChars(const char_type* aSet);
template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
bool StripChars(const incompatible_char_type* aSet, const fallible_t&);
template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
void StripChars(const incompatible_char_type* aSet);
/**
* This method strips whitespace throughout the string.
*/
void StripWhitespace();
bool StripWhitespace(const fallible_t&);
/**
* swaps occurence of 1 string for another
*/
void ReplaceChar(char_type aOldChar, char_type aNewChar);
void ReplaceChar(const char_type* aSet, char_type aNewChar);
template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
void ReplaceChar(const char* aSet, char16_t aNewChar);
/**
* Replace all occurrences of aTarget with aNewValue.
* The complexity of this function is O(n+m), n being the length of the string
* and m being the length of aNewValue.
*/
void ReplaceSubstring(const self_type& aTarget, const self_type& aNewValue);
void ReplaceSubstring(const char_type* aTarget, const char_type* aNewValue);
[[nodiscard]] bool ReplaceSubstring(const self_type& aTarget,
const self_type& aNewValue,
const fallible_t&);
[[nodiscard]] bool ReplaceSubstring(const char_type* aTarget,
const char_type* aNewValue,
const fallible_t&);
/**
* This method trims characters found in aTrimSet from
* either end of the underlying string.
*
* @param aSet -- contains chars to be trimmed from both ends
* @param aEliminateLeading
* @param aEliminateTrailing
* @param aIgnoreQuotes -- if true, causes surrounding quotes to be ignored
* @return this
*/
void Trim(const char* aSet, bool aEliminateLeading = true,
bool aEliminateTrailing = true, bool aIgnoreQuotes = false);
/**
* This method strips whitespace from string.
* You can control whether whitespace is yanked from start and end of
* string as well.
*
* @param aEliminateLeading controls stripping of leading ws
* @param aEliminateTrailing controls stripping of trailing ws
*/
void CompressWhitespace(bool aEliminateLeading = true,
bool aEliminateTrailing = true);
#endif // !MOZ_STRING_WITH_OBSOLETE_API
/**
* Allow this string to be bound to a character buffer
* until the string is rebound or mutated; the caller
* must ensure that the buffer outlives the string.
*/
void Rebind(const char_type* aData, size_type aLength);
/**
* verify restrictions for dependent strings
*/
void AssertValidDependentString() {
MOZ_ASSERT(this->mData, "nsTDependentString must wrap a non-NULL buffer");
MOZ_ASSERT(this->mLength != size_type(-1),
"nsTDependentString has bogus length");
MOZ_DIAGNOSTIC_ASSERT(this->mData[substring_type::mLength] == 0,
"nsTDependentString must wrap only null-terminated "
"strings. You are probably looking for "
"nsTDependentSubstring.");
}
protected:
// allow subclasses to initialize fields directly
nsTString(char_type* aData, size_type aLength, DataFlags aDataFlags,
ClassFlags aClassFlags)
: substring_type(aData, aLength, aDataFlags,
aClassFlags | ClassFlags::NULL_TERMINATED) {}
friend const nsTString<char>& VoidCString();
friend const nsTString<char16_t>& VoidString();
// Used by Null[C]String.
explicit nsTString(DataFlags aDataFlags)
: substring_type(char_traits::sEmptyBuffer, 0,
aDataFlags | DataFlags::TERMINATED,
ClassFlags::NULL_TERMINATED) {}
enum class TrailingCharsPolicy {
Disallow,
Allow,
};
// Utility function for ToDouble and ToDoubleAllowTrailingChars.
double ToDouble(TrailingCharsPolicy aTrailingCharsPolicy,
nsresult* aErrorCode) const;
struct Segment {
uint32_t mBegin, mLength;
Segment(uint32_t aBegin, uint32_t aLength)
: mBegin(aBegin), mLength(aLength) {}
};
};
// TODO(erahm): Do something with ToDouble so that we can extern the
// nsTString templates.
// extern template class nsTString<char>;
// extern template class nsTString<char16_t>;
/**
* nsTAutoStringN
*
* Subclass of nsTString that adds support for stack-based string
* allocation. It is normally not a good idea to use this class on the
* heap, because it will allocate space which may be wasted if the string
* it contains is significantly smaller or any larger than 64 characters.
*
* NAMES:
* nsAutoStringN / nsTAutoString for wide characters
* nsAutoCStringN / nsTAutoCString for narrow characters
*/
template <typename T, size_t N>
class MOZ_NON_MEMMOVABLE nsTAutoStringN : public nsTString<T> {
public:
typedef nsTAutoStringN<T, N> self_type;
typedef nsTString<T> base_string_type;
typedef typename base_string_type::string_type string_type;
typedef typename base_string_type::char_type char_type;
typedef typename base_string_type::char_traits char_traits;
typedef typename base_string_type::substring_type substring_type;
typedef typename base_string_type::size_type size_type;
typedef typename base_string_type::substring_tuple_type substring_tuple_type;
// These are only for internal use within the string classes:
typedef typename base_string_type::DataFlags DataFlags;
typedef typename base_string_type::ClassFlags ClassFlags;
public:
/**
* constructors
*/
nsTAutoStringN()
: string_type(mStorage, 0, DataFlags::TERMINATED | DataFlags::INLINE,
ClassFlags::INLINE),
mInlineCapacity(N - 1) {
// null-terminate
mStorage[0] = char_type(0);
}
explicit nsTAutoStringN(char_type aChar) : self_type() {
this->Assign(aChar);
}
explicit nsTAutoStringN(const char_type* aData,
size_type aLength = size_type(-1))
: self_type() {
this->Assign(aData, aLength);
}
#if defined(MOZ_USE_CHAR16_WRAPPER)
template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
explicit nsTAutoStringN(char16ptr_t aData, size_type aLength = size_type(-1))
: self_type(static_cast<const char16_t*>(aData), aLength) {}
#endif
nsTAutoStringN(const self_type& aStr) : self_type() { this->Assign(aStr); }
nsTAutoStringN(self_type&& aStr) : self_type() {
this->Assign(std::move(aStr));
}
explicit nsTAutoStringN(const substring_type& aStr) : self_type() {
this->Assign(aStr);
}
explicit nsTAutoStringN(substring_type&& aStr) : self_type() {
this->Assign(std::move(aStr));
}
MOZ_IMPLICIT nsTAutoStringN(const substring_tuple_type& aTuple)
: self_type() {
this->Assign(aTuple);
}
// |operator=| does not inherit, so we must define our own
self_type& operator=(char_type aChar) {
this->Assign(aChar);
return *this;
}
self_type& operator=(const char_type* aData) {
this->Assign(aData);
return *this;
}
#if defined(MOZ_USE_CHAR16_WRAPPER)
template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
self_type& operator=(char16ptr_t aStr) {
this->Assign(aStr);
return *this;
}
#endif
self_type& operator=(const self_type& aStr) {
this->Assign(aStr);
return *this;
}
self_type& operator=(self_type&& aStr) {
this->Assign(std::move(aStr));
return *this;
}
self_type& operator=(const substring_type& aStr) {
this->Assign(aStr);
return *this;
}
self_type& operator=(substring_type&& aStr) {
this->Assign(std::move(aStr));
return *this;
}
self_type& operator=(const substring_tuple_type& aTuple) {
this->Assign(aTuple);
return *this;
}
static const size_t kStorageSize = N;
protected:
friend class nsTSubstring<T>;
const size_type mInlineCapacity;
private:
char_type mStorage[N];
};
// Externs for the most common nsTAutoStringN variations.
extern template class nsTAutoStringN<char, 64>;
extern template class nsTAutoStringN<char16_t, 64>;
//
// nsAutoString stores pointers into itself which are invalidated when an
// nsTArray is resized, so nsTArray must not be instantiated with nsAutoString
// elements!
//
template <class E>
class nsTArrayElementTraits;
template <typename T>
class nsTArrayElementTraits<nsTAutoString<T>> {
public:
template <class A>
struct Dont_Instantiate_nsTArray_of;
template <class A>
struct Instead_Use_nsTArray_of;
static Dont_Instantiate_nsTArray_of<nsTAutoString<T>>* Construct(
Instead_Use_nsTArray_of<nsTString<T>>* aE) {
return 0;
}
template <class A>
static Dont_Instantiate_nsTArray_of<nsTAutoString<T>>* Construct(
Instead_Use_nsTArray_of<nsTString<T>>* aE, const A& aArg) {
return 0;
}
template <class... Args>
static Dont_Instantiate_nsTArray_of<nsTAutoString<T>>* Construct(
Instead_Use_nsTArray_of<nsTString<T>>* aE, Args&&... aArgs) {
return 0;
}
static Dont_Instantiate_nsTArray_of<nsTAutoString<T>>* Destruct(
Instead_Use_nsTArray_of<nsTString<T>>* aE) {
return 0;
}
};
/**
* getter_Copies support for adopting raw string out params that are
* heap-allocated, e.g.:
*
* char* gStr;
* void GetBlah(char** aStr)
* {
* *aStr = strdup(gStr);
* }
*
* // This works, but is clumsy.
* void Inelegant()
* {
* char* buf;
* GetBlah(&buf);
* nsCString str;
* str.Adopt(buf);
* // ...
* }
*
* // This is nicer.
* void Elegant()
* {
* nsCString str;
* GetBlah(getter_Copies(str));
* // ...
* }
*/
template <typename T>
class MOZ_STACK_CLASS nsTGetterCopies {
public:
typedef T char_type;
explicit nsTGetterCopies(nsTSubstring<T>& aStr)
: mString(aStr), mData(nullptr) {}
~nsTGetterCopies() {
mString.Adopt(mData); // OK if mData is null
}
operator char_type**() { return &mData; }
private:
nsTSubstring<T>& mString;
char_type* mData;
};
// See the comment above nsTGetterCopies_CharT for how to use this.
template <typename T>
inline nsTGetterCopies<T> getter_Copies(nsTSubstring<T>& aString) {
return nsTGetterCopies<T>(aString);
}
#endif