Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  nsTArray.h

  Sprache: C
 

/* 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/. */


#ifndef nsTArray_h_
#define nsTArray_h_

#include <string.h>

#include <algorithm>
#include <functional>
#include <initializer_list>
#include <iterator>
#include <new>
#include <ostream>
#include <type_traits>
#include <utility>

#include "mozilla/ArrayIterator.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/BinarySearch.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/FunctionTypeTraits.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/NotNull.h"
#include "mozilla/Span.h"
#include "mozilla/fallible.h"
#include "mozilla/mozalloc.h"
#include "nsAlgorithm.h"
#include "nsDebug.h"
#include "nsISupports.h"
#include "nsRegionFwd.h"
#include "nsTArrayForwardDeclare.h"

namespace JS {
template <class T>
class Heap;
/* namespace JS */

class nsCycleCollectionTraversalCallback;
struct TraceCallbacks;
class nsRegion;

namespace mozilla::a11y {
class BatchData;
}

namespace mozilla {
namespace layers {
class Animation;
class FrameStats;
struct PropertyAnimationGroup;
struct TileClient;
}  // namespace layers
}  // namespace mozilla

namespace mozilla {
struct SerializedStructuredCloneBuffer;
class SourceBufferTask;
}  // namespace mozilla

namespace mozilla::dom::binding_detail {
template <typename, typename>
class RecordEntry;
}

namespace mozilla::dom {
class MessagePortIdentifier;
template <typename T>
struct Nullable;
class OwningFileOrDirectory;
class OwningStringOrBooleanOrObject;
class OwningUTF8StringOrDouble;
class Pref;
class RefMessageData;
class ResponsiveImageCandidate;
class ServiceWorkerRegistrationData;
namespace indexedDB {
class SerializedStructuredCloneReadInfo;
class ObjectStoreCursorResponse;
class IndexCursorResponse;
}  // namespace indexedDB
}  // namespace mozilla::dom

namespace mozilla::ipc {
class ContentSecurityPolicy;
template <class T>
class Endpoint;
}  // namespace mozilla::ipc

class JSStructuredCloneData;

template <class T>
class RefPtr;

//
// nsTArray<E> is a resizable array class, like std::vector.
//
// Unlike std::vector, which follows C++'s construction/destruction rules,
// By default, nsTArray assumes that instances of E can be relocated safely
// using memory utils (memcpy/memmove).
//
// The public classes defined in this header are
//
//   nsTArray<E>,
//   CopyableTArray<E>,
//   FallibleTArray<E>,
//   AutoTArray<E, N>,
//   CopyableAutoTArray<E, N>
//
// nsTArray, CopyableTArray, AutoTArray and CopyableAutoTArray are infallible by
// default. To opt-in to fallible behaviour, use the `mozilla::fallible`
// parameter and check the return value.
//
// CopyableTArray and CopyableAutoTArray< are copy-constructible and
// copy-assignable. Use these only when syntactically necessary to avoid implcit
// unintentional copies. nsTArray/AutoTArray can be conveniently copied using
// the Clone() member function. Consider using std::move where possible.
//
// If you just want to declare the nsTArray types (e.g., if you're in a header
// file and don't need the full nsTArray definitions) consider including
// nsTArrayForwardDeclare.h instead of nsTArray.h.
//
// The template parameter E specifies the type of the elements and has the
// following requirements:
//
//   E MUST be safely memmove()'able.
//   E MUST define a copy-constructor.
//   E MAY define operator< for sorting.
//   E MAY define operator== for searching.
//
// (Note that the memmove requirement may be relaxed for certain types - see
// nsTArray_RelocationStrategy below.)
//
// There is a public type value_type defined as E within each array class, and
// we reference the type under this name below.
//
// For member functions taking a Comparator instance, Comparator must be either
// a functor with a tri-state comparison function with a signature compatible to
//
//   /** @return negative iff a < b, 0 iff a == b, positive iff a > b */
//   int (const value_type& a, const value_type& b);
//
// or a class defining member functions with signatures compatible to:
//
//   class Comparator {
//     public:
//       /** @return True if the elements are equals; false otherwise. */
//       bool Equals(const value_type& a, const value_type& b) const;
//
//       /** @return True if (a < b); false otherwise. */
//       bool LessThan(const value_type& a, const value_type& b) const;
//   };
//
// The Equals member function is used for searching, and the LessThan member
// function is used for searching and sorting.  Note that some member functions,
// e.g. Compare, are templates where a different type Item can be used for the
// element to compare to. In that case, the signatures must be compatible to
// allow those comparisons, but the details are not documented here.
//

//
// nsTArrayFallibleResult and nsTArrayInfallibleResult types are proxy types
// which are used because you cannot use a templated type which is bound to
// void as an argument to a void function.  In order to work around that, we
// encode either a void or a boolean inside these proxy objects, and pass them
// to the aforementioned function instead, and then use the type information to
// decide what to do in the function.
//
// Note that public nsTArray methods should never return a proxy type.  Such
// types are only meant to be used in the internal nsTArray helper methods.
// Public methods returning non-proxy types cannot be called from other
// nsTArray members.
//
struct nsTArrayFallibleResult {
  // Note: allows implicit conversions from and to bool
  MOZ_IMPLICIT constexpr nsTArrayFallibleResult(bool aResult)
      : mResult(aResult) {}

  MOZ_IMPLICIT constexpr operator bool() { return mResult; }

 private:
  bool mResult;
};

struct nsTArrayInfallibleResult {};

//
// nsTArray*Allocators must all use the same |free()|, to allow swap()'ing
// between fallible and infallible variants.
//

struct nsTArrayFallibleAllocatorBase {
  typedef bool ResultType;
  typedef nsTArrayFallibleResult ResultTypeProxy;

  static constexpr ResultType Result(ResultTypeProxy aResult) {
    return aResult;
  }
  static constexpr bool Successful(ResultTypeProxy aResult) { return aResult; }
  static constexpr ResultTypeProxy SuccessResult() { return true; }
  static constexpr ResultTypeProxy FailureResult() { return false; }
  static constexpr ResultType ConvertBoolToResultType(bool aValue) {
    return aValue;
  }
};

struct nsTArrayInfallibleAllocatorBase {
  typedef void ResultType;
  typedef nsTArrayInfallibleResult ResultTypeProxy;

  static constexpr ResultType Result(ResultTypeProxy aResult) {}
  static constexpr bool Successful(ResultTypeProxy) { return true; }
  static constexpr ResultTypeProxy SuccessResult() { return ResultTypeProxy(); }

  [[noreturn]] static ResultTypeProxy FailureResult() {
    MOZ_CRASH("Infallible nsTArray should never fail");
  }

  template <typename T>
  static constexpr ResultType ConvertBoolToResultType(T aValue) {
    if (!aValue) {
      MOZ_CRASH("infallible nsTArray should never convert false to ResultType");
    }
  }

  template <typename T>
  static constexpr ResultType ConvertBoolToResultType(
      const mozilla::NotNull<T>& aValue) {}
};

struct nsTArrayFallibleAllocator : nsTArrayFallibleAllocatorBase {
  static void* Malloc(size_t aSize) { return malloc(aSize); }
  static void* Realloc(void* aPtr, size_t aSize) {
    return realloc(aPtr, aSize);
  }

  static void Free(void* aPtr) { free(aPtr); }
  static void SizeTooBig(size_t) {}
};

struct nsTArrayInfallibleAllocator : nsTArrayInfallibleAllocatorBase {
  static void* Malloc(size_t aSize) MOZ_NONNULL_RETURN {
    return moz_xmalloc(aSize);
  }
  static void* Realloc(void* aPtr, size_t aSize) MOZ_NONNULL_RETURN {
    return moz_xrealloc(aPtr, aSize);
  }

  static void Free(void* aPtr) { free(aPtr); }
  static void SizeTooBig(size_t aSize) { NS_ABORT_OOM(aSize); }
};

// nsTArray_base stores elements into the space allocated beyond
// sizeof(*this).  This is done to minimize the size of the nsTArray
// object when it is empty.
struct nsTArrayHeader {
  uint32_t mLength;
  uint32_t mCapacity : 31;
  uint32_t mIsAutoArray : 1;
};

// Given an nsTArray which is an AutoTArray, this is the offset from the `this`
// pointer to the `mAutoBuf` member. We guarantee this by forcing mAutoBuf to be
// 8-byte aligned, which makes the location independent of the alignment of T
// (since we don't support over-aligned values in nsTArray).
constexpr static size_t kAutoTArrayHeaderOffset = 8;

extern "C" {
extern const nsTArrayHeader sEmptyTArrayHeader;
}

template <class T>
class nsCOMPtr;

namespace mozilla {
template <class T>
class OwningNonNull;
}  // namespace mozilla

namespace detail {
template <typename Iter, typename Comparator>
void AssertStrictWeakOrder(Iter aBegin, Iter aEnd, const Comparator& aCmp) {
  // This check is present in newer libc++ versions, and it is a useful check,
  // so check for it ourselves if we're not using libc++. Ported from:
  // https://github.com/llvm/llvm-project/blob/cf0efb31880dab5f5b2f20bda6634c68a42d6908/libcxx/include/__cxx03/__debug_utils/strict_weak_ordering_check.h#L28
#if defined(DEBUG) && !defined(_LIBCPP_VERSION)
  MOZ_ASSERT(std::is_sorted(aBegin, aEnd, aCmp),
             "Invalid strict-weak ordering comparator");
  // Limit the number of elements we need to check.
  auto size = std::min(size_t(aEnd - aBegin), size_t(100));
  size_t p = 0;
  while (p < size) {
    size_t q = p + size_t(1);
    // Find first element that is greater than *(aBegin+p).
    while (q < size && !aCmp(*(aBegin + p), *(aBegin + q))) {
      ++q;
    }
    // Check that the elements from p to q are equal between each other.
    for (size_t b = p; b < q; ++b) {
      for (size_t a = p; a <= b; ++a) {
        MOZ_ASSERT(!aCmp(*(aBegin + a), *(aBegin + b)),
                   "Your comparator is not a valid strict-weak ordering");
        MOZ_ASSERT(!aCmp(*(aBegin + b), *(aBegin + a)),
                   "Your comparator is not a valid strict-weak ordering");
      }
    }
    // Check that elements between p and q are less than between q and size.
    for (size_t a = p; a < q; ++a) {
      for (size_t b = q; b < size; ++b) {
        MOZ_ASSERT(aCmp(*(aBegin + a), *(aBegin + b)),
                   "Your comparator is not a valid strict-weak ordering");
        MOZ_ASSERT(!aCmp(*(aBegin + b), *(aBegin + a)),
                   "Your comparator is not a valid strict-weak ordering");
      }
    }
    // Skip these equal elements.
    p = q;
  }
#endif
}

template <typename T>
struct SafeElementAtPointerValue;

template <typename T>
struct SafeElementAtPointerValue<T*> {
  using type = T*;
};
template <typename T>
struct SafeElementAtPointerValue<nsCOMPtr<T>> {
  using type = T*;
};
template <typename T>
struct SafeElementAtPointerValue<RefPtr<T>> {
  using type = T*;
};
template <typename T>
struct SafeElementAtPointerValue<mozilla::OwningNonNull<T>> {
  using type = T*;
};
}  // namespace detail

// This class serves as a base class for nsTArray.  It shouldn't be used
// directly.  It holds common implementation code that does not depend on the
// element type of the nsTArray.
//
template <typename RelocationStrategy>
class nsTArray_base {
  // Needed for AppendElements from an array with a different allocator, which
  // calls ShiftData.
  template <typename E, typename Alloc>
  friend class nsTArray_Impl;

 protected:
  using Header = nsTArrayHeader;

 public:
  using size_type = size_t;
  using index_type = size_t;

  // @return The number of elements in the array.
  size_type Length() const { return mHdr->mLength; }

  // @return True if the array is empty or false otherwise.
  bool IsEmpty() const { return Length() == 0; }

  // @return The number of elements that can fit in the array without forcing
  // the array to be re-allocated.  The length of an array is always less
  // than or equal to its capacity.
  size_type Capacity() const { return mHdr->mCapacity; }

#ifdef DEBUG
  void* DebugGetHeader() const { return mHdr; }
#endif

  nsTArray_base(const nsTArray_base&) = delete;
  nsTArray_base& operator=(const nsTArray_base&) = delete;

 protected:
  nsTArray_base() = default;
  ~nsTArray_base();

  // Resize the storage if necessary to achieve the requested capacity.
  // @param aCapacity The requested number of array elements.
  // @param aElemSize The size of an array element.
  // @return False if insufficient memory is available; true otherwise.
  template <typename Alloc>
  typename Alloc::ResultTypeProxy EnsureCapacity(size_type aCapacity,
                                                 size_type aElemSize) {
    // Do this check here so that our callers can inline it.
    if (aCapacity <= mHdr->mCapacity) {
      return Alloc::SuccessResult();
    }
    return EnsureCapacityImpl<Alloc>(aCapacity, aElemSize);
  }

  // The rest of EnsureCapacity. Should only be called if aCapacity >
  // mHdr->mCapacity.
  template <typename Alloc>
  typename Alloc::ResultTypeProxy EnsureCapacityImpl(size_type aCapacity,
                                                     size_type aElemSize);

  // Extend the storage to accommodate aCount extra elements.
  // @param aLength The current size of the array.
  // @param aCount The number of elements to add.
  // @param aElemSize The size of an array element.
  // @return False if insufficient memory is available or the new length
  //   would overflow; true otherwise.
  template <typename Alloc>
  typename Alloc::ResultTypeProxy ExtendCapacity(size_type aLength,
                                                 size_type aCount,
                                                 size_type aElemSize);

  // Tries to resize the storage to the minimum required amount. If this fails,
  // the array is left as-is.
  // @param aElemSize  The size of an array element.
  void ShrinkCapacity(size_type aElemSize);

  // Resizes the storage to 0. This may only be called when Length() is already
  // 0.
  void ShrinkCapacityToZero();

  // This method may be called to resize a "gap" in the array by shifting
  // elements around.  It updates mLength appropriately.  If the resulting
  // array has zero elements, then the array's memory is free'd.
  // @param aStart     The starting index of the gap.
  // @param aOldLen    The current length of the gap.
  // @param aNewLen    The desired length of the gap.
  // @param aElemSize  The size of an array element.
  template <typename Alloc>
  void ShiftData(index_type aStart, size_type aOldLen, size_type aNewLen,
                 size_type aElemSize);

  // This method may be called to swap elements from the end of the array to
  // fill a "gap" in the array. If the resulting array has zero elements, then
  // the array's memory is free'd.
  // @param aStart     The starting index of the gap.
  // @param aCount     The length of the gap.
  // @param aElemSize  The size of an array element.
  template <typename Alloc>
  void SwapFromEnd(index_type aStart, size_type aCount, size_type aElemSize);

  // This method increments the length member of the array's header.
  // Note that mHdr may actually be sEmptyTArrayHeader in the case where a
  // zero-length array is inserted into our array. But then aNum should
  // always be 0.
  void IncrementLength(size_t aNum) {
    if (HasEmptyHeader()) {
      if (MOZ_UNLIKELY(aNum != 0)) {
        // Writing a non-zero length to the empty header would be extremely bad.
        MOZ_CRASH();
      }
    } else {
      mHdr->mLength += aNum;
    }
  }

  // This method inserts blank slots into the array.
  // @param aIndex the place to insert the new elements. This must be no
  //               greater than the current length of the array.
  // @param aCount the number of slots to insert
  // @param aElementSize the size of an array element.
  template <typename Alloc>
  typename Alloc::ResultTypeProxy InsertSlotsAt(index_type aIndex,
                                                size_type aCount,
                                                size_type aElementSize);

  template <typename Alloc>
  typename Alloc::ResultTypeProxy SwapArrayElements(
      nsTArray_base<RelocationStrategy>& aOther, size_type aElemSize);

  void MoveConstructNonAutoArray(nsTArray_base<RelocationStrategy>& aOther,
                                 size_type aElemSize);

  void MoveInit(nsTArray_base<RelocationStrategy>& aOther, size_type aElemSize);

  // Helper function for move construction and SwapArrayElements.
  // Takes the storage from the nsTArray as a non-auto Header pointer.
  // If the array is holding a reference to an AutoTArray buffer,
  // it will be moved to the heap before being returned.
  template <typename Alloc>
  Header* TakeHeaderForMove(size_type aElemSize);

  // Returns whether we're using our auto-array inline buffer.
  bool UsesAutoArrayBuffer() const { return mHdr == GetAutoArrayHeader(); }
  Header* GetAutoArrayHeader() const {
    if (!mHdr->mIsAutoArray) {
      return nullptr;
    }
    return const_cast<Header*>(reinterpret_cast<const Header*>(
        reinterpret_cast<const uint8_t*>(this) + kAutoTArrayHeaderOffset));
  }

  // The array's elements (prefixed with a Header).  This pointer is never
  // null.  If the array is empty, then this will point to sEmptyTArrayHeader.
  Header* mHdr{EmptyHdr()};

  Header* Hdr() const MOZ_NONNULL_RETURN { return mHdr; }
  Header** PtrToHdr() MOZ_NONNULL_RETURN { return &mHdr; }
  static constexpr Header* EmptyHdr() MOZ_NONNULL_RETURN {
    return const_cast<Header*>(&sEmptyTArrayHeader);
  }

