//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // // $NoKeywords: $ // // A growable memory class. //===========================================================================// #ifndef UTLMEMORY_H #define UTLMEMORY_H #ifdef _WIN32 #pragma once #endif #include "tier0/dbg.h" #include <string.h> #include "tier0/platform.h" #include "tier0/memalloc.h" #include "tier0/memdbgon.h" #pragma warning (disable:4100) #pragma warning (disable:4514) //----------------------------------------------------------------------------- #ifdef UTLMEMORY_TRACK #define UTLMEMORY_TRACK_ALLOC() MemAlloc_RegisterAllocation( "Sum of all UtlMemory", 0, m_nAllocationCount * sizeof(T), m_nAllocationCount * sizeof(T), 0 ) #define UTLMEMORY_TRACK_FREE() if ( !m_pMemory ) ; else MemAlloc_RegisterDeallocation( "Sum of all UtlMemory", 0, m_nAllocationCount * sizeof(T), m_nAllocationCount * sizeof(T), 0 ) #else #define UTLMEMORY_TRACK_ALLOC() ((void)0) #define UTLMEMORY_TRACK_FREE() ((void)0) #endif //----------------------------------------------------------------------------- // The CUtlMemory class: // A growable memory class which doubles in size by default. //----------------------------------------------------------------------------- template< class T > class CUtlMemory { public: // constructor, destructor CUtlMemory( int nGrowSize = 0, int nInitSize = 0 ); CUtlMemory( T* pMemory, int numElements ); CUtlMemory( const T* pMemory, int numElements ); ~CUtlMemory(); // element access T& operator[]( int i ); const T& operator[]( int i ) const; T& Element( int i ); const T& Element( int i ) const; // Can we use this index? bool IsIdxValid( int i ) const; // Gets the base address (can change when adding elements!) T* Base(); const T* Base() const; // Attaches the buffer to external memory.... void SetExternalBuffer( T* pMemory, int numElements ); void SetExternalBuffer( const T* pMemory, int numElements ); // Fast swap void Swap( CUtlMemory< T > &mem ); // Switches the buffer from an external memory buffer to a reallocatable buffer // Will copy the current contents of the external buffer to the reallocatable buffer void ConvertToGrowableMemory( int nGrowSize ); // Size int NumAllocated() const; int Count() const; // Grows the memory, so that at least allocated + num elements are allocated 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 ); // is the memory externally allocated? bool IsExternallyAllocated() const; // is the memory read only? bool IsReadOnly() const; // Set the size by which the memory grows void SetGrowSize( int size ); protected: void ValidateGrowSize() { #ifdef _XBOX if ( m_nGrowSize && m_nGrowSize != EXTERNAL_BUFFER_MARKER ) { // Max grow size at 128 bytes on XBOX const int MAX_GROW = 128; if ( m_nGrowSize * sizeof(T) > MAX_GROW ) { m_nGrowSize = max( 1, MAX_GROW / sizeof(T) ); } } #endif } enum { EXTERNAL_BUFFER_MARKER = -1, EXTERNAL_CONST_BUFFER_MARKER = -2, }; T* m_pMemory; int m_nAllocationCount; int m_nGrowSize; }; //----------------------------------------------------------------------------- // The CUtlMemoryFixed class: // A fixed memory class //----------------------------------------------------------------------------- template< typename T, size_t SIZE > class CUtlMemoryFixed { public: // constructor, destructor CUtlMemoryFixed( int nGrowSize = 0, int nInitSize = 0 ) { Assert( nInitSize == 0 || nInitSize == SIZE ); } CUtlMemoryFixed( T* pMemory, int numElements ) { Assert( 0 ); } // Can we use this index? bool IsIdxValid( int i ) const { return (i >= 0) && (i < SIZE); } // Gets the base address T* Base() { return (T*)(&m_Memory[0]); } const T* Base() const { return (T*)(&m_Memory[0]); } // element access T& operator[]( int i ) { Assert( IsIdxValid(i) ); return Base()[i]; } const T& operator[]( int i ) const { Assert( IsIdxValid(i) ); return Base()[i]; } T& Element( int i ) { Assert( IsIdxValid(i) ); return Base()[i]; } const T& Element( int i ) const { Assert( IsIdxValid(i) ); return Base()[i]; } // Attaches the buffer to external memory.... void SetExternalBuffer( T* pMemory, int numElements ) { Assert( 0 ); } // Size int NumAllocated() const { return SIZE; } int Count() const { return SIZE; } // Grows the memory, so that at least allocated + num elements are allocated void Grow( int num = 1 ) { Assert( 0 ); } // Makes sure we've got at least this much memory void EnsureCapacity( int num ) { Assert( num <= SIZE ); } // Memory deallocation void Purge() {} // Purge all but the given number of elements (NOT IMPLEMENTED IN CUtlMemoryFixed) void Purge( int numElements ) { Assert( 0 ); } // is the memory externally allocated? bool IsExternallyAllocated() const { return false; } // Set the size by which the memory grows void SetGrowSize( int size ) {} private: char m_Memory[SIZE*sizeof(T)]; }; //----------------------------------------------------------------------------- // constructor, destructor //----------------------------------------------------------------------------- template< class T > CUtlMemory<T>::CUtlMemory( int nGrowSize, int nInitAllocationCount ) : m_pMemory(0), m_nAllocationCount( nInitAllocationCount ), m_nGrowSize( nGrowSize ) { ValidateGrowSize(); Assert( nGrowSize >= 0 ); if (m_nAllocationCount) { UTLMEMORY_TRACK_ALLOC(); MEM_ALLOC_CREDIT_CLASS(); m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) ); } } template< class T > CUtlMemory<T>::CUtlMemory( T* pMemory, int numElements ) : m_pMemory(pMemory), m_nAllocationCount( numElements ) { // Special marker indicating externally supplied modifyable memory m_nGrowSize = EXTERNAL_BUFFER_MARKER; } template< class T > CUtlMemory<T>::CUtlMemory( const T* pMemory, int numElements ) : m_pMemory( (T*)pMemory ), m_nAllocationCount( numElements ) { // Special marker indicating externally supplied modifyable memory m_nGrowSize = EXTERNAL_CONST_BUFFER_MARKER; } template< class T > CUtlMemory<T>::~CUtlMemory() { Purge(); } //----------------------------------------------------------------------------- // Fast swap //----------------------------------------------------------------------------- template< class T > void CUtlMemory<T>::Swap( CUtlMemory< T > &mem ) { swap( m_nGrowSize, mem.m_nGrowSize ); swap( m_pMemory, mem.m_pMemory ); swap( m_nAllocationCount, mem.m_nAllocationCount ); } //----------------------------------------------------------------------------- // Switches the buffer from an external memory buffer to a reallocatable buffer //----------------------------------------------------------------------------- template< class T > void CUtlMemory<T>::ConvertToGrowableMemory( int nGrowSize ) { if ( !IsExternallyAllocated() ) return; m_nGrowSize = nGrowSize; if (m_nAllocationCount) { UTLMEMORY_TRACK_ALLOC(); MEM_ALLOC_CREDIT_CLASS(); int nNumBytes = m_nAllocationCount * sizeof(T); T *pMemory = (T*)malloc( nNumBytes ); memcpy( pMemory, m_pMemory, nNumBytes ); m_pMemory = pMemory; } else { m_pMemory = NULL; } } //----------------------------------------------------------------------------- // Attaches the buffer to external memory.... //----------------------------------------------------------------------------- template< class T > void CUtlMemory<T>::SetExternalBuffer( T* pMemory, int numElements ) { // Blow away any existing allocated memory Purge(); m_pMemory = pMemory; m_nAllocationCount = numElements; // Indicate that we don't own the memory m_nGrowSize = EXTERNAL_BUFFER_MARKER; } template< class T > void CUtlMemory<T>::SetExternalBuffer( const T* pMemory, int numElements ) { // Blow away any existing allocated memory Purge(); m_pMemory = const_cast<T*>( pMemory ); m_nAllocationCount = numElements; // Indicate that we don't own the memory m_nGrowSize = EXTERNAL_CONST_BUFFER_MARKER; } //----------------------------------------------------------------------------- // element access //----------------------------------------------------------------------------- template< class T > inline T& CUtlMemory<T>::operator[]( int i ) { Assert( !IsReadOnly() ); Assert( IsIdxValid(i) ); return m_pMemory[i]; } template< class T > inline const T& CUtlMemory<T>::operator[]( int i ) const { Assert( IsIdxValid(i) ); return m_pMemory[i]; } template< class T > inline T& CUtlMemory<T>::Element( int i ) { Assert( !IsReadOnly() ); Assert( IsIdxValid(i) ); return m_pMemory[i]; } template< class T > inline const T& CUtlMemory<T>::Element( int i ) const { Assert( IsIdxValid(i) ); return m_pMemory[i]; } //----------------------------------------------------------------------------- // is the memory externally allocated? //----------------------------------------------------------------------------- template< class T > bool CUtlMemory<T>::IsExternallyAllocated() const { return (m_nGrowSize < 0); } //----------------------------------------------------------------------------- // is the memory read only? //----------------------------------------------------------------------------- template< class T > bool CUtlMemory<T>::IsReadOnly() const { return (m_nGrowSize == EXTERNAL_CONST_BUFFER_MARKER); } template< class T > void CUtlMemory<T>::SetGrowSize( int nSize ) { Assert( !IsExternallyAllocated() ); Assert( nSize >= 0 ); m_nGrowSize = nSize; ValidateGrowSize(); } //----------------------------------------------------------------------------- // Gets the base address (can change when adding elements!) //----------------------------------------------------------------------------- template< class T > inline T* CUtlMemory<T>::Base() { Assert( !IsReadOnly() ); return m_pMemory; } template< class T > inline const T *CUtlMemory<T>::Base() const { return m_pMemory; } //----------------------------------------------------------------------------- // Size //----------------------------------------------------------------------------- template< class T > inline int CUtlMemory<T>::NumAllocated() const { return m_nAllocationCount; } template< class T > inline int CUtlMemory<T>::Count() const { return m_nAllocationCount; } //----------------------------------------------------------------------------- // Is element index valid? //----------------------------------------------------------------------------- template< class T > inline bool CUtlMemory<T>::IsIdxValid( int i ) const { return (i >= 0) && (i < m_nAllocationCount); } //----------------------------------------------------------------------------- // Grows the memory //----------------------------------------------------------------------------- inline int UtlMemory_CalcNewAllocationCount( int nAllocationCount, int nGrowSize, int nNewSize, int nBytesItem ) { if ( nGrowSize ) { nAllocationCount = ((1 + ((nNewSize - 1) / nGrowSize)) * nGrowSize); } else { if ( !nAllocationCount ) { // Compute an allocation which is at least as big as a cache line... nAllocationCount = (31 + nBytesItem) / nBytesItem; } while (nAllocationCount < nNewSize) { #ifndef _XBOX nAllocationCount *= 2; #else int nNewAllocationCount = ( nAllocationCount * 9) / 8; // 12.5 % if ( nNewAllocationCount > nAllocationCount ) nAllocationCount = nNewAllocationCount; else nAllocationCount *= 2; #endif } } return nAllocationCount; } template< class T > void CUtlMemory<T>::Grow( int num ) { Assert( num > 0 ); if ( IsExternallyAllocated() ) { // Can't grow a buffer whose memory was externally allocated Assert(0); return; } // Make sure we have at least numallocated + num allocations. // Use the grow rules specified for this memory (in m_nGrowSize) int nAllocationRequested = m_nAllocationCount + num; UTLMEMORY_TRACK_FREE(); m_nAllocationCount = UtlMemory_CalcNewAllocationCount( m_nAllocationCount, m_nGrowSize, nAllocationRequested, sizeof(T) ); UTLMEMORY_TRACK_ALLOC(); if (m_pMemory) { MEM_ALLOC_CREDIT_CLASS(); m_pMemory = (T*)realloc( m_pMemory, m_nAllocationCount * sizeof(T) ); Assert( m_pMemory ); } else { MEM_ALLOC_CREDIT_CLASS(); m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) ); Assert( m_pMemory ); } } //----------------------------------------------------------------------------- // Makes sure we've got at least this much memory //----------------------------------------------------------------------------- template< class T > inline void CUtlMemory<T>::EnsureCapacity( int num ) { if (m_nAllocationCount >= num) return; if ( IsExternallyAllocated() ) { // Can't grow a buffer whose memory was externally allocated Assert(0); return; } UTLMEMORY_TRACK_FREE(); m_nAllocationCount = num; UTLMEMORY_TRACK_ALLOC(); if (m_pMemory) { MEM_ALLOC_CREDIT_CLASS(); m_pMemory = (T*)realloc( m_pMemory, m_nAllocationCount * sizeof(T) ); } else { MEM_ALLOC_CREDIT_CLASS(); m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) ); } } //----------------------------------------------------------------------------- // Memory deallocation //----------------------------------------------------------------------------- template< class T > void CUtlMemory<T>::Purge() { if ( !IsExternallyAllocated() ) { if (m_pMemory) { UTLMEMORY_TRACK_FREE(); free( (void*)m_pMemory ); m_pMemory = 0; } m_nAllocationCount = 0; } } template< class T > void CUtlMemory<T>::Purge( int numElements ) { Assert( numElements >= 0 ); if( numElements > m_nAllocationCount ) { // Ensure this isn't a grow request in disguise. Assert( numElements <= m_nAllocationCount ); return; } // If we have zero elements, simply do a purge: if( numElements == 0 ) { Purge(); return; } if ( IsExternallyAllocated() ) { // Can't shrink a buffer whose memory was externally allocated, fail silently like purge return; } // If the number of elements is the same as the allocation count, we are done. if( numElements == m_nAllocationCount ) { return; } if( !m_pMemory ) { // Allocation count is non zero, but memory is null. Assert( m_pMemory ); return; } UTLMEMORY_TRACK_FREE(); m_nAllocationCount = numElements; UTLMEMORY_TRACK_ALLOC(); // Allocation count > 0, shrink it down. MEM_ALLOC_CREDIT_CLASS(); m_pMemory = (T*)realloc( m_pMemory, m_nAllocationCount * sizeof(T) ); } //----------------------------------------------------------------------------- // The CUtlMemory class: // A growable memory class which doubles in size by default. //----------------------------------------------------------------------------- template< class T, int nAlignment > class CUtlMemoryAligned : public CUtlMemory<T> { public: // constructor, destructor CUtlMemoryAligned( int nGrowSize = 0, int nInitSize = 0 ); CUtlMemoryAligned( T* pMemory, int numElements ); CUtlMemoryAligned( const T* pMemory, int numElements ); ~CUtlMemoryAligned(); // Attaches the buffer to external memory.... void SetExternalBuffer( T* pMemory, int numElements ); void SetExternalBuffer( const T* pMemory, int numElements ); // Grows the memory, so that at least allocated + num elements are allocated 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 (NOT IMPLEMENTED IN CUtlMemoryAligned) void Purge( int numElements ) { Assert( 0 ); } private: void *Align( const void *pAddr ); void* m_pMemoryBase; }; //----------------------------------------------------------------------------- // Aligns a pointer //----------------------------------------------------------------------------- template< class T, int nAlignment > void *CUtlMemoryAligned<T, nAlignment>::Align( const void *pAddr ) { size_t nAlignmentMask = nAlignment - 1; return (void*)( ((size_t)pAddr + nAlignmentMask) & (~nAlignmentMask) ); } //----------------------------------------------------------------------------- // constructor, destructor //----------------------------------------------------------------------------- template< class T, int nAlignment > CUtlMemoryAligned<T, nAlignment>::CUtlMemoryAligned( int nGrowSize, int nInitAllocationCount ) : m_pMemoryBase(0) { CUtlMemory<T>::m_pMemory = 0; CUtlMemory<T>::m_nAllocationCount = nInitAllocationCount; CUtlMemory<T>::m_nGrowSize = nGrowSize; ValidateGrowSize(); // Alignment must be a power of two COMPILE_TIME_ASSERT( (nAlignment & (nAlignment-1)) == 0 ); Assert( (nGrowSize >= 0) && (nGrowSize != EXTERNAL_BUFFER_MARKER) ); if ( CUtlMemory<T>::m_nAllocationCount ) { UTLMEMORY_TRACK_ALLOC(); MEM_ALLOC_CREDIT_CLASS(); m_pMemoryBase = malloc( nInitAllocationCount * sizeof(T) + (nAlignment - 1) ); CUtlMemory<T>::m_pMemory = (T*)Align( m_pMemoryBase ); } } template< class T, int nAlignment > CUtlMemoryAligned<T, nAlignment>::CUtlMemoryAligned( T* pMemory, int numElements ) : m_pMemoryBase(pMemory) { // Special marker indicating externally supplied memory CUtlMemory<T>::m_nGrowSize = CUtlMemory<T>::EXTERNAL_BUFFER_MARKER; CUtlMemory<T>::m_pMemory = (T*)Align( pMemory ); CUtlMemory<T>::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory<T>::m_pMemory ) / sizeof(T); } template< class T, int nAlignment > CUtlMemoryAligned<T, nAlignment>::CUtlMemoryAligned( const T* pMemory, int numElements ) : m_pMemoryBase(pMemory) { // Special marker indicating externally supplied memory CUtlMemory<T>::m_nGrowSize = CUtlMemory<T>::EXTERNAL_CONST_BUFFER_MARKER; CUtlMemory<T>::m_pMemory = (T*)Align( pMemory ); CUtlMemory<T>::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory<T>::m_pMemory ) / sizeof(T); } template< class T, int nAlignment > CUtlMemoryAligned<T, nAlignment>::~CUtlMemoryAligned() { Purge(); } //----------------------------------------------------------------------------- // Attaches the buffer to external memory.... //----------------------------------------------------------------------------- template< class T, int nAlignment > void CUtlMemoryAligned<T, nAlignment>::SetExternalBuffer( T* pMemory, int numElements ) { // Blow away any existing allocated memory Purge(); m_pMemoryBase = pMemory; CUtlMemory<T>::m_pMemory = (T*)Align( pMemory ); CUtlMemory<T>::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory<T>::m_pMemory ) / sizeof(T); // Indicate that we don't own the memory CUtlMemory<T>::m_nGrowSize = CUtlMemory<T>::EXTERNAL_BUFFER_MARKER; } template< class T, int nAlignment > void CUtlMemoryAligned<T, nAlignment>::SetExternalBuffer( const T* pMemory, int numElements ) { // Blow away any existing allocated memory Purge(); m_pMemoryBase = pMemory; CUtlMemory<T>::m_pMemory = (T*)Align( pMemory ); CUtlMemory<T>::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory<T>::m_pMemory ) / sizeof(T); // Indicate that we don't own the memory CUtlMemory<T>::m_nGrowSize = CUtlMemory<T>::EXTERNAL_CONST_BUFFER_MARKER; } //----------------------------------------------------------------------------- // Grows the memory //----------------------------------------------------------------------------- template< class T, int nAlignment > void CUtlMemoryAligned<T, nAlignment>::Grow( int num ) { Assert( num > 0 ); if ( IsExternallyAllocated() ) { // Can't grow a buffer whose memory was externally allocated Assert(0); return; } UTLMEMORY_TRACK_FREE(); // Make sure we have at least numallocated + num allocations. // Use the grow rules specified for this memory (in m_nGrowSize) int nAllocationRequested = CUtlMemory<T>::m_nAllocationCount + num; CUtlMemory<T>::m_nAllocationCount = UtlMemory_CalcNewAllocationCount( CUtlMemory<T>::m_nAllocationCount, CUtlMemory<T>::m_nGrowSize, nAllocationRequested, sizeof(T) ); UTLMEMORY_TRACK_ALLOC(); if (m_pMemoryBase) { MEM_ALLOC_CREDIT_CLASS(); m_pMemoryBase = realloc( m_pMemoryBase, CUtlMemory<T>::m_nAllocationCount * sizeof(T) + (nAlignment - 1) ); Assert( m_pMemoryBase ); } else { MEM_ALLOC_CREDIT_CLASS(); m_pMemoryBase = malloc( CUtlMemory<T>::m_nAllocationCount * sizeof(T) + (nAlignment - 1) ); Assert( m_pMemoryBase ); } CUtlMemory<T>::m_pMemory = (T*)Align( m_pMemoryBase ); } //----------------------------------------------------------------------------- // Makes sure we've got at least this much memory //----------------------------------------------------------------------------- template< class T, int nAlignment > inline void CUtlMemoryAligned<T, nAlignment>::EnsureCapacity( int num ) { if ( CUtlMemory<T>::m_nAllocationCount >= num ) return; if ( IsExternallyAllocated() ) { // Can't grow a buffer whose memory was externally allocated Assert(0); return; } UTLMEMORY_TRACK_FREE(); CUtlMemory<T>::m_nAllocationCount = num; UTLMEMORY_TRACK_ALLOC(); if (m_pMemoryBase) { MEM_ALLOC_CREDIT_CLASS(); m_pMemoryBase = realloc( m_pMemoryBase, CUtlMemory<T>::m_nAllocationCount * sizeof(T) + (nAlignment - 1) ); } else { MEM_ALLOC_CREDIT_CLASS(); m_pMemoryBase = malloc( CUtlMemory<T>::m_nAllocationCount * sizeof(T) + (nAlignment - 1) ); } CUtlMemory<T>::m_pMemory = (T*)Align( m_pMemoryBase ); } //----------------------------------------------------------------------------- // Memory deallocation //----------------------------------------------------------------------------- template< class T, int nAlignment > void CUtlMemoryAligned<T, nAlignment>::Purge() { if ( !IsExternallyAllocated() ) { if (m_pMemoryBase) { UTLMEMORY_TRACK_FREE(); free( (void*)m_pMemoryBase ); m_pMemoryBase = 0; CUtlMemory<T>::m_pMemory = 0; } CUtlMemory<T>::m_nAllocationCount = 0; } } #include "tier0/memdbgoff.h" #endif // UTLMEMORY_H
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 5821 | Knut Wikstrom |
Added Valve Source code. This is NOT to be commited to other than new code from Valve. |