summaryrefslogtreecommitdiffstats
path: root/xpcom/string/nsTSubstring.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xpcom/string/nsTSubstring.h1454
1 files changed, 1454 insertions, 0 deletions
diff --git a/xpcom/string/nsTSubstring.h b/xpcom/string/nsTSubstring.h
new file mode 100644
index 0000000000..d54dcafd7b
--- /dev/null
+++ b/xpcom/string/nsTSubstring.h
@@ -0,0 +1,1454 @@
+/* -*- 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 nsTSubstring_h
+#define nsTSubstring_h
+
+#include <iterator>
+#include <type_traits>
+
+#include "mozilla/Casting.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/IntegerTypeTraits.h"
+#include "mozilla/Result.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/Span.h"
+#include "mozilla/Unused.h"
+
+#include "nsTStringRepr.h"
+
+#ifndef MOZILLA_INTERNAL_API
+# error "Using XPCOM strings is limited to code linked into libxul."
+#endif
+
+// The max number of logically uninitialized code units to
+// fill with a marker byte or to mark as unintialized for
+// memory checking. (Limited to avoid quadratic behavior.)
+const size_t kNsStringBufferMaxPoison = 16;
+
+class nsStringBuffer;
+template <typename T>
+class nsTSubstringSplitter;
+template <typename T>
+class nsTString;
+template <typename T>
+class nsTSubstring;
+
+namespace mozilla {
+
+/**
+ * This handle represents permission to perform low-level writes
+ * the storage buffer of a string in a manner that's aware of the
+ * actual capacity of the storage buffer allocation and that's
+ * cache-friendly in the sense that the writing of zero terminator
+ * for C compatibility can happen in linear memory access order
+ * (i.e. the zero terminator write takes place after writing
+ * new content to the string as opposed to the zero terminator
+ * write happening first causing a non-linear memory write for
+ * cache purposes).
+ *
+ * If you requested a prefix to be preserved when starting
+ * or restarting the bulk write, the prefix is present at the
+ * start of the buffer exposed by this handle as Span or
+ * as a raw pointer, and it's your responsibility to start
+ * writing after after the preserved prefix (which you
+ * presumably wanted not to overwrite since you asked for
+ * it to be preserved).
+ *
+ * In a success case, you must call Finish() with the new
+ * length of the string. In failure cases, it's OK to return
+ * early from the function whose local variable this handle is.
+ * The destructor of this class takes care of putting the
+ * string in a valid and mostly harmless state in that case
+ * by setting the value of a non-empty string to a single
+ * REPLACEMENT CHARACTER or in the case of nsACString that's
+ * too short for a REPLACEMENT CHARACTER to fit, an ASCII
+ * SUBSTITUTE.
+ *
+ * You must not allow this handle to outlive the string you
+ * obtained it from.
+ *
+ * You must not access the string you obtained this handle
+ * from in any way other than through this handle until
+ * you call Finish() on the handle or the handle goes out
+ * of scope.
+ *
+ * Once you've called Finish(), you must not call any
+ * methods on this handle and must not use values previously
+ * obtained.
+ *
+ * Once you call RestartBulkWrite(), you must not use
+ * values previously obtained from this handle and must
+ * reobtain the new corresponding values.
+ */
+template <typename T>
+class BulkWriteHandle final {
+ friend class nsTSubstring<T>;
+
+ public:
+ typedef typename mozilla::detail::nsTStringRepr<T> base_string_type;
+ typedef typename base_string_type::size_type size_type;
+
+ /**
+ * Pointer to the start of the writable buffer. Never nullptr.
+ *
+ * This pointer is valid until whichever of these happens first:
+ * 1) Finish() is called
+ * 2) RestartBulkWrite() is called
+ * 3) BulkWriteHandle goes out of scope
+ */
+ T* Elements() const {
+ MOZ_ASSERT(mString);
+ return mString->mData;
+ }
+
+ /**
+ * How many code units can be written to the buffer.
+ * (Note: This is not the same as the string's Length().)
+ *
+ * This value is valid until whichever of these happens first:
+ * 1) Finish() is called
+ * 2) RestartBulkWrite() is called
+ * 3) BulkWriteHandle goes out of scope
+ */
+ size_type Length() const {
+ MOZ_ASSERT(mString);
+ return mCapacity;
+ }
+
+ /**
+ * Pointer past the end of the buffer.
+ *
+ * This pointer is valid until whichever of these happens first:
+ * 1) Finish() is called
+ * 2) RestartBulkWrite() is called
+ * 3) BulkWriteHandle goes out of scope
+ */
+ T* End() const { return Elements() + Length(); }
+
+ /**
+ * The writable buffer as Span.
+ *
+ * This Span is valid until whichever of these happens first:
+ * 1) Finish() is called
+ * 2) RestartBulkWrite() is called
+ * 3) BulkWriteHandle goes out of scope
+ */
+ auto AsSpan() const { return mozilla::Span<T>{Elements(), Length()}; }
+
+ /**
+ * Autoconvert to the buffer as writable Span.
+ *
+ * This Span is valid until whichever of these happens first:
+ * 1) Finish() is called
+ * 2) RestartBulkWrite() is called
+ * 3) BulkWriteHandle goes out of scope
+ */
+ operator mozilla::Span<T>() const { return AsSpan(); }
+
+ /**
+ * Restart the bulk write with a different capacity.
+ *
+ * This method invalidates previous return values
+ * of the other methods above.
+ *
+ * Can fail if out of memory leaving the buffer
+ * in the state before this call.
+ *
+ * @param aCapacity the new requested capacity
+ * @param aPrefixToPreserve the number of code units at
+ * the start of the string to
+ * copy over to the new buffer
+ * @param aAllowShrinking whether the string is
+ * allowed to attempt to
+ * allocate a smaller buffer
+ * for its content and copy
+ * the data over.
+ */
+ mozilla::Result<mozilla::Ok, nsresult> RestartBulkWrite(
+ size_type aCapacity, size_type aPrefixToPreserve, bool aAllowShrinking) {
+ MOZ_ASSERT(mString);
+ MOZ_TRY_VAR(mCapacity, mString->StartBulkWriteImpl(
+ aCapacity, aPrefixToPreserve, aAllowShrinking));
+ return mozilla::Ok();
+ }
+
+ /**
+ * Indicate that the bulk write finished successfully.
+ *
+ * @param aLength the number of code units written;
+ * must not exceed Length()
+ * @param aAllowShrinking whether the string is
+ * allowed to attempt to
+ * allocate a smaller buffer
+ * for its content and copy
+ * the data over.
+ */
+ void Finish(size_type aLength, bool aAllowShrinking) {
+ MOZ_ASSERT(mString);
+ MOZ_ASSERT(aLength <= mCapacity);
+ if (!aLength) {
+ // Truncate is safe even when the string is in an invalid state
+ mString->Truncate();
+ mString = nullptr;
+ return;
+ }
+ if (aAllowShrinking) {
+ mozilla::Unused << mString->StartBulkWriteImpl(aLength, aLength, true);
+ }
+ mString->FinishBulkWriteImpl(aLength);
+ mString = nullptr;
+ }
+
+ BulkWriteHandle(BulkWriteHandle&& aOther)
+ : mString(aOther.Forget()), mCapacity(aOther.mCapacity) {}
+
+ ~BulkWriteHandle() {
+ if (!mString || !mCapacity) {
+ return;
+ }
+ // The old zero terminator may be gone by now, so we need
+ // to write a new one somewhere and make length match.
+ // We can use a length between 1 and self.capacity.
+ // The contents of the string can be partially uninitialized
+ // or partially initialized in a way that would be dangerous
+ // if parsed by some recipient. It's prudent to write something
+ // same as the contents of the string. U+FFFD is the safest
+ // placeholder, but when it doesn't fit, let's use ASCII
+ // substitute. Merely truncating the string to a zero-length
+ // string might be dangerous in some scenarios. See
+ // https://www.unicode.org/reports/tr36/#Substituting_for_Ill_Formed_Subsequences
+ // for closely related scenario.
+ auto ptr = Elements();
+ // Cast the pointer below to silence warnings
+ if (sizeof(T) == 1) {
+ unsigned char* charPtr = reinterpret_cast<unsigned char*>(ptr);
+ if (mCapacity >= 3) {
+ *charPtr++ = 0xEF;
+ *charPtr++ = 0xBF;
+ *charPtr++ = 0xBD;
+ mString->mLength = 3;
+ } else {
+ *charPtr++ = 0x1A;
+ mString->mLength = 1;
+ }
+ *charPtr = 0;
+ } else if (sizeof(T) == 2) {
+ char16_t* charPtr = reinterpret_cast<char16_t*>(ptr);
+ *charPtr++ = 0xFFFD;
+ *charPtr = 0;
+ mString->mLength = 1;
+ } else {
+ MOZ_ASSERT_UNREACHABLE("Only 8-bit and 16-bit code units supported.");
+ }
+ }
+
+ BulkWriteHandle() = delete;
+ BulkWriteHandle(const BulkWriteHandle&) = delete;
+ BulkWriteHandle& operator=(const BulkWriteHandle&) = delete;
+
+ private:
+ BulkWriteHandle(nsTSubstring<T>* aString, size_type aCapacity)
+ : mString(aString), mCapacity(aCapacity) {}
+
+ nsTSubstring<T>* Forget() {
+ auto string = mString;
+ mString = nullptr;
+ return string;
+ }
+
+ nsTSubstring<T>* mString; // nullptr upon finish
+ size_type mCapacity;
+};
+
+} // namespace mozilla
+
+/**
+ * nsTSubstring is an abstract string class. From an API perspective, this
+ * class is the root of the string class hierarchy. It represents a single
+ * contiguous array of characters, which may or may not be null-terminated.
+ * This type is not instantiated directly. A sub-class is instantiated
+ * instead. For example, see nsTString.
+ *
+ * NAMES:
+ * nsAString for wide characters
+ * nsACString for narrow characters
+ *
+ */
+template <typename T>
+class nsTSubstring : public mozilla::detail::nsTStringRepr<T> {
+ friend class mozilla::BulkWriteHandle<T>;
+ friend class nsStringBuffer;
+
+ public:
+ typedef nsTSubstring<T> self_type;
+
+ typedef nsTString<T> string_type;
+
+ typedef typename mozilla::detail::nsTStringRepr<T> base_string_type;
+ typedef typename base_string_type::substring_type substring_type;
+
+ typedef typename base_string_type::fallible_t fallible_t;
+
+ typedef typename base_string_type::char_type char_type;
+ typedef typename base_string_type::char_traits char_traits;
+ typedef
+ typename base_string_type::incompatible_char_type incompatible_char_type;
+
+ typedef typename base_string_type::substring_tuple_type substring_tuple_type;
+
+ typedef typename base_string_type::const_iterator const_iterator;
+ typedef typename base_string_type::iterator iterator;
+
+ typedef typename base_string_type::comparator_type comparator_type;
+
+ typedef typename base_string_type::const_char_iterator const_char_iterator;
+
+ typedef typename base_string_type::string_view string_view;
+
+ typedef typename base_string_type::index_type index_type;
+ typedef typename base_string_type::size_type size_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;
+ typedef typename base_string_type::LengthStorage LengthStorage;
+
+ // this acts like a virtual destructor
+ ~nsTSubstring() { Finalize(); }
+
+ /**
+ * writing iterators
+ *
+ * BeginWriting() makes the string mutable (if it isn't
+ * already) and returns (or writes into an outparam) a
+ * pointer that provides write access to the string's buffer.
+ *
+ * Note: Consider if BulkWrite() suits your use case better
+ * than BeginWriting() combined with SetLength().
+ *
+ * Note: Strings autoconvert into writable mozilla::Span,
+ * which may suit your use case better than calling
+ * BeginWriting() directly.
+ *
+ * When writing via the pointer obtained from BeginWriting(),
+ * you are allowed to write at most the number of code units
+ * indicated by Length() or, alternatively, write up to, but
+ * not including, the position indicated by EndWriting().
+ *
+ * In particular, calling SetCapacity() does not affect what
+ * the above paragraph says.
+ */
+
+ iterator BeginWriting() {
+ if (!EnsureMutable()) {
+ AllocFailed(base_string_type::mLength);
+ }
+
+ return base_string_type::mData;
+ }
+
+ iterator BeginWriting(const fallible_t&) {
+ return EnsureMutable() ? base_string_type::mData : iterator(0);
+ }
+
+ iterator EndWriting() {
+ if (!EnsureMutable()) {
+ AllocFailed(base_string_type::mLength);
+ }
+
+ return base_string_type::mData + base_string_type::mLength;
+ }
+
+ iterator EndWriting(const fallible_t&) {
+ return EnsureMutable()
+ ? (base_string_type::mData + base_string_type::mLength)
+ : iterator(0);
+ }
+
+ /**
+ * Perform string to int conversion.
+ * @param aErrorCode will contain error if one occurs
+ * @param aRadix is the radix to use. Only 10 and 16 are supported.
+ * @return int rep of string value, and possible (out) error code
+ */
+ int32_t ToInteger(nsresult* aErrorCode, uint32_t aRadix = 10) const;
+
+ /**
+ * Perform string to 64-bit int conversion.
+ * @param aErrorCode will contain error if one occurs
+ * @param aRadix is the radix to use. Only 10 and 16 are supported.
+ * @return 64-bit int rep of string value, and possible (out) error code
+ */
+ int64_t ToInteger64(nsresult* aErrorCode, uint32_t aRadix = 10) const;
+
+ /**
+ * assignment
+ */
+
+ void NS_FASTCALL Assign(char_type aChar);
+ [[nodiscard]] bool NS_FASTCALL Assign(char_type aChar, const fallible_t&);
+
+ void NS_FASTCALL Assign(const char_type* aData,
+ size_type aLength = size_type(-1));
+ [[nodiscard]] bool NS_FASTCALL Assign(const char_type* aData,
+ const fallible_t&);
+ [[nodiscard]] bool NS_FASTCALL Assign(const char_type* aData,
+ size_type aLength, const fallible_t&);
+
+ void NS_FASTCALL Assign(const self_type&);
+ [[nodiscard]] bool NS_FASTCALL Assign(const self_type&, const fallible_t&);
+
+ void NS_FASTCALL Assign(self_type&&);
+ [[nodiscard]] bool NS_FASTCALL Assign(self_type&&, const fallible_t&);
+
+ void NS_FASTCALL Assign(const substring_tuple_type&);
+ [[nodiscard]] bool NS_FASTCALL Assign(const substring_tuple_type&,
+ const fallible_t&);
+
+#if defined(MOZ_USE_CHAR16_WRAPPER)
+ template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
+ void Assign(char16ptr_t aData) {
+ Assign(static_cast<const char16_t*>(aData));
+ }
+
+ template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
+ void Assign(char16ptr_t aData, size_type aLength) {
+ Assign(static_cast<const char16_t*>(aData), aLength);
+ }
+
+ template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
+ [[nodiscard]] bool Assign(char16ptr_t aData, size_type aLength,
+ const fallible_t& aFallible) {
+ return Assign(static_cast<const char16_t*>(aData), aLength, aFallible);
+ }
+#endif
+
+ void NS_FASTCALL AssignASCII(const char* aData, size_type aLength);
+ [[nodiscard]] bool NS_FASTCALL AssignASCII(const char* aData,
+ size_type aLength,
+ const fallible_t&);
+
+ void NS_FASTCALL AssignASCII(const char* aData) {
+ AssignASCII(aData, strlen(aData));
+ }
+ [[nodiscard]] bool NS_FASTCALL AssignASCII(const char* aData,
+ const fallible_t& aFallible) {
+ return AssignASCII(aData, strlen(aData), aFallible);
+ }
+
+ // AssignLiteral must ONLY be called with an actual literal string, or
+ // a character array *constant* of static storage duration declared
+ // without an explicit size and with an initializer that is a string
+ // literal or is otherwise null-terminated.
+ // Use Assign or AssignASCII for other character array variables.
+ //
+ // This method does not need a fallible version, because it uses the
+ // POD buffer of the literal as the string's buffer without allocating.
+ // The literal does not need to be ASCII. If this a 16-bit string, this
+ // method takes a u"" literal. (The overload on 16-bit strings that takes
+ // a "" literal takes only ASCII.)
+ template <int N>
+ void AssignLiteral(const char_type (&aStr)[N]) {
+ AssignLiteral(aStr, N - 1);
+ }
+
+ // AssignLiteral must ONLY be called with an actual literal string, or
+ // a char array *constant* declared without an explicit size and with an
+ // initializer that is a string literal or is otherwise null-terminated.
+ // Use AssignASCII for other char array variables.
+ //
+ // This method takes an 8-bit (ASCII-only!) string that is expanded
+ // into a 16-bit string at run time causing a run-time allocation.
+ // To avoid the run-time allocation (at the cost of the literal
+ // taking twice the size in the binary), use the above overload that
+ // takes a u"" string instead. Using the overload that takes a u""
+ // literal is generally preferred when working with 16-bit strings.
+ //
+ // There is not a fallible version of this method because it only really
+ // applies to small allocations that we wouldn't want to check anyway.
+ template <int N, typename Q = T,
+ typename EnableIfChar16 = typename mozilla::Char16OnlyT<Q>>
+ void AssignLiteral(const incompatible_char_type (&aStr)[N]) {
+ AssignASCII(aStr, N - 1);
+ }
+
+ self_type& operator=(char_type aChar) {
+ Assign(aChar);
+ return *this;
+ }
+ self_type& operator=(const char_type* aData) {
+ Assign(aData);
+ return *this;
+ }
+#if defined(MOZ_USE_CHAR16_WRAPPER)
+ template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
+ self_type& operator=(char16ptr_t aData) {
+ Assign(aData);
+ return *this;
+ }
+#endif
+ self_type& operator=(const self_type& aStr) {
+ Assign(aStr);
+ return *this;
+ }
+ self_type& operator=(self_type&& aStr) {
+ Assign(std::move(aStr));
+ return *this;
+ }
+ self_type& operator=(const substring_tuple_type& aTuple) {
+ Assign(aTuple);
+ return *this;
+ }
+
+ // Adopt a heap-allocated char sequence for this string; is Voided if aData
+ // is null. Useful for e.g. converting an strdup'd C string into an
+ // nsCString. See also getter_Copies(), which is a useful wrapper.
+ void NS_FASTCALL Adopt(char_type* aData, size_type aLength = size_type(-1));
+
+ /**
+ * buffer manipulation
+ */
+
+ void NS_FASTCALL Replace(index_type aCutStart, size_type aCutLength,
+ char_type aChar);
+ [[nodiscard]] bool NS_FASTCALL Replace(index_type aCutStart,
+ size_type aCutLength, char_type aChar,
+ const fallible_t&);
+ void NS_FASTCALL Replace(index_type aCutStart, size_type aCutLength,
+ const char_type* aData,
+ size_type aLength = size_type(-1));
+ [[nodiscard]] bool NS_FASTCALL Replace(index_type aCutStart,
+ size_type aCutLength,
+ const char_type* aData,
+ size_type aLength, const fallible_t&);
+ void Replace(index_type aCutStart, size_type aCutLength,
+ const self_type& aStr) {
+ Replace(aCutStart, aCutLength, aStr.Data(), aStr.Length());
+ }
+ [[nodiscard]] bool Replace(index_type aCutStart, size_type aCutLength,
+ const self_type& aStr,
+ const fallible_t& aFallible) {
+ return Replace(aCutStart, aCutLength, aStr.Data(), aStr.Length(),
+ aFallible);
+ }
+ void NS_FASTCALL Replace(index_type aCutStart, size_type aCutLength,
+ const substring_tuple_type& aTuple);
+
+ // ReplaceLiteral must ONLY be called with an actual literal string, or
+ // a character array *constant* of static storage duration declared
+ // without an explicit size and with an initializer that is a string
+ // literal or is otherwise null-terminated.
+ // Use Replace for other character array variables.
+ template <int N>
+ void ReplaceLiteral(index_type aCutStart, size_type aCutLength,
+ const char_type (&aStr)[N]) {
+ ReplaceLiteral(aCutStart, aCutLength, aStr, N - 1);
+ }
+
+ /**
+ * |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->Length(), aCount);
+ return Mid(aResult, this->mLength - aCount, aCount);
+ }
+
+ /**
+ * This method strips whitespace throughout the string.
+ */
+ void StripWhitespace();
+ bool StripWhitespace(const fallible_t&);
+
+ /**
+ * This method is used to remove all occurrences of aChar from this
+ * string.
+ *
+ * @param aChar -- char to be stripped
+ */
+ void StripChar(char_type aChar);
+
+ /**
+ * This method is used to remove all occurrences of aChars from this
+ * string.
+ *
+ * @param aChars -- chars to be stripped
+ */
+ void StripChars(const char_type* aChars);
+
+ /**
+ * This method is used to remove all occurrences of some characters this
+ * from this string. The characters removed have the corresponding
+ * entries in the bool array set to true; we retain all characters
+ * with code beyond 127.
+ * THE CALLER IS RESPONSIBLE for making sure the complete boolean
+ * array, 128 entries, is properly initialized.
+ *
+ * See also: ASCIIMask class.
+ *
+ * @param aToStrip -- Array where each entry is true if the
+ * corresponding ASCII character is to be stripped. All
+ * characters beyond code 127 are retained. Note that this
+ * parameter is of ASCIIMaskArray type, but we expand the typedef
+ * to avoid having to include nsASCIIMask.h in this include file
+ * as it brings other includes.
+ */
+ void StripTaggedASCII(const std::array<bool, 128>& aToStrip);
+
+ /**
+ * A shortcut to strip \r and \n.
+ */
+ void StripCRLF();
+
+ /**
+ * swaps occurence of 1 string for another
+ */
+ void ReplaceChar(char_type aOldChar, char_type aNewChar);
+ void ReplaceChar(const string_view& aSet, char_type 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 aSet from either end of the
+ * underlying string.
+ *
+ * @param aSet -- contains chars to be trimmed from both ends
+ * @param aTrimLeading
+ * @param aTrimTrailing
+ * @param aIgnoreQuotes -- if true, causes surrounding quotes to be ignored
+ * @return this
+ */
+ void Trim(const std::string_view& aSet, bool aTrimLeading = true,
+ bool aTrimTrailing = 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 aTrimLeading controls stripping of leading ws
+ * @param aTrimTrailing controls stripping of trailing ws
+ */
+ void CompressWhitespace(bool aTrimLeading = true, bool aTrimTrailing = true);
+
+ void Append(char_type aChar);
+
+ [[nodiscard]] bool Append(char_type aChar, const fallible_t& aFallible);
+
+ void Append(const char_type* aData, size_type aLength = size_type(-1));
+
+ [[nodiscard]] bool Append(const char_type* aData, size_type aLength,
+ const fallible_t& aFallible);
+
+#if defined(MOZ_USE_CHAR16_WRAPPER)
+ template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
+ void Append(char16ptr_t aData, size_type aLength = size_type(-1)) {
+ Append(static_cast<const char16_t*>(aData), aLength);
+ }
+#endif
+
+ void Append(const self_type& aStr);
+
+ [[nodiscard]] bool Append(const self_type& aStr, const fallible_t& aFallible);
+
+ void Append(const substring_tuple_type& aTuple);
+
+ [[nodiscard]] bool Append(const substring_tuple_type& aTuple,
+ const fallible_t& aFallible);
+
+ void AppendASCII(const char* aData, size_type aLength = size_type(-1));
+
+ [[nodiscard]] bool AppendASCII(const char* aData,
+ const fallible_t& aFallible);
+
+ [[nodiscard]] bool AppendASCII(const char* aData, size_type aLength,
+ const fallible_t& aFallible);
+
+ // Appends a literal string ("" literal in the 8-bit case and u"" literal
+ // in the 16-bit case) to the string.
+ //
+ // AppendLiteral must ONLY be called with an actual literal string, or
+ // a character array *constant* of static storage duration declared
+ // without an explicit size and with an initializer that is a string
+ // literal or is otherwise null-terminated.
+ // Use Append or AppendASCII for other character array variables.
+ template <int N>
+ void AppendLiteral(const char_type (&aStr)[N]) {
+ // The case where base_string_type::mLength is zero is intentionally
+ // left unoptimized (could be optimized as call to AssignLiteral),
+ // because it's rare/nonexistent. If you add that optimization,
+ // please be sure to also check that
+ // !(base_string_type::mDataFlags & DataFlags::REFCOUNTED)
+ // to avoid undoing the effects of SetCapacity().
+ Append(aStr, N - 1);
+ }
+
+ template <int N>
+ void AppendLiteral(const char_type (&aStr)[N], const fallible_t& aFallible) {
+ // The case where base_string_type::mLength is zero is intentionally
+ // left unoptimized (could be optimized as call to AssignLiteral),
+ // because it's rare/nonexistent. If you add that optimization,
+ // please be sure to also check that
+ // !(base_string_type::mDataFlags & DataFlags::REFCOUNTED)
+ // to avoid undoing the effects of SetCapacity().
+ return Append(aStr, N - 1, aFallible);
+ }
+
+ // Only enable for T = char16_t
+ //
+ // Appends an 8-bit literal string ("" literal) to a 16-bit string by
+ // expanding it. The literal must only contain ASCII.
+ //
+ // Using u"" literals with 16-bit strings is generally preferred.
+ template <int N, typename Q = T,
+ typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
+ void AppendLiteral(const incompatible_char_type (&aStr)[N]) {
+ AppendASCII(aStr, N - 1);
+ }
+
+ // Only enable for T = char16_t
+ template <int N, typename Q = T,
+ typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
+ [[nodiscard]] bool AppendLiteral(const incompatible_char_type (&aStr)[N],
+ const fallible_t& aFallible) {
+ return AppendASCII(aStr, N - 1, aFallible);
+ }
+
+ /**
+ * Append a formatted string to the current string. Uses the
+ * standard printf format codes. This uses NSPR formatting, which will be
+ * locale-aware for floating-point values. You probably don't want to use
+ * this with floating-point values as a result.
+ */
+ void AppendPrintf(const char* aFormat, ...) MOZ_FORMAT_PRINTF(2, 3);
+ void AppendVprintf(const char* aFormat, va_list aAp) MOZ_FORMAT_PRINTF(2, 0);
+ void AppendInt(int32_t aInteger) { AppendIntDec(aInteger); }
+ void AppendInt(int32_t aInteger, int aRadix) {
+ if (aRadix == 10) {
+ AppendIntDec(aInteger);
+ } else if (aRadix == 8) {
+ AppendIntOct(static_cast<uint32_t>(aInteger));
+ } else {
+ AppendIntHex(static_cast<uint32_t>(aInteger));
+ }
+ }
+ void AppendInt(uint32_t aInteger) { AppendIntDec(aInteger); }
+ void AppendInt(uint32_t aInteger, int aRadix) {
+ if (aRadix == 10) {
+ AppendIntDec(aInteger);
+ } else if (aRadix == 8) {
+ AppendIntOct(aInteger);
+ } else {
+ AppendIntHex(aInteger);
+ }
+ }
+ void AppendInt(int64_t aInteger) { AppendIntDec(aInteger); }
+ void AppendInt(int64_t aInteger, int aRadix) {
+ if (aRadix == 10) {
+ AppendIntDec(aInteger);
+ } else if (aRadix == 8) {
+ AppendIntOct(static_cast<uint64_t>(aInteger));
+ } else {
+ AppendIntHex(static_cast<uint64_t>(aInteger));
+ }
+ }
+ void AppendInt(uint64_t aInteger) { AppendIntDec(aInteger); }
+ void AppendInt(uint64_t aInteger, int aRadix) {
+ if (aRadix == 10) {
+ AppendIntDec(aInteger);
+ } else if (aRadix == 8) {
+ AppendIntOct(aInteger);
+ } else {
+ AppendIntHex(aInteger);
+ }
+ }
+
+ private:
+ void AppendIntDec(int32_t);
+ void AppendIntDec(uint32_t);
+ void AppendIntOct(uint32_t);
+ void AppendIntHex(uint32_t);
+ void AppendIntDec(int64_t);
+ void AppendIntDec(uint64_t);
+ void AppendIntOct(uint64_t);
+ void AppendIntHex(uint64_t);
+
+ public:
+ /**
+ * Append the given float to this string
+ */
+ void NS_FASTCALL AppendFloat(float aFloat);
+ void NS_FASTCALL AppendFloat(double aFloat);
+
+ self_type& operator+=(char_type aChar) {
+ Append(aChar);
+ return *this;
+ }
+ self_type& operator+=(const char_type* aData) {
+ Append(aData);
+ return *this;
+ }
+#if defined(MOZ_USE_CHAR16_WRAPPER)
+ template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
+ self_type& operator+=(char16ptr_t aData) {
+ Append(aData);
+ return *this;
+ }
+#endif
+ self_type& operator+=(const self_type& aStr) {
+ Append(aStr);
+ return *this;
+ }
+ self_type& operator+=(const substring_tuple_type& aTuple) {
+ Append(aTuple);
+ return *this;
+ }
+
+ void Insert(char_type aChar, index_type aPos) { Replace(aPos, 0, aChar); }
+ void Insert(const char_type* aData, index_type aPos,
+ size_type aLength = size_type(-1)) {
+ Replace(aPos, 0, aData, aLength);
+ }
+#if defined(MOZ_USE_CHAR16_WRAPPER)
+ template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
+ void Insert(char16ptr_t aData, index_type aPos,
+ size_type aLength = size_type(-1)) {
+ Insert(static_cast<const char16_t*>(aData), aPos, aLength);
+ }
+#endif
+ void Insert(const self_type& aStr, index_type aPos) {
+ Replace(aPos, 0, aStr);
+ }
+ void Insert(const substring_tuple_type& aTuple, index_type aPos) {
+ Replace(aPos, 0, aTuple);
+ }
+
+ // InsertLiteral must ONLY be called with an actual literal string, or
+ // a character array *constant* of static storage duration declared
+ // without an explicit size and with an initializer that is a string
+ // literal or is otherwise null-terminated.
+ // Use Insert for other character array variables.
+ template <int N>
+ void InsertLiteral(const char_type (&aStr)[N], index_type aPos) {
+ ReplaceLiteral(aPos, 0, aStr, N - 1);
+ }
+
+ void Cut(index_type aCutStart, size_type aCutLength) {
+ Replace(aCutStart, aCutLength, char_traits::sEmptyBuffer, 0);
+ }
+
+ nsTSubstringSplitter<T> Split(const char_type aChar) const;
+
+ /**
+ * buffer sizing
+ */
+
+ /**
+ * Attempts to set the capacity to the given size in number of
+ * code units without affecting the length of the string in
+ * order to avoid reallocation during a subsequent sequence of
+ * appends.
+ *
+ * This method is appropriate to use before a sequence of multiple
+ * operations from the following list (without operations that are
+ * not on the list between the SetCapacity() call and operations
+ * from the list):
+ *
+ * Append()
+ * AppendASCII()
+ * AppendLiteral() (except if the string is empty: bug 1487606)
+ * AppendPrintf()
+ * AppendInt()
+ * AppendFloat()
+ * LossyAppendUTF16toASCII()
+ * AppendASCIItoUTF16()
+ *
+ * DO NOT call SetCapacity() if the subsequent operations on the
+ * string do not meet the criteria above. Operations that undo
+ * the benefits of SetCapacity() include but are not limited to:
+ *
+ * SetLength()
+ * Truncate()
+ * Assign()
+ * AssignLiteral()
+ * Adopt()
+ * CopyASCIItoUTF16()
+ * LossyCopyUTF16toASCII()
+ * AppendUTF16toUTF8()
+ * AppendUTF8toUTF16()
+ * CopyUTF16toUTF8()
+ * CopyUTF8toUTF16()
+ *
+ * If your string is an nsAuto[C]String and you are calling
+ * SetCapacity() with a constant N, please instead declare the
+ * string as nsAuto[C]StringN<N+1> without calling SetCapacity().
+ *
+ * There is no need to include room for the null terminator: it is
+ * the job of the string class.
+ *
+ * Note: Calling SetCapacity() does not give you permission to
+ * use the pointer obtained from BeginWriting() to write
+ * past the current length (as returned by Length()) of the
+ * string. Please use either BulkWrite() or SetLength()
+ * instead.
+ *
+ * Note: SetCapacity() won't make the string shorter if
+ * called with an argument smaller than the length of the
+ * string.
+ *
+ * Note: You must not use previously obtained iterators
+ * or spans after calling SetCapacity().
+ */
+ void NS_FASTCALL SetCapacity(size_type aNewCapacity);
+ [[nodiscard]] bool NS_FASTCALL SetCapacity(size_type aNewCapacity,
+ const fallible_t&);
+
+ /**
+ * Changes the logical length of the string, potentially
+ * allocating a differently-sized buffer for the string.
+ *
+ * When making the string shorter, this method never
+ * reports allocation failure.
+ *
+ * Exposes uninitialized memory if the string got longer.
+ *
+ * If called with the argument 0, releases the
+ * heap-allocated buffer, if any. (But the no-argument
+ * overload of Truncate() is a more idiomatic and efficient
+ * option than SetLength(0).)
+ *
+ * Note: You must not use previously obtained iterators
+ * or spans after calling SetLength().
+ */
+ void NS_FASTCALL SetLength(size_type aNewLength);
+ [[nodiscard]] bool NS_FASTCALL SetLength(size_type aNewLength,
+ const fallible_t&);
+
+ /**
+ * Like SetLength() but asserts in that the string
+ * doesn't become longer. Never fails, so doesn't need a
+ * fallible variant.
+ *
+ * Note: You must not use previously obtained iterators
+ * or spans after calling Truncate().
+ */
+ void Truncate(size_type aNewLength) {
+ MOZ_RELEASE_ASSERT(aNewLength <= base_string_type::mLength,
+ "Truncate cannot make string longer");
+ mozilla::DebugOnly<bool> success = SetLength(aNewLength, mozilla::fallible);
+ MOZ_ASSERT(success);
+ }
+
+ /**
+ * A more efficient overload for Truncate(0). Releases the
+ * heap-allocated buffer if any.
+ */
+ void Truncate();
+
+ /**
+ * buffer access
+ */
+
+ /**
+ * Get a const pointer to the string's internal buffer. The caller
+ * MUST NOT modify the characters at the returned address.
+ *
+ * @returns The length of the buffer in characters.
+ */
+ inline size_type GetData(const char_type** aData) const {
+ *aData = base_string_type::mData;
+ return base_string_type::mLength;
+ }
+
+ /**
+ * Get a pointer to the string's internal buffer, optionally resizing
+ * the buffer first. If size_type(-1) is passed for newLen, then the
+ * current length of the string is used. The caller MAY modify the
+ * characters at the returned address (up to but not exceeding the
+ * length of the string).
+ *
+ * @returns The length of the buffer in characters or 0 if unable to
+ * satisfy the request due to low-memory conditions.
+ */
+ size_type GetMutableData(char_type** aData,
+ size_type aNewLen = size_type(-1)) {
+ if (!EnsureMutable(aNewLen)) {
+ AllocFailed(aNewLen == size_type(-1) ? base_string_type::Length()
+ : aNewLen);
+ }
+
+ *aData = base_string_type::mData;
+ return base_string_type::Length();
+ }
+
+ size_type GetMutableData(char_type** aData, size_type aNewLen,
+ const fallible_t&) {
+ if (!EnsureMutable(aNewLen)) {
+ *aData = nullptr;
+ return 0;
+ }
+
+ *aData = base_string_type::mData;
+ return base_string_type::mLength;
+ }
+
+#if defined(MOZ_USE_CHAR16_WRAPPER)
+ template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
+ size_type GetMutableData(wchar_t** aData, size_type aNewLen = size_type(-1)) {
+ return GetMutableData(reinterpret_cast<char16_t**>(aData), aNewLen);
+ }
+
+ template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
+ size_type GetMutableData(wchar_t** aData, size_type aNewLen,
+ const fallible_t& aFallible) {
+ return GetMutableData(reinterpret_cast<char16_t**>(aData), aNewLen,
+ aFallible);
+ }
+#endif
+
+ mozilla::Span<char_type> GetMutableData(size_type aNewLen = size_type(-1)) {
+ if (!EnsureMutable(aNewLen)) {
+ AllocFailed(aNewLen == size_type(-1) ? base_string_type::Length()
+ : aNewLen);
+ }
+
+ return mozilla::Span{base_string_type::mData, base_string_type::Length()};
+ }
+
+ mozilla::Maybe<mozilla::Span<char_type>> GetMutableData(size_type aNewLen,
+ const fallible_t&) {
+ if (!EnsureMutable(aNewLen)) {
+ return mozilla::Nothing();
+ }
+ return Some(
+ mozilla::Span{base_string_type::mData, base_string_type::Length()});
+ }
+
+ /**
+ * Span integration
+ */
+
+ operator mozilla::Span<const char_type>() const {
+ return mozilla::Span{base_string_type::BeginReading(),
+ base_string_type::Length()};
+ }
+
+ void Append(mozilla::Span<const char_type> aSpan) {
+ Append(aSpan.Elements(), aSpan.Length());
+ }
+
+ [[nodiscard]] bool Append(mozilla::Span<const char_type> aSpan,
+ const fallible_t& aFallible) {
+ return Append(aSpan.Elements(), aSpan.Length(), aFallible);
+ }
+
+ void NS_FASTCALL AssignASCII(mozilla::Span<const char> aData) {
+ AssignASCII(aData.Elements(), aData.Length());
+ }
+ [[nodiscard]] bool NS_FASTCALL AssignASCII(mozilla::Span<const char> aData,
+ const fallible_t& aFallible) {
+ return AssignASCII(aData.Elements(), aData.Length(), aFallible);
+ }
+
+ void AppendASCII(mozilla::Span<const char> aData) {
+ AppendASCII(aData.Elements(), aData.Length());
+ }
+
+ template <typename Q = T, typename EnableIfChar = mozilla::CharOnlyT<Q>>
+ operator mozilla::Span<const uint8_t>() const {
+ return mozilla::Span{
+ reinterpret_cast<const uint8_t*>(base_string_type::BeginReading()),
+ base_string_type::Length()};
+ }
+
+ template <typename Q = T, typename EnableIfChar = mozilla::CharOnlyT<Q>>
+ void Append(mozilla::Span<const uint8_t> aSpan) {
+ Append(reinterpret_cast<const char*>(aSpan.Elements()), aSpan.Length());
+ }
+
+ template <typename Q = T, typename EnableIfChar = mozilla::CharOnlyT<Q>>
+ [[nodiscard]] bool Append(mozilla::Span<const uint8_t> aSpan,
+ const fallible_t& aFallible) {
+ return Append(reinterpret_cast<const char*>(aSpan.Elements()),
+ aSpan.Length(), aFallible);
+ }
+
+ void Insert(mozilla::Span<const char_type> aSpan, index_type aPos) {
+ Insert(aSpan.Elements(), aPos, aSpan.Length());
+ }
+
+ /**
+ * string data is never null, but can be marked void. if true, the
+ * string will be truncated. @see nsTSubstring::IsVoid
+ */
+
+ void NS_FASTCALL SetIsVoid(bool);
+
+ /**
+ * If the string uses a shared buffer, this method
+ * clears the pointer without releasing the buffer.
+ */
+ void ForgetSharedBuffer() {
+ if (base_string_type::mDataFlags & DataFlags::REFCOUNTED) {
+ SetToEmptyBuffer();
+ }
+ }
+
+ protected:
+ void AssertValid() {
+ MOZ_DIAGNOSTIC_ASSERT(!(this->mClassFlags & ClassFlags::INVALID_MASK));
+ MOZ_DIAGNOSTIC_ASSERT(!(this->mDataFlags & DataFlags::INVALID_MASK));
+ MOZ_ASSERT(!(this->mClassFlags & ClassFlags::NULL_TERMINATED) ||
+ (this->mDataFlags & DataFlags::TERMINATED),
+ "String classes whose static type guarantees a null-terminated "
+ "buffer must not be assigned a non-null-terminated buffer.");
+ }
+
+ public:
+ /**
+ * this is public to support automatic conversion of tuple to string
+ * base type, which helps avoid converting to nsTAString.
+ */
+ MOZ_IMPLICIT nsTSubstring(const substring_tuple_type& aTuple)
+ : base_string_type(nullptr, 0, DataFlags(0), ClassFlags(0)) {
+ AssertValid();
+ Assign(aTuple);
+ }
+
+ size_t SizeOfExcludingThisIfUnshared(
+ mozilla::MallocSizeOf aMallocSizeOf) const;
+ size_t SizeOfIncludingThisIfUnshared(
+ mozilla::MallocSizeOf aMallocSizeOf) const;
+
+ /**
+ * WARNING: Only use these functions if you really know what you are
+ * doing, because they can easily lead to double-counting strings. If
+ * you do use them, please explain clearly in a comment why it's safe
+ * and won't lead to double-counting.
+ */
+ size_t SizeOfExcludingThisEvenIfShared(
+ mozilla::MallocSizeOf aMallocSizeOf) const;
+ size_t SizeOfIncludingThisEvenIfShared(
+ mozilla::MallocSizeOf aMallocSizeOf) const;
+
+ template <class N>
+ void NS_ABORT_OOM(T) {
+ struct never {}; // a compiler-friendly way to do static_assert(false)
+ static_assert(
+ std::is_same_v<N, never>,
+ "In string classes, use AllocFailed to account for sizeof(char_type). "
+ "Use the global ::NS_ABORT_OOM if you really have a count of bytes.");
+ }
+
+ MOZ_ALWAYS_INLINE void AllocFailed(size_t aLength) {
+ ::NS_ABORT_OOM(aLength * sizeof(char_type));
+ }
+
+ protected:
+ // default initialization
+ nsTSubstring()
+ : base_string_type(char_traits::sEmptyBuffer, 0, DataFlags::TERMINATED,
+ ClassFlags(0)) {
+ AssertValid();
+ }
+
+ // copy-constructor, constructs as dependent on given object
+ // (NOTE: this is for internal use only)
+ nsTSubstring(const self_type& aStr)
+ : base_string_type(aStr.base_string_type::mData,
+ aStr.base_string_type::mLength,
+ aStr.base_string_type::mDataFlags &
+ (DataFlags::TERMINATED | DataFlags::VOIDED),
+ ClassFlags(0)) {
+ AssertValid();
+ }
+
+ // initialization with ClassFlags
+ explicit nsTSubstring(ClassFlags aClassFlags)
+ : base_string_type(char_traits::sEmptyBuffer, 0, DataFlags::TERMINATED,
+ aClassFlags) {
+ AssertValid();
+ }
+
+ /**
+ * allows for direct initialization of a nsTSubstring object.
+ */
+ nsTSubstring(char_type* aData, size_type aLength, DataFlags aDataFlags,
+ ClassFlags aClassFlags)
+#if defined(NS_BUILD_REFCNT_LOGGING)
+# define XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
+ ;
+#else
+# undef XPCOM_STRING_CONSTRUCTOR_OUT_OF_LINE
+ : base_string_type(aData, aLength, aDataFlags, aClassFlags) {
+ AssertValid();
+ }
+#endif /* NS_BUILD_REFCNT_LOGGING */
+
+ void SetToEmptyBuffer() {
+ base_string_type::mData = char_traits::sEmptyBuffer;
+ base_string_type::mLength = 0;
+ base_string_type::mDataFlags = DataFlags::TERMINATED;
+ AssertValid();
+ }
+
+ void SetData(char_type* aData, LengthStorage aLength, DataFlags aDataFlags) {
+ base_string_type::mData = aData;
+ base_string_type::mLength = aLength;
+ base_string_type::mDataFlags = aDataFlags;
+ AssertValid();
+ }
+
+ /**
+ * this function releases mData and does not change the value of
+ * any of its member variables. in other words, this function acts
+ * like a destructor.
+ */
+ void NS_FASTCALL Finalize();
+
+ public:
+ /**
+ * Starts a low-level write transaction to the string.
+ *
+ * Prepares the string for mutation such that the capacity
+ * of the string is at least aCapacity. The returned handle
+ * exposes the actual, potentially larger, capacity.
+ *
+ * If meeting the capacity or mutability requirement requires
+ * reallocation, aPrefixToPreserve code units are copied from the
+ * start of the old buffer to the start of the new buffer.
+ * aPrefixToPreserve must not be greater than the string's current
+ * length or greater than aCapacity.
+ *
+ * aAllowShrinking indicates whether an allocation may be
+ * performed when the string is already mutable and the requested
+ * capacity is smaller than the current capacity.
+ *
+ * If this method returns successfully, you must not access
+ * the string except through the returned BulkWriteHandle
+ * until either the BulkWriteHandle goes out of scope or
+ * you call Finish() on the BulkWriteHandle.
+ *
+ * Compared to SetLength() and BeginWriting(), this more
+ * complex API accomplishes two things:
+ * 1) It exposes the actual capacity which may be larger
+ * than the requested capacity, which is useful in some
+ * multi-step write operations that don't allocate for
+ * the worst case up front.
+ * 2) It writes the zero terminator after the string
+ * content has been written, which results in a
+ * cache-friendly linear write pattern.
+ */
+ mozilla::Result<mozilla::BulkWriteHandle<T>, nsresult> NS_FASTCALL BulkWrite(
+ size_type aCapacity, size_type aPrefixToPreserve, bool aAllowShrinking);
+
+ /**
+ * THIS IS NOT REALLY A PUBLIC METHOD! DO NOT CALL FROM OUTSIDE
+ * THE STRING IMPLEMENTATION. (It's public only because friend
+ * declarations don't allow extern or static and this needs to
+ * be called from Rust FFI glue.)
+ *
+ * Prepares mData to be mutated such that the capacity of the string
+ * (not counting the zero-terminator) is at least aCapacity.
+ * Returns the actual capacity, which may be larger than what was
+ * requested or Err(NS_ERROR_OUT_OF_MEMORY) on allocation failure.
+ *
+ * mLength is ignored by this method. If the buffer is reallocated,
+ * aUnitsToPreserve specifies how many code units to copy over to
+ * the new buffer. The old buffer is freed if applicable.
+ *
+ * Unless the return value is Err(NS_ERROR_OUT_OF_MEMORY) to signal
+ * failure or 0 to signal that the string has been set to
+ * the special empty state, this method leaves the string in an
+ * invalid state! The caller is responsible for calling
+ * FinishBulkWrite() (or in Rust calling
+ * nsA[C]StringBulkWriteHandle::finish()), which put the string
+ * into a valid state by setting mLength and zero-terminating.
+ * This method sets the flag to claim that the string is
+ * zero-terminated before it actually is.
+ *
+ * Once this method has been called and before FinishBulkWrite()
+ * has been called, only accessing mData or calling this method
+ * again are valid operations. Do not call any other methods or
+ * access other fields between calling this method and
+ * FinishBulkWrite().
+ *
+ * @param aCapacity The requested capacity. The return value
+ * will be greater than or equal to this value.
+ * @param aPrefixToPreserve The number of code units at the start
+ * of the old buffer to copy into the
+ * new buffer.
+ * @parem aAllowShrinking If true, an allocation may be performed
+ * if the requested capacity is smaller
+ * than the current capacity.
+ * @param aSuffixLength The length, in code units, of a suffix
+ * to move.
+ * @param aOldSuffixStart The old start index of the suffix to
+ * move.
+ * @param aNewSuffixStart The new start index of the suffix to
+ * move.
+ *
+ */
+ mozilla::Result<size_type, nsresult> NS_FASTCALL StartBulkWriteImpl(
+ size_type aCapacity, size_type aPrefixToPreserve = 0,
+ bool aAllowShrinking = true, size_type aSuffixLength = 0,
+ size_type aOldSuffixStart = 0, size_type aNewSuffixStart = 0);
+
+ private:
+ void AssignOwned(self_type&& aStr);
+ bool AssignNonDependent(const substring_tuple_type& aTuple,
+ size_type aTupleLength,
+ const mozilla::fallible_t& aFallible);
+
+ /**
+ * Do not call this except from within FinishBulkWriteImpl() and
+ * SetCapacity().
+ */
+ MOZ_ALWAYS_INLINE void NS_FASTCALL
+ FinishBulkWriteImplImpl(LengthStorage aLength) {
+ base_string_type::mData[aLength] = char_type(0);
+ base_string_type::mLength = aLength;
+#ifdef DEBUG
+ // ifdefed in order to avoid the call to Capacity() in non-debug
+ // builds.
+ //
+ // Our string is mutable, so Capacity() doesn't return zero.
+ // Capacity() doesn't include the space for the zero terminator,
+ // but we want to unitialize that slot, too. Since we start
+ // counting after the zero terminator the we just wrote above,
+ // we end up overwriting the space for terminator not reflected
+ // in the capacity number.
+ char_traits::uninitialize(
+ base_string_type::mData + aLength + 1,
+ XPCOM_MIN(size_t(Capacity() - aLength), kNsStringBufferMaxPoison));
+#endif
+ }
+
+ protected:
+ /**
+ * Restores the string to a valid state after a call to StartBulkWrite()
+ * that returned a non-error result. The argument to this method
+ * must be less than or equal to the value returned by the most recent
+ * StartBulkWrite() call.
+ */
+ void NS_FASTCALL FinishBulkWriteImpl(size_type aLength);
+
+ /**
+ * this function prepares a section of mData to be modified. if
+ * necessary, this function will reallocate mData and possibly move
+ * existing data to open up the specified section.
+ *
+ * @param aCutStart specifies the starting offset of the section
+ * @param aCutLength specifies the length of the section to be replaced
+ * @param aNewLength specifies the length of the new section
+ *
+ * for example, suppose mData contains the string "abcdef" then
+ *
+ * ReplacePrep(2, 3, 4);
+ *
+ * would cause mData to look like "ab____f" where the characters
+ * indicated by '_' have an unspecified value and can be freely
+ * modified. this function will null-terminate mData upon return.
+ *
+ * this function returns false if is unable to allocate sufficient
+ * memory.
+ */
+ [[nodiscard]] bool ReplacePrep(index_type aCutStart, size_type aCutLength,
+ size_type aNewLength);
+
+ [[nodiscard]] bool NS_FASTCALL ReplacePrepInternal(index_type aCutStart,
+ size_type aCutLength,
+ size_type aNewFragLength,
+ size_type aNewTotalLength);
+
+ /**
+ * returns the number of writable storage units starting at mData.
+ * the value does not include space for the null-terminator character.
+ *
+ * NOTE: this function returns 0 if mData is immutable (or the buffer
+ * is 0-sized).
+ */
+ size_type NS_FASTCALL Capacity() const;
+
+ /**
+ * this helper function can be called prior to directly manipulating
+ * the contents of mData. see, for example, BeginWriting.
+ */
+ [[nodiscard]] bool NS_FASTCALL
+ EnsureMutable(size_type aNewLen = size_type(-1));
+
+ void NS_FASTCALL ReplaceLiteral(index_type aCutStart, size_type aCutLength,
+ const char_type* aData, size_type aLength);
+
+ public:
+ // NOTE: this method is declared public _only_ for convenience for
+ // callers who don't have access to the original nsLiteralString_CharT.
+ void NS_FASTCALL AssignLiteral(const char_type* aData, size_type aLength);
+};
+
+extern template class nsTSubstring<char>;
+extern template class nsTSubstring<char16_t>;
+
+static_assert(sizeof(nsTSubstring<char>) ==
+ sizeof(mozilla::detail::nsTStringRepr<char>),
+ "Don't add new data fields to nsTSubstring_CharT. "
+ "Add to nsTStringRepr<T> instead.");
+
+#include "nsCharSeparatedTokenizer.h"
+#include "nsTDependentSubstring.h"
+
+/**
+ * Span integration
+ */
+namespace mozilla {
+Span(const nsTSubstring<char>&)->Span<const char>;
+Span(const nsTSubstring<char16_t>&)->Span<const char16_t>;
+
+} // namespace mozilla
+
+#endif