  [[nodiscard]] bool HasEmptyHeader() const { return mHdr == EmptyHdr(); }
};

namespace detail {

// Used for argument checking in nsTArrayElementTraits::Emplace.
template <typename... T>
struct ChooseFirst;

template <>
struct ChooseFirst<> {
  // Choose a default type that is guaranteed to not match E* for any
  // nsTArray<E>.
  typedef void Type;
};

template <typename A, typename... Args>
struct ChooseFirst<A, Args...> {
  typedef A Type;
};

}  // namespace detail

//
// This class defines convenience functions for element specific operations.
// Specialize this template if necessary.
//
template <class E>
class nsTArrayElementTraits {
 public:
  // Invoke the default constructor in place.
  static inline void Construct(E* aE) {
    // Do NOT call "E()"! That triggers C++ "default initialization"
    // which zeroes out POD ("plain old data") types such as regular
    // ints.  We don't want that because it can be a performance issue
    // and people don't expect it; nsTArray should work like a regular
    // C/C++ array in this respect.
    new (static_cast<void*>(aE)) E;
  }
  // Invoke the copy-constructor in place.
  template <class A>
  static inline void Construct(E* aE, A&& aArg) {
    using E_NoCV = std::remove_cv_t<E>;
    using A_NoCV = std::remove_cv_t<A>;
    static_assert(!std::is_same_v<E_NoCV*, A_NoCV>,
                  "For safety, we disallow constructing nsTArray<E> elements "
                  "from E* pointers. See bug 960591.");
    new (static_cast<void*>(aE)) E(std::forward<A>(aArg));
  }
  // Construct in place.
  template <class... Args>
  static inline void Emplace(E* aE, Args&&... aArgs) {
    using E_NoCV = std::remove_cv_t<E>;
    using A_NoCV =
        std::remove_cv_t<typename ::detail::ChooseFirst<Args...>::Type>;
    static_assert(!std::is_same_v<E_NoCV*, A_NoCV>,
                  "For safety, we disallow constructing nsTArray<E> elements "
                  "from E* pointers. See bug 960591.");
    new (static_cast<void*>(aE)) E(std::forward<Args>(aArgs)...);
  }
  // Invoke the destructor in place.
  static inline void Destruct(E* aE) { aE->~E(); }
};

// The default comparator used by nsTArray
template <class A, class B>
class nsDefaultComparator {
 public:
  bool Equals(const A& aA, const B& aB) const { return aA == aB; }
  bool LessThan(const A& aA, const B& aB) const { return aA < aB; }
};

//
// Normally elements are copied with memcpy and memmove, but for some element
// types that is problematic.  The nsTArray_RelocationStrategy template class
// can be specialized to ensure that copying calls constructors and destructors
// instead, as is done below for JS::Heap<E> elements.
//

//
// A class that defines how to copy elements using memcpy/memmove.
//
struct nsTArray_RelocateUsingMemutils {
  const static bool allowRealloc = true;

  static void RelocateNonOverlappingRegionWithHeader(void* aDest,
                                                     const void* aSrc,
                                                     size_t aCount,
                                                     size_t aElemSize) {
    memcpy(aDest, aSrc, sizeof(nsTArrayHeader) + aCount * aElemSize);
  }

  static void RelocateOverlappingRegion(void* aDest, void* aSrc, size_t aCount,
                                        size_t aElemSize) {
    memmove(aDest, aSrc, aCount * aElemSize);
  }

  static void RelocateNonOverlappingRegion(void* aDest, void* aSrc,
                                           size_t aCount, size_t aElemSize) {
    memcpy(aDest, aSrc, aCount * aElemSize);
  }
};

//
// A template class that defines how to relocate elements using the type's move
// constructor and destructor appropriately.
//
template <class ElemType>
struct nsTArray_RelocateUsingMoveConstructor {
  typedef nsTArrayElementTraits<ElemType> traits;

  const static bool allowRealloc = false;

  static void RelocateNonOverlappingRegionWithHeader(void* aDest, void* aSrc,
                                                     size_t aCount,
                                                     size_t aElemSize) {
    nsTArrayHeader* destHeader = static_cast<nsTArrayHeader*>(aDest);
    nsTArrayHeader* srcHeader = static_cast<nsTArrayHeader*>(aSrc);
    *destHeader = *srcHeader;
    RelocateNonOverlappingRegion(
        static_cast<uint8_t*>(aDest) + sizeof(nsTArrayHeader),
        static_cast<uint8_t*>(aSrc) + sizeof(nsTArrayHeader), aCount,
        aElemSize);
  }

  // RelocateNonOverlappingRegion and RelocateOverlappingRegion are defined by
  // analogy with memmove and memcpy that are used for relocation of
  // trivially-relocatable types through nsTArray_RelocateUsingMemutils. What
  // they actually do is slightly different: RelocateOverlappingRegion checks to
  // see which direction the movement needs to take place, whether from
  // back-to-front of the range to be moved or from front-to-back.
  // RelocateNonOverlappingRegion assumes that relocating front-to-back is
  // always valid.  They use RelocateRegionForward and RelocateRegionBackward,
  // which are analogous to std::move and std::move_backward respectively,
  // except they don't move-assign the destination from the source but
  // move-construct the destination from the source and destroy the source.
  static void RelocateOverlappingRegion(void* aDest, void* aSrc, size_t aCount,
                                        size_t aElemSize) {
    ElemType* destBegin = static_cast<ElemType*>(aDest);
    ElemType* srcBegin = static_cast<ElemType*>(aSrc);

    // If destination and source are the same, this is a no-op.
    // In practice, we don't do this.
    if (destBegin == srcBegin) {
      return;
    }

    ElemType* srcEnd = srcBegin + aCount;
    ElemType* destEnd = destBegin + aCount;

    // Figure out whether to relocate back-to-front or front-to-back.
    if (srcEnd > destBegin && srcEnd < destEnd) {
      RelocateRegionBackward(srcBegin, srcEnd, destEnd);
    } else {
      RelocateRegionForward(srcBegin, srcEnd, destBegin);
    }
  }

  static void RelocateNonOverlappingRegion(void* aDest, void* aSrc,
                                           size_t aCount, size_t aElemSize) {
    ElemType* destBegin = static_cast<ElemType*>(aDest);
    ElemType* srcBegin = static_cast<ElemType*>(aSrc);
    ElemType* srcEnd = srcBegin + aCount;
#ifdef DEBUG
    ElemType* destEnd = destBegin + aCount;
    MOZ_ASSERT(srcEnd <= destBegin || srcBegin >= destEnd);
#endif
    RelocateRegionForward(srcBegin, srcEnd, destBegin);
  }

 private:
  static void RelocateRegionForward(ElemType* srcBegin, ElemType* srcEnd,
                                    ElemType* destBegin) {
    ElemType* srcElem = srcBegin;
    ElemType* destElem = destBegin;

    while (srcElem != srcEnd) {
      RelocateElement(srcElem, destElem);
      ++destElem;
      ++srcElem;
    }
  }

  static void RelocateRegionBackward(ElemType* srcBegin, ElemType* srcEnd,
                                     ElemType* destEnd) {
    ElemType* srcElem = srcEnd;
    ElemType* destElem = destEnd;
    while (srcElem != srcBegin) {
      --destElem;
      --srcElem;
      RelocateElement(srcElem, destElem);
    }
  }

  static void RelocateElement(ElemType* srcElem, ElemType* destElem) {
    traits::Construct(destElem, std::move(*srcElem));
    traits::Destruct(srcElem);
  }
};

//
// The default behaviour is to use memcpy/memmove for everything.
//
template <class E>
struct MOZ_NEEDS_MEMMOVABLE_TYPE nsTArray_RelocationStrategy {
  using Type = nsTArray_RelocateUsingMemutils;
};

//
// Some classes require constructors/destructors to be called, so they are
// specialized here.
//
#define MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(E)     \
  template <>                                              \
  struct nsTArray_RelocationStrategy<E> {                  \
    using Type = nsTArray_RelocateUsingMoveConstructor<E>; \
  };

#define MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR_FOR_TEMPLATE(T) \
  template <typename S>                                             \
  struct nsTArray_RelocationStrategy<T<S>> {                        \
    using Type = nsTArray_RelocateUsingMoveConstructor<T<S>>;       \
  };

MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR_FOR_TEMPLATE(JS::Heap)
MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR_FOR_TEMPLATE(std::function)
MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR_FOR_TEMPLATE(mozilla::ipc::Endpoint)

MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(nsRegion)
MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(nsIntRegion)
MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(mozilla::layers::TileClient)
MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(
    mozilla::SerializedStructuredCloneBuffer)
MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(
    mozilla::dom::indexedDB::ObjectStoreCursorResponse)
MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(
    mozilla::dom::indexedDB::IndexCursorResponse)
MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(
    mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo);
MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(JSStructuredCloneData)
MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(mozilla::SourceBufferTask)

namespace detail {

// These helpers allow us to differentiate between tri-state comparator
// functions and classes with LessThan() and Equal() methods. If an object, when
// called as a function with two instances of our element type, returns an int,
// we treat it as a tri-state comparator.
//
// T is the type of the comparator object we want to check. L and R are the
// types that we'll be comparing.
//
// V is never passed, and is only used to allow us to specialize on the return
// value of the comparator function.
template <typename T, typename L, typename R, typename V = int>
struct IsCompareMethod : std::false_type {};

template <typename T, typename L, typename R>
struct IsCompareMethod<
    T, L, R, decltype(std::declval<T>()(std::declval<L>(), std::declval<R>()))>
    : std::true_type {};

// These two wrappers allow us to use either a tri-state comparator, or an
// object with Equals() and LessThan() methods interchangeably. They provide a
// tri-state Compare() method, and Equals() method, and a LessThan() method.
//
// Depending on the type of the underlying comparator, they either pass these
// through directly, or synthesize them from the methods available on the
// comparator.
//
// Callers should always use the most-specific of these methods that match their
// purpose.

// Comparator wrapper for a tri-state comparator function
template <typename T, typename L, typename R,
          bool IsCompare = IsCompareMethod<T, L, R>::value>
struct CompareWrapper {
#ifdef _MSC_VER
#  pragma warning(push)
#  pragma warning(disable : 4180/* Silence "qualifier applied to function \
                                     type has no meaning" warning */

#endif
  MOZ_IMPLICIT CompareWrapper(const T& aComparator)
      : mComparator(aComparator) {}

  template <typename A, typename B>
  int Compare(A& aLeft, B& aRight) const {
    return mComparator(aLeft, aRight);
  }

  template <typename A, typename B>
  bool Equals(A& aLeft, B& aRight) const {
    return Compare(aLeft, aRight) == 0;
  }

  template <typename A, typename B>
  bool LessThan(A& aLeft, B& aRight) const {
    return Compare(aLeft, aRight) < 0;
  }

  const T& mComparator;
#ifdef _MSC_VER
#  pragma warning(pop)
#endif
};

// Comparator wrapper for a class with Equals() and LessThan() methods.
template <typename T, typename L, typename R>
struct CompareWrapper<T, L, R, false> {
  MOZ_IMPLICIT CompareWrapper(const T& aComparator)
      : mComparator(aComparator) {}

  template <typename A, typename B>
  int Compare(A& aLeft, B& aRight) const {
    if (LessThan(aLeft, aRight)) {
      return -1;
    }
    if (Equals(aLeft, aRight)) {
      return 0;
    }
    return 1;
  }

  template <typename A, typename B>
  bool Equals(A& aLeft, B& aRight) const {
    return mComparator.Equals(aLeft, aRight);
  }

  template <typename A, typename B>
  bool LessThan(A& aLeft, B& aRight) const {
    return mComparator.LessThan(aLeft, aRight);
  }

