/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is C++ array template. * * The Initial Developer of the Original Code is Google Inc. * Portions created by the Initial Developer are Copyright (C) 2005 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Darin Fisher * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef nsTArray_h__ #define nsTArray_h__ #include "prtypes.h" #include "nsQuickSort.h" #include "nsDebug.h" #include "nsTraceRefcnt.h" #include NEW_H // // 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. // class NS_COM_GLUE nsTArray_base { public: typedef PRUint32 size_type; typedef PRUint32 index_type; // A special value that is used to indicate an invalid or unknown index // into the array. enum { NoIndex = index_type(-1) }; // @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. PRBool 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() { return mHdr; } #endif protected: #ifndef NS_BUILD_REFCNT_LOGGING nsTArray_base() : mHdr(&sEmptyHdr) { } #else nsTArray_base(); ~nsTArray_base(); #endif // NS_BUILD_REFCNT_LOGGING // Resize the storage if necessary to achieve the requested capacity. // @param capacity The requested number of array elements. // @param elementSize The size of an array element. // @return False if insufficient memory is available; true otherwise. PRBool EnsureCapacity(size_type capacity, size_type elementSize); // Resize the storage to the minimum required amount. // @param elementSize The size of an array element. void ShrinkCapacity(size_type elementSize); // 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 elementSize The size of an array element. void ShiftData(index_type start, size_type oldLen, size_type newLen, size_type elementSize); // 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(PRUint32 n) { NS_ASSERTION(mHdr != &sEmptyHdr || n == 0, "bad data pointer"); 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. PRBool InsertSlotsAt(index_type index, size_type count, size_type elementSize); protected: // NOTE: This method isn't heavily optimized if either array is an // nsAutoTArray. PRBool SwapArrayElements(nsTArray_base& other, size_type elementSize); // Helper function for SwapArrayElements. Ensures that if the array // is an nsAutoTArray that it doesn't use the built-in buffer. PRBool EnsureNotUsingAutoArrayBuffer(size_type elemSize); // We prefix mData with a structure of this type. This is done to minimize // the size of the nsTArray object when it is empty. struct Header { PRUint32 mLength; PRUint32 mCapacity : 31; PRUint32 mIsAutoArray : 1; }; // Returns true if this nsTArray is an nsAutoTArray with a built-in buffer. PRBool IsAutoArray() { return mHdr->mIsAutoArray; } // Returns a Header for the built-in buffer of this nsAutoTArray. Header* GetAutoArrayBuffer() { NS_ASSERTION(IsAutoArray(), "Should be an auto array to call this"); return reinterpret_cast(&mHdr + 1); } // Returns true if this is an nsAutoTArray and it currently uses the // built-in buffer to store its elements. PRBool UsesAutoArrayBuffer() { return mHdr->mIsAutoArray && mHdr == GetAutoArrayBuffer(); } // This is not const since we may actually write to it. However we will // always write to it the same data that it already contains. See // IncrementLength static Header sEmptyHdr; // 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; }; // // 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) { 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(); } }; // This class exists because VC6 cannot handle static template functions. // Otherwise, the Compare method would be defined directly on nsTArray. template class nsQuickSortComparator { public: typedef E elem_type; // 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. See nsTArray::Sort. 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); } }; // The default comparator used by nsTArray template class nsDefaultComparator { public: PRBool Equals(const A& a, const B& b) const { return a == b; } PRBool LessThan(const A& a, const B& b) const { return a < b; } }; // // The templatized array class that dynamically resizes its storage as elements // are added. This class is designed to behave a bit like std::vector. // // The template parameter specifies the type of the elements (elem_type), and // has the following requirements: // // elem_type MUST define a copy-constructor. // elem_type MAY define operator< for sorting. // elem_type MAY define operator== for searching. // // 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. */ // PRBool Equals(const elem_type& a, const elem_type& b) const; // // /** @return True if (a < b); false otherwise. */ // PRBool LessThan(const elem_type& a, const elem_type& b) const; // }; // // The Equals method is used for searching, and the LessThan method is used // for sorting. // template class nsTArray : public nsTArray_base { public: typedef E elem_type; typedef nsTArray self_type; typedef nsTArrayElementTraits elem_traits; // // Finalization method // ~nsTArray() { Clear(); } // // Initialization methods // nsTArray() {} // Initialize this array and pre-allocate some number of elements. explicit nsTArray(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. nsTArray(const self_type& other) { AppendElements(other); } // 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. nsTArray& operator=(const self_type& other) { ReplaceElementsAt(0, Length(), other.Elements(), other.Length()); return *this; } // // 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(mHdr + 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(mHdr + 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) { NS_ASSERTION(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 { NS_ASSERTION(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); } // // Search methods // // 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 = iter + Length(); for (; iter != end; ++iter) { if (comp.Equals(*iter, item)) return 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 { if (start >= Length()) start = Length() - 1; const elem_type* end = Elements() - 1, *iter = end + start + 1; for (; iter != end; --iter) { if (comp.Equals(*iter, item)) return 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()); } // // Mutation methods // // 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 (!EnsureCapacity(Length() + arrayLen - count, sizeof(elem_type))) return nsnull; DestructRange(start, count); ShiftData(start, count, arrayLen, sizeof(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 *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& 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 (!EnsureCapacity(Length() + 1, sizeof(elem_type))) return nsnull; ShiftData(index, 0, 1, sizeof(elem_type)); elem_type *elem = Elements() + index; elem_traits::Construct(elem); return elem; } // 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 (!EnsureCapacity(Length() + arrayLen, sizeof(elem_type))) return nsnull; index_type len = Length(); AssignRange(len, arrayLen, array); IncrementLength(arrayLen); return Elements() + len; } // A variation on the AppendElements method defined above. template elem_type *AppendElements(const nsTArray& 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 (!EnsureCapacity(Length() + count, sizeof(elem_type))) return nsnull; elem_type *elems = Elements() + Length(); size_type i; for (i = 0; i < count; ++i) { elem_traits::Construct(elems + i); } 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); } // 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) { NS_ASSERTION(count == 0 || start < Length(), "Invalid start index"); NS_ASSERTION(start + count <= Length(), "Invalid length"); DestructRange(start, count); ShiftData(start, count, 0, sizeof(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 PR_TRUE if the element was found template PRBool RemoveElement(const Item& item, const Comparator& comp) { index_type i = IndexOf(item, 0, comp); if (i == NoIndex) return PR_FALSE; RemoveElementAt(i); return PR_TRUE; } // A variation on the RemoveElement method defined above that assumes // that 'operator==' is defined for elem_type. template PRBool RemoveElement(const Item& item) { return RemoveElement(item, nsDefaultComparator()); } // This method causes the elements contained in this array and the given // array to be swapped. // NOTE: This method isn't heavily optimized if either array is an // nsAutoTArray. PRBool SwapElements(self_type& other) { return SwapArrayElements(other, sizeof(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 PRBool SetCapacity(size_type capacity) { return 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. PRBool SetLength(size_type newLen) { size_type oldLen = Length(); if (newLen > oldLen) { return InsertElementsAt(oldLen, newLen - oldLen) != nsnull; } RemoveElementsAt(newLen, oldLen - newLen); return PR_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 (!nsTArray_base::InsertSlotsAt(index, count, sizeof(elem_type))) { return nsnull; } // 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 (!nsTArray_base::InsertSlotsAt(index, count, sizeof(elem_type))) { return nsnull; } // 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)); } // // Sorting // // This method sorts the elements of the array. It uses the LessThan // method defined on the given Comparator object to collate elements. // @param c The Comparator to used to collate elements. template void Sort(const Comparator& comp) { NS_QuickSort(Elements(), Length(), sizeof(elem_type), nsQuickSortComparator::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()); } protected: // 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) { elem_type *iter = Elements() + start, *end = iter + count; for (; iter != end; ++iter, ++values) { elem_traits::Construct(iter, *values); } } }; template class nsAutoTArray : public nsTArray { public: typedef nsTArray base_type; typedef typename base_type::Header Header; typedef typename base_type::elem_type elem_type; nsAutoTArray() { base_type::mHdr = reinterpret_cast(&mAutoBuf); base_type::mHdr->mLength = 0; base_type::mHdr->mCapacity = N; base_type::mHdr->mIsAutoArray = 1; NS_ASSERTION(base_type::GetAutoArrayBuffer() == reinterpret_cast(&mAutoBuf), "GetAutoArrayBuffer needs to be fixed"); } protected: char mAutoBuf[sizeof(Header) + N * sizeof(elem_type)]; }; #endif // nsTArray_h__