sqwarmed/sdk_src/public/tier1/memstack.h

346 lines
9.5 KiB
C++

//===== Copyright © 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"
//-----------------------------------------------------------------------------
typedef unsigned MemoryStackMark_t;
class CMemoryStack
{
public:
CMemoryStack();
~CMemoryStack();
bool Init( unsigned maxSize = 0, unsigned commitSize = 0, unsigned initialCommit = 0, unsigned alignment = 16 );
#ifdef _X360
bool InitPhysical( uint size, uint nBaseAddrAlignment, uint alignment = 16, uint32 nAdditionalFlags = 0 );
#endif
void Term();
int GetSize();
int GetMaxSize();
int GetUsed();
void *Alloc( unsigned bytes, bool bClear = false ) RESTRICT;
MemoryStackMark_t GetCurrentAllocPoint();
void FreeToAllocPoint( MemoryStackMark_t mark, bool bDecommit = true );
void FreeAll( bool bDecommit = true );
void Access( void **ppRegion, unsigned *pBytes );
void PrintContents();
void *GetBase();
const void *GetBase() const { return const_cast<CMemoryStack *>(this)->GetBase(); }
bool CommitSize( int );
private:
bool CommitTo( byte * ) RESTRICT;
void RegisterAllocation();
void RegisterDeallocation();
byte *m_pNextAlloc;
byte *m_pCommitLimit;
byte *m_pAllocLimit;
byte *m_pBase;
bool m_bRegisteredAllocation;
unsigned m_maxSize;
unsigned m_alignment;
#ifdef _WIN32
unsigned m_commitSize;
unsigned m_minCommit;
#endif
#ifdef _X360
bool m_bPhysical;
#endif
};
//-------------------------------------
FORCEINLINE void *CMemoryStack::Alloc( unsigned bytes, bool bClear ) RESTRICT
{
Assert( m_pBase );
int alignment = m_alignment;
if ( bytes )
{
bytes = AlignValue( bytes, alignment );
}
else
{
bytes = 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;
return pResult;
}
//-------------------------------------
inline bool CMemoryStack::CommitSize( int nBytes )
{
if ( GetSize() != nBytes )
{
return CommitTo( m_pBase + nBytes );
}
return true;
}
//-------------------------------------
inline int CMemoryStack::GetMaxSize()
{
return m_maxSize;
}
//-------------------------------------
inline int CMemoryStack::GetUsed()
{
return ( m_pNextAlloc - m_pBase );
}
//-------------------------------------
inline void *CMemoryStack::GetBase()
{
return m_pBase;
}
//-------------------------------------
inline MemoryStackMark_t CMemoryStack::GetCurrentAllocPoint()
{
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( 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 ) {}
private:
CMemoryStack m_MemoryStack;
int m_nAllocated;
};
#ifdef _X360
//-----------------------------------------------------------------------------
// A memory stack used for allocating physical memory on the 360
// Usage pattern anticipates we usually never go over the initial allocation
// When we do so, we're ok with slightly slower allocation
//-----------------------------------------------------------------------------
class CPhysicalMemoryStack
{
public:
CPhysicalMemoryStack();
~CPhysicalMemoryStack();
// The physical memory stack is allocated in chunks. We will initially
// allocate nInitChunkCount chunks, which will always be in memory.
// When FreeAll() is called, it will free down to the initial chunk count
// but not below it.
bool Init( size_t nChunkSizeInBytes, size_t nAlignment, int nInitialChunkCount, uint32 nAdditionalFlags );
void Term();
size_t GetSize() const;
size_t GetPeakUsed() const;
size_t GetUsed() const;
size_t GetFramePeakUsed() const;
MemoryStackMark_t GetCurrentAllocPoint() const;
void FreeToAllocPoint( MemoryStackMark_t mark, bool bUnused = true ); // bUnused is for interface compat with CMemoryStack
void *Alloc( size_t nSizeInBytes, bool bClear = false ) RESTRICT;
void FreeAll( bool bUnused = true ); // bUnused is for interface compat with CMemoryStack
void PrintContents();
private:
void *AllocFromOverflow( size_t nSizeInBytes );
struct PhysicalChunk_t
{
uint8 *m_pBase;
uint8 *m_pNextAlloc;
uint8 *m_pAllocLimit;
};
PhysicalChunk_t m_InitialChunk;
CUtlVector< PhysicalChunk_t > m_ExtraChunks;
size_t m_nUsage;
size_t m_nFramePeakUsage;
size_t m_nPeakUsage;
size_t m_nAlignment;
size_t m_nChunkSizeInBytes;
int m_nFirstAvailableChunk;
int m_nAdditionalFlags;
PhysicalChunk_t *m_pLastAllocedChunk;
};
//-------------------------------------
FORCEINLINE void *CPhysicalMemoryStack::Alloc( size_t nSizeInBytes, bool bClear ) RESTRICT
{
if ( nSizeInBytes )
{
nSizeInBytes = AlignValue( nSizeInBytes, m_nAlignment );
}
else
{
nSizeInBytes = m_nAlignment;
}
// Can't do an allocation bigger than the chunk size
Assert( nSizeInBytes <= m_nChunkSizeInBytes );
void *pResult = m_InitialChunk.m_pNextAlloc;
uint8 *pNextAlloc = m_InitialChunk.m_pNextAlloc + nSizeInBytes;
if ( pNextAlloc <= m_InitialChunk.m_pAllocLimit )
{
m_InitialChunk.m_pNextAlloc = pNextAlloc;
m_pLastAllocedChunk = &m_InitialChunk;
}
else
{
pResult = AllocFromOverflow( nSizeInBytes );
}
m_nUsage += nSizeInBytes;
m_nFramePeakUsage = MAX( m_nUsage, m_nFramePeakUsage );
m_nPeakUsage = MAX( m_nUsage, m_nPeakUsage );
if ( bClear )
{
memset( pResult, 0, nSizeInBytes );
}
return pResult;
}
//-------------------------------------
inline size_t CPhysicalMemoryStack::GetPeakUsed() const
{
return m_nPeakUsage;
}
//-------------------------------------
inline size_t CPhysicalMemoryStack::GetUsed() const
{
return m_nUsage;
}
inline size_t CPhysicalMemoryStack::GetFramePeakUsed() const
{
return m_nFramePeakUsage;
}
inline MemoryStackMark_t CPhysicalMemoryStack::GetCurrentAllocPoint() const
{
Assert( m_pLastAllocedChunk );
return ( m_pLastAllocedChunk->m_pNextAlloc - m_pLastAllocedChunk->m_pBase );
}
#endif // _X360
#endif // MEMSTACK_H