  const T& mComparator;
};

}  // namespace detail

enum class SortBoundsCheck { Enable, Disable };

//
// nsTArray_Impl contains most of the guts supporting nsTArray, FallibleTArray,
// AutoTArray.
//
// The only situation in which you might need to use nsTArray_Impl in your code
// is if you're writing code which mutates a TArray which may or may not be
// infallible.
//
// Code which merely reads from a TArray which may or may not be infallible can
// simply cast the TArray to |const nsTArray&|; both fallible and infallible
// TArrays can be cast to |const nsTArray&|.
//
template <class E, class Alloc>
class nsTArray_Impl
    : public nsTArray_base<typename nsTArray_RelocationStrategy<E>::Type> {
 private:
  friend class nsTArray<E>;

  typedef nsTArrayFallibleAllocator FallibleAlloc;
  typedef nsTArrayInfallibleAllocator InfallibleAlloc;

 public:
  typedef typename nsTArray_RelocationStrategy<E>::Type relocation_type;
  typedef nsTArray_base<relocation_type> base_type;
  typedef typename base_type::size_type size_type;
  typedef typename base_type::index_type index_type;
  typedef E value_type;
  typedef nsTArray_Impl<E, Alloc> self_type;
  typedef nsTArrayElementTraits<E> elem_traits;
  typedef mozilla::ArrayIterator<value_type&, self_type> iterator;
  typedef mozilla::ArrayIterator<const value_type&, self_type> const_iterator;
  typedef std::reverse_iterator<iterator> reverse_iterator;
  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

  using base_type::EmptyHdr;

  // A special value that is used to indicate an invalid or unknown index
  // into the array.
  static const index_type NoIndex = index_type(-1);

  using base_type::Length;

  //
  // Finalization method
  //

  ~nsTArray_Impl() {
    if (!base_type::IsEmpty()) {
      ClearAndRetainStorage();
    }
    // mHdr cleanup will be handled by base destructor
  }

  //
  // Initialization methods
  //

  nsTArray_Impl() = default;

  // Initialize this array and pre-allocate some number of elements.
  explicit nsTArray_Impl(size_type aCapacity) { SetCapacity(aCapacity); }

  // Initialize this array with an r-value.
  // Allow different types of allocators, since the allocator doesn't matter.
  template <typename Allocator>
  explicit nsTArray_Impl(nsTArray_Impl<E, Allocator>&& aOther) noexcept {
    // We cannot be a (Copyable)AutoTArray because that overrides this ctor.
    MOZ_ASSERT(!this->UsesAutoArrayBuffer());

    // This does not use SwapArrayElements because that's unnecessarily complex.
    this->MoveConstructNonAutoArray(aOther, sizeof(value_type));
  }

  // The array's copy-constructor performs a 'deep' copy of the given array.
  // @param aOther The array object to copy.
  //
  // It's very important that we declare this method as taking |const
  // self_type&| as opposed to taking |const nsTArray_Impl<E, OtherAlloc>| for
  // an arbitrary OtherAlloc.
  //
  // If we don't declare a constructor taking |const self_type&|, C++ generates
  // a copy-constructor for this class which merely copies the object's
  // members, which is obviously wrong.
  //
  // You can pass an nsTArray_Impl<E, OtherAlloc> to this method because
  // nsTArray_Impl<E, X> can be cast to const nsTArray_Impl<E, Y>&.  So the
  // effect on the API is the same as if we'd declared this method as taking
  // |const nsTArray_Impl<E, OtherAlloc>&|.
  nsTArray_Impl(const nsTArray_Impl&) = default;

  // Allow converting to a const array with a different kind of allocator,
  // Since the allocator doesn't matter for const arrays
  template <typename Allocator>
  [[nodiscard]] operator const nsTArray_Impl<E, Allocator>&() const& {
    return *reinterpret_cast<const nsTArray_Impl<E, Allocator>*>(this);
  }
  // And we have to do this for our subclasses too
  [[nodiscard]] operator const nsTArray<E>&() const& {
    return *reinterpret_cast<const nsTArray<E>*>(this);
  }
  [[nodiscard]] operator const FallibleTArray<E>&() const& {
    return *reinterpret_cast<const FallibleTArray<E>*>(this);
  }

  // The array's assignment operator performs a 'deep' copy of the given
  // array.  It is optimized to reuse existing storage if possible.
  // @param aOther The array object to copy.
  nsTArray_Impl& operator=(const nsTArray_Impl&) = default;

  // The array's move assignment operator steals the underlying data from
  // the other array.
  // @param other  The array object to move from.
  self_type& operator=(self_type&& aOther) {
    if (this != &aOther) {
      Clear();
      this->MoveInit(aOther, sizeof(value_type));
    }
    return *this;
  }

  // Return true if this array has the same length and the same
  // elements as |aOther|.
  template <typename Allocator>
  [[nodiscard]] bool operator==(
      const nsTArray_Impl<E, Allocator>& aOther) const {
    size_type len = Length();
    if (len != aOther.Length()) {
      return false;
    }

    // XXX std::equal would be as fast or faster here
    for (index_type i = 0; i < len; ++i) {
      if (!(operator[](i) == aOther[i])) {
        return false;
      }
    }

    return true;
  }

  // Return true if this array does not have the same length and the same
  // elements as |aOther|.
  [[nodiscard]] bool operator!=(const self_type& aOther) const {
    return !operator==(aOther);
  }

  // If Alloc == FallibleAlloc, ReplaceElementsAt might fail, without a way to
  // signal this to the caller, so we disallow copying via operator=. Callers
  // should use ReplaceElementsAt with a fallible argument instead, and check
  // the result.
  template <typename Allocator,
            typename = std::enable_if_t<std::is_same_v<Alloc, InfallibleAlloc>,
                                        Allocator>>
  self_type& operator=(const nsTArray_Impl<E, Allocator>& aOther) {
    AssignInternal<InfallibleAlloc>(aOther.Elements(), aOther.Length());
    return *this;
  }

  template <typename Allocator>
  self_type& operator=(nsTArray_Impl<E, Allocator>&& aOther) {
    Clear();
    this->MoveInit(aOther, sizeof(value_type));
    return *this;
  }

  // @return The amount of memory used by this nsTArray_Impl, excluding
  // sizeof(*this). If you want to measure anything hanging off the array, you
  // must iterate over the elements and measure them individually; hence the
  // "Shallow" prefix.
  [[nodiscard]] size_t ShallowSizeOfExcludingThis(
      mozilla::MallocSizeOf aMallocSizeOf) const {
    if (this->UsesAutoArrayBuffer() || this->HasEmptyHeader()) {
      return 0;
    }
    return aMallocSizeOf(this->Hdr());
  }

  // @return The amount of memory used by this nsTArray_Impl, including
  // sizeof(*this). If you want to measure anything hanging off the array, you
  // must iterate over the elements and measure them individually; hence the
  // "Shallow" prefix.
  [[nodiscard]] size_t ShallowSizeOfIncludingThis(
      mozilla::MallocSizeOf aMallocSizeOf) const {
    return aMallocSizeOf(this) + ShallowSizeOfExcludingThis(aMallocSizeOf);
  }

  //
  // Accessor methods
  //

  // This method provides direct access to the array elements.
  // @return A pointer to the first element of the array.  If the array is
  // empty, then this pointer must not be dereferenced.
  [[nodiscard]] value_type* Elements() MOZ_NONNULL_RETURN {
    return reinterpret_cast<value_type*>(Hdr() + 1);
  }

  // This method provides direct, readonly access to the array elements.
  // @return A pointer to the first element of the array.  If the array is
  // empty, then this pointer must not be dereferenced.
  [[nodiscard]] const value_type* Elements() const MOZ_NONNULL_RETURN {
    return reinterpret_cast<const value_type*>(Hdr() + 1);
  }

  // This method provides direct access to an element of the array. The given
  // index must be within the array bounds.
  // @param aIndex The index of an element in the array.
  // @return A reference to the i'th element of the array.
  [[nodiscard]] value_type& ElementAt(index_type aIndex) {
    if (MOZ_UNLIKELY(aIndex >= Length())) {
      mozilla::detail::InvalidArrayIndex_CRASH(aIndex, Length());
    }
    return Elements()[aIndex];
  }

  // This method provides direct, readonly access to an element of the array
  // The given index must be within the array bounds.
  // @param aIndex The index of an element in the array.
  // @return A const reference to the i'th element of the array.
  [[nodiscard]] const value_type& ElementAt(index_type aIndex) const {
    if (MOZ_UNLIKELY(aIndex >= Length())) {
      mozilla::detail::InvalidArrayIndex_CRASH(aIndex, Length());
    }
    return Elements()[aIndex];
  }

  // This method provides direct access to an element of the array in a bounds
  // safe manner. If the requested index is out of bounds the provided default
  // value is returned.
  // @param aIndex The index of an element in the array.
  // @param aDef   The value to return if the index is out of bounds.
  [[nodiscard]] value_type& SafeElementAt(index_type aIndex, value_type& aDef) {
    return aIndex < Length() ? Elements()[aIndex] : aDef;
  }

  // This method provides direct access to an element of the array in a bounds
  // safe manner. If the requested index is out of bounds the provided default
  // value is returned.
  // @param aIndex The index of an element in the array.
  // @param aDef   The value to return if the index is out of bounds.
  [[nodiscard]] const value_type& SafeElementAt(index_type aIndex,
                                                const value_type& aDef) const {
    return aIndex < Length() ? Elements()[aIndex] : aDef;
  }

  [[nodiscard]] auto SafeElementAt(index_type aIndex) const {
    typename ::detail::SafeElementAtPointerValue<E>::type result;
    if (aIndex < Length()) {
      result = Elements()[aIndex];
    } else {
      result = nullptr;
    }
    return result;
  }

  // Shorthand for ElementAt(aIndex)
  [[nodiscard]] value_type& operator[](index_type aIndex) {
    return ElementAt(aIndex);
  }

  // Shorthand for ElementAt(aIndex)
  [[nodiscard]] const value_type& operator[](index_type aIndex) const {
    return ElementAt(aIndex);
  }

  // Shorthand for ElementAt(length - 1)
  [[nodiscard]] value_type& LastElement() { return ElementAt(Length() - 1); }

  // Shorthand for ElementAt(length - 1)
  [[nodiscard]] const value_type& LastElement() const {
    return ElementAt(Length() - 1);
  }

  // Shorthand for SafeElementAt(length - 1, def)
  [[nodiscard]] value_type& SafeLastElement(value_type& aDef) {
    return SafeElementAt(Length() - 1, aDef);
  }

  // Shorthand for SafeElementAt(length - 1, def)
  [[nodiscard]] const value_type& SafeLastElement(
      const value_type& aDef) const {
    return SafeElementAt(Length() - 1, aDef);
  }

  // Methods for range-based for loops.
  [[nodiscard]] iterator begin() { return iterator(*this, 0); }
  [[nodiscard]] const_iterator begin() const {
    return const_iterator(*this, 0);
  }
  [[nodiscard]] const_iterator cbegin() const { return begin(); }
  [[nodiscard]] iterator end() { return iterator(*this, Length()); }
  [[nodiscard]] const_iterator end() const {
    return const_iterator(*this, Length());
  }
  [[nodiscard]] const_iterator cend() const { return end(); }

  // Methods for reverse iterating.
  [[nodiscard]] reverse_iterator rbegin() { return reverse_iterator(end()); }
  [[nodiscard]] const_reverse_iterator rbegin() const {
    return const_reverse_iterator(end());
  }
  [[nodiscard]] const_reverse_iterator crbegin() const { return rbegin(); }
  [[nodiscard]] reverse_iterator rend() { return reverse_iterator(begin()); }
  [[nodiscard]] const_reverse_iterator rend() const {
    return const_reverse_iterator(begin());
  }
  [[nodiscard]] const_reverse_iterator crend() const { return rend(); }

  // Span integration

  [[nodiscard]] operator mozilla::Span<value_type>() {
    return mozilla::Span<value_type>(Elements(), Length());
  }

  [[nodiscard]] operator mozilla::Span<const value_type>() const {
    return mozilla::Span<const value_type>(Elements(), Length());
  }

  //
  // Search methods
  //

  // This method searches for the first element in this array that is equal
  // to the given element.
  // @param aItem  The item to search for.
  // @param aComp  The Comparator used to determine element equality.
  // @return       true if the element was found.
  template <class Item, class Comparator>
  [[nodiscard]] bool Contains(const Item& aItem,
                              const Comparator& aComp) const {
    return ApplyIf(
        aItem, 0, aComp, []() { return true; }, []() { return false; });
  }

  // Like Contains(), but assumes a sorted array.
  template <class Item, class Comparator>
  [[nodiscard]] bool ContainsSorted(const Item& aItem,
                                    const Comparator& aComp) const {
    return BinaryIndexOf(aItem, aComp) != NoIndex;
  }

  // This method searches for the first element in this array that is equal
  // to the given element.  This method assumes that 'operator==' is defined
  // for value_type.
  // @param aItem  The item to search for.
  // @return       true if the element was found.
  template <class Item>
  [[nodiscard]] bool Contains(const Item& aItem) const {
    return Contains(aItem, nsDefaultComparator<value_type, Item>());
  }

  // Like Contains(), but assumes a sorted array.
  template <class Item>
  [[nodiscard]] bool ContainsSorted(const Item& aItem) const {
    return BinaryIndexOf(aItem) != NoIndex;
  }

  // This method searches for the offset of the first element in this
  // array that is equal to the given element.
  // @param aItem  The item to search for.
  // @param aStart The index to start from.
  // @param aComp  The Comparator used to determine element equality.
  // @return       The index of the found element or NoIndex if not found.
  template <class Item, class Comparator>
  [[nodiscard]] index_type IndexOf(const Item& aItem, index_type aStart,
                                   const Comparator& aComp) const {
    ::detail::CompareWrapper<Comparator, value_type, Item> comp(aComp);

    const value_type* iter = Elements() + aStart;
    const value_type* iend = Elements() + Length();
    for (; iter != iend; ++iter) {
      if (comp.Equals(*iter, aItem)) {
        return index_type(iter - Elements());
      }
    }
    return NoIndex;
  }

  // This method searches for the offset of the first element in this
  // array that is equal to the given element.  This method assumes
  // that 'operator==' is defined for value_type.
  // @param aItem  The item to search for.
  // @param aStart The index to start from.
  // @return       The index of the found element or NoIndex if not found.
  template <class Item>
  [[nodiscard]] index_type IndexOf(const Item& aItem,
                                   index_type aStart = 0const {
    return IndexOf(aItem, aStart, nsDefaultComparator<value_type, Item>());
  }

  // This method searches for the offset of the last element in this
  // array that is equal to the given element.
  // @param aItem  The item to search for.
  // @param aStart The index to start from.  If greater than or equal to the
  //               length of the array, then the entire array is searched.
  // @param aComp  The Comparator used to determine element equality.
  // @return       The index of the found element or NoIndex if not found.
  template <class Item, class Comparator>
  [[nodiscard]] index_type LastIndexOf(const Item& aItem, index_type aStart,
                                       const Comparator& aComp) const {
    ::detail::CompareWrapper<Comparator, value_type, Item> comp(aComp);

    size_type endOffset = aStart >= Length() ? Length() : aStart + 1;
    const value_type* iend = Elements() - 1;
    const value_type* iter = iend + endOffset;
    for (; iter != iend; --iter) {
      if (comp.Equals(*iter, aItem)) {
        return index_type(iter - Elements());
      }
    }
    return NoIndex;
  }

  // This method searches for the offset of the last element in this
  // array that is equal to the given element.  This method assumes
  // that 'operator==' is defined for value_type.
  // @param aItem  The item to search for.
  // @param aStart The index to start from.  If greater than or equal to the
  //               length of the array, then the entire array is searched.
  // @return       The index of the found element or NoIndex if not found.
  template <class Item>
  [[nodiscard]] index_type LastIndexOf(const Item& aItem,
                                       index_type aStart = NoIndex) const {
    return LastIndexOf(aItem, aStart, nsDefaultComparator<value_type, Item>());
  }

  // This method searches for the offset for the element in this array
  // that is equal to the given element. The array is assumed to be sorted.
  // If there is more than one equivalent element, there is no guarantee
  // on which one will be returned.
  // @param aItem  The item to search for.
  // @param aComp  The Comparator used.
  // @return       The index of the found element or NoIndex if not found.
  template <class Item, class Comparator>
  [[nodiscard]] index_type BinaryIndexOf(const Item& aItem,
                                         const Comparator& aComp) const {
    using mozilla::BinarySearchIf;
    ::detail::CompareWrapper<Comparator, value_type, Item> comp(aComp);

    size_t index;
    bool found = BinarySearchIf(
        Elements(), 0, Length(),
        // Note: We pass the Compare() args here in reverse order and negate the
        // results for compatibility reasons. Some existing callers use Equals()
        // functions with first arguments which match aElement but not aItem, or
        // second arguments that match aItem but not aElement. To accommodate
        // those callers, we preserve the argument order of the older version of
        // this API. These callers, however, should be fixed, and this special
        // case removed.
        [&](const value_type& aElement) {
          return -comp.Compare(aElement, aItem);
        },
        &index);
    return found ? index : NoIndex;
  }

  // This method searches for the offset for the element in this array
  // that is equal to the given element. The array is assumed to be sorted.
  // This method assumes that 'operator==' and 'operator<' are defined.
  // @param aItem  The item to search for.
  // @return       The index of the found element or NoIndex if not found.
  template <class Item>
  [[nodiscard]] index_type BinaryIndexOf(const Item& aItem) const {
    return BinaryIndexOf(aItem, nsDefaultComparator<value_type, Item>());
  }

  //
  // Mutation methods
  //
 private:
  template <typename ActualAlloc, class Item>
  typename ActualAlloc::ResultType AssignInternal(const Item* aArray,
                                                  size_type aArrayLen);

 public:
  template <class Allocator, typename ActualAlloc = Alloc>
  [[nodiscard]] typename ActualAlloc::ResultType Assign(
      const nsTArray_Impl<E, Allocator>& aOther) {
    return AssignInternal<ActualAlloc>(aOther.Elements(), aOther.Length());
  }

  template <class Allocator>
  [[nodiscard]] bool Assign(const nsTArray_Impl<E, Allocator>& aOther,
                            const mozilla::fallible_t&) {
    return Assign<Allocator, FallibleAlloc>(aOther);
  }

  template <class Allocator>
  void Assign(nsTArray_Impl<E, Allocator>&& aOther) {
    Clear();
    this->MoveInit(aOther, sizeof(value_type));
  }

  // This method call the destructor on each element of the array, empties it,
  // but does not shrink the array's capacity.
  // See also SetLengthAndRetainStorage.
  // Make sure to call Compact() if needed to avoid keeping a huge array
  // around.
  MOZ_REINITIALIZES void ClearAndRetainStorage() {
    if (this->HasEmptyHeader()) {
      return;
    }

    DestructRange(0, Length());
    base_type::mHdr->mLength = 0;
  }

  // This method modifies the length of the array, but unlike SetLength
  // it doesn't deallocate/reallocate the current internal storage.
  // The new length MUST be shorter than or equal to the current capacity.
  // If the new length is larger than the existing length of the array,
  // then new elements will be constructed using value_type's default
  // constructor.  If shorter, elements will be destructed and removed.
  // See also ClearAndRetainStorage.
  // @param aNewLen  The desired length of this array.
  void SetLengthAndRetainStorage(size_type aNewLen) {
    MOZ_ASSERT(aNewLen <= base_type::Capacity());
    size_type oldLen = Length();
    if (aNewLen > oldLen) {
      /// XXX(Bug 1631367) SetLengthAndRetainStorage should be disabled for
      /// FallibleTArray.
      InsertElementsAtInternal<InfallibleAlloc>(oldLen, aNewLen - oldLen);
      return;
    }
    if (aNewLen < oldLen) {
      DestructRange(aNewLen, oldLen - aNewLen);
      base_type::mHdr->mLength = aNewLen;
    }
  }

  // This method replaces a range of elements in this array.
  // @param aStart    The starting index of the elements to replace.
  // @param aCount    The number of elements to replace.  This may be zero to
  //                  insert elements without removing any existing elements.
  // @param aArray    The values to copy into this array.  Must be non-null,
  //                  and these elements must not already exist in the array
  //                  being modified.
  // @param aArrayLen The number of values to copy into this array.
  // @return          A pointer to the new elements in the array, or null if
  //                  the operation failed due to insufficient memory.
 private:
  template <typename ActualAlloc, class Item>
  value_type* ReplaceElementsAtInternal(index_type aStart, size_type aCount,
                                        const Item* aArray,
                                        size_type aArrayLen);

 public:
  template <class Item>
  [[nodiscard]] value_type* ReplaceElementsAt(index_type aStart,
                                              size_type aCount,
                                              const Item* aArray,
                                              size_type aArrayLen,
                                              const mozilla::fallible_t&) {
    return ReplaceElementsAtInternal<FallibleAlloc>(aStart, aCount, aArray,
                                                    aArrayLen);
  }

  // A variation on the ReplaceElementsAt method defined above.
  template <class Item>
  [[nodiscard]] value_type* ReplaceElementsAt(index_type aStart,
                                              size_type aCount,
                                              const nsTArray<Item>& aArray,
                                              const mozilla::fallible_t&) {
    return ReplaceElementsAtInternal<FallibleAlloc>(aStart, aCount, aArray);
  }

  template <class Item>
  [[nodiscard]] value_type* ReplaceElementsAt(index_type aStart,
                                              size_type aCount,
                                              mozilla::Span<Item> aSpan,
                                              const mozilla::fallible_t&) {
    return ReplaceElementsAtInternal<FallibleAlloc>(aStart, aCount, aSpan);
  }

  // A variation on the ReplaceElementsAt method defined above.
  template <class Item>
  [[nodiscard]] value_type* ReplaceElementsAt(index_type aStart,
                                              size_type aCount,
                                              const Item& aItem,
                                              const mozilla::fallible_t&) {
    return ReplaceElementsAtInternal<FallibleAlloc>(aStart, aCount, aItem);
  }

  // A variation on the ReplaceElementsAt method defined above.
  template <class Item>
  mozilla::NotNull<value_type*> ReplaceElementAt(index_type aIndex,
                                                 Item&& aItem) {
    value_type* const elem = &ElementAt(aIndex);
    elem_traits::Destruct(elem);
    elem_traits::Construct(elem, std::forward<Item>(aItem));
    return mozilla::WrapNotNullUnchecked(elem);
  }

  // InsertElementsAt is ReplaceElementsAt with 0 elements to replace.
  // XXX Provide a proper documentation of InsertElementsAt.
  template <class Item>
  [[nodiscard]] value_type* InsertElementsAt(index_type aIndex,
                                             const Item* aArray,
                                             size_type aArrayLen,
                                             const mozilla::fallible_t&) {
    return ReplaceElementsAtInternal<FallibleAlloc>(aIndex, 0, aArray,
                                                    aArrayLen);
  }

  template <class Item, class Allocator>
  [[nodiscard]] value_type* InsertElementsAt(
      index_type aIndex, const nsTArray_Impl<Item, Allocator>& aArray,
      const mozilla::fallible_t&) {
    return ReplaceElementsAtInternal<FallibleAlloc>(
        aIndex, 0, aArray.Elements(), aArray.Length());
  }

  template <class Item>
  [[nodiscard]] value_type* InsertElementsAt(index_type aIndex,
                                             mozilla::Span<Item> aSpan,
                                             const mozilla::fallible_t&) {
    return ReplaceElementsAtInternal<FallibleAlloc>(aIndex, 0, aSpan.Elements(),
                                                    aSpan.Length());
  }

 private:
  template <typename ActualAlloc>
  value_type* InsertElementAtInternal(index_type aIndex);

  // Insert a new element without copy-constructing. This is useful to avoid
  // temporaries.
  // @return A pointer to the newly inserted element, or null on OOM.
 public:
  [[nodiscard]] value_type* InsertElementAt(index_type aIndex,
                                            const mozilla::fallible_t&) {
    return InsertElementAtInternal<FallibleAlloc>(aIndex);
  }

 private:
  template <typename ActualAlloc, class Item>
  value_type* InsertElementAtInternal(index_type aIndex, Item&& aItem);

  // Insert a new element, move constructing if possible.
 public:
  template <class Item>
  [[nodiscard]] value_type* InsertElementAt(index_type aIndex, Item&& aItem,
                                            const mozilla::fallible_t&) {
    return InsertElementAtInternal<FallibleAlloc>(aIndex,
                                                  std::forward<Item>(aItem));
  }

  // Reconstruct the element at the given index, and return a pointer to the
  // reconstructed element.  This will destroy the existing element and
  // default-construct a new one, giving you a state much like what single-arg
  // InsertElementAt(), or no-arg AppendElement() does, but without changing the
  // length of the array.
  //
  // array[idx] = value_type()
  //
  // would accomplish the same thing as long as value_type has the appropriate
  // moving operator=, but some types don't for various reasons.
  mozilla::NotNull<value_type*> ReconstructElementAt(index_type aIndex) {
    value_type* elem = &ElementAt(aIndex);
    elem_traits::Destruct(elem);
    elem_traits::Construct(elem);
    return mozilla::WrapNotNullUnchecked(elem);
  }

  // This method searches for the smallest index of an element that is strictly
  // greater than |aItem|. If |aItem| is inserted at this index, the array will
  // remain sorted and |aItem| would come after all elements that are equal to
  // it. If |aItem| is greater than or equal to all elements in the array, the
  // array length is returned.
  //
  // Note that consumers who want to know whether there are existing items equal
  // to |aItem| in the array can just check that the return value here is > 0
  // and indexing into the previous slot gives something equal to |aItem|.
  //
  //
  // @param aItem  The item to search for.
  // @param aComp  The Comparator used.
  // @return        The index of greatest element <= to |aItem|
  // @precondition The array is sorted
  template <class Item, class Comparator>
  [[nodiscard]] index_type IndexOfFirstElementGt(
      const Item& aItem, const Comparator& aComp) const {
    using mozilla::BinarySearchIf;
    ::detail::CompareWrapper<Comparator, value_type, Item> comp(aComp);

    size_t index;
    BinarySearchIf(
        Elements(), 0, Length(),
        [&](const value_type& aElement) {
          return comp.Compare(aElement, aItem) <= 0 ? 1 : -1;
        },
        &index);
    return index;
  }

  // A variation on the IndexOfFirstElementGt method defined above.
  template <class Item>
  [[nodiscard]] index_type IndexOfFirstElementGt(const Item& aItem) const {
    return IndexOfFirstElementGt(aItem,
                                 nsDefaultComparator<value_type, Item>());
  }

 private:
  template <typename ActualAlloc, class Item, class Comparator>
  value_type* InsertElementSortedInternal(Item&& aItem,
                                          const Comparator& aComp) {
    index_type index = IndexOfFirstElementGt<Item, Comparator>(aItem, aComp);
    return InsertElementAtInternal<ActualAlloc>(index,
                                                std::forward<Item>(aItem));
  }

  // Inserts |aItem| at such an index to guarantee that if the array
  // was previously sorted, it will remain sorted after this
  // insertion.
 public:
  template <class Item, class Comparator>
  [[nodiscard]] value_type* InsertElementSorted(Item&& aItem,
                                                const Comparator& aComp,
                                                const mozilla::fallible_t&) {
    return InsertElementSortedInternal<FallibleAlloc>(std::forward<Item>(aItem),
                                                      aComp);
  }

  // A variation on the InsertElementSorted method defined above.
 public:
  template <class Item>
  [[nodiscard]] value_type* InsertElementSorted(Item&& aItem,
                                                const mozilla::fallible_t&) {
    return InsertElementSortedInternal<FallibleAlloc>(
        std::forward<Item>(aItem), nsDefaultComparator<value_type, Item>{});
  }

 private:
  template <typename ActualAlloc, class Item>
  value_type* AppendElementsInternal(const Item* aArray, size_type aArrayLen);

  // This method appends elements to the end of this array.
  // @param aArray    The elements to append to this array.
  // @param aArrayLen The number of elements to append to this array.
  // @return          A pointer to the new elements in the array, or null if
  //                  the operation failed due to insufficient memory.
 public:
  template <class Item>
  [[nodiscard]] value_type* AppendElements(const Item* aArray,
                                           size_type aArrayLen,
                                           const mozilla::fallible_t&) {
    return AppendElementsInternal<FallibleAlloc>(aArray, aArrayLen);
  }

  template <class Item>
  [[nodiscard]] value_type* AppendElements(mozilla::Span<Item> aSpan,
                                           const mozilla::fallible_t&) {
    return AppendElementsInternal<FallibleAlloc>(aSpan.Elements(),
                                                 aSpan.Length());
  }

  // A variation on the AppendElements method defined above.
  template <class Item, class Allocator>
  [[nodiscard]] value_type* AppendElements(
      const nsTArray_Impl<Item, Allocator>& aArray,
      const mozilla::fallible_t&) {
    return AppendElementsInternal<FallibleAlloc>(aArray.Elements(),
                                                 aArray.Length());
  }

 private:
  template <typename ActualAlloc, class Item, class Allocator>
  value_type* AppendElementsInternal(nsTArray_Impl<Item, Allocator>&& aArray);

  // Move all elements from another array to the end of this array.
  // @return A pointer to the newly appended elements, or null on OOM.
 public:
  template <class Item, class Allocator>
  [[nodiscard]] value_type* AppendElements(
      nsTArray_Impl<Item, Allocator>&& aArray, const mozilla::fallible_t&) {
    return AppendElementsInternal<FallibleAlloc>(std::move(aArray));
  }

  // Append a new element, constructed in place from the provided arguments.
 protected:
  template <typename ActualAlloc, class... Args>
  value_type* EmplaceBackInternal(Args&&... aItem);

 public:
  template <class... Args>
  [[nodiscard]] value_type* EmplaceBack(const mozilla::fallible_t&,
                                        Args&&... aArgs) {
    return EmplaceBackInternal<FallibleAlloc, Args...>(
        std::forward<Args>(aArgs)...);
  }

 private:
  template <typename ActualAlloc, class Item>
  value_type* AppendElementInternal(Item&& aItem);

  // Append a new element, move constructing if possible.
 public:
  template <class Item>
  [[nodiscard]] value_type* AppendElement(Item&& aItem,
                                          const mozilla::fallible_t&) {
    return AppendElementInternal<FallibleAlloc>(std::forward<Item>(aItem));
  }

 private:
  template <typename ActualAlloc>
  value_type* AppendElementsInternal(size_type aCount) {
    if (!ActualAlloc::Successful(this->template ExtendCapacity<ActualAlloc>(
            Length(), aCount, sizeof(value_type)))) {
      return nullptr;
    }
    value_type* elems = Elements() + Length();
    size_type i;
    for (i = 0; i < aCount; ++i) {
      elem_traits::Construct(elems + i);
    }
    this->IncrementLength(aCount);
    return elems;
  }

  // Append new elements without copy-constructing. This is useful to avoid
  // temporaries.
  // @return A pointer to the newly appended elements, or null on OOM.
 public:
  [[nodiscard]] value_type* AppendElements(size_type aCount,
                                           const mozilla::fallible_t&) {
    return AppendElementsInternal<FallibleAlloc>(aCount);
  }

 private:
  // Append a new element without copy-constructing. This is useful to avoid
  // temporaries.
  // @return A pointer to the newly appended element, or null on OOM.
 public:
  [[nodiscard]] value_type* AppendElement(const mozilla::fallible_t&) {
    return AppendElements(1, mozilla::fallible);
  }

  // This method removes a single element from this array, like
  // std::vector::erase.
  // @param pos to the element to remove
  const_iterator RemoveElementAt(const_iterator pos) {
    MOZ_ASSERT(pos.GetArray() == this);

    RemoveElementAt(pos.GetIndex());
    return pos;
  }

  // This method removes a range of elements from this array, like
  // std::vector::erase.
  // @param first iterator to the first of elements to remove
  // @param last iterator to the last of elements to remove
  const_iterator RemoveElementsRange(const_iterator first,
                                     const_iterator last) {
    MOZ_ASSERT(first.GetArray() == this);
    MOZ_ASSERT(last.GetArray() == this);
    MOZ_ASSERT(last.GetIndex() >= first.GetIndex());

    RemoveElementsAt(first.GetIndex(), last.GetIndex() - first.GetIndex());
    return first;
  }

  // This method removes a range of elements from this array.
  // @param aStart The starting index of the elements to remove.
  // @param aCount The number of elements to remove.
  void RemoveElementsAt(index_type aStart, size_type aCount);

 private:
  // Remove a range of elements from this array, but do not check that
  // the range is in bounds.
  // @param aStart The starting index of the elements to remove.
  // @param aCount The number of elements to remove.
  void RemoveElementsAtUnsafe(index_type aStart, size_type aCount);

 public:
  // Similar to the above, but it removes just one element. This does bounds
  // checking only in debug builds.
  void RemoveElementAtUnsafe(index_type aIndex) {
    MOZ_ASSERT(aIndex < Length(), "Trying to remove an invalid element");
    RemoveElementsAtUnsafe(aIndex, 1);
  }

  // A variation on the RemoveElementsAt method defined above.
  void RemoveElementAt(index_type aIndex) { RemoveElementsAt(aIndex, 1); }

  // A variation on RemoveElementAt that removes the last element.
  void RemoveLastElement() { RemoveLastElements(1); }

  // A variation on RemoveElementsAt that removes the last 'aCount' elements.
  void RemoveLastElements(const size_type aCount) {
    // This assertion is redundant, but produces a better error message than the
    // release assertion within TruncateLength.
    MOZ_ASSERT(aCount <= Length());
    TruncateLength(Length() - aCount);
  }

  // Removes the last element of the array and returns a copy of it.
  [[nodiscard]] value_type PopLastElement() {
    // This function intentionally does not call ElementsAt and calls
    // TruncateLengthUnsafe directly to avoid multiple release checks for
    // non-emptiness.
    // This debug assertion is redundant, but produces a better error message
    // than the release assertion below.
    MOZ_ASSERT(!base_type::IsEmpty());
    const size_type oldLen = Length();
    if (MOZ_UNLIKELY(0 == oldLen)) {
      mozilla::detail::InvalidArrayIndex_CRASH(10);
    }
    value_type elem = std::move(Elements()[oldLen - 1]);
    TruncateLengthUnsafe(oldLen - 1);
    return elem;
  }

  // This method performs index-based removals from an array without preserving
  // the order of the array. This is useful if you are using the array as a
  // set-like data structure.
  //
  // These removals are efficient, as they move as few elements as possible. At
  // most N elements, where N is the number of removed elements, will have to
  // be relocated.
  //
  // ## Examples
  //
  // When removing an element from the end of the array, it can be removed in
  // place, by destroying it and decrementing the length.
  //
  // [ 1, 2, 3 ] => [ 1, 2 ]
  //         ^
  //
  // When removing any other single element, it is removed by swapping it with
  // the last element, and then decrementing the length as before.
  //
  // [ 1, 2, 3, 4, 5, 6 ]  => [ 1, 6, 3, 4, 5 ]
  //      ^
  //
  // This method also supports efficiently removing a range of elements. If they
  // are at the end, then they can all be removed like in the one element case.
  //
  // [ 1, 2, 3, 4, 5, 6 ] => [ 1, 2 ]
  //         ^--------^
  //
  // If more elements are removed than exist after the removed section, the
  // remaining elements will be shifted down like in a normal removal.
  //
  // [ 1, 2, 3, 4, 5, 6, 7, 8 ] => [ 1, 2, 7, 8 ]
  //         ^--------^
  //
  // And if fewer elements are removed than exist after the removed section,
  // elements will be moved from the end of the array to fill the vacated space.
  //
  // [ 1, 2, 3, 4, 5, 6, 7, 8 ] => [ 1, 7, 8, 4, 5, 6 ]
  //      ^--^
  //
  // @param aStart The starting index of the elements to remove. @param aCount
  // The number of elements to remove.
  void UnorderedRemoveElementsAt(index_type aStart, size_type aCount);

  // A variation on the UnorderedRemoveElementsAt method defined above to remove
  // a single element. This operation is sometimes called `SwapRemove`.
  //
  // This method is O(1), but does not preserve the order of the elements.
  void UnorderedRemoveElementAt(index_type aIndex) {
    UnorderedRemoveElementsAt(aIndex, 1);
  }

  void Clear() {
    ClearAndRetainStorage();
    base_type::ShrinkCapacityToZero();
  }

  // This method removes elements based on the return value of the
  // callback function aPredicate. If the function returns true for
  // an element, the element is removed. aPredicate will be called
  // for each element in order. It is not safe to access the array
  // inside aPredicate.
  //
  // Returns the number of elements removed.
  template <typename Predicate>
  size_type RemoveElementsBy(Predicate aPredicate);

  // This helper function combines IndexOf with RemoveElementAt to "search
  // and destroy" the first element that is equal to the given element.
  // @param aItem The item to search for.
  // @param aComp The Comparator used to determine element equality.
  // @return true if the element was found
  template <class Item, class Comparator>
  bool RemoveElement(const Item& aItem, const Comparator& aComp) {
    index_type i = IndexOf(aItem, 0, aComp);
    if (i == NoIndex) {
      return false;
    }
    RemoveElementsAtUnsafe(i, 1);
    return true;
  }

  // A variation on the RemoveElement method defined above that assumes
  // that 'operator==' is defined for value_type.
  template <class Item>
  bool RemoveElement(const Item& aItem) {
    return RemoveElement(aItem, nsDefaultComparator<value_type, Item>());
  }

  // Variations for RemoveElement that uses unordered removal.
  template <class Item, class Comparator>
  bool UnorderedRemoveElement(const Item& aItem, const Comparator& aComp) {
    index_type i = IndexOf(aItem, 0, aComp);
    if (i == NoIndex) {
      return false;
    }
    UnorderedRemoveElementAt(i);
    return true;
  }

  template <class Item>
  bool UnorderedRemoveElement(const Item& aItem) {
    return UnorderedRemoveElement(aItem,
                                  nsDefaultComparator<value_type, Item>());
  }

  // This helper function combines IndexOfFirstElementGt with
  // RemoveElementAt to "search and destroy" the last element that
  // is equal to the given element.
  // @param aItem The item to search for.
  // @param aComp The Comparator used to determine element equality.
  // @return true if the element was found
  template <class Item, class Comparator>
  bool RemoveElementSorted(const Item& aItem, const Comparator& aComp) {
    index_type index = IndexOfFirstElementGt(aItem, aComp);
    if (index > 0 && aComp.Equals(ElementAt(index - 1), aItem)) {
      RemoveElementsAtUnsafe(index - 11);
      return true;
    }
    return false;
  }

  // A variation on the RemoveElementSorted method defined above.
  template <class Item>
  bool RemoveElementSorted(const Item& aItem) {
    return RemoveElementSorted(aItem, nsDefaultComparator<value_type, Item>());
  }

  // This method causes the elements contained in this array and the given
  // array to be swapped.
  template <class Allocator>
  void SwapElements(nsTArray_Impl<E, Allocator>& aOther) {
    // The only case this might fail were if someone called this with a
    // AutoTArray upcast to nsTArray_Impl, under the conditions mentioned in the
    // overload for AutoTArray below.
    this->template SwapArrayElements<InfallibleAlloc>(aOther,
                                                      sizeof(value_type));
  }

  template <size_t N>
  void SwapElements(AutoTArray<E, N>& aOther) {
    // Allocation might fail if Alloc==FallibleAlloc and
    // Allocator==InfallibleAlloc and aOther uses auto storage. Allow this for
    // small inline sizes, and crash in the rare case of a small OOM error.
    static_assert(!std::is_same_v<Alloc, FallibleAlloc> ||
                  sizeof(E) * N <= 1024);
    this->template SwapArrayElements<InfallibleAlloc>(aOther,
                                                      sizeof(value_type));
  }

  template <class Allocator>
  [[nodiscard]] auto SwapElements(nsTArray_Impl<E, Allocator>& aOther,
                                  const mozilla::fallible_t&) {
    // Allocation might fail if Alloc==FallibleAlloc and
    // Allocator==InfallibleAlloc and aOther uses auto storage.
    return FallibleAlloc::Result(
        this->template SwapArrayElements<FallibleAlloc>(aOther,
                                                        sizeof(value_type)));
  }

 private:
  // Used by ApplyIf functions to invoke a callable that takes either:
  // - Nothing: F(void)
  // - Index only: F(size_t)
  // - Reference to element only: F(maybe-const value_type&)
  // - Both index and reference: F(size_t, maybe-const value_type&)
  // `value_type` must be const when called from const method.
  template <typename T, typename Param0, typename Param1>
  struct InvokeWithIndexAndOrReferenceHelper {
    static constexpr bool valid = false;
  };
  template <typename T>
  struct InvokeWithIndexAndOrReferenceHelper<T, voidvoid> {
    static constexpr bool valid = true;
    template <typename F>
    static auto Invoke(F&& f, size_t, T&) {
      return f();
    }
  };
  template <typename T>
  struct InvokeWithIndexAndOrReferenceHelper<T, size_t, void> {
    static constexpr bool valid = true;
    template <typename F>
    static auto Invoke(F&& f, size_t i, T&) {
      return f(i);
    }
  };
  template <typename T>
  struct InvokeWithIndexAndOrReferenceHelper<T, T&, void> {
    static constexpr bool valid = true;
    template <typename F>
    static auto Invoke(F&& f, size_t, T& e) {
      return f(e);
    }
  };
  template <typename T>
  struct InvokeWithIndexAndOrReferenceHelper<T, const T&, void> {
    static constexpr bool valid = true;
    template <typename F>
    static auto Invoke(F&& f, size_t, T& e) {
      return f(e);
    }
  };
  template <typename T>
  struct InvokeWithIndexAndOrReferenceHelper<T, size_t, T&> {
    static constexpr bool valid = true;
    template <typename F>
    static auto Invoke(F&& f, size_t i, T& e) {
      return f(i, e);
    }
  };
  template <typename T>
  struct InvokeWithIndexAndOrReferenceHelper<T, size_t, const T&> {
    static constexpr bool valid = true;
    template <typename F>
    static auto Invoke(F&& f, size_t i, T& e) {
      return f(i, e);
    }
  };
  template <typename T, typename F>
  static auto InvokeWithIndexAndOrReference(F&& f, size_t i, T& e) {
    using Invoker = InvokeWithIndexAndOrReferenceHelper<
        T, typename mozilla::FunctionTypeTraits<F>::template ParameterType<0>,
        typename mozilla::FunctionTypeTraits<F>::template ParameterType<1>>;
    static_assert(Invoker::valid,
                  "ApplyIf's Function parameters must match either: (void), "
                  "(size_t), (maybe-const value_type&), or "
                  "(size_t, maybe-const value_type&)");
    return Invoker::Invoke(std::forward<F>(f), i, e);
  }

 public:
  // 'Apply' family of methods.
  //
  // The advantages of using Apply methods with lambdas include:
  // - Safety of accessing elements from within the call, when the array cannot
  //   have been modified between the iteration and the subsequent access.
  // - Avoiding moot conversions: pointer->index during a search, followed by
  //   index->pointer after the search when accessing the element.
  // - Embedding your code into the algorithm, giving the compiler more chances
  //   to optimize.

  // Search for the first element comparing equal to aItem with the given
  // comparator (`==` by default).
  // If such an element exists, return the result of evaluating either:
  // - `aFunction()`
  // - `aFunction(index_type)`
  // - `aFunction(maybe-const? value_type&)`
  // - `aFunction(index_type, maybe-const? value_type&)`
  // (`aFunction` must have one of the above signatures with these exact types,
  //  including references; implicit conversions or generic types not allowed.
  //  If `this` array is const, the referenced `value_type` must be const too;
  //  otherwise it may be either const or non-const.)
  // But if the element is not found, return the result of evaluating
  // `aFunctionElse()`.
  template <class Item, class Comparator, class Function, class FunctionElse>
  auto ApplyIf(const Item& aItem, index_type aStart, const Comparator& aComp,
               Function&& aFunction, FunctionElse&& aFunctionElse) const {
    static_assert(
        std::is_same_v<
            typename mozilla::FunctionTypeTraits<Function>::ReturnType,
            typename mozilla::FunctionTypeTraits<FunctionElse>::ReturnType>,
        "ApplyIf's `Function` and `FunctionElse` must return the same type.");

    ::detail::CompareWrapper<Comparator, value_type, Item> comp(aComp);

    const value_type* const elements = Elements();
    const value_type* const iend = elements + Length();
    for (const value_type* iter = elements + aStart; iter != iend; ++iter) {
      if (comp.Equals(*iter, aItem)) {
        return InvokeWithIndexAndOrReference<const value_type>(
            std::forward<Function>(aFunction), iter - elements, *iter);
      }
    }
    return aFunctionElse();
  }
  template <class Item, class Comparator, class Function, class FunctionElse>
  auto ApplyIf(const Item& aItem, index_type aStart, const Comparator& aComp,
               Function&& aFunction, FunctionElse&& aFunctionElse) {
    static_assert(
        std::is_same_v<
            typename mozilla::FunctionTypeTraits<Function>::ReturnType,
            typename mozilla::FunctionTypeTraits<FunctionElse>::ReturnType>,
        "ApplyIf's `Function` and `FunctionElse` must return the same type.");

    ::detail::CompareWrapper<Comparator, value_type, Item> comp(aComp);

    value_type* const elements = Elements();
    value_type* const iend = elements + Length();
    for (value_type* iter = elements + aStart; iter != iend; ++iter) {
      if (comp.Equals(*iter, aItem)) {
        return InvokeWithIndexAndOrReference<value_type>(
            std::forward<Function>(aFunction), iter - elements, *iter);
      }
    }
    return aFunctionElse();
  }
  template <class Item, class Function, class FunctionElse>
  auto ApplyIf(const Item& aItem, index_type aStart, Function&& aFunction,
               FunctionElse&& aFunctionElse) const {
    return ApplyIf(aItem, aStart, nsDefaultComparator<value_type, Item>(),
                   std::forward<Function>(aFunction),
                   std::forward<FunctionElse>(aFunctionElse));
  }
  template <class Item, class Function, class FunctionElse>
  auto ApplyIf(const Item& aItem, index_type aStart, Function&& aFunction,
               FunctionElse&& aFunctionElse) {
    return ApplyIf(aItem, aStart, nsDefaultComparator<value_type, Item>(),
                   std::forward<Function>(aFunction),
                   std::forward<FunctionElse>(aFunctionElse));
  }
  template <class Item, class Function, class FunctionElse>
  auto ApplyIf(const Item& aItem, Function&& aFunction,
               FunctionElse&& aFunctionElse) const {
    return ApplyIf(aItem, 0, std::forward<Function>(aFunction),
                   std::forward<FunctionElse>(aFunctionElse));
  }
  template <class Item, class Function, class FunctionElse>
  auto ApplyIf(const Item& aItem, Function&& aFunction,
               FunctionElse&& aFunctionElse) {
    return ApplyIf(aItem, 0, std::forward<Function>(aFunction),
                   std::forward<FunctionElse>(aFunctionElse));
  }

  //
  // Allocation
  //

  // This method may increase the capacity of this array object to the
  // specified amount.  This method may be called in advance of several
  // AppendElement operations to minimize heap re-allocations.  This method
  // will not reduce the number of elements in this array.
  // @param aCapacity The desired capacity of this array.
  // @return True if the operation succeeded; false if we ran out of memory
 protected:
  template <typename ActualAlloc = Alloc>
  MOZ_REINITIALIZES typename ActualAlloc::ResultType SetCapacity(
      size_type aCapacity) {
    return ActualAlloc::Result(this->template EnsureCapacity<ActualAlloc>(
        aCapacity, sizeof(value_type)));
  }

 public:
  [[nodiscard]] bool SetCapacity(size_type aCapacity,
                                 const mozilla::fallible_t&) {
    return SetCapacity<FallibleAlloc>(aCapacity);
  }

  // This method modifies the length of the array.  If the new length is
  // larger than the existing length of the array, then new elements will be
  // constructed using value_type's default constructor.  Otherwise, this call
  // removes elements from the array (see also RemoveElementsAt).
  // @param aNewLen The desired length of this array.
  // @return True if the operation succeeded; false otherwise.
  // See also TruncateLength for a more efficient variant if the new length is
  // guaranteed to be smaller than the old.
 protected:
  template <typename ActualAlloc = Alloc>
  typename ActualAlloc::ResultType SetLength(size_type aNewLen) {
    const size_type oldLen = Length();
    if (aNewLen > oldLen) {
      return ActualAlloc::ConvertBoolToResultType(
          InsertElementsAtInternal<ActualAlloc>(oldLen, aNewLen - oldLen) !=
          nullptr);
    }

    TruncateLengthUnsafe(aNewLen);
    return ActualAlloc::ConvertBoolToResultType(true);
  }

 public:
  [[nodiscard]] bool SetLength(size_type aNewLen, const mozilla::fallible_t&) {
    return SetLength<FallibleAlloc>(aNewLen);
  }

  // This method modifies the length of the array, but may only be
  // called when the new length is shorter than the old.  It can
  // therefore be called when value_type has no default constructor,
  // unlike SetLength.  It removes elements from the array (see also
  // RemoveElementsAt).
  // @param aNewLen The desired length of this array.
  void TruncateLength(size_type aNewLen) {
    // This assertion is redundant, but produces a better error message than the
    // release assertion below.
    MOZ_ASSERT(aNewLen <= Length(), "caller should use SetLength instead");

    if (MOZ_UNLIKELY(aNewLen > Length())) {
      mozilla::detail::InvalidArrayIndex_CRASH(aNewLen, Length());
    }

    TruncateLengthUnsafe(aNewLen);
  }

 private:
  void TruncateLengthUnsafe(size_type aNewLen) {
    const size_type oldLen = Length();
    if (oldLen) {
      DestructRange(aNewLen, oldLen - aNewLen);
      base_type::mHdr->mLength = aNewLen;
    }
  }

  // This method ensures that the array has length at least the given
  // length.  If the current length is shorter than the given length,
  // then new elements will be constructed using value_type's default
  // constructor.
  // @param aMinLen The desired minimum length of this array.
  // @return True if the operation succeeded; false otherwise.
 protected:
  template <typename ActualAlloc = Alloc>
  typename ActualAlloc::ResultType EnsureLengthAtLeast(size_type aMinLen) {
    size_type oldLen = Length();
    if (aMinLen > oldLen) {
      return ActualAlloc::ConvertBoolToResultType(
          !!InsertElementsAtInternal<ActualAlloc>(oldLen, aMinLen - oldLen));
    }
    return ActualAlloc::ConvertBoolToResultType(true);
  }

 public:
  [[nodiscard]] bool EnsureLengthAtLeast(size_type aMinLen,
                                         const mozilla::fallible_t&) {
    return EnsureLengthAtLeast<FallibleAlloc>(aMinLen);
  }

  // This method inserts elements into the array, constructing
  // them using value_type's default constructor.
  // @param aIndex the place to insert the new elements. This must be no
  //               greater than the current length of the array.
  // @param aCount the number of elements to insert
 private:
  template <typename ActualAlloc>
  value_type* InsertElementsAtInternal(index_type aIndex, size_type aCount) {
    if (!ActualAlloc::Successful(this->template InsertSlotsAt<ActualAlloc>(
            aIndex, aCount, sizeof(value_type)))) {
      return nullptr;
    }

    // Initialize the extra array elements
    value_type* iter = Elements() + aIndex;
    value_type* iend = iter + aCount;
    for (; iter != iend; ++iter) {
      elem_traits::Construct(iter);
    }

    return Elements() + aIndex;
  }

 public:
  [[nodiscard]] value_type* InsertElementsAt(index_type aIndex,
                                             size_type aCount,
                                             const mozilla::fallible_t&) {
    return InsertElementsAtInternal<FallibleAlloc>(aIndex, aCount);
  }

  // This method inserts elements into the array, constructing them
  // value_type's copy constructor (or whatever one-arg constructor
  // happens to match the Item type).
  // @param aIndex the place to insert the new elements. This must be no
  //               greater than the current length of the array.
  // @param aCount the number of elements to insert.
  // @param aItem the value to use when constructing the new elements.
 private:
  template <typename ActualAlloc, class Item>
  value_type* InsertElementsAtInternal(index_type aIndex, size_type aCount,
                                       const Item& aItem);

 public:
  template <class Item>
  [[nodiscard]] value_type* InsertElementsAt(index_type aIndex,
                                             size_type aCount,
                                             const Item& aItem,
                                             const mozilla::fallible_t&) {
    return InsertElementsAt<Item, FallibleAlloc>(aIndex, aCount, aItem);
  }

  // This method may be called to minimize the memory used by this array.
  void Compact() { ShrinkCapacity(sizeof(value_type)); }

  //
  // Sorting
  //

  // This method sorts the elements of the array.  It uses the LessThan
  // method defined on the given Comparator object to collate elements or
  // it wraps a tri-state comparison lambda into such a comparator.
  // It uses std::sort. It expects value_type to be move assignable and
  // constructible and uses those always, regardless of the chosen
  // nsTArray_RelocationStrategy.
  //
  // @param aComp The Comparator used to collate elements.
  template <SortBoundsCheck Check = SortBoundsCheck::Enable, class Comparator>
  void Sort(const Comparator& aComp) {
    static_assert(std::is_move_assignable_v<value_type>);
    static_assert(std::is_move_constructible_v<value_type>);

    ::detail::CompareWrapper<Comparator, value_type, value_type> comp(aComp);
    auto compFn = [&comp](const auto& left, const auto& right) {
      return comp.LessThan(left, right);
    };
    if constexpr (Check == SortBoundsCheck::Enable) {
      std::sort(begin(), end(), compFn);
    } else {
      std::sort(Elements(), Elements() + Length(), compFn);
    }
    ::detail::AssertStrictWeakOrder(Elements(), Elements() + Length(), compFn);
  }

  // A variation on the Sort method defined above that assumes that
  // 'operator<' is defined for 'value_type'.
  template <SortBoundsCheck Check = SortBoundsCheck::Enable>
  void Sort() {
    Sort(nsDefaultComparator<value_type, value_type>());
  }

  // This method sorts the elements of the array in a stable way (i.e. not
  // changing the relative order of elements considered equal by the
  // Comparator).  It uses the LessThan method defined on the given Comparator
  // object to collate elements.
  // It uses std::stable_sort. It expects value_type to be move assignable and
  // constructible and uses those always, regardless of the chosen
  // nsTArray_RelocationStrategy.
  //
  // @param aComp The Comparator used to collate elements.
  template <SortBoundsCheck Check = SortBoundsCheck::Enable, class Comparator>
  void StableSort(const Comparator& aComp) {
    static_assert(std::is_move_assignable_v<value_type>);
    static_assert(std::is_move_constructible_v<value_type>);

    const ::detail::CompareWrapper<Comparator, value_type, value_type> comp(
        aComp);
    auto compFn = [&comp](const auto& lhs, const auto& rhs) {
      return comp.LessThan(lhs, rhs);
    };
    if constexpr (Check == SortBoundsCheck::Enable) {
      std::stable_sort(begin(), end(), compFn);
    } else {
      std::stable_sort(Elements(), Elements() + Length(), compFn);
    }
    ::detail::AssertStrictWeakOrder(Elements(), Elements() + Length(), compFn);
  }

  // A variation on the StableSort method defined above that assumes that
  // 'operator<' is defined for 'value_type'.
  template <SortBoundsCheck Check = SortBoundsCheck::Enable>
  void StableSort() {
    StableSort(nsDefaultComparator<value_type, value_type>());
  }

  // This method reverses the array in place.
  void Reverse() {
    value_type* elements = Elements();
    const size_type len = Length();
    for (index_type i = 0, iend = len / 2; i < iend; ++i) {
      std::swap(elements[i], elements[len - i - 1]);
    }
  }

 protected:
  using base_type::Hdr;
  using base_type::ShrinkCapacity;

  // This method invokes value_type's destructor on a range of elements.
  // @param aStart The index of the first element to destroy.
  // @param aCount The number of elements to destroy.
  void DestructRange(index_type aStart, size_type aCount) {
    value_type* iter = Elements() + aStart;
    value_type* iend = iter + aCount;
    for (; iter != iend; ++iter) {
      elem_traits::Destruct(iter);
    }
  }

  // This method invokes value_type's copy-constructor on a range of elements.
  // @param aStart  The index of the first element to construct.
  // @param aCount  The number of elements to construct.
  // @param aValues The array of elements to copy.
  template <class Item>
  void AssignRange(index_type aStart, size_type aCount, const Item* aValues) {
    std::uninitialized_copy(aValues, aValues + aCount, Elements() + aStart);
  }
};

template <typename E, class Alloc>
template <typename ActualAlloc, class Item>
auto nsTArray_Impl<E, Alloc>::AssignInternal(const Item* aArray,
                                             size_type aArrayLen) ->
    typename ActualAlloc::ResultType {
  static_assert(std::is_same_v<ActualAlloc, InfallibleAlloc> ||
                std::is_same_v<ActualAlloc, FallibleAlloc>);

  if constexpr (std::is_same_v<ActualAlloc, InfallibleAlloc>) {
    ClearAndRetainStorage();
  }
  // Adjust memory allocation up-front to catch errors in the fallible case.
  // We might relocate the elements to be destroyed unnecessarily. This could be
  // optimized, but would make things more complicated.
  if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
          aArrayLen, sizeof(value_type)))) {
    return ActualAlloc::ConvertBoolToResultType(false);
  }

  MOZ_ASSERT_IF(this->HasEmptyHeader(), aArrayLen == 0);
  if (!this->HasEmptyHeader()) {
    if constexpr (std::is_same_v<ActualAlloc, FallibleAlloc>) {
      ClearAndRetainStorage();
    }
    AssignRange(0, aArrayLen, aArray);
    base_type::mHdr->mLength = aArrayLen;
  }

  return ActualAlloc::ConvertBoolToResultType(true);
}

