|
|
//===== 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<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 ) { 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<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
// free old blocks if shrinking
for ( int i = m_nBlocks; i < nBlocksOld; ++i ) { UTLBLOCKMEMORY_TRACK_FREE(); free( (void*)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( "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
|