Add CMemStack class

This commit is contained in:
Kawe Mazidjatari 2022-11-29 00:01:47 +01:00
parent edeb8ea586
commit 3a4a5a08f8
4 changed files with 843 additions and 0 deletions

105
r5dev/public/imemalloc.h Normal file
View File

@ -0,0 +1,105 @@
//====== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. =======//
//
// Purpose: This header should never be used directly from leaf code!!!
// Instead, just add the file memoverride.cpp into your project and all this
// will automagically be used
//
// $NoKeywords: $
//=============================================================================//
#ifndef TIER0_IMEMALLOC_H
#define TIER0_IMEMALLOC_H
/// This interface class is used to let the mem_dump command retrieve
/// information about memory allocations outside of the heap. It is currently
/// used by CMemoryStack to report on its allocations.
abstract_class IMemoryInfo
{
public:
virtual const char* GetMemoryName() const = 0; // User friendly name for this stack or pool
virtual size_t GetAllocatedBytes() const = 0; // Number of bytes currently allocated
virtual size_t GetCommittedBytes() const = 0; // Bytes committed -- may be greater than allocated.
virtual size_t GetReservedBytes() const = 0; // Bytes reserved -- may be greater than committed.
virtual size_t GetHighestBytes() const = 0; // The maximum number of bytes allocated or committed.
};
//-----------------------------------------------------------------------------
#if (defined(_DEBUG) || defined(USE_MEM_DEBUG))
struct MemAllocFileLine_t
{
const char* pszFile;
int line;
};
#define MEMALLOC_DEFINE_EXTERNAL_TRACKING( tag ) \
static CUtlMap<void *, MemAllocFileLine_t, int> s_##tag##Allocs( DefLessFunc( void *) ); \
CUtlMap<void *, MemAllocFileLine_t, int> * g_p##tag##Allocs = &s_##tag##Allocs; \
static CThreadFastMutex s_##tag##AllocsMutex; \
CThreadFastMutex * g_p##tag##AllocsMutex = &s_##tag##AllocsMutex; \
const char * g_psz##tag##Alloc = strcpy( (char *)MemAlloc_Alloc( strlen( #tag "Alloc" ) + 1, "intentional leak", 0 ), #tag "Alloc" );
#define MEMALLOC_DECLARE_EXTERNAL_TRACKING( tag ) \
extern CUtlMap<void *, MemAllocFileLine_t, int> * g_p##tag##Allocs; \
extern CThreadFastMutex *g_p##tag##AllocsMutex; \
extern const char * g_psz##tag##Alloc;
#define MemAlloc_RegisterExternalAllocation( tag, p, size ) \
if ( !p ) \
; \
else \
{ \
AUTO_LOCK_FM( *g_p##tag##AllocsMutex ); \
MemAllocFileLine_t fileLine = { g_psz##tag##Alloc, 0 }; \
g_pMemAlloc->GetActualDbgInfo( fileLine.pszFile, fileLine.line ); \
if ( fileLine.pszFile != g_psz##tag##Alloc ) \
{ \
g_p##tag##Allocs->Insert( p, fileLine ); \
} \
\
MemAlloc_RegisterAllocation( fileLine.pszFile, fileLine.line, size, size, 0); \
}
#define MemAlloc_RegisterExternalDeallocation( tag, p, size ) \
if ( !p ) \
; \
else \
{ \
AUTO_LOCK_FM( *g_p##tag##AllocsMutex ); \
MemAllocFileLine_t fileLine = { g_psz##tag##Alloc, 0 }; \
CUtlMap<void *, MemAllocFileLine_t, int>::IndexType_t iRecordedFileLine = g_p##tag##Allocs->Find( p ); \
if ( iRecordedFileLine != g_p##tag##Allocs->InvalidIndex() ) \
{ \
fileLine = (*g_p##tag##Allocs)[iRecordedFileLine]; \
g_p##tag##Allocs->RemoveAt( iRecordedFileLine ); \
} \
\
MemAlloc_RegisterDeallocation( fileLine.pszFile, fileLine.line, size, size, 0); \
}
#else
#define MEMALLOC_DEFINE_EXTERNAL_TRACKING( tag )
#define MEMALLOC_DECLARE_EXTERNAL_TRACKING( tag )
#define MemAlloc_RegisterExternalAllocation( tag, p, size ) ((void)0)
#define MemAlloc_RegisterExternalDeallocation( tag, p, size ) ((void)0)
#endif
//-----------------------------------------------------------------------------
#if (defined(_DEBUG) || defined(USE_MEM_DEBUG))
#define MEM_ALLOC_CREDIT_(tag) CMemAllocAttributeAlloction memAllocAttributeAlloction( tag, __LINE__ )
#define MemAlloc_PushAllocDbgInfo( pszFile, line ) g_pMemAlloc->PushAllocDbgInfo( pszFile, line )
#define MemAlloc_PopAllocDbgInfo() g_pMemAlloc->PopAllocDbgInfo()
#define MemAlloc_RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) g_pMemAlloc->RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime )
#define MemAlloc_RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) g_pMemAlloc->RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime )
#else
#define MEM_ALLOC_CREDIT_(tag) ((void)0)
#define MemAlloc_PushAllocDbgInfo( pszFile, line ) ((void)0)
#define MemAlloc_PopAllocDbgInfo() ((void)0)
#define MemAlloc_RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0)
#define MemAlloc_RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime ) ((void)0)
#endif
#endif /* TIER0_MEMALLOC_H */

View File

@ -217,6 +217,12 @@ void Swap(T& a, T& b)
b = temp;
}
template <typename T>
inline T AlignValue(T val, uintptr_t alignment)
{
return (T)(((uintp)val + alignment - 1) & ~(alignment - 1));
}
#else
#define clamp(val, min, max) (((val) > (max)) ? (max) : (((val) < (min)) ? (min) : (val)))

488
r5dev/tier1/memstack.cpp Normal file
View File

@ -0,0 +1,488 @@
//====== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. =======//
//
// Purpose:
//
//=============================================================================//
#include "core/stdafx.h"
#include "tier0/dbg.h"
#include "tier0/memstd.h"
#include "memstack.h"
#include "utlmap.h"
//#include "tier0/memdbgon.h"
#ifdef _WIN32
#pragma warning(disable:4073)
#pragma init_seg(lib)
#endif
#define VA_COMMIT_FLAGS MEM_COMMIT
#define VA_RESERVE_FLAGS MEM_RESERVE
static volatile bool bSpewAllocations = false; // TODO: Register CMemoryStacks with g_pMemAlloc, so it can spew a summary
//-----------------------------------------------------------------------------
MEMALLOC_DEFINE_EXTERNAL_TRACKING(CMemoryStack);
//-----------------------------------------------------------------------------
void PrintStatus( void* p )
{
CMemoryStack* pMemoryStack = (CMemoryStack*)p;
pMemoryStack->PrintContents();
}
CMemoryStack::CMemoryStack()
: m_pNextAlloc( NULL )
, m_pCommitLimit( NULL )
, m_pAllocLimit( NULL )
, m_pHighestAllocLimit( NULL )
, m_pBase( NULL )
, m_bRegisteredAllocation( false )
, m_maxSize( 0 )
, m_alignment( 16 )
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE
, m_commitIncrement( 0 )
, m_minCommit( 0 )
#ifdef _PS3
, m_pVirtualMemorySection( NULL )
#endif
#endif
{
AddMemoryInfoCallback( this );
m_pszAllocOwner = strdup( "CMemoryStack unattributed" );
}
//-------------------------------------
CMemoryStack::~CMemoryStack()
{
if ( m_pBase )
Term();
RemoveMemoryInfoCallback( this );
free( m_pszAllocOwner );
}
//-------------------------------------
bool CMemoryStack::Init( const char *pszAllocOwner, unsigned maxSize, unsigned commitIncrement, unsigned initialCommit, unsigned alignment )
{
Assert( !m_pBase );
m_bPhysical = false;
m_maxSize = maxSize;
m_alignment = AlignValue( alignment, 4 );
Assert( m_alignment == alignment );
Assert( m_maxSize > 0 );
SetAllocOwner( pszAllocOwner );
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE
#ifdef _PS3
// Memory can only be committed in page-size increments on PS3
static const unsigned PS3_PAGE_SIZE = 64*1024;
if ( commitSize < PS3_PAGE_SIZE )
commitSize = PS3_PAGE_SIZE;
#endif
if ( commitIncrement != 0 )
{
m_commitIncrement = commitIncrement;
}
unsigned pageSize;
#ifdef _PS3
pageSize = PS3_PAGE_SIZE;
#elif defined( _X360 )
pageSize = 64 * 1024;
#else
SYSTEM_INFO sysInfo;
GetSystemInfo( &sysInfo );
Assert( !( sysInfo.dwPageSize & (sysInfo.dwPageSize-1)) );
pageSize = sysInfo.dwPageSize;
#endif
if ( m_commitIncrement == 0 )
{
m_commitIncrement = pageSize;
}
else
{
m_commitIncrement = AlignValue( m_commitIncrement, pageSize );
}
m_maxSize = AlignValue( m_maxSize, m_commitIncrement );
Assert( m_maxSize % pageSize == 0 && m_commitIncrement % pageSize == 0 && m_commitIncrement <= m_maxSize );
#ifdef _WIN32
m_pBase = (unsigned char *)VirtualAlloc( NULL, m_maxSize, VA_RESERVE_FLAGS, PAGE_NOACCESS );
#else
m_pVirtualMemorySection = g_pMemAlloc->AllocateVirtualMemorySection( m_maxSize );
if ( !m_pVirtualMemorySection )
{
Warning( "AllocateVirtualMemorySection failed( size=%d )\n", m_maxSize );
Assert( 0 );
m_pBase = NULL;
}
else
{
m_pBase = ( byte* ) m_pVirtualMemorySection->GetBaseAddress();
}
#endif
if ( !m_pBase )
{
#if !defined( NO_MALLOC_OVERRIDE )
g_pMemAlloc->OutOfMemory();
#endif
return false;
}
m_pCommitLimit = m_pNextAlloc = m_pBase;
if ( initialCommit )
{
initialCommit = AlignValue( initialCommit, m_commitIncrement );
Assert( initialCommit <= m_maxSize );
bool bInitialCommitSucceeded = false;
#ifdef _WIN32
bInitialCommitSucceeded = !!VirtualAlloc( m_pCommitLimit, initialCommit, VA_COMMIT_FLAGS, PAGE_READWRITE );
#else
m_pVirtualMemorySection->CommitPages( m_pCommitLimit, initialCommit );
bInitialCommitSucceeded = true;
#endif
if ( !bInitialCommitSucceeded )
{
#if !defined( NO_MALLOC_OVERRIDE )
g_pMemAlloc->OutOfMemory( initialCommit );
#endif
return false;
}
m_minCommit = initialCommit;
m_pCommitLimit += initialCommit;
RegisterAllocation();
}
#else
m_pBase = (byte*)MemAlloc_AllocAligned( m_maxSize, alignment ? alignment : 1 );
m_pNextAlloc = m_pBase;
m_pCommitLimit = m_pBase + m_maxSize;
#endif
m_pHighestAllocLimit = m_pNextAlloc;
m_pAllocLimit = m_pBase + m_maxSize;
return ( m_pBase != NULL );
}
//-------------------------------------
#ifdef _GAMECONSOLE
bool CMemoryStack::InitPhysical( const char *pszAllocOwner, uint size, uint nBaseAddrAlignment, uint alignment, uint32 nFlags )
{
m_bPhysical = true;
m_maxSize = m_commitIncrement = size;
m_alignment = AlignValue( alignment, 4 );
SetAllocOwner( pszAllocOwner );
#ifdef _X360
int flags = PAGE_READWRITE | nFlags;
if ( size >= 16*1024*1024 )
{
flags |= MEM_16MB_PAGES;
}
else
{
flags |= MEM_LARGE_PAGES;
}
m_pBase = (unsigned char *)XPhysicalAlloc( m_maxSize, MAXULONG_PTR, nBaseAddrAlignment, flags );
#elif defined (_PS3)
m_pBase = (byte*)nFlags;
m_pBase = (byte*)AlignValue( (uintp)m_pBase, m_alignment );
#else
#pragma error
#endif
Assert( m_pBase );
m_pNextAlloc = m_pBase;
m_pCommitLimit = m_pBase + m_maxSize;
m_pAllocLimit = m_pBase + m_maxSize;
m_pHighestAllocLimit = m_pNextAlloc;
RegisterAllocation();
return ( m_pBase != NULL );
}
#endif
//-------------------------------------
void CMemoryStack::Term()
{
FreeAll();
if ( m_pBase )
{
#ifdef _GAMECONSOLE
if ( m_bPhysical )
{
#if defined( _X360 )
XPhysicalFree( m_pBase );
#elif defined( _PS3 )
#else
#pragma error
#endif
m_pCommitLimit = m_pBase = NULL;
m_maxSize = 0;
RegisterDeallocation(true);
m_bPhysical = false;
return;
}
#endif // _GAMECONSOLE
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE
#if defined(_WIN32)
VirtualFree( m_pBase, 0, MEM_RELEASE );
#else
m_pVirtualMemorySection->Release();
m_pVirtualMemorySection = NULL;
#endif
#else
MemAlloc_FreeAligned( m_pBase );
#endif
m_pBase = NULL;
// Zero these variables to avoid getting misleading mem_dump
// results when m_pBase is NULL.
m_pNextAlloc = NULL;
m_pCommitLimit = NULL;
m_pHighestAllocLimit = NULL;
m_maxSize = 0;
RegisterDeallocation(true);
}
}
//-------------------------------------
int CMemoryStack::GetSize() const
{
if ( m_bPhysical )
return m_maxSize;
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE
return m_pCommitLimit - m_pBase;
#else
return m_maxSize;
#endif
}
//-------------------------------------
bool CMemoryStack::CommitTo( byte *pNextAlloc ) RESTRICT
{
if ( m_bPhysical )
{
return NULL;
}
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE
unsigned char * pNewCommitLimit = AlignValue( pNextAlloc, m_commitIncrement );
ptrdiff_t commitIncrement = pNewCommitLimit - m_pCommitLimit;
if( m_pCommitLimit + commitIncrement > m_pAllocLimit )
{
#if !defined( NO_MALLOC_OVERRIDE )
g_pMemAlloc->OutOfMemory( commitIncrement );
#endif
return false;
}
if ( pNewCommitLimit > m_pCommitLimit )
{
RegisterDeallocation(false);
bool bAllocationSucceeded = false;
#ifdef _WIN32
bAllocationSucceeded = !!VirtualAlloc( m_pCommitLimit, commitIncrement, VA_COMMIT_FLAGS, PAGE_READWRITE );
#else
bAllocationSucceeded = m_pVirtualMemorySection->CommitPages( m_pCommitLimit, commitIncrement );
#endif
if ( !bAllocationSucceeded )
{
#if !defined( NO_MALLOC_OVERRIDE )
g_pMemAlloc->OutOfMemory( commitIncrement );
#endif
return false;
}
m_pCommitLimit = pNewCommitLimit;
RegisterAllocation();
}
else if ( pNewCommitLimit < m_pCommitLimit )
{
if ( m_pNextAlloc > pNewCommitLimit )
{
Warning( eDLL_T::COMMON, "ATTEMPTED TO DECOMMIT OWNED MEMORY STACK SPACE\n" );
pNewCommitLimit = AlignValue( m_pNextAlloc, m_commitIncrement );
}
if ( pNewCommitLimit < m_pCommitLimit )
{
RegisterDeallocation(false);
ptrdiff_t decommitIncrement = m_pCommitLimit - pNewCommitLimit;
#ifdef _WIN32
VirtualFree( pNewCommitLimit, decommitIncrement, MEM_DECOMMIT );
#else
m_pVirtualMemorySection->DecommitPages( pNewCommitLimit, decommitIncrement );
#endif
m_pCommitLimit = pNewCommitLimit;
RegisterAllocation();
}
}
return true;
#else
return false;
#endif
}
// Identify the owner of this memory stack's memory
void CMemoryStack::SetAllocOwner( const char *pszAllocOwner )
{
if ( !pszAllocOwner || !Q_strcmp( m_pszAllocOwner, pszAllocOwner ) )
return;
free( m_pszAllocOwner );
m_pszAllocOwner = strdup( pszAllocOwner );
}
void CMemoryStack::RegisterAllocation()
{
// 'physical' allocations on PS3 come from RSX local memory, so we don't count them here:
if ( IsPS3() && m_bPhysical )
return;
if ( GetSize() )
{
if ( m_bRegisteredAllocation )
Warning( eDLL_T::COMMON, "CMemoryStack: ERROR - mismatched RegisterAllocation/RegisterDeallocation!\n" );
// NOTE: we deliberately don't use MemAlloc_RegisterExternalAllocation. CMemoryStack needs to bypass 'GetActualDbgInfo'
// due to the way it allocates memory: there's just one representative memory address (m_pBase), it grows at unpredictable
// times (in CommitTo, not every Alloc call) and it is freed en-masse (instead of freeing each individual allocation).
MemAlloc_RegisterAllocation( m_pszAllocOwner, 0, GetSize(), GetSize(), 0 );
}
m_bRegisteredAllocation = true;
// Temp memorystack spew: very useful when we crash out of memory
if ( IsGameConsole() && bSpewAllocations ) DevMsg( eDLL_T::COMMON, "CMemoryStack: %4.1fMB (%s)\n", GetSize()/(float)(1024*1024), m_pszAllocOwner );
}
void CMemoryStack::RegisterDeallocation( bool bShouldSpewSize )
{
// 'physical' allocations on PS3 come from RSX local memory, so we don't count them here:
if ( IsPS3() && m_bPhysical )
return;
if ( GetSize() )
{
if ( !m_bRegisteredAllocation )
Warning( eDLL_T::COMMON, "CMemoryStack: ERROR - mismatched RegisterAllocation/RegisterDeallocation!\n" );
MemAlloc_RegisterDeallocation( m_pszAllocOwner, 0, GetSize(), GetSize(), 0 );
}
m_bRegisteredAllocation = false;
// Temp memorystack spew: very useful when we crash out of memory
if ( bShouldSpewSize && IsGameConsole() && bSpewAllocations ) DevMsg( eDLL_T::COMMON, "CMemoryStack: %4.1fMB (%s)\n", GetSize()/(float)(1024*1024), m_pszAllocOwner );
}
//-------------------------------------
void CMemoryStack::FreeToAllocPoint( MemoryStackMark_t mark, bool bDecommit )
{
mark = AlignValue( mark, m_alignment );
byte *pAllocPoint = m_pBase + mark;
Assert( pAllocPoint >= m_pBase && pAllocPoint <= m_pNextAlloc );
if ( pAllocPoint >= m_pBase && pAllocPoint <= m_pNextAlloc )
{
m_pNextAlloc = pAllocPoint;
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE
if ( bDecommit && !m_bPhysical )
{
CommitTo( MAX( m_pNextAlloc, (m_pBase + m_minCommit) ) );
}
#endif
}
}
//-------------------------------------
void CMemoryStack::FreeAll( bool bDecommit )
{
if ( m_pBase && ( m_pBase < m_pCommitLimit ) )
{
FreeToAllocPoint( 0, bDecommit );
}
}
//-------------------------------------
void CMemoryStack::Access( void **ppRegion, unsigned *pBytes )
{
*ppRegion = m_pBase;
*pBytes = ( m_pNextAlloc - m_pBase);
}
const char* CMemoryStack::GetMemoryName() const
{
return m_pszAllocOwner;
}
size_t CMemoryStack::GetAllocatedBytes() const
{
return GetUsed();
}
size_t CMemoryStack::GetCommittedBytes() const
{
return GetSize();
}
size_t CMemoryStack::GetReservedBytes() const
{
return GetMaxSize();
}
//size_t CMemoryStack::GetHighestBytes() const
//{
// size_t highest = m_pHighestAllocLimit - m_pBase;
// return highest;
//}
//-------------------------------------
//void CMemoryStack::PrintContents() const
//{
// size_t highest = m_pHighestAllocLimit - m_pBase;
// MEMORY_BASIC_INFORMATION info;
// char moduleName[260];
// strcpy( moduleName, "unknown module" );
// // Because this code is statically linked into each DLL, this function and the PrintStatus
// // function will be in the DLL that constructed the CMemoryStack object. We can then
// // retrieve the DLL name to give slightly more verbose memory dumps.
// if ( VirtualQuery( &PrintStatus, &info, sizeof( info ) ) == sizeof( info ) )
// {
// GetModuleFileNameA( (HMODULE) info.AllocationBase, moduleName, _countof( moduleName ) );
// moduleName[ _countof( moduleName )-1 ] = 0;
// }
// DevMsg( eDLL_T::COMMON, "CMemoryStack %s in %s\n", m_pszAllocOwner, moduleName );
// DevMsg( eDLL_T::COMMON, " Total used memory: %d KB\n", GetUsed() / 1024 );
// DevMsg( eDLL_T::COMMON, " Total committed memory: %d KB\n", GetSize() / 1024 );
// DevMsg( eDLL_T::COMMON, " Max committed memory: %u KB out of %d KB\n", (unsigned)highest / 1024, GetMaxSize() / 1024 );
//}

244
r5dev/tier1/memstack.h Normal file
View File

@ -0,0 +1,244 @@
//===== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose: A fast stack memory allocator that uses virtual memory if available
//
//===========================================================================//
#ifndef MEMSTACK_H
#define MEMSTACK_H
#if defined( _WIN32 )
#pragma once
#endif
#include "tier1/utlvector.h"
#include "public/imemalloc.h"
#if defined( _WIN32 ) || defined( _PS3 )
#define MEMSTACK_VIRTUAL_MEMORY_AVAILABLE
#endif
//-----------------------------------------------------------------------------
typedef unsigned MemoryStackMark_t;
class CMemoryStack : private IMemoryInfo
{
public:
CMemoryStack();
~CMemoryStack();
bool Init( const char *pszAllocOwner, unsigned maxSize = 0, unsigned commitIncrement = 0, unsigned initialCommit = 0, unsigned alignment = 16 );
#ifdef _GAMECONSOLE
bool InitPhysical( const char *pszAllocOwner, uint size, uint nBaseAddrAlignment, uint alignment = 16, uint32 nAdditionalFlags = 0 );
#endif
void Term();
int GetSize() const;
int GetMaxSize() const ;
int GetUsed() const;
void *Alloc( unsigned bytes, bool bClear = false ) RESTRICT;
MemoryStackMark_t GetCurrentAllocPoint() const;
void FreeToAllocPoint( MemoryStackMark_t mark, bool bDecommit = true );
void FreeAll( bool bDecommit = true );
void Access( void **ppRegion, unsigned *pBytes );
void PrintContents() const;
void *GetBase();
const void *GetBase() const { return const_cast<CMemoryStack *>(this)->GetBase(); }
bool CommitSize( int nBytes );
void SetAllocOwner( const char *pszAllocOwner );
private:
bool CommitTo( byte * ) RESTRICT;
void RegisterAllocation();
void RegisterDeallocation( bool bShouldSpew );
const char* GetMemoryName() const OVERRIDE; // User friendly name for this stack or pool
size_t GetAllocatedBytes() const OVERRIDE; // Number of bytes currently allocated
size_t GetCommittedBytes() const OVERRIDE; // Bytes committed -- may be greater than allocated.
size_t GetReservedBytes() const OVERRIDE; // Bytes reserved -- may be greater than committed.
size_t GetHighestBytes() const OVERRIDE; // The maximum number of bytes allocated or committed.
byte *m_pNextAlloc; // Current alloc point (m_pNextAlloc - m_pBase == allocated bytes)
byte *m_pCommitLimit; // The current end of the committed memory. On systems without dynamic commit/decommit this is always m_pAllocLimit
byte *m_pAllocLimit; // The top of the allocated address space (m_pBase + m_maxSize)
// Track the highest alloc limit seen.
byte *m_pHighestAllocLimit;
byte *m_pBase;
bool m_bRegisteredAllocation;
bool m_bPhysical;
char *m_pszAllocOwner;
unsigned m_maxSize; // m_maxSize stores how big the stack can grow. It measures the reservation size.
unsigned m_alignment;
#ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE
unsigned m_commitIncrement;
unsigned m_minCommit;
#endif
#if defined( MEMSTACK_VIRTUAL_MEMORY_AVAILABLE ) && defined( _PS3 )
IVirtualMemorySection *m_pVirtualMemorySection;
#endif
private:
// Make the assignment operator and copy constructor private and unimplemented.
CMemoryStack& operator=( const CMemoryStack& );
CMemoryStack( const CMemoryStack& );
};
//-------------------------------------
FORCEINLINE void *CMemoryStack::Alloc( unsigned bytes, bool bClear ) RESTRICT
{
Assert( m_pBase );
bytes = MAX( bytes, m_alignment );
bytes = AlignValue( bytes, m_alignment );
void *pResult = m_pNextAlloc;
byte *pNextAlloc = m_pNextAlloc + bytes;
if ( pNextAlloc > m_pCommitLimit )
{
if ( !CommitTo( pNextAlloc ) )
{
return NULL;
}
}
if ( bClear )
{
memset( pResult, 0, bytes );
}
m_pNextAlloc = pNextAlloc;
m_pHighestAllocLimit = Max( m_pNextAlloc, m_pHighestAllocLimit );
return pResult;
}
//-------------------------------------
inline bool CMemoryStack::CommitSize( int nBytes )
{
if ( GetSize() != nBytes )
{
return CommitTo( m_pBase + nBytes );
}
return true;
}
//-------------------------------------
// How big can this memory stack grow? This is equivalent to how many
// bytes are reserved.
inline int CMemoryStack::GetMaxSize() const
{
return m_maxSize;
}
//-------------------------------------
inline int CMemoryStack::GetUsed() const
{
return ( m_pNextAlloc - m_pBase );
}
//-------------------------------------
inline void *CMemoryStack::GetBase()
{
return m_pBase;
}
//-------------------------------------
inline MemoryStackMark_t CMemoryStack::GetCurrentAllocPoint() const
{
return ( m_pNextAlloc - m_pBase );
}
//-----------------------------------------------------------------------------
// The CUtlMemoryStack class:
// A fixed memory class
//-----------------------------------------------------------------------------
template< typename T, typename I, size_t MAX_SIZE, size_t COMMIT_SIZE = 0, size_t INITIAL_COMMIT = 0 >
class CUtlMemoryStack
{
public:
// constructor, destructor
CUtlMemoryStack( int nGrowSize = 0, int nInitSize = 0 ) { m_MemoryStack.Init( "CUtlMemoryStack", MAX_SIZE * sizeof(T), COMMIT_SIZE * sizeof(T), INITIAL_COMMIT * sizeof(T), 4 ); COMPILE_TIME_ASSERT( sizeof(T) % 4 == 0 ); }
CUtlMemoryStack( T* pMemory, int numElements ) { Assert( 0 ); }
// Can we use this index?
bool IsIdxValid( I i ) const { long x=i; return (x >= 0) && (x < m_nAllocated); }
// Specify the invalid ('null') index that we'll only return on failure
static const I INVALID_INDEX = ( I )-1; // For use with COMPILE_TIME_ASSERT
static I InvalidIndex() { return INVALID_INDEX; }
class Iterator_t
{
Iterator_t( I i ) : index( i ) {}
I index;
friend class CUtlMemoryStack<T,I,MAX_SIZE, COMMIT_SIZE, INITIAL_COMMIT>;
public:
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( m_nAllocated ? 0 : InvalidIndex() ); }
Iterator_t Next( const Iterator_t &it ) const { return Iterator_t( it.index < m_nAllocated ? 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 { long x=it.index; return x >= 0 && x < m_nAllocated; }
Iterator_t InvalidIterator() const { return Iterator_t( InvalidIndex() ); }
// Gets the base address
T* Base() { return (T*)m_MemoryStack.GetBase(); }
const T* Base() const { return (const T*)m_MemoryStack.GetBase(); }
// element access
T& operator[]( I i ) { Assert( IsIdxValid(i) ); return Base()[i]; }
const T& operator[]( I i ) const { Assert( IsIdxValid(i) ); return Base()[i]; }
T& Element( I i ) { Assert( IsIdxValid(i) ); return Base()[i]; }
const T& Element( I 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 m_nAllocated; }
int Count() const { return m_nAllocated; }
// Grows the memory, so that at least allocated + num elements are allocated
void Grow( int num = 1 ) { Assert( num > 0 ); m_nAllocated += num; m_MemoryStack.Alloc( num * sizeof(T) ); }
// Makes sure we've got at least this much memory
void EnsureCapacity( int num ) { Assert( num <= MAX_SIZE ); if ( m_nAllocated < num ) Grow( num - m_nAllocated ); }
// Memory deallocation
void Purge() { m_MemoryStack.FreeAll(); m_nAllocated = 0; }
// is the memory externally allocated?
bool IsExternallyAllocated() const { return false; }
// Set the size by which the memory grows
void SetGrowSize( int size ) { Assert( 0 ); }
// Identify the owner of this memory stack's memory
void SetAllocOwner( const char *pszAllocOwner ) { m_MemoryStack.SetAllocOwner( pszAllocOwner ); }
private:
CMemoryStack m_MemoryStack;
int m_nAllocated;
};
#endif // MEMSTACK_H