template <typename E, class Alloc>
template <typename ActualAlloc, class Item>
auto nsTArray_Impl<E, Alloc>::ReplaceElementsAtInternal(index_type aStart,
                                                        size_type aCount,
                                                        const Item* aArray,
                                                        size_type aArrayLen)
    -> value_type* {
  if (MOZ_UNLIKELY(aStart > Length())) {
    mozilla::detail::InvalidArrayIndex_CRASH(aStart, Length());
  }
  if (MOZ_UNLIKELY(aCount > Length() - aStart)) {
    mozilla::detail::InvalidArrayIndex_CRASH(aStart + aCount, Length());
  }

  // Adjust memory allocation up-front to catch errors.
  if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
          Length() + aArrayLen - aCount, sizeof(value_type)))) {
    return nullptr;
  }
  DestructRange(aStart, aCount);
  this->template ShiftData<ActualAlloc>(aStart, aCount, aArrayLen,
                                        sizeof(value_type));
  AssignRange(aStart, aArrayLen, aArray);
  return Elements() + aStart;
}

template <typename E, class Alloc>
void nsTArray_Impl<E, Alloc>::RemoveElementsAt(index_type aStart,
                                               size_type aCount) {
  MOZ_ASSERT(aCount == 0 || aStart < Length(), "Invalid aStart index");

  mozilla::CheckedInt<index_type> rangeEnd = aStart;
  rangeEnd += aCount;

  if (MOZ_UNLIKELY(!rangeEnd.isValid() || rangeEnd.value() > Length())) {
    mozilla::detail::InvalidArrayIndex_CRASH(aStart, Length());
  }

  RemoveElementsAtUnsafe(aStart, aCount);
}

