//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // // $NoKeywords: $ // // A growable memory class. //===========================================================================// #ifndef UTLBLOCKMEMORY_H #define UTLBLOCKMEMORY_H #ifdef _WIN32 #pragma once #endif #include "tier0/dbg.h" #include "tier0/platform.h" #include "mathlib/mathlib.h" //#include "tier0/memalloc.h" //#include "tier0/memdbgon.h" #pragma warning (disable:4100) #pragma warning (disable:4514) //----------------------------------------------------------------------------- #ifdef UTBLOCKLMEMORY_TRACK #define UTLBLOCKMEMORY_TRACK_ALLOC() MemAlloc_RegisterAllocation( "||Sum of all UtlBlockMemory||", 0, NumAllocated() * sizeof(T), NumAllocated() * sizeof(T), 0 ) #define UTLBLOCKMEMORY_TRACK_FREE() if ( !m_pMemory ) ; else MemAlloc_RegisterDeallocation( "||Sum of all UtlBlockMemory||", 0, NumAllocated() * sizeof(T), NumAllocated() * sizeof(T), 0 ) #else #define UTLBLOCKMEMORY_TRACK_ALLOC() ((void)0) #define UTLBLOCKMEMORY_TRACK_FREE() ((void)0) #endif //----------------------------------------------------------------------------- // The CUtlBlockMemory class: // A growable memory class that allocates non-sequential blocks, but is indexed sequentially //----------------------------------------------------------------------------- template< class T, class I > class CUtlBlockMemory { public: // constructor, destructor CUtlBlockMemory(int nGrowSize = 0, int nInitSize = 0); ~CUtlBlockMemory(); // Set the size by which the memory grows - round up to the next power of 2 void Init(int nGrowSize = 0, int nInitSize = 0); // here to match CUtlMemory, but only used by ResetDbgInfo, so it can just return NULL T* Base() { return NULL; } const T* Base() const { return NULL; } class Iterator_t { public: Iterator_t(I i) : index(i) {} I index; bool operator==(const Iterator_t it) const { return index == it.index; } bool operator!=(const Iterator_t it) const { return index != it.index; } }; Iterator_t First() const { return Iterator_t(IsIdxValid(0) ? 0 : InvalidIndex()); } Iterator_t Next(const Iterator_t& it) const { return Iterator_t(IsIdxValid(it.index + 1) ? it.index + 1 : InvalidIndex()); } I GetIndex(const Iterator_t& it) const { return it.index; } bool IsIdxAfter(I i, const Iterator_t& it) const { return i > it.index; } bool IsValidIterator(const Iterator_t& it) const { return IsIdxValid(it.index); } Iterator_t InvalidIterator() const { return Iterator_t(InvalidIndex()); } // element access T& operator[](I i); const T& operator[](I i) const; T& Element(I i); const T& Element(I i) const; // Can we use this index? bool IsIdxValid(I i) const; static I InvalidIndex() { return (I)-1; } void Swap(CUtlBlockMemory< T, I >& mem); // Size int NumAllocated() const; int Count() const { return NumAllocated(); } // Grows memory by max(num,growsize) rounded up to the next power of 2, and returns the allocation index/ptr void Grow(int num = 1); // Makes sure we've got at least this much memory void EnsureCapacity(int num); // Memory deallocation void Purge(); // Purge all but the given number of elements void Purge(int numElements); protected: int Index(int major, int minor) const { return (major << m_nIndexShift) | minor; } int MajorIndex(int i) const { return i >> m_nIndexShift; } int MinorIndex(int i) const { return i & m_nIndexMask; } void ChangeSize(int nBlocks); int NumElementsInBlock() const { return m_nIndexMask + 1; } T** m_pMemory; int m_nBlocks; int m_nIndexMask : 27; int m_nIndexShift : 5; }; //----------------------------------------------------------------------------- // constructor, destructor //----------------------------------------------------------------------------- template< class T, class I > CUtlBlockMemory::CUtlBlockMemory(int nGrowSize, int nInitAllocationCount) : m_pMemory(0), m_nBlocks(0), m_nIndexMask(0), m_nIndexShift(0) { Init(nGrowSize, nInitAllocationCount); } template< class T, class I > CUtlBlockMemory::~CUtlBlockMemory() { Purge(); } //----------------------------------------------------------------------------- // Fast swap //----------------------------------------------------------------------------- template< class T, class I > void CUtlBlockMemory::Swap(CUtlBlockMemory< T, I >& mem) { V_swap(m_pMemory, mem.m_pMemory); V_swap(m_nBlocks, mem.m_nBlocks); V_swap(m_nIndexMask, mem.m_nIndexMask); V_swap(m_nIndexShift, mem.m_nIndexShift); } //----------------------------------------------------------------------------- // Set the size by which the memory grows - round up to the next power of 2 //----------------------------------------------------------------------------- template< class T, class I > void CUtlBlockMemory::Init(int nGrowSize /* = 0 */, int nInitSize /* = 0 */) { Purge(); if (nGrowSize == 0) { // default grow size is smallest size s.t. c++ allocation overhead is ~6% of block size nGrowSize = (127 + sizeof(T)) / sizeof(T); } nGrowSize = SmallestPowerOfTwoGreaterOrEqual(nGrowSize); m_nIndexMask = nGrowSize - 1; m_nIndexShift = 0; while (nGrowSize > 1) { nGrowSize >>= 1; ++m_nIndexShift; } Assert(m_nIndexMask + 1 == (1 << m_nIndexShift)); Grow(nInitSize); } //----------------------------------------------------------------------------- // element access //----------------------------------------------------------------------------- template< class T, class I > inline T& CUtlBlockMemory::operator[](I i) { Assert(IsIdxValid(i)); T* pBlock = m_pMemory[MajorIndex(i)]; return pBlock[MinorIndex(i)]; } template< class T, class I > inline const T& CUtlBlockMemory::operator[](I i) const { Assert(IsIdxValid(i)); const T* pBlock = m_pMemory[MajorIndex(i)]; return pBlock[MinorIndex(i)]; } template< class T, class I > inline T& CUtlBlockMemory::Element(I i) { Assert(IsIdxValid(i)); T* pBlock = m_pMemory[MajorIndex(i)]; return pBlock[MinorIndex(i)]; } template< class T, class I > inline const T& CUtlBlockMemory::Element(I i) const { Assert(IsIdxValid(i)); const T* pBlock = m_pMemory[MajorIndex(i)]; return pBlock[MinorIndex(i)]; } //----------------------------------------------------------------------------- // Size //----------------------------------------------------------------------------- template< class T, class I > inline int CUtlBlockMemory::NumAllocated() const { return m_nBlocks * NumElementsInBlock(); } //----------------------------------------------------------------------------- // Is element index valid? //----------------------------------------------------------------------------- template< class T, class I > inline bool CUtlBlockMemory::IsIdxValid(I i) const { return (i >= 0) && (MajorIndex(i) < m_nBlocks); } template< class T, class I > void CUtlBlockMemory::Grow(int num) { if (num <= 0) return; int nBlockSize = NumElementsInBlock(); int nBlocks = (num + nBlockSize - 1) / nBlockSize; ChangeSize(m_nBlocks + nBlocks); } template< class T, class I > void CUtlBlockMemory::ChangeSize(int nBlocks) { UTLBLOCKMEMORY_TRACK_FREE(); // this must stay before the recalculation of m_nBlocks, since it implicitly uses the old value int nBlocksOld = m_nBlocks; m_nBlocks = nBlocks; UTLBLOCKMEMORY_TRACK_ALLOC(); // this must stay after the recalculation of m_nBlocks, since it implicitly uses the new value // free old blocks if shrinking for (int i = m_nBlocks; i < nBlocksOld; ++i) { UTLBLOCKMEMORY_TRACK_FREE(); MemAllocSingleton()->Free(m_pMemory[i]); } if (m_pMemory) { MEM_ALLOC_CREDIT_CLASS(); m_pMemory = (T**)realloc(m_pMemory, m_nBlocks * sizeof(T*)); Assert(m_pMemory); } else { MEM_ALLOC_CREDIT_CLASS(); m_pMemory = (T**)malloc(m_nBlocks * sizeof(T*)); Assert(m_pMemory); } if (!m_pMemory) { Error(eDLL_T::COMMON, EXIT_FAILURE, "CUtlBlockMemory overflow!\n"); } // allocate new blocks if growing int nBlockSize = NumElementsInBlock(); for (int i = nBlocksOld; i < m_nBlocks; ++i) { MEM_ALLOC_CREDIT_CLASS(); m_pMemory[i] = (T*)malloc(nBlockSize * sizeof(T)); Assert(m_pMemory[i]); } } //----------------------------------------------------------------------------- // Makes sure we've got at least this much memory //----------------------------------------------------------------------------- template< class T, class I > inline void CUtlBlockMemory::EnsureCapacity(int num) { Grow(num - NumAllocated()); } //----------------------------------------------------------------------------- // Memory deallocation //----------------------------------------------------------------------------- template< class T, class I > void CUtlBlockMemory::Purge() { if (!m_pMemory) return; for (int i = 0; i < m_nBlocks; ++i) { UTLBLOCKMEMORY_TRACK_FREE(); MemAllocSingleton()->Free(m_pMemory[i]); } m_nBlocks = 0; UTLBLOCKMEMORY_TRACK_FREE(); MemAllocSingleton()->Free(m_pMemory); m_pMemory = 0; } template< class T, class I > void CUtlBlockMemory::Purge(int numElements) { Assert(numElements >= 0); int nAllocated = NumAllocated(); if (numElements > nAllocated) { // Ensure this isn't a grow request in disguise. Assert(numElements <= nAllocated); return; } if (numElements <= 0) { Purge(); return; } int nBlockSize = NumElementsInBlock(); int nBlocksOld = m_nBlocks; int nBlocks = (numElements + nBlockSize - 1) / nBlockSize; // If the number of blocks is the same as the allocated number of blocks, we are done. if (nBlocks == m_nBlocks) return; ChangeSize(nBlocks); } //#include "tier0/memdbgoff.h" #endif // UTLBLOCKMEMORY_H