You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
350 lines
9.7 KiB
350 lines
9.7 KiB
//========= Copyright 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<T,I>::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<T,I>::~CUtlBlockMemory() |
|
{ |
|
Purge(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Fast swap |
|
//----------------------------------------------------------------------------- |
|
template< class T, class I > |
|
void CUtlBlockMemory<T,I>::Swap( CUtlBlockMemory< T, I > &mem ) |
|
{ |
|
this->swap( m_pMemory, mem.m_pMemory ); |
|
this->swap( m_nBlocks, mem.m_nBlocks ); |
|
this->swap( m_nIndexMask, mem.m_nIndexMask ); |
|
this->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<T,I>::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<T,I>::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<T,I>::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<T,I>::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<T,I>::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<T,I>::NumAllocated() const |
|
{ |
|
return m_nBlocks * NumElementsInBlock(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Is element index valid? |
|
//----------------------------------------------------------------------------- |
|
template< class T, class I > |
|
inline bool CUtlBlockMemory<T,I>::IsIdxValid( I i ) const |
|
{ |
|
return ( i >= 0 ) && ( MajorIndex( i ) < m_nBlocks ); |
|
} |
|
|
|
template< class T, class I > |
|
void CUtlBlockMemory<T,I>::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<T,I>::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 |
|
|
|
if ( m_pMemory ) |
|
{ |
|
// free old blocks if shrinking |
|
// Only possible if m_pMemory is non-NULL (and avoids PVS-Studio warning) |
|
for ( int i = m_nBlocks; i < nBlocksOld; ++i ) |
|
{ |
|
UTLBLOCKMEMORY_TRACK_FREE(); |
|
free( (void*)m_pMemory[ i ] ); |
|
} |
|
|
|
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( "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<T,I>::EnsureCapacity( int num ) |
|
{ |
|
Grow( num - NumAllocated() ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Memory deallocation |
|
//----------------------------------------------------------------------------- |
|
template< class T, class I > |
|
void CUtlBlockMemory<T,I>::Purge() |
|
{ |
|
if ( !m_pMemory ) |
|
return; |
|
|
|
for ( int i = 0; i < m_nBlocks; ++i ) |
|
{ |
|
UTLBLOCKMEMORY_TRACK_FREE(); |
|
free( (void*)m_pMemory[ i ] ); |
|
} |
|
m_nBlocks = 0; |
|
|
|
UTLBLOCKMEMORY_TRACK_FREE(); |
|
free( (void*)m_pMemory ); |
|
m_pMemory = 0; |
|
} |
|
|
|
template< class T, class I > |
|
void CUtlBlockMemory<T,I>::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
|
|
|