template <typename E, class Alloc>
void nsTArray_Impl<E, Alloc>::RemoveElementsAtUnsafe(index_type aStart,
                                                     size_type aCount) {
  DestructRange(aStart, aCount);
  this->template ShiftData<InfallibleAlloc>(aStart, aCount, 0,
                                            sizeof(value_type));
}

template <typename E, class Alloc>
void nsTArray_Impl<E, Alloc>::UnorderedRemoveElementsAt(index_type aStart,
                                                        size_type aCount) {
  MOZ_ASSERT(aCount == 0 || aStart < Length(), "Invalid aStart index");

  mozilla::CheckedInt<index_type> rangeEnd = aStart;
  rangeEnd += aCount;

  if (MOZ_UNLIKELY(!rangeEnd.isValid() || rangeEnd.value() > Length())) {
    mozilla::detail::InvalidArrayIndex_CRASH(aStart, Length());
  }

  // Destroy the elements which are being removed, and then swap elements in to
  // replace them from the end. See the docs on the declaration of this
  // function.
  DestructRange(aStart, aCount);
  this->template SwapFromEnd<InfallibleAlloc>(aStart, aCount,
                                              sizeof(value_type));
}

template <typename E, class Alloc>
template <typename Predicate>
auto nsTArray_Impl<E, Alloc>::RemoveElementsBy(Predicate aPredicate)
    -> size_type {
  if (this->HasEmptyHeader()) {
    return 0;
  }

  index_type j = 0;
  const index_type len = Length();
  value_type* const elements = Elements();
  for (index_type i = 0; i < len; ++i) {
    const bool result = aPredicate(elements[i]);

    // Check that the array has not been modified by the predicate.
    MOZ_DIAGNOSTIC_ASSERT(len == base_type::mHdr->mLength &&
                          elements == Elements());

    if (result) {
      elem_traits::Destruct(elements + i);
    } else {
      if (j < i) {
        relocation_type::RelocateNonOverlappingRegion(
            elements + j, elements + i, 1sizeof(value_type));
      }
      ++j;
    }
  }

  base_type::mHdr->mLength = j;
  return len - j;
}

