//====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======// // // Purpose: // // $NoKeywords: $ // // A growable array class that maintains a free list and keeps elements // in the same location //=============================================================================// #ifndef UTLVECTOR_H #define UTLVECTOR_H #ifdef _WIN32 #pragma once #endif #include "tier1/utlmemory.h" #include "tier1/utlblockmemory.h" #include "tier1/strtools.h" #define FOR_EACH_VEC( vecName, iteratorName ) \ for ( int iteratorName = 0; (vecName).IsUtlVector && iteratorName < (vecName).Count(); iteratorName++ ) #define FOR_EACH_VEC_BACK( vecName, iteratorName ) \ for ( int iteratorName = (vecName).Count()-1; (vecName).IsUtlVector && iteratorName >= 0; iteratorName-- ) // UtlVector derives from this so we can do the type check above struct base_vector_t { public: enum { IsUtlVector = true }; // Used to match this at compiletime }; //----------------------------------------------------------------------------- // The CUtlVector class: // A growable array class which doubles in size by default. // It will always keep all elements consecutive in memory, and may move the // elements around in memory (via a PvRealloc) when elements are inserted or // removed. Clients should therefore refer to the elements of the vector // by index (they should *never* maintain pointers to elements in the vector). //----------------------------------------------------------------------------- template< class T, class A = CUtlMemory > class CUtlVector : public base_vector_t { typedef A CAllocator; public: typedef T ElemType_t; typedef T* iterator; typedef const T* const_iterator; // Set the growth policy and initial capacity. Count will always be zero. This is different from std::vector // where the constructor sets count as well as capacity. // growSize of zero implies the default growth pattern which is exponential. explicit CUtlVector(int growSize = 0, int initialCapacity = 0); // Initialize with separately allocated buffer, setting the capacity and count. // The container will not be growable. CUtlVector(T* pMemory, int initialCapacity, int initialCount = 0); ~CUtlVector(); // Copy the array. CUtlVector& operator=(const CUtlVector& other); // element access T& operator[](int i); const T& operator[](int i) const; T& Element(int i); const T& Element(int i) const; T& Head(); const T& Head() const; T& Tail(); const T& Tail() const; // STL compatible member functions. These allow easier use of std::sort // and they are forward compatible with the C++ 11 range-based for loops. iterator begin() { return Base(); } const_iterator begin() const { return Base(); } iterator end() { return Base() + Count(); } const_iterator end() const { return Base() + Count(); } // Gets the base address (can change when adding elements!) T* Base() { return m_Memory.Base(); } const T* Base() const { return m_Memory.Base(); } // Returns the number of elements in the vector int Count() const; /// are there no elements? For compatibility with lists. inline bool IsEmpty(void) const { return (Count() == 0); } // Is element index valid? bool IsValidIndex(int i) const; static int InvalidIndex(); // Adds an element, uses default constructor int AddToHead(); int AddToTail(); T* AddToTailGetPtr(); int InsertBefore(int elem); int InsertAfter(int elem); // Adds an element, uses copy constructor int AddToHead(const T& src); int AddToTail(const T& src); int InsertBefore(int elem, const T& src); int InsertAfter(int elem, const T& src); // Adds multiple elements, uses default constructor int AddMultipleToHead(int num); int AddMultipleToTail(int num); int AddMultipleToTail(int num, const T* pToCopy); int InsertMultipleBefore(int elem, int num); int InsertMultipleBefore(int elem, int num, const T* pToCopy); int InsertMultipleAfter(int elem, int num); // Calls RemoveAll() then AddMultipleToTail. // SetSize is a synonym for SetCount void SetSize(int size); // SetCount deletes the previous contents of the container and sets the // container to have this many elements. // Use GetCount to retrieve the current count. void SetCount(int count); void SetCountNonDestructively(int count); //sets count by adding or removing elements to tail TODO: This should probably be the default behavior for SetCount // Calls SetSize and copies each element. void CopyArray(const T* pArray, int size); // Fast swap void Swap(CUtlVector< T, A >& vec); // Add the specified array to the tail. int AddVectorToTail(CUtlVector const& src); // Finds an element (element needs operator== defined) int Find(const T& src) const; void FillWithValue(const T& src); bool HasElement(const T& src) const; // Makes sure we have enough memory allocated to store a requested # of elements // Use NumAllocated() to retrieve the current capacity. void EnsureCapacity(int num); // Makes sure we have at least this many elements // Use GetCount to retrieve the current count. void EnsureCount(int num); // Element removal void FastRemove(int elem); // doesn't preserve order void Remove(int elem); // preserves order, shifts elements bool FindAndRemove(const T& src); // removes first occurrence of src, preserves order, shifts elements bool FindAndFastRemove(const T& src); // removes first occurrence of src, doesn't preserve order void RemoveMultiple(int elem, int num); // preserves order, shifts elements void RemoveMultipleFromHead(int num); // removes num elements from tail void RemoveMultipleFromTail(int num); // removes num elements from tail void RemoveAll(); // doesn't deallocate memory // Memory deallocation void Purge(); // Purges the list and calls delete on each element in it. void PurgeAndDeleteElements(); // Compacts the vector to the number of elements actually in use void Compact(); // Set the size by which it grows when it needs to allocate more memory. void SetGrowSize(int size) { m_Memory.SetGrowSize(size); } int NumAllocated() const; // Only use this if you really know what you're doing! void Sort(int(__cdecl* pfnCompare)(const T*, const T*)); // Call this to quickly sort non-contiguously allocated vectors void InPlaceQuickSort(int(__cdecl* pfnCompare)(const T*, const T*)); // reverse the order of elements void Reverse(); #ifdef DBGFLAG_VALIDATE void Validate(CValidator& validator, char* pchName); // Validate our internal structures #endif // DBGFLAG_VALIDATE int SortedFindLessOrEqual(const T& search, bool(__cdecl* pfnLessFunc)(const T& src1, const T& src2, void* pCtx), void* pLessContext) const; int SortedInsert(const T& src, bool(__cdecl* pfnLessFunc)(const T& src1, const T& src2, void* pCtx), void* pLessContext); /// sort using std:: and expecting a "<" function to be defined for the type void Sort(void); /// sort using std:: with a predicate. e.g. [] -> bool ( T &a, T &b ) { return a < b; } template void SortPredicate(F&& predicate); protected: // Can't copy this unless we explicitly do it! CUtlVector(CUtlVector const& vec) { Assert(0); } // Grows the vector void GrowVector(int num = 1); // Shifts elements.... void ShiftElementsRight(int elem, int num = 1); void ShiftElementsLeft(int elem, int num = 1); CAllocator m_Memory; int m_Size; #ifndef _X360 // For easier access to the elements through the debugger // it's in release builds so this can be used in libraries correctly // Unused in r1/r2/r5? //T* m_pElements; //inline void ResetDbgInfo() //{ // m_pElements = Base(); //} #else inline void ResetDbgInfo() {} #endif private: void InPlaceQuickSort_r(int(__cdecl* pfnCompare)(const T*, const T*), int nLeft, int nRight); }; // this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice template < class T > class CUtlBlockVector : public CUtlVector< T, CUtlBlockMemory< T, int > > { public: explicit CUtlBlockVector(int growSize = 0, int initSize = 0) : CUtlVector< T, CUtlBlockMemory< T, int > >(growSize, initSize) {} }; //----------------------------------------------------------------------------- // The CUtlVectorMT class: // An array class with spurious mutex protection. Nothing is actually protected // unless you call Lock and Unlock. Also, the Mutex_t is actually not a type // but a member which probably isn't used. //----------------------------------------------------------------------------- template< class BASE_UTLVECTOR, class MUTEX_TYPE = CThreadFastMutex > class CUtlVectorMT : public BASE_UTLVECTOR, public MUTEX_TYPE { typedef BASE_UTLVECTOR BaseClass; public: // MUTEX_TYPE Mutex_t; // constructor, destructor explicit CUtlVectorMT(int growSize = 0, int initSize = 0) : BaseClass(growSize, initSize) {} CUtlVectorMT(typename BaseClass::ElemType_t* pMemory, int numElements) : BaseClass(pMemory, numElements) {} }; //----------------------------------------------------------------------------- // The CUtlVectorFixed class: // A array class with a fixed allocation scheme //----------------------------------------------------------------------------- template< class T, size_t MAX_SIZE > class CUtlVectorFixed : public CUtlVector< T, CUtlMemoryFixed > { typedef CUtlVector< T, CUtlMemoryFixed > BaseClass; public: // constructor, destructor explicit CUtlVectorFixed(int growSize = 0, int initSize = 0) : BaseClass(growSize, initSize) {} CUtlVectorFixed(T* pMemory, int numElements) : BaseClass(pMemory, numElements) {} }; //----------------------------------------------------------------------------- // The CUtlVectorFixedGrowable class: // A array class with a fixed allocation scheme backed by a dynamic one //----------------------------------------------------------------------------- template< class T, size_t MAX_SIZE > class CUtlVectorFixedGrowable : public CUtlVector< T, CUtlMemoryFixedGrowable > { typedef CUtlVector< T, CUtlMemoryFixedGrowable > BaseClass; public: // constructor, destructor explicit CUtlVectorFixedGrowable(int growSize = 0) : BaseClass(growSize, MAX_SIZE) {} }; // A fixed growable vector that's castable to CUtlVector template< class T, size_t FIXED_SIZE > class CUtlVectorFixedGrowableCompat : public CUtlVector< T > { typedef CUtlVector< T > BaseClass; public: // constructor, destructor CUtlVectorFixedGrowableCompat(int growSize = 0) : BaseClass(nullptr, FIXED_SIZE, growSize) { this->m_Memory.m_pMemory = m_FixedMemory.Base(); } AlignedByteArray_t< FIXED_SIZE, T > m_FixedMemory; }; //----------------------------------------------------------------------------- // The CUtlVectorConservative class: // A array class with a conservative allocation scheme //----------------------------------------------------------------------------- template< class T > class CUtlVectorConservative : public CUtlVector< T, CUtlMemoryConservative > { typedef CUtlVector< T, CUtlMemoryConservative > BaseClass; public: // constructor, destructor explicit CUtlVectorConservative(int growSize = 0, int initSize = 0) : BaseClass(growSize, initSize) {} CUtlVectorConservative(T* pMemory, int numElements) : BaseClass(pMemory, numElements) {} }; //----------------------------------------------------------------------------- // The CUtlVectorUltra Conservative class: // A array class with a very conservative allocation scheme, with customizable allocator // Especially useful if you have a lot of vectors that are sparse, or if you're // carefully packing holders of vectors //----------------------------------------------------------------------------- #pragma warning(push) #pragma warning(disable : 4200) // warning C4200: nonstandard extension used : zero-sized array in struct/union #pragma warning(disable : 4815 ) // warning C4815: 'staticData' : zero-sized array in stack object will have no elements class CUtlVectorUltraConservativeAllocator { public: static void* Alloc(size_t nSize) { return MemAllocSingleton()->Alloc(nSize); } static void* Realloc(void* pMem, size_t nSize) { return MemAllocSingleton()->Realloc(pMem, nSize); } static void Free(void* pMem) { MemAllocSingleton()->Free(pMem); } static size_t GetSize(void* pMem) { return MemAllocSingleton()->GetSize(pMem); } }; template class CUtlVectorUltraConservative : private A { public: // Don't inherit from base_vector_t because multiple-inheritance increases // class size! enum { IsUtlVector = true }; // Used to match this at compiletime CUtlVectorUltraConservative() { m_pData = StaticData(); } ~CUtlVectorUltraConservative() { RemoveAll(); } int Count() const { return m_pData->m_Size; } static int InvalidIndex() { return -1; } inline bool IsValidIndex(int i) const { return (i >= 0) && (i < Count()); } T& operator[](int i) { Assert(IsValidIndex(i)); return m_pData->m_Elements[i]; } const T& operator[](int i) const { Assert(IsValidIndex(i)); return m_pData->m_Elements[i]; } T& Element(int i) { Assert(IsValidIndex(i)); return m_pData->m_Elements[i]; } const T& Element(int i) const { Assert(IsValidIndex(i)); return m_pData->m_Elements[i]; } void EnsureCapacity(int num) { int nCurCount = Count(); if (num <= nCurCount) { return; } if (m_pData == StaticData()) { m_pData = (Data_t*)A::Alloc(sizeof(Data_t) + (num * sizeof(T))); m_pData->m_Size = 0; } else { int nNeeded = sizeof(Data_t) + (num * sizeof(T)); int nHave = A::GetSize(m_pData); if (nNeeded > nHave) { m_pData = (Data_t*)A::Realloc(m_pData, nNeeded); } } } int AddToTail(const T& src) { int iNew = Count(); EnsureCapacity(Count() + 1); m_pData->m_Elements[iNew] = src; m_pData->m_Size++; return iNew; } T* AddToTailGetPtr() { return &Element(AddToTail()); } void RemoveAll() { if (Count()) { for (int i = m_pData->m_Size; --i >= 0; ) { // Global scope to resolve conflict with Scaleform 4.0 ::Destruct(&m_pData->m_Elements[i]); } } if (m_pData != StaticData()) { A::Free(m_pData); m_pData = StaticData(); } } void PurgeAndDeleteElements() { if (m_pData != StaticData()) { for (int i = 0; i < m_pData->m_Size; i++) { delete Element(i); } RemoveAll(); } } void FastRemove(int elem) { Assert(IsValidIndex(elem)); // Global scope to resolve conflict with Scaleform 4.0 ::Destruct(&Element(elem)); if (Count() > 0) { if (elem != m_pData->m_Size - 1) memcpy(&Element(elem), &Element(m_pData->m_Size - 1), sizeof(T)); --m_pData->m_Size; } if (!m_pData->m_Size) { A::Free(m_pData); m_pData = StaticData(); } } void Remove(int elem) { // Global scope to resolve conflict with Scaleform 4.0 ::Destruct(&Element(elem)); ShiftElementsLeft(elem); --m_pData->m_Size; if (!m_pData->m_Size) { A::Free(m_pData); m_pData = StaticData(); } } int Find(const T& src) const { int nCount = Count(); for (int i = 0; i < nCount; ++i) { if (Element(i) == src) return i; } return -1; } bool FindAndRemove(const T& src) { int elem = Find(src); if (elem != -1) { Remove(elem); return true; } return false; } bool FindAndFastRemove(const T& src) { int elem = Find(src); if (elem != -1) { FastRemove(elem); return true; } return false; } bool DebugCompileError_ANonVectorIsUsedInThe_FOR_EACH_VEC_Macro(void) const { return true; } struct Data_t { int m_Size; T m_Elements[]; }; Data_t* m_pData; private: void ShiftElementsLeft(int elem, int num = 1) { int Size = Count(); Assert(IsValidIndex(elem) || (Size == 0) || (num == 0)); int numToMove = Size - elem - num; if ((numToMove > 0) && (num > 0)) { memmove(&Element(elem), &Element(elem + num), numToMove * sizeof(T)); #ifdef _DEBUG memset(&Element(Size - num), 0xDD, num * sizeof(T)); #endif } } static Data_t* StaticData() { static Data_t staticData; Assert(staticData.m_Size == 0); return &staticData; } }; #pragma warning(pop) // Make sure nobody adds multiple inheritance and makes this class bigger. COMPILE_TIME_ASSERT(sizeof(CUtlVectorUltraConservative) == sizeof(void*)); //----------------------------------------------------------------------------- // The CCopyableUtlVector class: // A array class that allows copy construction (so you can nest a CUtlVector inside of another one of our containers) // WARNING - this class lets you copy construct which can be an expensive operation if you don't carefully control when it happens // Only use this when nesting a CUtlVector() inside of another one of our container classes (i.e a CUtlMap) //----------------------------------------------------------------------------- template< class T > class CCopyableUtlVector : public CUtlVector< T, CUtlMemory > { typedef CUtlVector< T, CUtlMemory > BaseClass; public: explicit CCopyableUtlVector(int growSize = 0, int initSize = 0) : BaseClass(growSize, initSize) {} CCopyableUtlVector(T* pMemory, int numElements) : BaseClass(pMemory, numElements) {} virtual ~CCopyableUtlVector() {} CCopyableUtlVector(CCopyableUtlVector const& vec) { this->CopyArray(vec.Base(), vec.Count()); } CCopyableUtlVector(CUtlVector const& vec) { this->CopyArray(vec.Base(), vec.Count()); } }; //----------------------------------------------------------------------------- // The CCopyableUtlVector class: // A array class that allows copy construction (so you can nest a CUtlVector inside of another one of our containers) // WARNING - this class lets you copy construct which can be an expensive operation if you don't carefully control when it happens // Only use this when nesting a CUtlVector() inside of another one of our container classes (i.e a CUtlMap) //----------------------------------------------------------------------------- template< class T, size_t MAX_SIZE > class CCopyableUtlVectorFixed : public CUtlVectorFixed< T, MAX_SIZE > { typedef CUtlVectorFixed< T, MAX_SIZE > BaseClass; public: explicit CCopyableUtlVectorFixed(int growSize = 0, int initSize = 0) : BaseClass(growSize, initSize) {} CCopyableUtlVectorFixed(T* pMemory, int numElements) : BaseClass(pMemory, numElements) {} virtual ~CCopyableUtlVectorFixed() {} CCopyableUtlVectorFixed(CCopyableUtlVectorFixed const& vec) { this->CopyArray(vec.Base(), vec.Count()); } }; // TODO (Ilya): It seems like all the functions in CUtlVector are simple enough that they should be inlined. //----------------------------------------------------------------------------- // constructor, destructor //----------------------------------------------------------------------------- template< typename T, class A > inline CUtlVector::CUtlVector(int growSize, int initSize) : m_Memory(growSize, initSize), m_Size(0) { //ResetDbgInfo(); } template< typename T, class A > inline CUtlVector::CUtlVector(T* pMemory, int allocationCount, int numElements) : m_Memory(pMemory, allocationCount), m_Size(numElements) { //ResetDbgInfo(); } template< typename T, class A > inline CUtlVector::~CUtlVector() { Purge(); } template< typename T, class A > inline CUtlVector& CUtlVector::operator=(const CUtlVector& other) { int nCount = other.Count(); SetSize(nCount); for (int i = 0; i < nCount; i++) { (*this)[i] = other[i]; } return *this; } //----------------------------------------------------------------------------- // element access //----------------------------------------------------------------------------- template< typename T, class A > inline T& CUtlVector::operator[](int i) { Assert(i < m_Size); return m_Memory[i]; } template< typename T, class A > inline const T& CUtlVector::operator[](int i) const { Assert(i < m_Size); return m_Memory[i]; } template< typename T, class A > inline T& CUtlVector::Element(int i) { Assert(i < m_Size); return m_Memory[i]; } template< typename T, class A > inline const T& CUtlVector::Element(int i) const { Assert(i < m_Size); return m_Memory[i]; } template< typename T, class A > inline T& CUtlVector::Head() { Assert(m_Size > 0); return m_Memory[0]; } template< typename T, class A > inline const T& CUtlVector::Head() const { Assert(m_Size > 0); return m_Memory[0]; } template< typename T, class A > inline T& CUtlVector::Tail() { Assert(m_Size > 0); return m_Memory[m_Size - 1]; } template< typename T, class A > inline const T& CUtlVector::Tail() const { Assert(m_Size > 0); return m_Memory[m_Size - 1]; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Reverse - reverse the order of elements, akin to std::vector<>::reverse() //----------------------------------------------------------------------------- template< typename T, class A > void CUtlVector::Reverse() { for (int i = 0; i < m_Size / 2; i++) { V_swap(m_Memory[i], m_Memory[m_Size - 1 - i]); #if defined( UTLVECTOR_TRACK_STACKS ) if (bTrackingEnabled) { V_swap(m_pElementStackStatsIndices[i], m_pElementStackStatsIndices[m_Size - 1 - i]); } #endif } } // Count //----------------------------------------------------------------------------- template< typename T, class A > inline int CUtlVector::Count() const { return m_Size; } //----------------------------------------------------------------------------- // Is element index valid? //----------------------------------------------------------------------------- template< typename T, class A > inline bool CUtlVector::IsValidIndex(int i) const { return (i >= 0) && (i < m_Size); } //----------------------------------------------------------------------------- // Returns in invalid index //----------------------------------------------------------------------------- template< typename T, class A > inline int CUtlVector::InvalidIndex() { return -1; } //----------------------------------------------------------------------------- // Grows the vector //----------------------------------------------------------------------------- template< typename T, class A > void CUtlVector::GrowVector(int num) { if (m_Size + num > m_Memory.NumAllocated()) { MEM_ALLOC_CREDIT_CLASS(); m_Memory.Grow(m_Size + num - m_Memory.NumAllocated()); } m_Size += num; //ResetDbgInfo(); } //----------------------------------------------------------------------------- // finds a particular element // You must sort the list before using or your results will be wrong //----------------------------------------------------------------------------- template< typename T, class A > int CUtlVector::SortedFindLessOrEqual(const T& search, bool(__cdecl* pfnLessFunc)(const T& src1, const T& src2, void* pCtx), void* pLessContext) const { int start = 0, end = Count() - 1; while (start <= end) { int mid = (start + end) >> 1; if (pfnLessFunc(Element(mid), search, pLessContext)) { start = mid + 1; } else if (pfnLessFunc(search, Element(mid), pLessContext)) { end = mid - 1; } else { return mid; } } return end; } template< typename T, class A > int CUtlVector::SortedInsert(const T& src, bool(__cdecl* pfnLessFunc)(const T& src1, const T& src2, void* pCtx), void* pLessContext) { int pos = SortedFindLessOrEqual(src, pfnLessFunc, pLessContext) + 1; GrowVector(); ShiftElementsRight(pos); CopyConstruct(&Element(pos), src); //UTLVECTOR_STACK_STATS_ALLOCATED_SINGLE( pos ); return pos; } //----------------------------------------------------------------------------- // Sorts the vector //----------------------------------------------------------------------------- template< typename T, class A > void CUtlVector::Sort(int(__cdecl* pfnCompare)(const T*, const T*)) { typedef int(__cdecl* QSortCompareFunc_t)(const void*, const void*); if (Count() <= 1) return; if (Base()) { qsort(Base(), Count(), sizeof(T), (QSortCompareFunc_t)(pfnCompare)); } else { Assert(0); // this path is untested // if you want to sort vectors that use a non-sequential memory allocator, // you'll probably want to patch in a quicksort algorithm here // I just threw in this bubble sort to have something just in case... for (int i = m_Size - 1; i >= 0; --i) { for (int j = 1; j <= i; ++j) { if (pfnCompare(&Element(j - 1), &Element(j)) < 0) { V_swap(Element(j - 1), Element(j)); } } } } } //---------------------------------------------------------------------------------------------- // Private function that does the in-place quicksort for non-contiguously allocated vectors. //---------------------------------------------------------------------------------------------- template< typename T, class A > void CUtlVector::InPlaceQuickSort_r(int(__cdecl* pfnCompare)(const T*, const T*), int nLeft, int nRight) { int nPivot; int nLeftIdx = nLeft; int nRightIdx = nRight; if (nRight - nLeft > 0) { nPivot = (nLeft + nRight) / 2; while ((nLeftIdx <= nPivot) && (nRightIdx >= nPivot)) { while ((pfnCompare(&Element(nLeftIdx), &Element(nPivot)) < 0) && (nLeftIdx <= nPivot)) { nLeftIdx++; } while ((pfnCompare(&Element(nRightIdx), &Element(nPivot)) > 0) && (nRightIdx >= nPivot)) { nRightIdx--; } V_swap(Element(nLeftIdx), Element(nRightIdx)); nLeftIdx++; nRightIdx--; if ((nLeftIdx - 1) == nPivot) { nPivot = nRightIdx = nRightIdx + 1; } else if (nRightIdx + 1 == nPivot) { nPivot = nLeftIdx = nLeftIdx - 1; } } InPlaceQuickSort_r(pfnCompare, nLeft, nPivot - 1); InPlaceQuickSort_r(pfnCompare, nPivot + 1, nRight); } } //---------------------------------------------------------------------------------------------- // Call this to quickly sort non-contiguously allocated vectors. Sort uses a slower bubble sort. //---------------------------------------------------------------------------------------------- template< typename T, class A > void CUtlVector::InPlaceQuickSort(int(__cdecl* pfnCompare)(const T*, const T*)) { InPlaceQuickSort_r(pfnCompare, 0, Count() - 1); } template< typename T, class A > void CUtlVector::Sort(void) { //STACK STATS TODO: Do we care about allocation tracking precision enough to match element origins across a sort? std::sort(Base(), Base() + Count()); } template< typename T, class A > template void CUtlVector::SortPredicate(F&& predicate) { std::sort(Base(), Base() + Count(), predicate); } //----------------------------------------------------------------------------- // Makes sure we have enough memory allocated to store a requested # of elements //----------------------------------------------------------------------------- template< typename T, class A > void CUtlVector::EnsureCapacity(int num) { MEM_ALLOC_CREDIT_CLASS(); m_Memory.EnsureCapacity(num); //ResetDbgInfo(); } //----------------------------------------------------------------------------- // Makes sure we have at least this many elements //----------------------------------------------------------------------------- template< typename T, class A > void CUtlVector::EnsureCount(int num) { if (Count() < num) { AddMultipleToTail(num - Count()); } } //----------------------------------------------------------------------------- // Shifts elements //----------------------------------------------------------------------------- template< typename T, class A > void CUtlVector::ShiftElementsRight(int elem, int num) { Assert(IsValidIndex(elem) || (m_Size == 0) || (num == 0)); int numToMove = m_Size - elem - num; if ((numToMove > 0) && (num > 0)) memmove(&Element(elem + num), &Element(elem), numToMove * sizeof(T)); } template< typename T, class A > void CUtlVector::ShiftElementsLeft(int elem, int num) { Assert(IsValidIndex(elem) || (m_Size == 0) || (num == 0)); int numToMove = m_Size - elem - num; if ((numToMove > 0) && (num > 0)) { memmove(&Element(elem), &Element(elem + num), numToMove * sizeof(T)); #ifdef _DEBUG memset(&Element(m_Size - num), 0xDD, num * sizeof(T)); #endif } } //----------------------------------------------------------------------------- // Adds an element, uses default constructor //----------------------------------------------------------------------------- template< typename T, class A > inline int CUtlVector::AddToHead() { return InsertBefore(0); } template< typename T, class A > inline int CUtlVector::AddToTail() { return InsertBefore(m_Size); } template< typename T, class A > inline T* CUtlVector::AddToTailGetPtr() { return &Element(AddToTail()); } template< typename T, class A > inline int CUtlVector::InsertAfter(int elem) { return InsertBefore(elem + 1); } template< typename T, class A > int CUtlVector::InsertBefore(int elem) { // Can insert at the end Assert((elem == Count()) || IsValidIndex(elem)); GrowVector(); ShiftElementsRight(elem); Construct(&Element(elem)); return elem; } //----------------------------------------------------------------------------- // Adds an element, uses copy constructor //----------------------------------------------------------------------------- template< typename T, class A > inline int CUtlVector::AddToHead(const T& src) { // Can't insert something that's in the list... reallocation may hose us Assert((Base() == NULL) || (&src < Base()) || (&src >= (Base() + Count()))); return InsertBefore(0, src); } template< typename T, class A > inline int CUtlVector::AddToTail(const T& src) { // Can't insert something that's in the list... reallocation may hose us Assert((Base() == NULL) || (&src < Base()) || (&src >= (Base() + Count()))); return InsertBefore(m_Size, src); } template< typename T, class A > inline int CUtlVector::InsertAfter(int elem, const T& src) { // Can't insert something that's in the list... reallocation may hose us Assert((Base() == NULL) || (&src < Base()) || (&src >= (Base() + Count()))); return InsertBefore(elem + 1, src); } template< typename T, class A > int CUtlVector::InsertBefore(int elem, const T& src) { // Can't insert something that's in the list... reallocation may hose us Assert((Base() == NULL) || (&src < Base()) || (&src >= (Base() + Count()))); // Can insert at the end Assert((elem == Count()) || IsValidIndex(elem)); GrowVector(); ShiftElementsRight(elem); CopyConstruct(&Element(elem), src); return elem; } //----------------------------------------------------------------------------- // Adds multiple elements, uses default constructor //----------------------------------------------------------------------------- template< typename T, class A > inline int CUtlVector::AddMultipleToHead(int num) { return InsertMultipleBefore(0, num); } template< typename T, class A > inline int CUtlVector::AddMultipleToTail(int num) { return InsertMultipleBefore(m_Size, num); } template< typename T, class A > inline int CUtlVector::AddMultipleToTail(int num, const T* pToCopy) { // Can't insert something that's in the list... reallocation may hose us Assert((Base() == NULL) || !pToCopy || (pToCopy + num <= Base()) || (pToCopy >= (Base() + Count()))); return InsertMultipleBefore(m_Size, num, pToCopy); } template< typename T, class A > int CUtlVector::InsertMultipleAfter(int elem, int num) { return InsertMultipleBefore(elem + 1, num); } template< typename T, class A > void CUtlVector::SetCount(int count) { RemoveAll(); AddMultipleToTail(count); } template< typename T, class A > inline void CUtlVector::SetSize(int size) { SetCount(size); } template< typename T, class A > void CUtlVector::SetCountNonDestructively(int count) { int delta = count - m_Size; if (delta > 0) AddMultipleToTail(delta); else if (delta < 0) RemoveMultipleFromTail(-delta); } template< typename T, class A > void CUtlVector::CopyArray(const T* pArray, int size) { // Can't insert something that's in the list... reallocation may hose us Assert((Base() == NULL) || !pArray || (Base() >= (pArray + size)) || (pArray >= (Base() + Count()))); SetSize(size); for (int i = 0; i < size; i++) { (*this)[i] = pArray[i]; } } template< typename T, class A > void CUtlVector::Swap(CUtlVector< T, A >& vec) { m_Memory.Swap(vec.m_Memory); V_swap(m_Size, vec.m_Size); #ifndef _X360 //V_swap(m_pElements, vec.m_pElements); #endif } template< typename T, class A > int CUtlVector::AddVectorToTail(CUtlVector const& src) { Assert(&src != this); int base = Count(); // Make space. int nSrcCount = src.Count(); EnsureCapacity(base + nSrcCount); // Copy the elements. m_Size += nSrcCount; for (int i = 0; i < nSrcCount; i++) { CopyConstruct(&Element(base + i), src[i]); } return base; } template< typename T, class A > inline int CUtlVector::InsertMultipleBefore(int elem, int num) { if (num == 0) return elem; // Can insert at the end Assert((elem == Count()) || IsValidIndex(elem)); GrowVector(num); ShiftElementsRight(elem, num); // Invoke default constructors for (int i = 0; i < num; ++i) { Construct(&Element(elem + i)); } return elem; } template< typename T, class A > inline int CUtlVector::InsertMultipleBefore(int elem, int num, const T* pToInsert) { if (num == 0) return elem; // Can insert at the end Assert((elem == Count()) || IsValidIndex(elem)); GrowVector(num); ShiftElementsRight(elem, num); // Invoke default constructors if (!pToInsert) { for (int i = 0; i < num; ++i) { Construct(&Element(elem + i)); } } else { for (int i = 0; i < num; i++) { CopyConstruct(&Element(elem + i), pToInsert[i]); } } return elem; } //----------------------------------------------------------------------------- // Finds an element (element needs operator== defined) //----------------------------------------------------------------------------- template< typename T, class A > int CUtlVector::Find(const T& src) const { for (int i = 0; i < Count(); ++i) { if (Element(i) == src) return i; } return -1; } template< typename T, class A > void CUtlVector::FillWithValue(const T& src) { for (int i = 0; i < Count(); i++) { Element(i) = src; } } template< typename T, class A > bool CUtlVector::HasElement(const T& src) const { return (Find(src) >= 0); } //----------------------------------------------------------------------------- // Element removal //----------------------------------------------------------------------------- template< typename T, class A > void CUtlVector::FastRemove(int elem) { Assert(IsValidIndex(elem)); // Global scope to resolve conflict with Scaleform 4.0 ::Destruct(&Element(elem)); if (m_Size > 0) { if (elem != m_Size - 1) memcpy(&Element(elem), &Element(m_Size - 1), sizeof(T)); --m_Size; } } template< typename T, class A > void CUtlVector::Remove(int elem) { // Global scope to resolve conflict with Scaleform 4.0 ::Destruct(&Element(elem)); ShiftElementsLeft(elem); --m_Size; } template< typename T, class A > bool CUtlVector::FindAndRemove(const T& src) { int elem = Find(src); if (elem != -1) { Remove(elem); return true; } return false; } template< typename T, class A > bool CUtlVector::FindAndFastRemove(const T& src) { int elem = Find(src); if (elem != -1) { FastRemove(elem); return true; } return false; } template< typename T, class A > void CUtlVector::RemoveMultiple(int elem, int num) { Assert(elem >= 0); Assert(elem + num <= Count()); // Global scope to resolve conflict with Scaleform 4.0 for (int i = elem + num; --i >= elem; ) ::Destruct(&Element(i)); ShiftElementsLeft(elem, num); m_Size -= num; } template< typename T, class A > void CUtlVector::RemoveMultipleFromHead(int num) { Assert(num <= Count()); // Global scope to resolve conflict with Scaleform 4.0 for (int i = num; --i >= 0; ) ::Destruct(&Element(i)); ShiftElementsLeft(0, num); m_Size -= num; } template< typename T, class A > void CUtlVector::RemoveMultipleFromTail(int num) { Assert(num <= Count()); // Global scope to resolve conflict with Scaleform 4.0 for (int i = m_Size - num; i < m_Size; i++) ::Destruct(&Element(i)); m_Size -= num; } template< typename T, class A > void CUtlVector::RemoveAll() { for (int i = m_Size; --i >= 0; ) { // Global scope to resolve conflict with Scaleform 4.0 ::Destruct(&Element(i)); } m_Size = 0; } //----------------------------------------------------------------------------- // Memory deallocation //----------------------------------------------------------------------------- template< typename T, class A > inline void CUtlVector::Purge() { RemoveAll(); m_Memory.Purge(); //ResetDbgInfo(); } template< typename T, class A > inline void CUtlVector::PurgeAndDeleteElements() { for (int i = 0; i < m_Size; i++) { delete Element(i); } Purge(); } template< typename T, class A > inline void CUtlVector::Compact() { m_Memory.Purge(m_Size); } template< typename T, class A > inline int CUtlVector::NumAllocated() const { return m_Memory.NumAllocated(); } //----------------------------------------------------------------------------- // Data and memory validation //----------------------------------------------------------------------------- #ifdef DBGFLAG_VALIDATE template< typename T, class A > void CUtlVector::Validate(CValidator& validator, char* pchName) { validator.Push(typeid(*this).name(), this, pchName); m_Memory.Validate(validator, "m_Memory"); validator.Pop(); } #endif // DBGFLAG_VALIDATE // A vector class for storing pointers, so that the elements pointed to by the pointers are deleted // on exit. template class CUtlVectorAutoPurge : public CUtlVector< T, CUtlMemory< T, int> > { public: ~CUtlVectorAutoPurge(void) { this->PurgeAndDeleteElements(); } }; // easy string list class with dynamically allocated strings. For use with V_SplitString, etc. // Frees the dynamic strings in destructor. class CUtlStringList : public CUtlVectorAutoPurge< char*> { public: void CopyAndAddToTail(char const* pString) // clone the string and add to the end { char* pNewStr = new char[1 + strlen(pString)]; strcpy(pNewStr, pString); AddToTail(pNewStr); } static int __cdecl SortFunc(char* const* sz1, char* const* sz2) { return strcmp(*sz1, *sz2); } CUtlStringList() {} // !TODO: //CUtlStringList(char const* pString, char const* pSeparator) //{ // SplitString(pString, pSeparator); //} //CUtlStringList(char const* pString, const char** pSeparators, int nSeparators) //{ // SplitString2(pString, pSeparators, nSeparators); //} //void SplitString(char const* pString, char const* pSeparator) //{ // V_SplitString(pString, pSeparator, *this); //} //void SplitString2(char const* pString, const char** pSeparators, int nSeparators) //{ // V_SplitString2(pString, pSeparators, nSeparators, *this); //} private: CUtlStringList(const CUtlStringList& other); // copying directly will cause double-release of the same strings; maybe we need to do a deep copy, but unless and until such need arises, this will guard against double-release }; // placing it here a few days before Cert to minimize disruption to the rest of codebase class CSplitString : public CUtlVector > { public: CSplitString(); CSplitString(const char* pString, const char* pSeparator); CSplitString(const char* pString, const char** pSeparators, int nSeparators); ~CSplitString(); void Set(const char* pString, const char** pSeparators, int nSeparators); // // NOTE: If you want to make Construct() public and implement Purge() here, you'll have to free m_szBuffer there // private: void Construct(const char* pString, const char** pSeparators, int nSeparators); void PurgeAndDeleteElements(); private: char* m_szBuffer; // a copy of original string, with '\0' instead of separators }; #endif // CCVECTOR_H