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