template <typename E, class Alloc>
template <typename ActualAlloc, class Item>
auto nsTArray_Impl<E, Alloc>::InsertElementsAtInternal(index_type aIndex,
                                                       size_type aCount,
                                                       const Item& aItem)
    -> value_type* {
  if (!ActualAlloc::Successful(this->template InsertSlotsAt<ActualAlloc>(
          aIndex, aCount, sizeof(value_type)))) {
    return nullptr;
  }

  // Initialize the extra array elements
  value_type* iter = Elements() + aIndex;
  value_type* iend = iter + aCount;
  for (; iter != iend; ++iter) {
    elem_traits::Construct(iter, aItem);
  }

  return Elements() + aIndex;
}

template <typename E, class Alloc>
template <typename ActualAlloc>
auto nsTArray_Impl<E, Alloc>::InsertElementAtInternal(index_type aIndex)
    -> value_type* {
  if (MOZ_UNLIKELY(aIndex > Length())) {
    mozilla::detail::InvalidArrayIndex_CRASH(aIndex, Length());
  }

  // Length() + 1 is guaranteed to not overflow, so EnsureCapacity is OK.
  if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
          Length() + 1sizeof(value_type)))) {
    return nullptr;
  }
  this->template ShiftData<ActualAlloc>(aIndex, 01sizeof(value_type));
  value_type* elem = Elements() + aIndex;
  elem_traits::Construct(elem);
  return elem;
}

template <typename E, class Alloc>
template <typename ActualAlloc, class Item>
auto nsTArray_Impl<E, Alloc>::InsertElementAtInternal(index_type aIndex,
                                                      Item&& aItem)
    -> value_type* {
  if (MOZ_UNLIKELY(aIndex > Length())) {
    mozilla::detail::InvalidArrayIndex_CRASH(aIndex, Length());
  }

  // Length() + 1 is guaranteed to not overflow, so EnsureCapacity is OK.
  if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
          Length() + 1sizeof(value_type)))) {
    return nullptr;
  }
  this->template ShiftData<ActualAlloc>(aIndex, 01sizeof(value_type));
  value_type* elem = Elements() + aIndex;
  elem_traits::Construct(elem, std::forward<Item>(aItem));
  return elem;
}

template <typename E, class Alloc>
template <typename ActualAlloc, class Item>
auto nsTArray_Impl<E, Alloc>::AppendElementsInternal(const Item* aArray,
                                                     size_type aArrayLen)
    -> value_type* {
  if (!ActualAlloc::Successful(this->template ExtendCapacity<ActualAlloc>(
          Length(), aArrayLen, sizeof(value_type)))) {
    return nullptr;
  }
  index_type len = Length();
  AssignRange(len, aArrayLen, aArray);
  this->IncrementLength(aArrayLen);
  return Elements() + len;
}

template <typename E, class Alloc>
template <typename ActualAlloc, class Item, class Allocator>
auto nsTArray_Impl<E, Alloc>::AppendElementsInternal(
    nsTArray_Impl<Item, Allocator>&& aArray) -> value_type* {
  if constexpr (std::is_same_v<Alloc, Allocator>) {
    MOZ_ASSERT(&aArray != this, "argument must be different aArray");
  }
  index_type len = Length();
  if (len == 0) {
    // XXX This might still be optimized. If aArray uses auto-storage but we
    // won't, we might better retain our storage if it's sufficiently large.
    this->ShrinkCapacityToZero();
    this->MoveInit(aArray, sizeof(value_type));
    return Elements();
  }

  index_type otherLen = aArray.Length();
  if (!ActualAlloc::Successful(this->template ExtendCapacity<ActualAlloc>(
          len, otherLen, sizeof(value_type)))) {
    return nullptr;
  }
  relocation_type::RelocateNonOverlappingRegion(
      Elements() + len, aArray.Elements(), otherLen, sizeof(value_type));
  this->IncrementLength(otherLen);
  aArray.template ShiftData<ActualAlloc>(0, otherLen, 0sizeof(value_type));
  return Elements() + len;
}

template <typename E, class Alloc>
template <typename ActualAlloc, class Item>
auto nsTArray_Impl<E, Alloc>::AppendElementInternal(Item&& aItem)
    -> value_type* {
  // Length() + 1 is guaranteed to not overflow, so EnsureCapacity is OK.
  if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
          Length() + 1sizeof(value_type)))) {
    return nullptr;
  }
  value_type* elem = Elements() + Length();
  elem_traits::Construct(elem, std::forward<Item>(aItem));
  this->mHdr->mLength += 1;
  return elem;
}

template <typename E, class Alloc>
template <typename ActualAlloc, class... Args>
auto nsTArray_Impl<E, Alloc>::EmplaceBackInternal(Args&&... aArgs)
    -> value_type* {
  // Length() + 1 is guaranteed to not overflow, so EnsureCapacity is OK.
  if (!ActualAlloc::Successful(this->template EnsureCapacity<ActualAlloc>(
          Length() + 1sizeof(value_type)))) {
    return nullptr;
  }
  value_type* elem = Elements() + Length();
  elem_traits::Emplace(elem, std::forward<Args>(aArgs)...);
  this->mHdr->mLength += 1;
  return elem;
}

template <typename E, typename Alloc>
inline void ImplCycleCollectionUnlink(nsTArray_Impl<E, Alloc>& aField) {
  aField.Clear();
}

template <typename E, typename Alloc, typename Callback>
inline void ImplCycleCollectionIndexedContainer(nsTArray_Impl<E, Alloc>& aField,
                                                Callback&& aCallback) {
  for (auto& value : aField) {
    aCallback(value);
  }
}

//
// nsTArray is an infallible vector class.  See the comment at the top of this
// file for more details.
//
template <class E>
class MOZ_GSL_OWNER nsTArray
    : public nsTArray_Impl<E, nsTArrayInfallibleAllocator> {
 public:
  using InfallibleAlloc = nsTArrayInfallibleAllocator;
  using base_type = nsTArray_Impl<E, InfallibleAlloc>;
  using self_type = nsTArray<E>;
  using typename base_type::index_type;
  using typename base_type::size_type;
  using typename base_type::value_type;

  constexpr nsTArray() = default;
  explicit nsTArray(size_type aCapacity) : base_type(aCapacity) {}
  MOZ_IMPLICIT nsTArray(std::initializer_list<E> aIL) {
    AppendElements(aIL.begin(), aIL.size());
  }

  template <class Item>
  nsTArray(const Item* aArray, size_type aArrayLen) {
    AppendElements(aArray, aArrayLen);
  }

  template <class Item>
  explicit nsTArray(mozilla::Span<Item> aSpan) {
    AppendElements(aSpan);
  }

  template <class Allocator>
  explicit nsTArray(const nsTArray_Impl<E, Allocator>& aOther)
      : base_type(aOther) {}
  template <class Allocator>
  MOZ_IMPLICIT nsTArray(nsTArray_Impl<E, Allocator>&& aOther)
      : base_type(std::move(aOther)) {}

  template <class Allocator>
  self_type& operator=(const nsTArray_Impl<E, Allocator>& aOther) {
    base_type::operator=(aOther);
    return *this;
  }
  template <class Allocator>
  self_type& operator=(nsTArray_Impl<E, Allocator>&& aOther) {
    // This is quite complex, since we don't know if we are an AutoTArray. While
    // AutoTArray overrides this operator=, this might be called on a nsTArray&
    // bound to an AutoTArray.
    base_type::operator=(std::move(aOther));
    return *this;
  }

  using base_type::AppendElement;
  using base_type::AppendElements;
  using base_type::EmplaceBack;
  using base_type::EnsureLengthAtLeast;
  using base_type::InsertElementAt;
  using base_type::InsertElementsAt;
  using base_type::InsertElementSorted;
  using base_type::ReplaceElementsAt;
  using base_type::SetCapacity;
  using base_type::SetLength;

  template <class Item>
  mozilla::NotNull<value_type*> AppendElements(const Item* aArray,
                                               size_type aArrayLen) {
    return mozilla::WrapNotNullUnchecked(
        this->template AppendElementsInternal<InfallibleAlloc>(aArray,
                                                               aArrayLen));
  }

  template <class Item>
  mozilla::NotNull<value_type*> AppendElements(mozilla::Span<Item> aSpan) {
    return mozilla::WrapNotNullUnchecked(
        this->template AppendElementsInternal<InfallibleAlloc>(aSpan.Elements(),
                                                               aSpan.Length()));
  }

  template <class Item, class Allocator>
  mozilla::NotNull<value_type*> AppendElements(
      const nsTArray_Impl<Item, Allocator>& aArray) {
    return mozilla::WrapNotNullUnchecked(
        this->template AppendElementsInternal<InfallibleAlloc>(
            aArray.Elements(), aArray.Length()));
  }

  template <class Item, class Allocator>
  mozilla::NotNull<value_type*> AppendElements(
      nsTArray_Impl<Item, Allocator>&& aArray) {
    return mozilla::WrapNotNullUnchecked(
        this->template AppendElementsInternal<InfallibleAlloc>(
            std::move(aArray)));
  }

  template <class Item>
  mozilla::NotNull<value_type*> AppendElement(Item&& aItem) {
    return mozilla::WrapNotNullUnchecked(
        this->template AppendElementInternal<InfallibleAlloc>(
            std::forward<Item>(aItem)));
  }

  mozilla::NotNull<value_type*> AppendElements(size_type aCount) {
    return mozilla::WrapNotNullUnchecked(
        this->template AppendElementsInternal<InfallibleAlloc>(aCount));
  }

  mozilla::NotNull<value_type*> AppendElement() {
    return mozilla::WrapNotNullUnchecked(
        this->template AppendElementsInternal<InfallibleAlloc>(1));
  }

  self_type Clone() const {
    self_type result;
    result.Assign(*this);
    return result;
  }

  mozilla::NotNull<value_type*> InsertElementsAt(index_type aIndex,
                                                 size_type aCount) {
    return mozilla::WrapNotNullUnchecked(
        this->template InsertElementsAtInternal<InfallibleAlloc>(aIndex,
                                                                 aCount));
  }

  template <class Item>
  mozilla::NotNull<value_type*> InsertElementsAt(index_type aIndex,
                                                 size_type aCount,
                                                 const Item& aItem) {
    return mozilla::WrapNotNullUnchecked(
        this->template InsertElementsAtInternal<InfallibleAlloc>(aIndex, aCount,
                                                                 aItem));
  }

  template <class Item>
  mozilla::NotNull<value_type*> InsertElementsAt(index_type aIndex,
                                                 const Item* aArray,
                                                 size_type aArrayLen) {
    return mozilla::WrapNotNullUnchecked(
        this->template ReplaceElementsAtInternal<InfallibleAlloc>(
            aIndex, 0, aArray, aArrayLen));
  }

  template <class Item, class Allocator>
  mozilla::NotNull<value_type*> InsertElementsAt(
      index_type aIndex, const nsTArray_Impl<Item, Allocator>& aArray) {
    return mozilla::WrapNotNullUnchecked(
        this->template ReplaceElementsAtInternal<InfallibleAlloc>(
            aIndex, 0, aArray.Elements(), aArray.Length()));
  }

  template <class Item>
  mozilla::NotNull<value_type*> InsertElementsAt(index_type aIndex,
                                                 mozilla::Span<Item> aSpan) {
    return mozilla::WrapNotNullUnchecked(
        this->template ReplaceElementsAtInternal<InfallibleAlloc>(
            aIndex, 0, aSpan.Elements(), aSpan.Length()));
  }

  mozilla::NotNull<value_type*> InsertElementAt(index_type aIndex) {
    return mozilla::WrapNotNullUnchecked(
        this->template InsertElementAtInternal<InfallibleAlloc>(aIndex));
  }

  template <class Item>
  mozilla::NotNull<value_type*> InsertElementAt(index_type aIndex,
                                                Item&& aItem) {
    return mozilla::WrapNotNullUnchecked(
        this->template InsertElementAtInternal<InfallibleAlloc>(
            aIndex, std::forward<Item>(aItem)));
  }

  template <class Item>
  mozilla::NotNull<value_type*> ReplaceElementsAt(index_type aStart,
                                                  size_type aCount,
                                                  const Item* aArray,
                                                  size_type aArrayLen) {
    return mozilla::WrapNotNullUnchecked(
        this->template ReplaceElementsAtInternal<InfallibleAlloc>(
            aStart, aCount, aArray, aArrayLen));
  }

  template <class Item>
  mozilla::NotNull<value_type*> ReplaceElementsAt(
      index_type aStart, size_type aCount, const nsTArray<Item>& aArray) {
    return ReplaceElementsAt(aStart, aCount, aArray.Elements(),
                             aArray.Length());
  }

  template <class Item>
  mozilla::NotNull<value_type*> ReplaceElementsAt(index_type aStart,
                                                  size_type aCount,
                                                  mozilla::Span<Item> aSpan) {
    return ReplaceElementsAt(aStart, aCount, aSpan.Elements(), aSpan.Length());
  }

  template <class Item>
  mozilla::NotNull<value_type*> ReplaceElementsAt(index_type aStart,
                                                  size_type aCount,
                                                  const Item& aItem) {
    return ReplaceElementsAt(aStart, aCount, &aItem, 1);
  }

  template <class Item, class Comparator>
  mozilla::NotNull<value_type*> InsertElementSorted(Item&& aItem,
                                                    const Comparator& aComp) {
    return mozilla::WrapNotNullUnchecked(
        this->template InsertElementSortedInternal<InfallibleAlloc>(
            std::forward<Item>(aItem), aComp));
  }

  template <class Item>
  mozilla::NotNull<value_type*> InsertElementSorted(Item&& aItem) {
    return mozilla::WrapNotNullUnchecked(
        this->template InsertElementSortedInternal<InfallibleAlloc>(
            std::forward<Item>(aItem),
            nsDefaultComparator<value_type, Item>{}));
  }

  template <class... Args>
  mozilla::NotNull<value_type*> EmplaceBack(Args&&... aArgs) {
    return mozilla::WrapNotNullUnchecked(
        this->template EmplaceBackInternal<InfallibleAlloc, Args...>(
            std::forward<Args>(aArgs)...));
  }
};

