michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsTArray_h__ michael@0: #define nsTArray_h__ michael@0: michael@0: #include "nsTArrayForwardDeclare.h" michael@0: #include "mozilla/Alignment.h" michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/TypeTraits.h" michael@0: michael@0: #include michael@0: michael@0: #include "nsCycleCollectionNoteChild.h" michael@0: #include "nsAlgorithm.h" michael@0: #include "nscore.h" michael@0: #include "nsQuickSort.h" michael@0: #include "nsDebug.h" michael@0: #include "nsISupportsImpl.h" michael@0: #include michael@0: michael@0: namespace JS { michael@0: template michael@0: class Heap; michael@0: } /* namespace JS */ michael@0: michael@0: class nsRegion; michael@0: class nsIntRegion; michael@0: michael@0: // michael@0: // nsTArray is a resizable array class, like std::vector. michael@0: // michael@0: // Unlike std::vector, which follows C++'s construction/destruction rules, michael@0: // nsTArray assumes that your "T" can be memmoved()'ed safely. michael@0: // michael@0: // The public classes defined in this header are michael@0: // michael@0: // nsTArray, michael@0: // FallibleTArray, michael@0: // nsAutoTArray, and michael@0: // AutoFallibleTArray. michael@0: // michael@0: // nsTArray and nsAutoTArray are infallible; if one tries to make an allocation michael@0: // which fails, it crashes the program. In contrast, FallibleTArray and michael@0: // AutoFallibleTArray are fallible; if you use one of these classes, you must michael@0: // check the return values of methods such as Append() which may allocate. If michael@0: // in doubt, choose an infallible type. michael@0: // michael@0: // InfallibleTArray and AutoInfallibleTArray are aliases for nsTArray and michael@0: // nsAutoTArray. michael@0: // michael@0: // If you just want to declare the nsTArray types (e.g., if you're in a header michael@0: // file and don't need the full nsTArray definitions) consider including michael@0: // nsTArrayForwardDeclare.h instead of nsTArray.h. michael@0: // michael@0: // The template parameter (i.e., T in nsTArray) specifies the type of the michael@0: // elements and has the following requirements: michael@0: // michael@0: // T MUST be safely memmove()'able. michael@0: // T MUST define a copy-constructor. michael@0: // T MAY define operator< for sorting. michael@0: // T MAY define operator== for searching. michael@0: // michael@0: // (Note that the memmove requirement may be relaxed for certain types - see michael@0: // nsTArray_CopyChooser below.) michael@0: // michael@0: // For methods taking a Comparator instance, the Comparator must be a class michael@0: // defining the following methods: michael@0: // michael@0: // class Comparator { michael@0: // public: michael@0: // /** @return True if the elements are equals; false otherwise. */ michael@0: // bool Equals(const elem_type& a, const Item& b) const; michael@0: // michael@0: // /** @return True if (a < b); false otherwise. */ michael@0: // bool LessThan(const elem_type& a, const Item& b) const; michael@0: // }; michael@0: // michael@0: // The Equals method is used for searching, and the LessThan method is used for michael@0: // searching and sorting. The |Item| type above can be arbitrary, but must michael@0: // match the Item type passed to the sort or search function. michael@0: // michael@0: michael@0: michael@0: // michael@0: // nsTArrayFallibleResult and nsTArrayInfallibleResult types are proxy types michael@0: // which are used because you cannot use a templated type which is bound to michael@0: // void as an argument to a void function. In order to work around that, we michael@0: // encode either a void or a boolean inside these proxy objects, and pass them michael@0: // to the aforementioned function instead, and then use the type information to michael@0: // decide what to do in the function. michael@0: // michael@0: // Note that public nsTArray methods should never return a proxy type. Such michael@0: // types are only meant to be used in the internal nsTArray helper methods. michael@0: // Public methods returning non-proxy types cannot be called from other michael@0: // nsTArray members. michael@0: // michael@0: struct nsTArrayFallibleResult michael@0: { michael@0: // Note: allows implicit conversions from and to bool michael@0: nsTArrayFallibleResult(bool result) michael@0: : mResult(result) michael@0: {} michael@0: michael@0: operator bool() { michael@0: return mResult; michael@0: } michael@0: michael@0: private: michael@0: bool mResult; michael@0: }; michael@0: michael@0: struct nsTArrayInfallibleResult michael@0: { michael@0: }; michael@0: michael@0: // michael@0: // nsTArray*Allocators must all use the same |free()|, to allow swap()'ing michael@0: // between fallible and infallible variants. michael@0: // michael@0: michael@0: struct nsTArrayFallibleAllocatorBase michael@0: { michael@0: typedef bool ResultType; michael@0: typedef nsTArrayFallibleResult ResultTypeProxy; michael@0: michael@0: static ResultType Result(ResultTypeProxy result) { michael@0: return result; michael@0: } michael@0: michael@0: static bool Successful(ResultTypeProxy result) { michael@0: return result; michael@0: } michael@0: michael@0: static ResultTypeProxy SuccessResult() { michael@0: return true; michael@0: } michael@0: michael@0: static ResultTypeProxy FailureResult() { michael@0: return false; michael@0: } michael@0: michael@0: static ResultType ConvertBoolToResultType(bool aValue) { michael@0: return aValue; michael@0: } michael@0: }; michael@0: michael@0: struct nsTArrayInfallibleAllocatorBase michael@0: { michael@0: typedef void ResultType; michael@0: typedef nsTArrayInfallibleResult ResultTypeProxy; michael@0: michael@0: static ResultType Result(ResultTypeProxy result) { michael@0: } michael@0: michael@0: static bool Successful(ResultTypeProxy) { michael@0: return true; michael@0: } michael@0: michael@0: static ResultTypeProxy SuccessResult() { michael@0: return ResultTypeProxy(); michael@0: } michael@0: michael@0: static ResultTypeProxy FailureResult() { michael@0: NS_RUNTIMEABORT("Infallible nsTArray should never fail"); michael@0: return ResultTypeProxy(); michael@0: } michael@0: michael@0: static ResultType ConvertBoolToResultType(bool aValue) { michael@0: if (!aValue) { michael@0: NS_RUNTIMEABORT("infallible nsTArray should never convert false to ResultType"); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: #if defined(MOZALLOC_HAVE_XMALLOC) michael@0: #include "mozilla/mozalloc_abort.h" michael@0: michael@0: struct nsTArrayFallibleAllocator : nsTArrayFallibleAllocatorBase michael@0: { michael@0: static void* Malloc(size_t size) { michael@0: return moz_malloc(size); michael@0: } michael@0: michael@0: static void* Realloc(void* ptr, size_t size) { michael@0: return moz_realloc(ptr, size); michael@0: } michael@0: michael@0: static void Free(void* ptr) { michael@0: moz_free(ptr); michael@0: } michael@0: michael@0: static void SizeTooBig(size_t) { michael@0: } michael@0: }; michael@0: michael@0: struct nsTArrayInfallibleAllocator : nsTArrayInfallibleAllocatorBase michael@0: { michael@0: static void* Malloc(size_t size) { michael@0: return moz_xmalloc(size); michael@0: } michael@0: michael@0: static void* Realloc(void* ptr, size_t size) { michael@0: return moz_xrealloc(ptr, size); michael@0: } michael@0: michael@0: static void Free(void* ptr) { michael@0: moz_free(ptr); michael@0: } michael@0: michael@0: static void SizeTooBig(size_t size) { michael@0: NS_ABORT_OOM(size); michael@0: } michael@0: }; michael@0: michael@0: #else michael@0: #include michael@0: michael@0: struct nsTArrayFallibleAllocator : nsTArrayFallibleAllocatorBase michael@0: { michael@0: static void* Malloc(size_t size) { michael@0: return malloc(size); michael@0: } michael@0: michael@0: static void* Realloc(void* ptr, size_t size) { michael@0: return realloc(ptr, size); michael@0: } michael@0: michael@0: static void Free(void* ptr) { michael@0: free(ptr); michael@0: } michael@0: michael@0: static void SizeTooBig(size_t) { michael@0: } michael@0: }; michael@0: michael@0: struct nsTArrayInfallibleAllocator : nsTArrayInfallibleAllocatorBase michael@0: { michael@0: static void* Malloc(size_t size) { michael@0: void* ptr = malloc(size); michael@0: if (MOZ_UNLIKELY(!ptr)) { michael@0: NS_ABORT_OOM(size); michael@0: } michael@0: return ptr; michael@0: } michael@0: michael@0: static void* Realloc(void* ptr, size_t size) { michael@0: void* newptr = realloc(ptr, size); michael@0: if (MOZ_UNLIKELY(!ptr && size)) { michael@0: NS_ABORT_OOM(size); michael@0: } michael@0: return newptr; michael@0: } michael@0: michael@0: static void Free(void* ptr) { michael@0: free(ptr); michael@0: } michael@0: michael@0: static void SizeTooBig(size_t size) { michael@0: NS_ABORT_OOM(size); michael@0: } michael@0: }; michael@0: michael@0: #endif michael@0: michael@0: // nsTArray_base stores elements into the space allocated beyond michael@0: // sizeof(*this). This is done to minimize the size of the nsTArray michael@0: // object when it is empty. michael@0: struct NS_COM_GLUE nsTArrayHeader michael@0: { michael@0: static nsTArrayHeader sEmptyHdr; michael@0: michael@0: uint32_t mLength; michael@0: uint32_t mCapacity : 31; michael@0: uint32_t mIsAutoArray : 1; michael@0: }; michael@0: michael@0: // This class provides a SafeElementAt method to nsTArray which does michael@0: // not take a second default value parameter. michael@0: template michael@0: struct nsTArray_SafeElementAtHelper michael@0: { michael@0: typedef E* elem_type; michael@0: typedef uint32_t index_type; michael@0: michael@0: // No implementation is provided for these two methods, and that is on michael@0: // purpose, since we don't support these functions on non-pointer type michael@0: // instantiations. michael@0: elem_type& SafeElementAt(index_type i); michael@0: const elem_type& SafeElementAt(index_type i) const; michael@0: }; michael@0: michael@0: template michael@0: struct nsTArray_SafeElementAtHelper michael@0: { michael@0: typedef E* elem_type; michael@0: typedef uint32_t index_type; michael@0: michael@0: elem_type SafeElementAt(index_type i) { michael@0: return static_cast (this)->SafeElementAt(i, nullptr); michael@0: } michael@0: michael@0: const elem_type SafeElementAt(index_type i) const { michael@0: return static_cast (this)->SafeElementAt(i, nullptr); michael@0: } michael@0: }; michael@0: michael@0: // E is the base type that the smart pointer is templated over; the michael@0: // smart pointer can act as E*. michael@0: template michael@0: struct nsTArray_SafeElementAtSmartPtrHelper michael@0: { michael@0: typedef E* elem_type; michael@0: typedef uint32_t index_type; michael@0: michael@0: elem_type SafeElementAt(index_type i) { michael@0: return static_cast (this)->SafeElementAt(i, nullptr); michael@0: } michael@0: michael@0: const elem_type SafeElementAt(index_type i) const { michael@0: return static_cast (this)->SafeElementAt(i, nullptr); michael@0: } michael@0: }; michael@0: michael@0: template class nsCOMPtr; michael@0: michael@0: template michael@0: struct nsTArray_SafeElementAtHelper, Derived> : michael@0: public nsTArray_SafeElementAtSmartPtrHelper michael@0: { michael@0: }; michael@0: michael@0: template class nsRefPtr; michael@0: michael@0: template michael@0: struct nsTArray_SafeElementAtHelper, Derived> : michael@0: public nsTArray_SafeElementAtSmartPtrHelper michael@0: { michael@0: }; michael@0: michael@0: // michael@0: // This class serves as a base class for nsTArray. It shouldn't be used michael@0: // directly. It holds common implementation code that does not depend on the michael@0: // element type of the nsTArray. michael@0: // michael@0: template michael@0: class nsTArray_base michael@0: { michael@0: // Allow swapping elements with |nsTArray_base|s created using a michael@0: // different allocator. This is kosher because all allocators use michael@0: // the same free(). michael@0: template michael@0: friend class nsTArray_base; michael@0: michael@0: protected: michael@0: typedef nsTArrayHeader Header; michael@0: michael@0: public: michael@0: typedef uint32_t size_type; michael@0: typedef uint32_t index_type; michael@0: michael@0: // @return The number of elements in the array. michael@0: size_type Length() const { michael@0: return mHdr->mLength; michael@0: } michael@0: michael@0: // @return True if the array is empty or false otherwise. michael@0: bool IsEmpty() const { michael@0: return Length() == 0; michael@0: } michael@0: michael@0: // @return The number of elements that can fit in the array without forcing michael@0: // the array to be re-allocated. The length of an array is always less michael@0: // than or equal to its capacity. michael@0: size_type Capacity() const { michael@0: return mHdr->mCapacity; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void* DebugGetHeader() const { michael@0: return mHdr; michael@0: } michael@0: #endif michael@0: michael@0: protected: michael@0: nsTArray_base(); michael@0: michael@0: ~nsTArray_base(); michael@0: michael@0: // Resize the storage if necessary to achieve the requested capacity. michael@0: // @param capacity The requested number of array elements. michael@0: // @param elemSize The size of an array element. michael@0: // @return False if insufficient memory is available; true otherwise. michael@0: typename Alloc::ResultTypeProxy EnsureCapacity(size_type capacity, size_type elemSize); michael@0: michael@0: // Resize the storage to the minimum required amount. michael@0: // @param elemSize The size of an array element. michael@0: // @param elemAlign The alignment in bytes of an array element. michael@0: void ShrinkCapacity(size_type elemSize, size_t elemAlign); michael@0: michael@0: // This method may be called to resize a "gap" in the array by shifting michael@0: // elements around. It updates mLength appropriately. If the resulting michael@0: // array has zero elements, then the array's memory is free'd. michael@0: // @param start The starting index of the gap. michael@0: // @param oldLen The current length of the gap. michael@0: // @param newLen The desired length of the gap. michael@0: // @param elemSize The size of an array element. michael@0: // @param elemAlign The alignment in bytes of an array element. michael@0: void ShiftData(index_type start, size_type oldLen, size_type newLen, michael@0: size_type elemSize, size_t elemAlign); michael@0: michael@0: // This method increments the length member of the array's header. michael@0: // Note that mHdr may actually be sEmptyHdr in the case where a michael@0: // zero-length array is inserted into our array. But then n should michael@0: // always be 0. michael@0: void IncrementLength(uint32_t n) { michael@0: if (mHdr == EmptyHdr()) { michael@0: if (MOZ_UNLIKELY(n != 0)) { michael@0: // Writing a non-zero length to the empty header would be extremely bad. michael@0: MOZ_CRASH(); michael@0: } michael@0: } else { michael@0: mHdr->mLength += n; michael@0: } michael@0: } michael@0: michael@0: // This method inserts blank slots into the array. michael@0: // @param index the place to insert the new elements. This must be no michael@0: // greater than the current length of the array. michael@0: // @param count the number of slots to insert michael@0: // @param elementSize the size of an array element. michael@0: // @param elemAlign the alignment in bytes of an array element. michael@0: bool InsertSlotsAt(index_type index, size_type count, michael@0: size_type elementSize, size_t elemAlign); michael@0: michael@0: protected: michael@0: template michael@0: typename Alloc::ResultTypeProxy michael@0: SwapArrayElements(nsTArray_base& other, michael@0: size_type elemSize, michael@0: size_t elemAlign); michael@0: michael@0: // This is an RAII class used in SwapArrayElements. michael@0: class IsAutoArrayRestorer { michael@0: public: michael@0: IsAutoArrayRestorer(nsTArray_base &array, size_t elemAlign); michael@0: ~IsAutoArrayRestorer(); michael@0: michael@0: private: michael@0: nsTArray_base &mArray; michael@0: size_t mElemAlign; michael@0: bool mIsAuto; michael@0: }; michael@0: michael@0: // Helper function for SwapArrayElements. Ensures that if the array michael@0: // is an nsAutoTArray that it doesn't use the built-in buffer. michael@0: bool EnsureNotUsingAutoArrayBuffer(size_type elemSize); michael@0: michael@0: // Returns true if this nsTArray is an nsAutoTArray with a built-in buffer. michael@0: bool IsAutoArray() const { michael@0: return mHdr->mIsAutoArray; michael@0: } michael@0: michael@0: // Returns a Header for the built-in buffer of this nsAutoTArray. michael@0: Header* GetAutoArrayBuffer(size_t elemAlign) { michael@0: MOZ_ASSERT(IsAutoArray(), "Should be an auto array to call this"); michael@0: return GetAutoArrayBufferUnsafe(elemAlign); michael@0: } michael@0: const Header* GetAutoArrayBuffer(size_t elemAlign) const { michael@0: MOZ_ASSERT(IsAutoArray(), "Should be an auto array to call this"); michael@0: return GetAutoArrayBufferUnsafe(elemAlign); michael@0: } michael@0: michael@0: // Returns a Header for the built-in buffer of this nsAutoTArray, but doesn't michael@0: // assert that we are an nsAutoTArray. michael@0: Header* GetAutoArrayBufferUnsafe(size_t elemAlign) { michael@0: return const_cast(static_cast*>(this)-> michael@0: GetAutoArrayBufferUnsafe(elemAlign)); michael@0: } michael@0: const Header* GetAutoArrayBufferUnsafe(size_t elemAlign) const; michael@0: michael@0: // Returns true if this is an nsAutoTArray and it currently uses the michael@0: // built-in buffer to store its elements. michael@0: bool UsesAutoArrayBuffer() const; michael@0: michael@0: // The array's elements (prefixed with a Header). This pointer is never michael@0: // null. If the array is empty, then this will point to sEmptyHdr. michael@0: Header *mHdr; michael@0: michael@0: Header* Hdr() const { michael@0: return mHdr; michael@0: } michael@0: michael@0: Header** PtrToHdr() { michael@0: return &mHdr; michael@0: } michael@0: michael@0: static Header* EmptyHdr() { michael@0: return &Header::sEmptyHdr; michael@0: } michael@0: }; michael@0: michael@0: // michael@0: // This class defines convenience functions for element specific operations. michael@0: // Specialize this template if necessary. michael@0: // michael@0: template michael@0: class nsTArrayElementTraits michael@0: { michael@0: public: michael@0: // Invoke the default constructor in place. michael@0: static inline void Construct(E *e) { michael@0: // Do NOT call "E()"! That triggers C++ "default initialization" michael@0: // which zeroes out POD ("plain old data") types such as regular michael@0: // ints. We don't want that because it can be a performance issue michael@0: // and people don't expect it; nsTArray should work like a regular michael@0: // C/C++ array in this respect. michael@0: new (static_cast(e)) E; michael@0: } michael@0: // Invoke the copy-constructor in place. michael@0: template michael@0: static inline void Construct(E *e, const A &arg) { michael@0: typedef typename mozilla::RemoveCV::Type E_NoCV; michael@0: typedef typename mozilla::RemoveCV::Type A_NoCV; michael@0: static_assert(!mozilla::IsSame::value, michael@0: "For safety, we disallow constructing nsTArray elements " michael@0: "from E* pointers. See bug 960591."); michael@0: new (static_cast(e)) E(arg); michael@0: } michael@0: // Invoke the destructor in place. michael@0: static inline void Destruct(E *e) { michael@0: e->~E(); michael@0: } michael@0: }; michael@0: michael@0: // The default comparator used by nsTArray michael@0: template michael@0: class nsDefaultComparator michael@0: { michael@0: public: michael@0: bool Equals(const A& a, const B& b) const { michael@0: return a == b; michael@0: } michael@0: bool LessThan(const A& a, const B& b) const { michael@0: return a < b; michael@0: } michael@0: }; michael@0: michael@0: template class InfallibleTArray; michael@0: template class FallibleTArray; michael@0: michael@0: template michael@0: struct AssignRangeAlgorithm { michael@0: template michael@0: static void implementation(ElemType* elements, IndexType start, michael@0: SizeType count, const Item *values) { michael@0: ElemType *iter = elements + start, *end = iter + count; michael@0: for (; iter != end; ++iter, ++values) michael@0: nsTArrayElementTraits::Construct(iter, *values); michael@0: } michael@0: }; michael@0: michael@0: template<> michael@0: struct AssignRangeAlgorithm { michael@0: template michael@0: static void implementation(ElemType* elements, IndexType start, michael@0: SizeType count, const Item *values) { michael@0: memcpy(elements + start, values, count * sizeof(ElemType)); michael@0: } michael@0: }; michael@0: michael@0: // michael@0: // Normally elements are copied with memcpy and memmove, but for some element michael@0: // types that is problematic. The nsTArray_CopyChooser template class can be michael@0: // specialized to ensure that copying calls constructors and destructors michael@0: // instead, as is done below for JS::Heap elements. michael@0: // michael@0: michael@0: // michael@0: // A class that defines how to copy elements using memcpy/memmove. michael@0: // michael@0: struct nsTArray_CopyWithMemutils michael@0: { michael@0: const static bool allowRealloc = true; michael@0: michael@0: static void CopyElements(void* dest, const void* src, size_t count, size_t elemSize) { michael@0: memcpy(dest, src, count * elemSize); michael@0: } michael@0: michael@0: static void CopyHeaderAndElements(void* dest, const void* src, size_t count, size_t elemSize) { michael@0: memcpy(dest, src, sizeof(nsTArrayHeader) + count * elemSize); michael@0: } michael@0: michael@0: static void MoveElements(void* dest, const void* src, size_t count, size_t elemSize) { michael@0: memmove(dest, src, count * elemSize); michael@0: } michael@0: }; michael@0: michael@0: // michael@0: // A template class that defines how to copy elements calling their constructors michael@0: // and destructors appropriately. michael@0: // michael@0: template michael@0: struct nsTArray_CopyWithConstructors michael@0: { michael@0: typedef nsTArrayElementTraits traits; michael@0: michael@0: const static bool allowRealloc = false; michael@0: michael@0: static void CopyElements(void* dest, void* src, size_t count, size_t elemSize) { michael@0: ElemType* destElem = static_cast(dest); michael@0: ElemType* srcElem = static_cast(src); michael@0: ElemType* destElemEnd = destElem + count; michael@0: #ifdef DEBUG michael@0: ElemType* srcElemEnd = srcElem + count; michael@0: MOZ_ASSERT(srcElemEnd <= destElem || srcElemEnd > destElemEnd); michael@0: #endif michael@0: while (destElem != destElemEnd) { michael@0: traits::Construct(destElem, *srcElem); michael@0: traits::Destruct(srcElem); michael@0: ++destElem; michael@0: ++srcElem; michael@0: } michael@0: } michael@0: michael@0: static void CopyHeaderAndElements(void* dest, void* src, size_t count, size_t elemSize) { michael@0: nsTArrayHeader* destHeader = static_cast(dest); michael@0: nsTArrayHeader* srcHeader = static_cast(src); michael@0: *destHeader = *srcHeader; michael@0: CopyElements(static_cast(dest) + sizeof(nsTArrayHeader), michael@0: static_cast(src) + sizeof(nsTArrayHeader), michael@0: count, elemSize); michael@0: } michael@0: michael@0: static void MoveElements(void* dest, void* src, size_t count, size_t elemSize) { michael@0: ElemType* destElem = static_cast(dest); michael@0: ElemType* srcElem = static_cast(src); michael@0: ElemType* destElemEnd = destElem + count; michael@0: ElemType* srcElemEnd = srcElem + count; michael@0: if (destElem == srcElem) { michael@0: return; // In practice, we don't do this. michael@0: } else if (srcElemEnd > destElem && srcElemEnd < destElemEnd) { michael@0: while (destElemEnd != destElem) { michael@0: --destElemEnd; michael@0: --srcElemEnd; michael@0: traits::Construct(destElemEnd, *srcElemEnd); michael@0: traits::Destruct(srcElem); michael@0: } michael@0: } else { michael@0: CopyElements(dest, src, count, elemSize); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: // michael@0: // The default behaviour is to use memcpy/memmove for everything. michael@0: // michael@0: template michael@0: struct nsTArray_CopyChooser { michael@0: typedef nsTArray_CopyWithMemutils Type; michael@0: }; michael@0: michael@0: // michael@0: // Some classes require constructors/destructors to be called, so they are michael@0: // specialized here. michael@0: // michael@0: template michael@0: struct nsTArray_CopyChooser > { michael@0: typedef nsTArray_CopyWithConstructors > Type; michael@0: }; michael@0: michael@0: template<> michael@0: struct nsTArray_CopyChooser { michael@0: typedef nsTArray_CopyWithConstructors Type; michael@0: }; michael@0: michael@0: template<> michael@0: struct nsTArray_CopyChooser { michael@0: typedef nsTArray_CopyWithConstructors Type; michael@0: }; michael@0: michael@0: // michael@0: // Base class for nsTArray_Impl that is templated on element type and derived michael@0: // nsTArray_Impl class, to allow extra conversions to be added for specific michael@0: // types. michael@0: // michael@0: template michael@0: struct nsTArray_TypedBase : public nsTArray_SafeElementAtHelper {}; michael@0: michael@0: // michael@0: // Specialization of nsTArray_TypedBase for arrays containing JS::Heap michael@0: // elements. michael@0: // michael@0: // These conversions are safe because JS::Heap and E share the same michael@0: // representation, and since the result of the conversions are const references michael@0: // we won't miss any barriers. michael@0: // michael@0: // The static_cast is necessary to obtain the correct address for the derived michael@0: // class since we are a base class used in multiple inheritance. michael@0: // michael@0: template michael@0: struct nsTArray_TypedBase, Derived> michael@0: : public nsTArray_SafeElementAtHelper, Derived> michael@0: { michael@0: operator const nsTArray& () { michael@0: static_assert(sizeof(E) == sizeof(JS::Heap), michael@0: "JS::Heap must be binary compatible with E."); michael@0: Derived* self = static_cast(this); michael@0: return *reinterpret_cast *>(self); michael@0: } michael@0: michael@0: operator const FallibleTArray& () { michael@0: Derived* self = static_cast(this); michael@0: return *reinterpret_cast *>(self); michael@0: } michael@0: }; michael@0: michael@0: michael@0: // michael@0: // nsTArray_Impl contains most of the guts supporting nsTArray, FallibleTArray, michael@0: // nsAutoTArray, and AutoFallibleTArray. michael@0: // michael@0: // The only situation in which you might need to use nsTArray_Impl in your code michael@0: // is if you're writing code which mutates a TArray which may or may not be michael@0: // infallible. michael@0: // michael@0: // Code which merely reads from a TArray which may or may not be infallible can michael@0: // simply cast the TArray to |const nsTArray&|; both fallible and infallible michael@0: // TArrays can be cast to |const nsTArray&|. michael@0: // michael@0: template michael@0: class nsTArray_Impl : public nsTArray_base::Type>, michael@0: public nsTArray_TypedBase > michael@0: { michael@0: public: michael@0: typedef typename nsTArray_CopyChooser::Type copy_type; michael@0: typedef nsTArray_base base_type; michael@0: typedef typename base_type::size_type size_type; michael@0: typedef typename base_type::index_type index_type; michael@0: typedef E elem_type; michael@0: typedef nsTArray_Impl self_type; michael@0: typedef nsTArrayElementTraits elem_traits; michael@0: typedef nsTArray_SafeElementAtHelper safeelementat_helper_type; michael@0: michael@0: using safeelementat_helper_type::SafeElementAt; michael@0: using base_type::EmptyHdr; michael@0: michael@0: // A special value that is used to indicate an invalid or unknown index michael@0: // into the array. michael@0: enum { michael@0: NoIndex = index_type(-1) michael@0: }; michael@0: michael@0: using base_type::Length; michael@0: michael@0: // michael@0: // Finalization method michael@0: // michael@0: michael@0: ~nsTArray_Impl() { Clear(); } michael@0: michael@0: // michael@0: // Initialization methods michael@0: // michael@0: michael@0: nsTArray_Impl() {} michael@0: michael@0: // Initialize this array and pre-allocate some number of elements. michael@0: explicit nsTArray_Impl(size_type capacity) { michael@0: SetCapacity(capacity); michael@0: } michael@0: michael@0: // The array's copy-constructor performs a 'deep' copy of the given array. michael@0: // @param other The array object to copy. michael@0: // michael@0: // It's very important that we declare this method as taking |const michael@0: // self_type&| as opposed to taking |const nsTArray_Impl| for michael@0: // an arbitrary OtherAlloc. michael@0: // michael@0: // If we don't declare a constructor taking |const self_type&|, C++ generates michael@0: // a copy-constructor for this class which merely copies the object's michael@0: // members, which is obviously wrong. michael@0: // michael@0: // You can pass an nsTArray_Impl to this method because michael@0: // nsTArray_Impl can be cast to const nsTArray_Impl&. So the michael@0: // effect on the API is the same as if we'd declared this method as taking michael@0: // |const nsTArray_Impl&|. michael@0: explicit nsTArray_Impl(const self_type& other) { michael@0: AppendElements(other); michael@0: } michael@0: michael@0: // Allow converting to a const array with a different kind of allocator, michael@0: // Since the allocator doesn't matter for const arrays michael@0: template michael@0: operator const nsTArray_Impl&() const { michael@0: return *reinterpret_cast*>(this); michael@0: } michael@0: // And we have to do this for our subclasses too michael@0: operator const nsTArray&() const { michael@0: return *reinterpret_cast*>(this); michael@0: } michael@0: operator const FallibleTArray&() const { michael@0: return *reinterpret_cast*>(this); michael@0: } michael@0: michael@0: // The array's assignment operator performs a 'deep' copy of the given michael@0: // array. It is optimized to reuse existing storage if possible. michael@0: // @param other The array object to copy. michael@0: self_type& operator=(const self_type& other) { michael@0: ReplaceElementsAt(0, Length(), other.Elements(), other.Length()); michael@0: return *this; michael@0: } michael@0: michael@0: // Return true if this array has the same length and the same michael@0: // elements as |other|. michael@0: template michael@0: bool operator==(const nsTArray_Impl& other) const { michael@0: size_type len = Length(); michael@0: if (len != other.Length()) michael@0: return false; michael@0: michael@0: // XXX std::equal would be as fast or faster here michael@0: for (index_type i = 0; i < len; ++i) michael@0: if (!(operator[](i) == other[i])) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Return true if this array does not have the same length and the same michael@0: // elements as |other|. michael@0: bool operator!=(const self_type& other) const { michael@0: return !operator==(other); michael@0: } michael@0: michael@0: template michael@0: self_type& operator=(const nsTArray_Impl& other) { michael@0: ReplaceElementsAt(0, Length(), other.Elements(), other.Length()); michael@0: return *this; michael@0: } michael@0: michael@0: // @return The amount of memory used by this nsTArray_Impl, excluding michael@0: // sizeof(*this). michael@0: size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { michael@0: if (this->UsesAutoArrayBuffer() || Hdr() == EmptyHdr()) michael@0: return 0; michael@0: return mallocSizeOf(this->Hdr()); michael@0: } michael@0: michael@0: // @return The amount of memory used by this nsTArray_Impl, including michael@0: // sizeof(*this). michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { michael@0: return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf); michael@0: } michael@0: michael@0: // michael@0: // Accessor methods michael@0: // michael@0: michael@0: // This method provides direct access to the array elements. michael@0: // @return A pointer to the first element of the array. If the array is michael@0: // empty, then this pointer must not be dereferenced. michael@0: elem_type* Elements() { michael@0: return reinterpret_cast(Hdr() + 1); michael@0: } michael@0: michael@0: // This method provides direct, readonly access to the array elements. michael@0: // @return A pointer to the first element of the array. If the array is michael@0: // empty, then this pointer must not be dereferenced. michael@0: const elem_type* Elements() const { michael@0: return reinterpret_cast(Hdr() + 1); michael@0: } michael@0: michael@0: // This method provides direct access to the i'th element of the array. michael@0: // The given index must be within the array bounds. michael@0: // @param i The index of an element in the array. michael@0: // @return A reference to the i'th element of the array. michael@0: elem_type& ElementAt(index_type i) { michael@0: MOZ_ASSERT(i < Length(), "invalid array index"); michael@0: return Elements()[i]; michael@0: } michael@0: michael@0: // This method provides direct, readonly access to the i'th element of the michael@0: // array. The given index must be within the array bounds. michael@0: // @param i The index of an element in the array. michael@0: // @return A const reference to the i'th element of the array. michael@0: const elem_type& ElementAt(index_type i) const { michael@0: MOZ_ASSERT(i < Length(), "invalid array index"); michael@0: return Elements()[i]; michael@0: } michael@0: michael@0: // This method provides direct access to the i'th element of the array in michael@0: // a bounds safe manner. If the requested index is out of bounds the michael@0: // provided default value is returned. michael@0: // @param i The index of an element in the array. michael@0: // @param def The value to return if the index is out of bounds. michael@0: elem_type& SafeElementAt(index_type i, elem_type& def) { michael@0: return i < Length() ? Elements()[i] : def; michael@0: } michael@0: michael@0: // This method provides direct access to the i'th element of the array in michael@0: // a bounds safe manner. If the requested index is out of bounds the michael@0: // provided default value is returned. michael@0: // @param i The index of an element in the array. michael@0: // @param def The value to return if the index is out of bounds. michael@0: const elem_type& SafeElementAt(index_type i, const elem_type& def) const { michael@0: return i < Length() ? Elements()[i] : def; michael@0: } michael@0: michael@0: // Shorthand for ElementAt(i) michael@0: elem_type& operator[](index_type i) { michael@0: return ElementAt(i); michael@0: } michael@0: michael@0: // Shorthand for ElementAt(i) michael@0: const elem_type& operator[](index_type i) const { michael@0: return ElementAt(i); michael@0: } michael@0: michael@0: // Shorthand for ElementAt(length - 1) michael@0: elem_type& LastElement() { michael@0: return ElementAt(Length() - 1); michael@0: } michael@0: michael@0: // Shorthand for ElementAt(length - 1) michael@0: const elem_type& LastElement() const { michael@0: return ElementAt(Length() - 1); michael@0: } michael@0: michael@0: // Shorthand for SafeElementAt(length - 1, def) michael@0: elem_type& SafeLastElement(elem_type& def) { michael@0: return SafeElementAt(Length() - 1, def); michael@0: } michael@0: michael@0: // Shorthand for SafeElementAt(length - 1, def) michael@0: const elem_type& SafeLastElement(const elem_type& def) const { michael@0: return SafeElementAt(Length() - 1, def); michael@0: } michael@0: michael@0: // michael@0: // Search methods michael@0: // michael@0: michael@0: // This method searches for the first element in this array that is equal michael@0: // to the given element. michael@0: // @param item The item to search for. michael@0: // @param comp The Comparator used to determine element equality. michael@0: // @return true if the element was found. michael@0: template michael@0: bool Contains(const Item& item, const Comparator& comp) const { michael@0: return IndexOf(item, 0, comp) != NoIndex; michael@0: } michael@0: michael@0: // This method searches for the first element in this array that is equal michael@0: // to the given element. This method assumes that 'operator==' is defined michael@0: // for elem_type. michael@0: // @param item The item to search for. michael@0: // @return true if the element was found. michael@0: template michael@0: bool Contains(const Item& item) const { michael@0: return IndexOf(item) != NoIndex; michael@0: } michael@0: michael@0: // This method searches for the offset of the first element in this michael@0: // array that is equal to the given element. michael@0: // @param item The item to search for. michael@0: // @param start The index to start from. michael@0: // @param comp The Comparator used to determine element equality. michael@0: // @return The index of the found element or NoIndex if not found. michael@0: template michael@0: index_type IndexOf(const Item& item, index_type start, michael@0: const Comparator& comp) const { michael@0: const elem_type* iter = Elements() + start, *end = Elements() + Length(); michael@0: for (; iter != end; ++iter) { michael@0: if (comp.Equals(*iter, item)) michael@0: return index_type(iter - Elements()); michael@0: } michael@0: return NoIndex; michael@0: } michael@0: michael@0: // This method searches for the offset of the first element in this michael@0: // array that is equal to the given element. This method assumes michael@0: // that 'operator==' is defined for elem_type. michael@0: // @param item The item to search for. michael@0: // @param start The index to start from. michael@0: // @return The index of the found element or NoIndex if not found. michael@0: template michael@0: index_type IndexOf(const Item& item, index_type start = 0) const { michael@0: return IndexOf(item, start, nsDefaultComparator()); michael@0: } michael@0: michael@0: // This method searches for the offset of the last element in this michael@0: // array that is equal to the given element. michael@0: // @param item The item to search for. michael@0: // @param start The index to start from. If greater than or equal to the michael@0: // length of the array, then the entire array is searched. michael@0: // @param comp The Comparator used to determine element equality. michael@0: // @return The index of the found element or NoIndex if not found. michael@0: template michael@0: index_type LastIndexOf(const Item& item, index_type start, michael@0: const Comparator& comp) const { michael@0: size_type endOffset = start >= Length() ? Length() : start + 1; michael@0: const elem_type* end = Elements() - 1, *iter = end + endOffset; michael@0: for (; iter != end; --iter) { michael@0: if (comp.Equals(*iter, item)) michael@0: return index_type(iter - Elements()); michael@0: } michael@0: return NoIndex; michael@0: } michael@0: michael@0: // This method searches for the offset of the last element in this michael@0: // array that is equal to the given element. This method assumes michael@0: // that 'operator==' is defined for elem_type. michael@0: // @param item The item to search for. michael@0: // @param start The index to start from. If greater than or equal to the michael@0: // length of the array, then the entire array is searched. michael@0: // @return The index of the found element or NoIndex if not found. michael@0: template michael@0: index_type LastIndexOf(const Item& item, michael@0: index_type start = NoIndex) const { michael@0: return LastIndexOf(item, start, nsDefaultComparator()); michael@0: } michael@0: michael@0: // This method searches for the offset for the element in this array michael@0: // that is equal to the given element. The array is assumed to be sorted. michael@0: // @param item The item to search for. michael@0: // @param comp The Comparator used. michael@0: // @return The index of the found element or NoIndex if not found. michael@0: template michael@0: index_type BinaryIndexOf(const Item& item, const Comparator& comp) const { michael@0: index_type low = 0, high = Length(); michael@0: while (high > low) { michael@0: index_type mid = (high + low) >> 1; michael@0: if (comp.Equals(ElementAt(mid), item)) michael@0: return mid; michael@0: if (comp.LessThan(ElementAt(mid), item)) michael@0: low = mid + 1; michael@0: else michael@0: high = mid; michael@0: } michael@0: return NoIndex; michael@0: } michael@0: michael@0: // This method searches for the offset for the element in this array michael@0: // that is equal to the given element. The array is assumed to be sorted. michael@0: // This method assumes that 'operator==' and 'operator<' are defined. michael@0: // @param item The item to search for. michael@0: // @return The index of the found element or NoIndex if not found. michael@0: template michael@0: index_type BinaryIndexOf(const Item& item) const { michael@0: return BinaryIndexOf(item, nsDefaultComparator()); michael@0: } michael@0: michael@0: // michael@0: // Mutation methods michael@0: // michael@0: // This method call the destructor on each element of the array, empties it, michael@0: // but does not shrink the array's capacity. michael@0: // See also SetLengthAndRetainStorage. michael@0: // Make sure to call Compact() if needed to avoid keeping a huge array michael@0: // around. michael@0: void ClearAndRetainStorage() { michael@0: if (base_type::mHdr == EmptyHdr()) { michael@0: return; michael@0: } michael@0: michael@0: DestructRange(0, Length()); michael@0: base_type::mHdr->mLength = 0; michael@0: } michael@0: michael@0: // This method modifies the length of the array, but unlike SetLength michael@0: // it doesn't deallocate/reallocate the current internal storage. michael@0: // The new length MUST be shorter than or equal to the current capacity. michael@0: // If the new length is larger than the existing length of the array, michael@0: // then new elements will be constructed using elem_type's default michael@0: // constructor. If shorter, elements will be destructed and removed. michael@0: // See also ClearAndRetainStorage. michael@0: // @param newLen The desired length of this array. michael@0: void SetLengthAndRetainStorage(size_type newLen) { michael@0: MOZ_ASSERT(newLen <= base_type::Capacity()); michael@0: size_type oldLen = Length(); michael@0: if (newLen > oldLen) { michael@0: InsertElementsAt(oldLen, newLen - oldLen); michael@0: return; michael@0: } michael@0: if (newLen < oldLen) { michael@0: DestructRange(newLen, oldLen - newLen); michael@0: base_type::mHdr->mLength = newLen; michael@0: } michael@0: } michael@0: michael@0: // This method replaces a range of elements in this array. michael@0: // @param start The starting index of the elements to replace. michael@0: // @param count The number of elements to replace. This may be zero to michael@0: // insert elements without removing any existing elements. michael@0: // @param array The values to copy into this array. Must be non-null, michael@0: // and these elements must not already exist in the array michael@0: // being modified. michael@0: // @param arrayLen The number of values to copy into this array. michael@0: // @return A pointer to the new elements in the array, or null if michael@0: // the operation failed due to insufficient memory. michael@0: template michael@0: elem_type *ReplaceElementsAt(index_type start, size_type count, michael@0: const Item* array, size_type arrayLen) { michael@0: // Adjust memory allocation up-front to catch errors. michael@0: if (!Alloc::Successful(this->EnsureCapacity(Length() + arrayLen - count, sizeof(elem_type)))) michael@0: return nullptr; michael@0: DestructRange(start, count); michael@0: this->ShiftData(start, count, arrayLen, sizeof(elem_type), MOZ_ALIGNOF(elem_type)); michael@0: AssignRange(start, arrayLen, array); michael@0: return Elements() + start; michael@0: } michael@0: michael@0: // A variation on the ReplaceElementsAt method defined above. michael@0: template michael@0: elem_type *ReplaceElementsAt(index_type start, size_type count, michael@0: const nsTArray& array) { michael@0: return ReplaceElementsAt(start, count, array.Elements(), array.Length()); michael@0: } michael@0: michael@0: // A variation on the ReplaceElementsAt method defined above. michael@0: template michael@0: elem_type *ReplaceElementsAt(index_type start, size_type count, michael@0: const Item& item) { michael@0: return ReplaceElementsAt(start, count, &item, 1); michael@0: } michael@0: michael@0: // A variation on the ReplaceElementsAt method defined above. michael@0: template michael@0: elem_type *ReplaceElementAt(index_type index, const Item& item) { michael@0: return ReplaceElementsAt(index, 1, &item, 1); michael@0: } michael@0: michael@0: // A variation on the ReplaceElementsAt method defined above. michael@0: template michael@0: elem_type *InsertElementsAt(index_type index, const Item* array, michael@0: size_type arrayLen) { michael@0: return ReplaceElementsAt(index, 0, array, arrayLen); michael@0: } michael@0: michael@0: // A variation on the ReplaceElementsAt method defined above. michael@0: template michael@0: elem_type *InsertElementsAt(index_type index, const nsTArray_Impl& array) { michael@0: return ReplaceElementsAt(index, 0, array.Elements(), array.Length()); michael@0: } michael@0: michael@0: // A variation on the ReplaceElementsAt method defined above. michael@0: template michael@0: elem_type *InsertElementAt(index_type index, const Item& item) { michael@0: return ReplaceElementsAt(index, 0, &item, 1); michael@0: } michael@0: michael@0: // Insert a new element without copy-constructing. This is useful to avoid michael@0: // temporaries. michael@0: // @return A pointer to the newly inserted element, or null on OOM. michael@0: elem_type* InsertElementAt(index_type index) { michael@0: if (!Alloc::Successful(this->EnsureCapacity(Length() + 1, sizeof(elem_type)))) michael@0: return nullptr; michael@0: this->ShiftData(index, 0, 1, sizeof(elem_type), MOZ_ALIGNOF(elem_type)); michael@0: elem_type *elem = Elements() + index; michael@0: elem_traits::Construct(elem); michael@0: return elem; michael@0: } michael@0: michael@0: // This method searches for the smallest index of an element that is strictly michael@0: // greater than |item|. If |item| is inserted at this index, the array will michael@0: // remain sorted and |item| would come after all elements that are equal to michael@0: // it. If |item| is greater than or equal to all elements in the array, the michael@0: // array length is returned. michael@0: // michael@0: // Note that consumers who want to know whether there are existing items equal michael@0: // to |item| in the array can just check that the return value here is > 0 and michael@0: // indexing into the previous slot gives something equal to |item|. michael@0: // michael@0: // michael@0: // @param item The item to search for. michael@0: // @param comp The Comparator used. michael@0: // @return The index of greatest element <= to |item| michael@0: // @precondition The array is sorted michael@0: template michael@0: index_type michael@0: IndexOfFirstElementGt(const Item& item, michael@0: const Comparator& comp) const { michael@0: // invariant: low <= [idx] <= high michael@0: index_type low = 0, high = Length(); michael@0: while (high > low) { michael@0: index_type mid = (high + low) >> 1; michael@0: // Comparators are not required to provide a LessThan(Item&, elem_type), michael@0: // so we can't do comp.LessThan(item, ElementAt(mid)). michael@0: if (comp.LessThan(ElementAt(mid), item) || michael@0: comp.Equals(ElementAt(mid), item)) { michael@0: // item >= ElementAt(mid), so our desired index is at least mid+1. michael@0: low = mid + 1; michael@0: } else { michael@0: // item < ElementAt(mid). Our desired index is therefore at most mid. michael@0: high = mid; michael@0: } michael@0: } michael@0: MOZ_ASSERT(high == low); michael@0: return low; michael@0: } michael@0: michael@0: // A variation on the IndexOfFirstElementGt method defined above. michael@0: template michael@0: index_type michael@0: IndexOfFirstElementGt(const Item& item) const { michael@0: return IndexOfFirstElementGt(item, nsDefaultComparator()); michael@0: } michael@0: michael@0: // Inserts |item| at such an index to guarantee that if the array michael@0: // was previously sorted, it will remain sorted after this michael@0: // insertion. michael@0: template michael@0: elem_type *InsertElementSorted(const Item& item, const Comparator& comp) { michael@0: index_type index = IndexOfFirstElementGt(item, comp); michael@0: return InsertElementAt(index, item); michael@0: } michael@0: michael@0: // A variation on the InsertElementSorted method defined above. michael@0: template michael@0: elem_type *InsertElementSorted(const Item& item) { michael@0: return InsertElementSorted(item, nsDefaultComparator()); michael@0: } michael@0: michael@0: // This method appends elements to the end of this array. michael@0: // @param array The elements to append to this array. michael@0: // @param arrayLen The number of elements to append to this array. michael@0: // @return A pointer to the new elements in the array, or null if michael@0: // the operation failed due to insufficient memory. michael@0: template michael@0: elem_type *AppendElements(const Item* array, size_type arrayLen) { michael@0: if (!Alloc::Successful(this->EnsureCapacity(Length() + arrayLen, sizeof(elem_type)))) michael@0: return nullptr; michael@0: index_type len = Length(); michael@0: AssignRange(len, arrayLen, array); michael@0: this->IncrementLength(arrayLen); michael@0: return Elements() + len; michael@0: } michael@0: michael@0: // A variation on the AppendElements method defined above. michael@0: template michael@0: elem_type *AppendElements(const nsTArray_Impl& array) { michael@0: return AppendElements(array.Elements(), array.Length()); michael@0: } michael@0: michael@0: // A variation on the AppendElements method defined above. michael@0: template michael@0: elem_type *AppendElement(const Item& item) { michael@0: return AppendElements(&item, 1); michael@0: } michael@0: michael@0: // Append new elements without copy-constructing. This is useful to avoid michael@0: // temporaries. michael@0: // @return A pointer to the newly appended elements, or null on OOM. michael@0: elem_type *AppendElements(size_type count) { michael@0: if (!Alloc::Successful(this->EnsureCapacity(Length() + count, sizeof(elem_type)))) michael@0: return nullptr; michael@0: elem_type *elems = Elements() + Length(); michael@0: size_type i; michael@0: for (i = 0; i < count; ++i) { michael@0: elem_traits::Construct(elems + i); michael@0: } michael@0: this->IncrementLength(count); michael@0: return elems; michael@0: } michael@0: michael@0: // Append a new element without copy-constructing. This is useful to avoid michael@0: // temporaries. michael@0: // @return A pointer to the newly appended element, or null on OOM. michael@0: elem_type *AppendElement() { michael@0: return AppendElements(1); michael@0: } michael@0: michael@0: // Move all elements from another array to the end of this array without michael@0: // calling copy constructors or destructors. michael@0: // @return A pointer to the newly appended elements, or null on OOM. michael@0: template michael@0: elem_type *MoveElementsFrom(nsTArray_Impl& array) { michael@0: MOZ_ASSERT(&array != this, "argument must be different array"); michael@0: index_type len = Length(); michael@0: index_type otherLen = array.Length(); michael@0: if (!Alloc::Successful(this->EnsureCapacity(len + otherLen, sizeof(elem_type)))) michael@0: return nullptr; michael@0: copy_type::CopyElements(Elements() + len, array.Elements(), otherLen, sizeof(elem_type)); michael@0: this->IncrementLength(otherLen); michael@0: array.ShiftData(0, otherLen, 0, sizeof(elem_type), MOZ_ALIGNOF(elem_type)); michael@0: return Elements() + len; michael@0: } michael@0: michael@0: // This method removes a range of elements from this array. michael@0: // @param start The starting index of the elements to remove. michael@0: // @param count The number of elements to remove. michael@0: void RemoveElementsAt(index_type start, size_type count) { michael@0: MOZ_ASSERT(count == 0 || start < Length(), "Invalid start index"); michael@0: MOZ_ASSERT(start + count <= Length(), "Invalid length"); michael@0: // Check that the previous assert didn't overflow michael@0: MOZ_ASSERT(start <= start + count, "Start index plus length overflows"); michael@0: DestructRange(start, count); michael@0: this->ShiftData(start, count, 0, sizeof(elem_type), MOZ_ALIGNOF(elem_type)); michael@0: } michael@0: michael@0: // A variation on the RemoveElementsAt method defined above. michael@0: void RemoveElementAt(index_type index) { michael@0: RemoveElementsAt(index, 1); michael@0: } michael@0: michael@0: // A variation on the RemoveElementsAt method defined above. michael@0: void Clear() { michael@0: RemoveElementsAt(0, Length()); michael@0: } michael@0: michael@0: // This helper function combines IndexOf with RemoveElementAt to "search michael@0: // and destroy" the first element that is equal to the given element. michael@0: // @param item The item to search for. michael@0: // @param comp The Comparator used to determine element equality. michael@0: // @return true if the element was found michael@0: template michael@0: bool RemoveElement(const Item& item, const Comparator& comp) { michael@0: index_type i = IndexOf(item, 0, comp); michael@0: if (i == NoIndex) michael@0: return false; michael@0: michael@0: RemoveElementAt(i); michael@0: return true; michael@0: } michael@0: michael@0: // A variation on the RemoveElement method defined above that assumes michael@0: // that 'operator==' is defined for elem_type. michael@0: template michael@0: bool RemoveElement(const Item& item) { michael@0: return RemoveElement(item, nsDefaultComparator()); michael@0: } michael@0: michael@0: // This helper function combines IndexOfFirstElementGt with michael@0: // RemoveElementAt to "search and destroy" the last element that michael@0: // is equal to the given element. michael@0: // @param item The item to search for. michael@0: // @param comp The Comparator used to determine element equality. michael@0: // @return true if the element was found michael@0: template michael@0: bool RemoveElementSorted(const Item& item, const Comparator& comp) { michael@0: index_type index = IndexOfFirstElementGt(item, comp); michael@0: if (index > 0 && comp.Equals(ElementAt(index - 1), item)) { michael@0: RemoveElementAt(index - 1); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // A variation on the RemoveElementSorted method defined above. michael@0: template michael@0: bool RemoveElementSorted(const Item& item) { michael@0: return RemoveElementSorted(item, nsDefaultComparator()); michael@0: } michael@0: michael@0: // This method causes the elements contained in this array and the given michael@0: // array to be swapped. michael@0: template michael@0: typename Alloc::ResultType michael@0: SwapElements(nsTArray_Impl& other) { michael@0: return Alloc::Result(this->SwapArrayElements(other, sizeof(elem_type), michael@0: MOZ_ALIGNOF(elem_type))); michael@0: } michael@0: michael@0: // michael@0: // Allocation michael@0: // michael@0: michael@0: // This method may increase the capacity of this array object by the michael@0: // specified amount. This method may be called in advance of several michael@0: // AppendElement operations to minimize heap re-allocations. This method michael@0: // will not reduce the number of elements in this array. michael@0: // @param capacity The desired capacity of this array. michael@0: // @return True if the operation succeeded; false if we ran out of memory michael@0: typename Alloc::ResultType SetCapacity(size_type capacity) { michael@0: return Alloc::Result(this->EnsureCapacity(capacity, sizeof(elem_type))); michael@0: } michael@0: michael@0: // This method modifies the length of the array. If the new length is michael@0: // larger than the existing length of the array, then new elements will be michael@0: // constructed using elem_type's default constructor. Otherwise, this call michael@0: // removes elements from the array (see also RemoveElementsAt). michael@0: // @param newLen The desired length of this array. michael@0: // @return True if the operation succeeded; false otherwise. michael@0: // See also TruncateLength if the new length is guaranteed to be michael@0: // smaller than the old. michael@0: typename Alloc::ResultType SetLength(size_type newLen) { michael@0: size_type oldLen = Length(); michael@0: if (newLen > oldLen) { michael@0: return Alloc::ConvertBoolToResultType(InsertElementsAt(oldLen, newLen - oldLen) != nullptr); michael@0: } michael@0: michael@0: TruncateLength(newLen); michael@0: return Alloc::ConvertBoolToResultType(true); michael@0: } michael@0: michael@0: // This method modifies the length of the array, but may only be michael@0: // called when the new length is shorter than the old. It can michael@0: // therefore be called when elem_type has no default constructor, michael@0: // unlike SetLength. It removes elements from the array (see also michael@0: // RemoveElementsAt). michael@0: // @param newLen The desired length of this array. michael@0: void TruncateLength(size_type newLen) { michael@0: size_type oldLen = Length(); michael@0: NS_ABORT_IF_FALSE(newLen <= oldLen, michael@0: "caller should use SetLength instead"); michael@0: RemoveElementsAt(newLen, oldLen - newLen); michael@0: } michael@0: michael@0: // This method ensures that the array has length at least the given michael@0: // length. If the current length is shorter than the given length, michael@0: // then new elements will be constructed using elem_type's default michael@0: // constructor. michael@0: // @param minLen The desired minimum length of this array. michael@0: // @return True if the operation succeeded; false otherwise. michael@0: typename Alloc::ResultType EnsureLengthAtLeast(size_type minLen) { michael@0: size_type oldLen = Length(); michael@0: if (minLen > oldLen) { michael@0: return Alloc::ConvertBoolToResultType(!!InsertElementsAt(oldLen, minLen - oldLen)); michael@0: } michael@0: return Alloc::ConvertBoolToResultType(true); michael@0: } michael@0: michael@0: // This method inserts elements into the array, constructing michael@0: // them using elem_type's default constructor. michael@0: // @param index the place to insert the new elements. This must be no michael@0: // greater than the current length of the array. michael@0: // @param count the number of elements to insert michael@0: elem_type *InsertElementsAt(index_type index, size_type count) { michael@0: if (!base_type::InsertSlotsAt(index, count, sizeof(elem_type), MOZ_ALIGNOF(elem_type))) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // Initialize the extra array elements michael@0: elem_type *iter = Elements() + index, *end = iter + count; michael@0: for (; iter != end; ++iter) { michael@0: elem_traits::Construct(iter); michael@0: } michael@0: michael@0: return Elements() + index; michael@0: } michael@0: michael@0: // This method inserts elements into the array, constructing them michael@0: // elem_type's copy constructor (or whatever one-arg constructor michael@0: // happens to match the Item type). michael@0: // @param index the place to insert the new elements. This must be no michael@0: // greater than the current length of the array. michael@0: // @param count the number of elements to insert. michael@0: // @param item the value to use when constructing the new elements. michael@0: template michael@0: elem_type *InsertElementsAt(index_type index, size_type count, michael@0: const Item& item) { michael@0: if (!base_type::InsertSlotsAt(index, count, sizeof(elem_type), MOZ_ALIGNOF(elem_type))) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // Initialize the extra array elements michael@0: elem_type *iter = Elements() + index, *end = iter + count; michael@0: for (; iter != end; ++iter) { michael@0: elem_traits::Construct(iter, item); michael@0: } michael@0: michael@0: return Elements() + index; michael@0: } michael@0: michael@0: // This method may be called to minimize the memory used by this array. michael@0: void Compact() { michael@0: ShrinkCapacity(sizeof(elem_type), MOZ_ALIGNOF(elem_type)); michael@0: } michael@0: michael@0: // michael@0: // Sorting michael@0: // michael@0: michael@0: // This function is meant to be used with the NS_QuickSort function. It michael@0: // maps the callback API expected by NS_QuickSort to the Comparator API michael@0: // used by nsTArray_Impl. See nsTArray_Impl::Sort. michael@0: template michael@0: static int Compare(const void* e1, const void* e2, void *data) { michael@0: const Comparator* c = reinterpret_cast(data); michael@0: const elem_type* a = static_cast(e1); michael@0: const elem_type* b = static_cast(e2); michael@0: return c->LessThan(*a, *b) ? -1 : (c->Equals(*a, *b) ? 0 : 1); michael@0: } michael@0: michael@0: // This method sorts the elements of the array. It uses the LessThan michael@0: // method defined on the given Comparator object to collate elements. michael@0: // @param comp The Comparator used to collate elements. michael@0: template michael@0: void Sort(const Comparator& comp) { michael@0: NS_QuickSort(Elements(), Length(), sizeof(elem_type), michael@0: Compare, const_cast(&comp)); michael@0: } michael@0: michael@0: // A variation on the Sort method defined above that assumes that michael@0: // 'operator<' is defined for elem_type. michael@0: void Sort() { michael@0: Sort(nsDefaultComparator()); michael@0: } michael@0: michael@0: // michael@0: // Binary Heap michael@0: // michael@0: michael@0: // Sorts the array into a binary heap. michael@0: // @param comp The Comparator used to create the heap michael@0: template michael@0: void MakeHeap(const Comparator& comp) { michael@0: if (!Length()) { michael@0: return; michael@0: } michael@0: index_type index = (Length() - 1) / 2; michael@0: do { michael@0: SiftDown(index, comp); michael@0: } while (index--); michael@0: } michael@0: michael@0: // A variation on the MakeHeap method defined above. michael@0: void MakeHeap() { michael@0: MakeHeap(nsDefaultComparator()); michael@0: } michael@0: michael@0: // Adds an element to the heap michael@0: // @param item The item to add michael@0: // @param comp The Comparator used to sift-up the item michael@0: template michael@0: elem_type *PushHeap(const Item& item, const Comparator& comp) { michael@0: if (!base_type::InsertSlotsAt(Length(), 1, sizeof(elem_type), MOZ_ALIGNOF(elem_type))) { michael@0: return nullptr; michael@0: } michael@0: // Sift up the new node michael@0: elem_type *elem = Elements(); michael@0: index_type index = Length() - 1; michael@0: index_type parent_index = (index - 1) / 2; michael@0: while (index && comp.LessThan(elem[parent_index], item)) { michael@0: elem[index] = elem[parent_index]; michael@0: index = parent_index; michael@0: parent_index = (index - 1) / 2; michael@0: } michael@0: elem[index] = item; michael@0: return &elem[index]; michael@0: } michael@0: michael@0: // A variation on the PushHeap method defined above. michael@0: template michael@0: elem_type *PushHeap(const Item& item) { michael@0: return PushHeap(item, nsDefaultComparator()); michael@0: } michael@0: michael@0: // Delete the root of the heap and restore the heap michael@0: // @param comp The Comparator used to restore the heap michael@0: template michael@0: void PopHeap(const Comparator& comp) { michael@0: if (!Length()) { michael@0: return; michael@0: } michael@0: index_type last_index = Length() - 1; michael@0: elem_type *elem = Elements(); michael@0: elem[0] = elem[last_index]; michael@0: TruncateLength(last_index); michael@0: if (Length()) { michael@0: SiftDown(0, comp); michael@0: } michael@0: } michael@0: michael@0: // A variation on the PopHeap method defined above. michael@0: void PopHeap() { michael@0: PopHeap(nsDefaultComparator()); michael@0: } michael@0: michael@0: protected: michael@0: using base_type::Hdr; michael@0: using base_type::ShrinkCapacity; michael@0: michael@0: // This method invokes elem_type's destructor on a range of elements. michael@0: // @param start The index of the first element to destroy. michael@0: // @param count The number of elements to destroy. michael@0: void DestructRange(index_type start, size_type count) { michael@0: elem_type *iter = Elements() + start, *end = iter + count; michael@0: for (; iter != end; ++iter) { michael@0: elem_traits::Destruct(iter); michael@0: } michael@0: } michael@0: michael@0: // This method invokes elem_type's copy-constructor on a range of elements. michael@0: // @param start The index of the first element to construct. michael@0: // @param count The number of elements to construct. michael@0: // @param values The array of elements to copy. michael@0: template michael@0: void AssignRange(index_type start, size_type count, michael@0: const Item *values) { michael@0: AssignRangeAlgorithm::value, michael@0: mozilla::IsSame::value> michael@0: ::implementation(Elements(), start, count, values); michael@0: } michael@0: michael@0: // This method sifts an item down to its proper place in a binary heap michael@0: // @param index The index of the node to start sifting down from michael@0: // @param comp The Comparator used to sift down michael@0: template michael@0: void SiftDown(index_type index, const Comparator& comp) { michael@0: elem_type *elem = Elements(); michael@0: elem_type item = elem[index]; michael@0: index_type end = Length() - 1; michael@0: while ((index * 2) < end) { michael@0: const index_type left = (index * 2) + 1; michael@0: const index_type right = (index * 2) + 2; michael@0: const index_type parent_index = index; michael@0: if (comp.LessThan(item, elem[left])) { michael@0: if (left < end && michael@0: comp.LessThan(elem[left], elem[right])) { michael@0: index = right; michael@0: } else { michael@0: index = left; michael@0: } michael@0: } else if (left < end && michael@0: comp.LessThan(item, elem[right])) { michael@0: index = right; michael@0: } else { michael@0: break; michael@0: } michael@0: elem[parent_index] = elem[index]; michael@0: } michael@0: elem[index] = item; michael@0: } michael@0: }; michael@0: michael@0: template michael@0: inline void michael@0: ImplCycleCollectionUnlink(nsTArray_Impl& aField) michael@0: { michael@0: aField.Clear(); michael@0: } michael@0: michael@0: template michael@0: inline void michael@0: ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, michael@0: nsTArray_Impl& aField, michael@0: const char* aName, michael@0: uint32_t aFlags = 0) michael@0: { michael@0: aFlags |= CycleCollectionEdgeNameArrayFlag; michael@0: uint32_t length = aField.Length(); michael@0: for (uint32_t i = 0; i < length; ++i) { michael@0: ImplCycleCollectionTraverse(aCallback, aField[i], aName, aFlags); michael@0: } michael@0: } michael@0: michael@0: // michael@0: // nsTArray is an infallible vector class. See the comment at the top of this michael@0: // file for more details. michael@0: // michael@0: template michael@0: class nsTArray : public nsTArray_Impl michael@0: { michael@0: public: michael@0: typedef nsTArray_Impl base_type; michael@0: typedef nsTArray self_type; michael@0: typedef typename base_type::size_type size_type; michael@0: michael@0: nsTArray() {} michael@0: explicit nsTArray(size_type capacity) : base_type(capacity) {} michael@0: explicit nsTArray(const nsTArray& other) : base_type(other) {} michael@0: michael@0: template michael@0: explicit nsTArray(const nsTArray_Impl& other) : base_type(other) {} michael@0: }; michael@0: michael@0: // michael@0: // FallibleTArray is a fallible vector class. michael@0: // michael@0: template michael@0: class FallibleTArray : public nsTArray_Impl michael@0: { michael@0: public: michael@0: typedef nsTArray_Impl base_type; michael@0: typedef FallibleTArray self_type; michael@0: typedef typename base_type::size_type size_type; michael@0: michael@0: FallibleTArray() {} michael@0: explicit FallibleTArray(size_type capacity) : base_type(capacity) {} michael@0: explicit FallibleTArray(const FallibleTArray& other) : base_type(other) {} michael@0: michael@0: template michael@0: explicit FallibleTArray(const nsTArray_Impl& other) : base_type(other) {} michael@0: }; michael@0: michael@0: // michael@0: // nsAutoArrayBase is a base class for AutoFallibleTArray and nsAutoTArray. michael@0: // You shouldn't use this class directly. michael@0: // michael@0: template michael@0: class nsAutoArrayBase : public TArrayBase michael@0: { michael@0: public: michael@0: typedef nsAutoArrayBase self_type; michael@0: typedef TArrayBase base_type; michael@0: typedef typename base_type::Header Header; michael@0: typedef typename base_type::elem_type elem_type; michael@0: michael@0: template michael@0: self_type& operator=(const nsTArray_Impl& other) { michael@0: base_type::operator=(other); michael@0: return *this; michael@0: } michael@0: michael@0: protected: michael@0: nsAutoArrayBase() { michael@0: Init(); michael@0: } michael@0: michael@0: // We need this constructor because nsAutoTArray and friends all have michael@0: // implicit copy-constructors. If we don't have this method, those michael@0: // copy-constructors will call nsAutoArrayBase's implicit copy-constructor, michael@0: // which won't call Init() and set up the auto buffer! michael@0: nsAutoArrayBase(const self_type &aOther) { michael@0: Init(); michael@0: this->AppendElements(aOther); michael@0: } michael@0: michael@0: private: michael@0: // nsTArray_base casts itself as an nsAutoArrayBase in order to get a pointer michael@0: // to mAutoBuf. michael@0: template michael@0: friend class nsTArray_base; michael@0: michael@0: void Init() { michael@0: static_assert(MOZ_ALIGNOF(elem_type) <= 8, michael@0: "can't handle alignments greater than 8, " michael@0: "see nsTArray_base::UsesAutoArrayBuffer()"); michael@0: // Temporary work around for VS2012 RC compiler crash michael@0: Header** phdr = base_type::PtrToHdr(); michael@0: *phdr = reinterpret_cast(&mAutoBuf); michael@0: (*phdr)->mLength = 0; michael@0: (*phdr)->mCapacity = N; michael@0: (*phdr)->mIsAutoArray = 1; michael@0: michael@0: MOZ_ASSERT(base_type::GetAutoArrayBuffer(MOZ_ALIGNOF(elem_type)) == michael@0: reinterpret_cast(&mAutoBuf), michael@0: "GetAutoArrayBuffer needs to be fixed"); michael@0: } michael@0: michael@0: // Declare mAutoBuf aligned to the maximum of the header's alignment and michael@0: // elem_type's alignment. We need to use a union rather than michael@0: // MOZ_ALIGNED_DECL because GCC is picky about what goes into michael@0: // __attribute__((aligned(foo))). michael@0: union { michael@0: char mAutoBuf[sizeof(nsTArrayHeader) + N * sizeof(elem_type)]; michael@0: // Do the max operation inline to ensure that it is a compile-time constant. michael@0: mozilla::AlignedElem<(MOZ_ALIGNOF(Header) > MOZ_ALIGNOF(elem_type)) michael@0: ? MOZ_ALIGNOF(Header) : MOZ_ALIGNOF(elem_type)> mAlign; michael@0: }; michael@0: }; michael@0: michael@0: // michael@0: // nsAutoTArray is an infallible vector class with N elements of inline michael@0: // storage. If you try to store more than N elements inside an michael@0: // nsAutoTArray, we'll call malloc() and store them all on the heap. michael@0: // michael@0: // Note that you can cast an nsAutoTArray to michael@0: // |const AutoFallibleTArray&|. michael@0: // michael@0: template michael@0: class nsAutoTArray : public nsAutoArrayBase, N> michael@0: { michael@0: typedef nsAutoTArray self_type; michael@0: typedef nsAutoArrayBase, N> Base; michael@0: michael@0: public: michael@0: nsAutoTArray() {} michael@0: michael@0: template michael@0: explicit nsAutoTArray(const nsTArray_Impl& other) { michael@0: Base::AppendElements(other); michael@0: } michael@0: michael@0: operator const AutoFallibleTArray&() const { michael@0: return *reinterpret_cast*>(this); michael@0: } michael@0: }; michael@0: michael@0: // michael@0: // AutoFallibleTArray is a fallible vector class with N elements of michael@0: // inline storage. michael@0: // michael@0: template michael@0: class AutoFallibleTArray : public nsAutoArrayBase, N> michael@0: { michael@0: typedef AutoFallibleTArray self_type; michael@0: typedef nsAutoArrayBase, N> Base; michael@0: michael@0: public: michael@0: AutoFallibleTArray() {} michael@0: michael@0: template michael@0: explicit AutoFallibleTArray(const nsTArray_Impl& other) { michael@0: Base::AppendElements(other); michael@0: } michael@0: michael@0: operator const nsAutoTArray&() const { michael@0: return *reinterpret_cast*>(this); michael@0: } michael@0: }; michael@0: michael@0: // Assert that nsAutoTArray doesn't have any extra padding inside. michael@0: // michael@0: // It's important that the data stored in this auto array takes up a multiple of michael@0: // 8 bytes; e.g. nsAutoTArray wouldn't work. Since nsAutoTArray michael@0: // contains a pointer, its size must be a multiple of alignof(void*). (This is michael@0: // because any type may be placed into an array, and there's no padding between michael@0: // elements of an array.) The compiler pads the end of the structure to michael@0: // enforce this rule. michael@0: // michael@0: // If we used nsAutoTArray below, this assertion would fail on a michael@0: // 64-bit system, where the compiler inserts 4 bytes of padding at the end of michael@0: // the auto array to make its size a multiple of alignof(void*) == 8 bytes. michael@0: michael@0: static_assert(sizeof(nsAutoTArray) == michael@0: sizeof(void*) + sizeof(nsTArrayHeader) + sizeof(uint32_t) * 2, michael@0: "nsAutoTArray shouldn't contain any extra padding, " michael@0: "see the comment"); michael@0: michael@0: // Definitions of nsTArray_Impl methods michael@0: #include "nsTArray-inl.h" michael@0: michael@0: #endif // nsTArray_h__