template <class E>
class MOZ_GSL_OWNER CopyableTArray : public nsTArray<E> {
 public:
  using nsTArray<E>::nsTArray;

  CopyableTArray(const CopyableTArray& aOther) : nsTArray<E>() {
    this->Assign(aOther);
  }
  CopyableTArray& operator=(const CopyableTArray& aOther) {
    if (this != &aOther) {
      this->Assign(aOther);
    }
    return *this;
  }
  template <typename Allocator>
  MOZ_IMPLICIT CopyableTArray(const nsTArray_Impl<E, Allocator>& aOther) {
    this->Assign(aOther);
  }
  template <typename Allocator>
  CopyableTArray& operator=(const nsTArray_Impl<E, Allocator>& aOther) {
    if constexpr (std::is_same_v<Allocator, nsTArrayInfallibleAllocator>) {
      if (this == &aOther) {
        return *this;
      }
    }
    this->Assign(aOther);
    return *this;
  }
  template <typename Allocator>
  MOZ_IMPLICIT CopyableTArray(nsTArray_Impl<E, Allocator>&& aOther)
      : nsTArray<E>{std::move(aOther)} {}
  template <typename Allocator>
  CopyableTArray& operator=(nsTArray_Impl<E, Allocator>&& aOther) {
    static_cast<nsTArray<E>&>(*this) = std::move(aOther);
    return *this;
  }

  CopyableTArray(CopyableTArray&&) = default;
  CopyableTArray& operator=(CopyableTArray&&) = default;
};

//
// FallibleTArray is a fallible vector class.
//
template <class E>
class MOZ_GSL_OWNER FallibleTArray
    : public nsTArray_Impl<E, nsTArrayFallibleAllocator> {
 public:
  typedef nsTArray_Impl<E, nsTArrayFallibleAllocator> base_type;
  typedef FallibleTArray<E> self_type;
  typedef typename base_type::size_type size_type;

  FallibleTArray() = default;
  explicit FallibleTArray(size_type aCapacity) : base_type(aCapacity) {}

  template <class Allocator>
  explicit FallibleTArray(const nsTArray_Impl<E, Allocator>& aOther)
      : base_type(aOther) {}
  template <class Allocator>
  explicit FallibleTArray(nsTArray_Impl<E, Allocator>&& aOther)
      : base_type(std::move(aOther)) {}

  template <class Allocator>
  self_type& operator=(const nsTArray_Impl<E, Allocator>& aOther) {
    base_type::operator=(aOther);
    return *this;
  }
  template <class Allocator>
  self_type& operator=(nsTArray_Impl<E, Allocator>&& aOther) {
    base_type::operator=(std::move(aOther));
    return *this;
  }
};

//
// AutoTArray<E, N> is like nsTArray<E>, but with N elements of inline storage.
// Storing more than N elements is fine, but it will cause a heap allocation.
//
template <class E, size_t N>
class MOZ_NON_MEMMOVABLE MOZ_GSL_OWNER AutoTArray : public nsTArray<E> {
  static_assert(N != 0"AutoTArray<E, 0> should be specialized");

 public:
  typedef AutoTArray<E, N> self_type;
  typedef nsTArray<E> base_type;
  typedef typename base_type::Header Header;
  typedef typename base_type::value_type value_type;

  AutoTArray() {
    static_assert(alignof(value_type) <= 8,
                  "can't handle alignments greater than 8, "
                  "see nsTArray_base::UsesAutoArrayBuffer()");
    static_assert(offsetof(AutoTArray, mAutoBuf) == kAutoTArrayHeaderOffset);
    this->mHdr = &mAutoBuf.mHdr;
  }

  AutoTArray(self_type&& aOther) : AutoTArray() {
    this->MoveInit(aOther, sizeof(value_type));
    MOZ_ASSERT(!this->HasEmptyHeader());
  }

  explicit AutoTArray(base_type&& aOther) : AutoTArray() {
    this->MoveInit(aOther, sizeof(value_type));
  }

  template <typename Allocator>
  explicit AutoTArray(nsTArray_Impl<value_type, Allocator>&& aOther)
      : AutoTArray() {
    this->MoveInit(aOther, sizeof(value_type));
  }

  MOZ_IMPLICIT AutoTArray(std::initializer_list<E> aIL) : AutoTArray() {
    this->AppendElements(aIL.begin(), aIL.size());
  }

  self_type& operator=(self_type&& aOther) {
    base_type::operator=(std::move(aOther));
    return *this;
  }

  template <typename Allocator>
  self_type& operator=(nsTArray_Impl<value_type, Allocator>&& aOther) {
    base_type::operator=(std::move(aOther));
    return *this;
  }

  // Intentionally hides nsTArray_Impl::Clone to make clones usually be
  // AutoTArray as well.
  self_type Clone() const {
    self_type result;
    result.Assign(*this);
    return result;
  }

 private:
  // Declare mAutoBuf aligned to 8 bytes so we have a constant offset to get
  // from the nsTArray to the buffer. This can create 4 otherwise-unnecessary
  // bytes of padding on 32-bit builds if value_type is not 8-byte aligned, but
  // that seems acceptable.
  struct alignas(8) AutoBuffer {
    nsTArrayHeader mHdr;
    union alignas(value_type) {
      // FIXME: Conceptually `value_type mElements[N]`, but that upsets the
      // hazard analysis, see bug 1880185.
      char mStorage[sizeof(value_type) * N];
    };
    AutoBuffer() : mHdr{.mLength = 0, .mCapacity = N, .mIsAutoArray = true} {}
    ~AutoBuffer() = default;
  } mAutoBuf;
  static_assert(offsetof(AutoBuffer, mStorage) == sizeof(nsTArrayHeader),
                "Shouldn't have padding");
};

//
// Specialization of AutoTArray<E, N> for the case where N == 0.
// AutoTArray<E, 0> behaves exactly like nsTArray<E>, but without this
// specialization, it stores a useless inline header.
//
// We do have many AutoTArray<E, 0> objects in memory: about 2,000 per tab as
// of May 2014. These are typically not explicitly AutoTArray<E, 0> but rather
// AutoTArray<E, N> for some value N depending on template parameters, in
// generic code.
//
// For that reason, we optimize this case with the below partial specialization,
// which ensures that AutoTArray<E, 0> is just like nsTArray<E>, without any
// inline header overhead.
//
template <class E>
class AutoTArray<E, 0> : public nsTArray<E> {
  using nsTArray<E>::nsTArray;
};

template <class E, size_t N>
struct nsTArray_RelocationStrategy<AutoTArray<E, N>> {
  using Type = nsTArray_RelocateUsingMoveConstructor<AutoTArray<E, N>>;
};

template <class E, size_t N>
class CopyableAutoTArray : public AutoTArray<E, N> {
 public:
  typedef CopyableAutoTArray<E, N> self_type;
  using AutoTArray<E, N>::AutoTArray;

  CopyableAutoTArray(const CopyableAutoTArray& aOther) : AutoTArray<E, N>() {
    this->Assign(aOther);
  }
  CopyableAutoTArray& operator=(const CopyableAutoTArray& aOther) {
    if (this != &aOther) {
      this->Assign(aOther);
    }
    return *this;
  }
  template <typename Allocator>
  MOZ_IMPLICIT CopyableAutoTArray(const nsTArray_Impl<E, Allocator>& aOther) {
    this->Assign(aOther);
  }
  template <typename Allocator>
  CopyableAutoTArray& operator=(const nsTArray_Impl<E, Allocator>& aOther) {
    if constexpr (std::is_same_v<Allocator, nsTArrayInfallibleAllocator>) {
      if (this == &aOther) {
        return *this;
      }
    }
    this->Assign(aOther);
    return *this;
  }
  template <typename Allocator>
  MOZ_IMPLICIT CopyableAutoTArray(nsTArray_Impl<E, Allocator>&& aOther)
      : AutoTArray<E, N>{std::move(aOther)} {}
  template <typename Allocator>
  CopyableAutoTArray& operator=(nsTArray_Impl<E, Allocator>&& aOther) {
    static_cast<AutoTArray<E, N>&>(*this) = std::move(aOther);
    return *this;
  }

  // CopyableTArray exists for cases where an explicit Clone is not possible.
  // These uses should not be mixed, so we delete Clone() here.
  self_type Clone() const = delete;

  CopyableAutoTArray(CopyableAutoTArray&&) = default;
  CopyableAutoTArray& operator=(CopyableAutoTArray&&) = default;
};

// NOTE: We don't use MOZ_COUNT_CTOR/MOZ_COUNT_DTOR to perform leak checking of
// nsTArray_base objects intentionally for the following reasons:
// * The leak logging isn't as useful as other types of logging, as
//   nsTArray_base is frequently relocated without invoking a constructor, such
//   as when stored within another nsTArray. This means that
//   XPCOM_MEM_LOG_CLASSES cannot be used to identify specific leaks of nsTArray
//   objects.
// * The nsTArray type is layout compatible with the ThinVec crate with the
//   correct flags, and ThinVec does not currently perform leak logging.
//   This means that if a large number of arrays are transferred between Rust
//   and C++ code using ThinVec, for example within another ThinVec, they
//   will not be logged correctly and might appear as e.g. negative leaks.
// * Leaks which have been found thanks to the leak logging added by this
//   type have often not been significant, and/or have needed to be
//   circumvented using some other mechanism. Most leaks found with this type
//   in them also include other types which will continue to be tracked.

template <typename RelocationStrategy>
nsTArray_base<RelocationStrategy>::~nsTArray_base() {
  if (!HasEmptyHeader() && !UsesAutoArrayBuffer()) {
    nsTArrayInfallibleAllocator::Free(mHdr);
  }
}

// defined in nsTArray.cpp
bool IsTwiceTheRequiredBytesRepresentableAsUint32(size_t aCapacity,
                                                  size_t aElemSize);

template <typename RelocationStrategy>
template <typename Alloc>
typename Alloc::ResultTypeProxy
nsTArray_base<RelocationStrategy>::ExtendCapacity(size_type aLength,
                                                  size_type aCount,
                                                  size_type aElemSize) {
  mozilla::CheckedInt<size_type> newLength = aLength;
  newLength += aCount;

  if (!newLength.isValid()) {
    return Alloc::FailureResult();
  }

  return this->EnsureCapacity<Alloc>(newLength.value(), aElemSize);
}

template <class RelocationStrategy>
template <typename Alloc>
typename Alloc::ResultTypeProxy
nsTArray_base<RelocationStrategy>::EnsureCapacityImpl(size_type aCapacity,
                                                      size_type aElemSize) {
  MOZ_ASSERT(aCapacity > mHdr->mCapacity,
             "Should have been checked by caller (EnsureCapacity)");

  // If the requested memory allocation exceeds size_type(-1)/2, then
  // our doubling algorithm may not be able to allocate it.
  // Additionally, if it exceeds uint32_t(-1) then we couldn't fit in the
  // Header::mCapacity member. Just bail out in cases like that.  We don't want
  // to be allocating 2 GB+ arrays anyway.
  if (!IsTwiceTheRequiredBytesRepresentableAsUint32(aCapacity, aElemSize)) {
    Alloc::SizeTooBig((size_t)aCapacity * aElemSize);
    return Alloc::FailureResult();
  }

  size_t reqSize = sizeof(Header) + aCapacity * aElemSize;

  if (HasEmptyHeader()) {
    // Malloc() new data
    Header* header = static_cast<Header*>(Alloc::Malloc(reqSize));
    if (!header) {
      return Alloc::FailureResult();
    }
    header->mLength = 0;
    header->mCapacity = aCapacity;
    header->mIsAutoArray = false;
    mHdr = header;

    return Alloc::SuccessResult();
  }

  // We increase our capacity so that the allocated buffer grows exponentially,
  // which gives us amortized O(1) appending. Below the threshold, we use
  // powers-of-two. Above the threshold, we grow by at least 1.125, rounding up
  // to the nearest MiB.
  const size_t slowGrowthThreshold = 8 * 1024 * 1024;

  size_t bytesToAlloc;
  if (reqSize >= slowGrowthThreshold) {
    size_t currSize = sizeof(Header) + Capacity() * aElemSize;
    size_t minNewSize = currSize + (currSize >> 3);  // multiply by 1.125
    bytesToAlloc = reqSize > minNewSize ? reqSize : minNewSize;

    // Round up to the next multiple of MiB.
    const size_t MiB = 1 << 20;
    bytesToAlloc = MiB * ((bytesToAlloc + MiB - 1) / MiB);
  } else {
    // Round up to the next power of two.
    bytesToAlloc = mozilla::RoundUpPow2(reqSize);
  }

  Header* header;
  if (UsesAutoArrayBuffer() || !RelocationStrategy::allowRealloc) {
    // Malloc() and copy
    header = static_cast<Header*>(Alloc::Malloc(bytesToAlloc));
    if (!header) {
      return Alloc::FailureResult();
    }

    RelocationStrategy::RelocateNonOverlappingRegionWithHeader(
        header, mHdr, Length(), aElemSize);

    if (!UsesAutoArrayBuffer()) {
      Alloc::Free(mHdr);
    }
  } else {
    // Realloc() existing data
    header = static_cast<Header*>(Alloc::Realloc(mHdr, bytesToAlloc));
    if (!header) {
      return Alloc::FailureResult();
    }
  }

  // How many elements can we fit in bytesToAlloc?
  size_t newCapacity = (bytesToAlloc - sizeof(Header)) / aElemSize;
  MOZ_ASSERT(newCapacity >= aCapacity, "Didn't enlarge the array enough!");
  header->mCapacity = newCapacity;

  mHdr = header;

  return Alloc::SuccessResult();
}

// We don't need use Alloc template parameter specified here because failure to
// shrink the capacity will leave the array unchanged.
template <typename RelocationStrategy>
void nsTArray_base<RelocationStrategy>::ShrinkCapacity(size_type aElemSize) {
  if (HasEmptyHeader()) {
    return;
  }

  size_type length = Length();
  // Try to switch to our auto-buffer if possible.
  if (auto* autoHdr = GetAutoArrayHeader()) {
    if (mHdr == autoHdr) {
      return;
    }
    if (autoHdr->mCapacity >= length) {
      RelocationStrategy::RelocateNonOverlappingRegion(autoHdr + 1, mHdr + 1,
                                                       length, aElemSize);
      autoHdr->mLength = length;
      nsTArrayFallibleAllocator::Free(mHdr);
      mHdr = autoHdr;
      return;
    }
  }

  if (length == 0) {
    MOZ_ASSERT(!mHdr->mIsAutoArray, "Should've been dealt with above.");
    nsTArrayFallibleAllocator::Free(mHdr);
    mHdr = EmptyHdr();
    return;
  }

  if (length >= mHdr->mCapacity) {  // should never be greater than...
    return;
  }

  size_type newSize = sizeof(Header) + length * aElemSize;

  Header* newHeader;
  if (!RelocationStrategy::allowRealloc) {
    // Malloc() and copy.
    newHeader =
        static_cast<Header*>(nsTArrayFallibleAllocator::Malloc(newSize));
    if (!newHeader) {
      return;
    }

    RelocationStrategy::RelocateNonOverlappingRegionWithHeader(
        newHeader, mHdr, Length(), aElemSize);

    nsTArrayFallibleAllocator::Free(mHdr);
  } else {
    // Realloc() existing data.
    newHeader =
        static_cast<Header*>(nsTArrayFallibleAllocator::Realloc(mHdr, newSize));
    if (!newHeader) {
      return;
    }
  }

  mHdr = newHeader;
  mHdr->mCapacity = length;
}

template <typename RelocationStrategy>
void nsTArray_base<RelocationStrategy>::ShrinkCapacityToZero() {
  MOZ_ASSERT(mHdr->mLength == 0);

  if (HasEmptyHeader()) {
    return;
  }

  Header* newHdr = EmptyHdr();
  if (auto* autoBuf = GetAutoArrayHeader()) {
    if (mHdr == autoBuf) {
      return;
    }
    newHdr = autoBuf;
    newHdr->mLength = 0;
  }

  nsTArrayFallibleAllocator::Free(mHdr);
  mHdr = newHdr;
}

template <typename RelocationStrategy>
template <typename Alloc>
void nsTArray_base<RelocationStrategy>::ShiftData(index_type aStart,
                                                  size_type aOldLen,
                                                  size_type aNewLen,
                                                  size_type aElemSize) {
  if (aOldLen == aNewLen) {
    return;
  }

  // Determine how many elements need to be shifted
  size_type num = mHdr->mLength - (aStart + aOldLen);

  // Compute the resulting length of the array
  mHdr->mLength += aNewLen - aOldLen;
  if (mHdr->mLength == 0) {
    ShrinkCapacityToZero();
    return;
  }
  // Maybe nothing needs to be shifted
  if (num == 0) {
    return;
  }
  // Perform shift (change units to bytes first)
  aStart *= aElemSize;
  aNewLen *= aElemSize;
  aOldLen *= aElemSize;
  char* baseAddr = reinterpret_cast<char*>(mHdr + 1) + aStart;
  RelocationStrategy::RelocateOverlappingRegion(
      baseAddr + aNewLen, baseAddr + aOldLen, num, aElemSize);
}

template <typename RelocationStrategy>
template <typename Alloc>
void nsTArray_base<RelocationStrategy>::SwapFromEnd(index_type aStart,
                                                    size_type aCount,
                                                    size_type aElemSize) {
  // This method is part of the implementation of
  // nsTArray::SwapRemoveElement{s,}At. For more information, read the
  // documentation on that method.
  if (aCount == 0) {
    return;
  }

  // We are going to be removing aCount elements. Update our length to point to
  // the new end of the array.
  size_type oldLength = mHdr->mLength;
  mHdr->mLength -= aCount;

  if (mHdr->mLength == 0) {
    // If we have no elements remaining in the array, we can free our buffer.
    ShrinkCapacityToZero();
    return;
  }

  // Determine how many elements we need to move from the end of the array into
  // the now-removed section. This will either be the number of elements which
  // were removed (if there are more elements in the tail of the array), or the
  // entire tail of the array, whichever is smaller.
  size_type relocCount = std::min(aCount, mHdr->mLength - aStart);
  if (relocCount == 0) {
    return;
  }

  // Move the elements which are now stranded after the end of the array back
  // into the now-vacated memory.
  index_type sourceBytes = (oldLength - relocCount) * aElemSize;
  index_type destBytes = aStart * aElemSize;

  // Perform the final copy. This is guaranteed to be a non-overlapping copy
  // as our source contains only still-valid entries, and the destination
  // contains only invalid entries which need to be overwritten.
  MOZ_ASSERT(sourceBytes >= destBytes,
             "The source should be after the destination.");
  MOZ_ASSERT(sourceBytes - destBytes >= relocCount * aElemSize,
             "The range should be nonoverlapping");

  char* baseAddr = reinterpret_cast<char*>(mHdr + 1);
  RelocationStrategy::RelocateNonOverlappingRegion(
      baseAddr + destBytes, baseAddr + sourceBytes, relocCount, aElemSize);
}

template <typename RelocationStrategy>
template <typename Alloc>
typename Alloc::ResultTypeProxy
nsTArray_base<RelocationStrategy>::InsertSlotsAt(index_type aIndex,
                                                 size_type aCount,
                                                 size_type aElemSize) {
  if (MOZ_UNLIKELY(aIndex > Length())) {
    mozilla::detail::InvalidArrayIndex_CRASH(aIndex, Length());
  }

  if (!Alloc::Successful(
          this->ExtendCapacity<Alloc>(Length(), aCount, aElemSize))) {
    return Alloc::FailureResult();
  }

  // Move the existing elements as needed.  Note that this will
  // change our mLength, so no need to call IncrementLength.
  ShiftData<Alloc>(aIndex, 0, aCount, aElemSize);

  return Alloc::SuccessResult();
}

template <class RelocationStrategy>
template <typename Alloc>
typename Alloc::ResultTypeProxy
nsTArray_base<RelocationStrategy>::SwapArrayElements(
    nsTArray_base<RelocationStrategy>& aOther, size_type aElemSize) {
  // If neither array uses an auto buffer which is big enough to store the
  // other array's elements, then ensure that both arrays use malloc'ed storage
  // and swap their mHdr pointers.
  if ((!UsesAutoArrayBuffer() || Capacity() < aOther.Length()) &&
      (!aOther.UsesAutoArrayBuffer() || aOther.Capacity() < Length())) {
    auto* thisHdr = TakeHeaderForMove<Alloc>(aElemSize);
    if (MOZ_UNLIKELY(!thisHdr)) {
      return Alloc::FailureResult();
    }
    auto* otherHdr = aOther.template TakeHeaderForMove<Alloc>(aElemSize);
    if (MOZ_UNLIKELY(!otherHdr)) {
      // Ensure thisHdr and the elements inside it are safely
      // cleaned up in this error case, by returning it to
      // being owned by this.
      MOZ_ASSERT(UsesAutoArrayBuffer() || HasEmptyHeader());
      thisHdr->mIsAutoArray = mHdr->mIsAutoArray;
      mHdr = thisHdr;
      return Alloc::FailureResult();
    }
    // Avoid replacing the potentially auto-buffer with the empty header if
    // we're empty.
    if (otherHdr != EmptyHdr()) {
      otherHdr->mIsAutoArray = mHdr->mIsAutoArray;
      mHdr = otherHdr;
    }
    if (thisHdr != EmptyHdr()) {
      thisHdr->mIsAutoArray = aOther.mHdr->mIsAutoArray;
      aOther.mHdr = thisHdr;
    }
    return Alloc::SuccessResult();
  }

  // Swap the two arrays by copying, since at least one is using an auto
  // buffer which is large enough to hold all of the aOther's elements.  We'll
  // copy the shorter array into temporary storage.
  //
  // (We could do better than this in some circumstances.  Suppose we're
  // swapping arrays X and Y.  X has space for 2 elements in its auto buffer,
  // but currently has length 4, so it's using malloc'ed storage.  Y has length
  // 2.  When we swap X and Y, we don't need to use a temporary buffer; we can
  // write Y straight into X's auto buffer, write X's malloc'ed buffer on top
  // of Y, and then switch X to using its auto buffer.)

  if (!Alloc::Successful(EnsureCapacity<Alloc>(aOther.Length(), aElemSize)) ||
      !Alloc::Successful(
          aOther.template EnsureCapacity<Alloc>(Length(), aElemSize))) {
    return Alloc::FailureResult();
  }

  // The EnsureCapacity calls above shouldn't have caused *both* arrays to
  // switch from their auto buffers to malloc'ed space.
  MOZ_ASSERT(UsesAutoArrayBuffer() || aOther.UsesAutoArrayBuffer(),
             "One of the arrays should be using its auto buffer.");

  size_type smallerLength = XPCOM_MIN(Length(), aOther.Length());
  size_type largerLength = XPCOM_MAX(Length(), aOther.Length());
  void* smallerElements;
  void* largerElements;
  if (Length() <= aOther.Length()) {
    smallerElements = Hdr() + 1;
    largerElements = aOther.Hdr() + 1;
  } else {
    smallerElements = aOther.Hdr() + 1;
    largerElements = Hdr() + 1;
  }

  // Allocate temporary storage for the smaller of the two arrays.  We want to
  // allocate this space on the stack, if it's not too large.  Sounds like a
  // job for AutoTArray!  (One of the two arrays we're swapping is using an
  // auto buffer, so we're likely not allocating a lot of space here.  But one
  // could, in theory, allocate a huge AutoTArray on the heap.)
  AutoTArray<uint8_t, 64 * sizeof(void*)> temp;
  if (!Alloc::Successful(temp.template EnsureCapacity<Alloc>(
          smallerLength * aElemSize, sizeof(uint8_t)))) {
    return Alloc::FailureResult();
  }

  RelocationStrategy::RelocateNonOverlappingRegion(
      temp.Elements(), smallerElements, smallerLength, aElemSize);
  RelocationStrategy::RelocateNonOverlappingRegion(
      smallerElements, largerElements, largerLength, aElemSize);
  RelocationStrategy::RelocateNonOverlappingRegion(
      largerElements, temp.Elements(), smallerLength, aElemSize);

  // Swap the arrays' lengths.
  MOZ_ASSERT((aOther.Length() == 0 || !HasEmptyHeader()) &&
                 (Length() == 0 || !aOther.HasEmptyHeader()),
             "Don't set sEmptyTArrayHeader's length.");
  size_type tempLength = Length();

  // Avoid writing to EmptyHdr, since it can trigger false
  // positives with TSan.
  if (!HasEmptyHeader()) {
    mHdr->mLength = aOther.Length();
  }
  if (!aOther.HasEmptyHeader()) {
    aOther.mHdr->mLength = tempLength;
  }

  return Alloc::SuccessResult();
}

template <class RelocationStrategy>
void nsTArray_base<RelocationStrategy>::MoveInit(
    nsTArray_base<RelocationStrategy>& aOther, size_type aElemSize) {
  // This method is similar to SwapArrayElements, but specialized for the case
  // where the target array is empty with no allocated heap storage. It is
  // provided and used to simplify template instantiation and enable better code
  // generation.

  MOZ_ASSERT(Length() == 0);
  MOZ_ASSERT(Capacity() == 0 || UsesAutoArrayBuffer());

  const auto newLength = aOther.Length();
  if (aOther.HasEmptyHeader()) {
    return;
  }

  // If this array doesn't use an auto buffer which is big enough to store the
  // other array's elements, and the other array is using malloc'ed storage,
  // take their mHdr pointer.
  if (!aOther.UsesAutoArrayBuffer() &&
      (!mHdr->mIsAutoArray || Capacity() < newLength)) {
    const bool thisIsAuto = mHdr->mIsAutoArray;
    Header* otherAutoHeader = aOther.GetAutoArrayHeader();
    mHdr = aOther.mHdr;
    mHdr->mIsAutoArray = thisIsAuto;
    if (otherAutoHeader) {
      aOther.mHdr = otherAutoHeader;
      otherAutoHeader->mLength = 0;
    } else {
      aOther.mHdr = EmptyHdr();
    }
    return;
  }

  if (newLength) {
    // Move the data by copying, since at least one has an auto
    // buffer which is large enough to hold all of the aOther's elements.
    EnsureCapacity<nsTArrayInfallibleAllocator>(newLength, aElemSize);

    // The EnsureCapacity calls above shouldn't have caused *both* arrays to
    // switch from their auto buffers to malloc'ed space.
    MOZ_ASSERT(UsesAutoArrayBuffer() || aOther.UsesAutoArrayBuffer(),
               "One of the arrays should be using its auto buffer.");

    RelocationStrategy::RelocateNonOverlappingRegion(
        Hdr() + 1, aOther.Hdr() + 1, newLength, aElemSize);

    // Swap the arrays' lengths.
    MOZ_ASSERT(!HasEmptyHeader() && !aOther.HasEmptyHeader(),
               "Both arrays should have capacity");

    // Update our buffer's length, and reduce the other buffer's length.
    mHdr->mLength = newLength;
    aOther.mHdr->mLength = 0;
  }
  aOther.ShrinkCapacityToZero();
}

template <typename RelocationStrategy>
void nsTArray_base<RelocationStrategy>::MoveConstructNonAutoArray(
    nsTArray_base<RelocationStrategy>& aOther, size_type aElemSize) {
  // We know that we are not an (Copyable)AutoTArray and we know that we are
  // empty, so don't use SwapArrayElements which doesn't know either of these
  // facts and is very complex. Use nsTArrayInfallibleAllocator regardless of
  // Alloc because this is called from a move constructor, which cannot report
  // an error to the caller.
  mHdr =
      aOther.template TakeHeaderForMove<nsTArrayInfallibleAllocator>(aElemSize);
  MOZ_ASSERT(!mHdr->mIsAutoArray);
}

template <class RelocationStrategy>
template <typename Alloc>
auto nsTArray_base<RelocationStrategy>::TakeHeaderForMove(size_type aElemSize)
    -> Header* {
  auto* autoHdr = GetAutoArrayHeader();
  if (!autoHdr) {
    return std::exchange(mHdr, EmptyHdr());
  }

  if (mHdr != autoHdr) {
    MOZ_ASSERT(mHdr->mIsAutoArray);
    MOZ_ASSERT(autoHdr->mIsAutoArray);
    autoHdr->mLength = 0;
    mHdr->mIsAutoArray = false;
    return std::exchange(mHdr, autoHdr);
  }

  const auto length = Length();
  if (!length) {
    return EmptyHdr();
  }

  size_type size = sizeof(Header) + length * aElemSize;
  Header* header = static_cast<Header*>(Alloc::Malloc(size));
  if (!header) {
    return nullptr;
  }

  RelocationStrategy::RelocateNonOverlappingRegionWithHeader(header, mHdr,
                                                             length, aElemSize);
  header->mCapacity = length;
  // This will be set by our caller if needed.
  header->mIsAutoArray = false;

  mHdr->mLength = 0;
  MOZ_ASSERT(UsesAutoArrayBuffer());
  MOZ_ASSERT(IsEmpty());
  return header;
}

namespace mozilla {
template <typename E, typename ArrayT>
class nsTArrayBackInserter {
  ArrayT* mArray;

  class Proxy {
    ArrayT& mArray;

   public:
    explicit Proxy(ArrayT& aArray) : mArray{aArray} {}

    template <typename E2>
    void operator=(E2&& aValue) {
      mArray.AppendElement(std::forward<E2>(aValue));
    }
  };

 public:
  using iterator_category = std::output_iterator_tag;
  using value_type = void;
  using difference_type = void;
  using pointer = void;
  using reference = void;
  explicit nsTArrayBackInserter(ArrayT& aArray) : mArray{&aArray} {}

  // Return a proxy so that nsTArrayBackInserter has the default special member
  // functions, and the operator= template is defined in Proxy rather than this
  // class (which otherwise breaks with recent MS STL versions).
  // See also Bug 1331137, comment 11.
  Proxy operator*() { return Proxy(*mArray); }

  nsTArrayBackInserter& operator++() { return *this; }
  nsTArrayBackInserter& operator++(int) { return *this; }
};
}  // namespace mozilla

template <typename E>
auto MakeBackInserter(nsTArray<E>& aArray) {
  return mozilla::nsTArrayBackInserter<E, nsTArray<E>>{aArray};
}

// Span integration
namespace mozilla {
template <typename E, class Alloc>
Span(nsTArray_Impl<E, Alloc>&) -> Span<E>;

template <typename E, class Alloc>
Span(const nsTArray_Impl<E, Alloc>&) -> Span<const E>;

// Provides a view on a nsTArray through which the existing array elements can
// be accessed in a non-const way, but the array itself cannot be modified, so
// that references to elements are guaranteed to be stable.
template <typename E>
class nsTArrayView {
 public:
  using element_type = E;
  using pointer = element_type*;
  using reference = element_type&;
  using index_type = typename Span<element_type>::index_type;
  using size_type = typename Span<element_type>::index_type;

  explicit nsTArrayView(nsTArray<element_type> aArray)
      : mArray(std::move(aArray)), mSpan(mArray) {}

  element_type& operator[](index_type aIndex) { return mSpan[aIndex]; }

  const element_type& operator[](index_type aIndex) const {
    return mSpan[aIndex];
  }

  size_type Length() const { return mSpan.Length(); }

  auto begin() { return mSpan.begin(); }
  auto end() { return mSpan.end(); }
  auto begin() const { return mSpan.begin(); }
  auto end() const { return mSpan.end(); }
  auto cbegin() const { return mSpan.cbegin(); }
  auto cend() const { return mSpan.cend(); }

  Span<element_type> AsSpan() { return mSpan; }
  Span<const element_type> AsSpan() const { return mSpan; }

 private:
  nsTArray<element_type> mArray;
  const Span<element_type> mSpan;
};

// NOTE(emilio): If changing the name of this or so, make sure to change
// specializations too.
template <typename Range,
          typename = std::enable_if_t<std::is_same_v<
              typename std::iterator_traits<typename std::remove_reference_t<
                  Range>::iterator>::iterator_category,
              std::random_access_iterator_tag>>>
size_t RangeSizeEstimate(const Range& aRange) {
  // See https://en.cppreference.com/w/cpp/iterator/begin, section 'User-defined
  // overloads'.
  using std::begin;
  using std::end;

  return std::distance(begin(aRange), end(aRange));
}

/**
 * Materialize a range as a nsTArray (or a compatible variant, like AutoTArray)
 * of an explicitly specified type. The array value type must be implicitly
 * convertible from the range's value type.
 */

template <typename Array, typename Range>
auto ToTArray(Range&& aRange) {
  using std::begin;
  using std::end;

  Array res;
  if (auto estimate = RangeSizeEstimate(aRange)) {
    res.SetCapacity(estimate);
  }
  std::copy(begin(aRange), end(aRange), MakeBackInserter(res));
  return res;
}

/**
 * Materialize a range as a nsTArray of its (decayed) value type.
 */

template <typename Range>
auto ToArray(Range&& aRange) {
  return ToTArray<nsTArray<std::decay_t<typename std::iterator_traits<
      typename std::remove_reference_t<Range>::iterator>::value_type>>>(
      std::forward<Range>(aRange));
}

/**
 * Appends all elements from a range to an array.
 */

template <typename Array, typename Range>
void AppendToArray(Array& aArray, Range&& aRange) {
  using std::begin;
  using std::end;
  if (auto estimate = RangeSizeEstimate(aRange)) {
    aArray.SetCapacity(aArray.Length() + estimate);
  }
  std::copy(begin(aRange), end(aRange), MakeBackInserter(aArray));
}

}  // namespace mozilla

// MOZ_DBG support

template <class E, class Alloc>
std::ostream& operator<<(std::ostream& aOut,
                         const nsTArray_Impl<E, Alloc>& aTArray) {
  return aOut << mozilla::Span(aTArray);
}

#endif  // nsTArray_h_

Messung V0.5 in Prozent
C=85 H=97 G=91

¤ Dauer der Verarbeitung: 0.60 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002