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.
356 lines
8.7 KiB
356 lines
8.7 KiB
//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// |
|
// |
|
// Purpose: |
|
// |
|
//===========================================================================// |
|
|
|
#include "tier1/mempool.h" |
|
#include <stdio.h> |
|
#include <memory.h> |
|
#include "tier0/dbg.h" |
|
#include <ctype.h> |
|
#include "tier1/strtools.h" |
|
|
|
#ifndef _PS3 |
|
#ifdef _OSX |
|
#include <malloc/malloc.h> |
|
#else |
|
#include <malloc.h> |
|
#endif |
|
#endif |
|
|
|
// Should be last include |
|
#include "tier0/memdbgon.h" |
|
|
|
MemoryPoolReportFunc_t CUtlMemoryPool::g_ReportFunc = 0; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Error reporting... (debug only) |
|
//----------------------------------------------------------------------------- |
|
|
|
void CUtlMemoryPool::SetErrorReportFunc( MemoryPoolReportFunc_t func ) |
|
{ |
|
g_ReportFunc = func; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CUtlMemoryPool::CUtlMemoryPool( int blockSize, int numElements, int growMode, const char *pszAllocOwner, int nAlignment ) |
|
{ |
|
#ifdef _X360 |
|
if( numElements > 0 && growMode != GROW_NONE ) |
|
{ |
|
numElements = 1; |
|
} |
|
#endif |
|
|
|
#ifdef PLATFORM_64BITS |
|
m_nAlignment = ( nAlignment != 0 ) ? nAlignment : 8; |
|
#else |
|
m_nAlignment = ( nAlignment != 0 ) ? nAlignment : 4; |
|
#endif |
|
|
|
Assert( IsPowerOfTwo( m_nAlignment ) ); |
|
m_BlockSize = blockSize < sizeof(void*) ? sizeof(void*) : blockSize; |
|
m_BlockSize = AlignValue( m_BlockSize, m_nAlignment ); |
|
m_BlocksPerBlob = numElements; |
|
m_PeakAlloc = 0; |
|
m_GrowMode = growMode; |
|
if ( !pszAllocOwner ) |
|
{ |
|
pszAllocOwner = __FILE__; |
|
} |
|
m_pszAllocOwner = pszAllocOwner; |
|
Init(); |
|
AddNewBlob(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Frees the memory contained in the mempool, and invalidates it for |
|
// any further use. |
|
// Input : *memPool - the mempool to shutdown |
|
//----------------------------------------------------------------------------- |
|
CUtlMemoryPool::~CUtlMemoryPool() |
|
{ |
|
if (m_BlocksAllocated > 0) |
|
{ |
|
ReportLeaks(); |
|
} |
|
Clear(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Resets the pool |
|
//----------------------------------------------------------------------------- |
|
void CUtlMemoryPool::Init() |
|
{ |
|
m_NumBlobs = 0; |
|
m_BlocksAllocated = 0; |
|
m_pHeadOfFreeList = 0; |
|
m_BlobHead.m_pNext = m_BlobHead.m_pPrev = &m_BlobHead; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Frees everything |
|
//----------------------------------------------------------------------------- |
|
void CUtlMemoryPool::Clear() |
|
{ |
|
// Free everything.. |
|
CBlob *pNext; |
|
for( CBlob *pCur = m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur = pNext ) |
|
{ |
|
pNext = pCur->m_pNext; |
|
free( pCur ); |
|
} |
|
Init(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Is an allocation within the pool? |
|
//----------------------------------------------------------------------------- |
|
bool CUtlMemoryPool::IsAllocationWithinPool( void *pMem ) const |
|
{ |
|
for( CBlob *pCur = m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur = pCur->m_pNext ) |
|
{ |
|
// Is the allocation within the blob? |
|
if ( ( pMem < pCur->m_Data ) || ( pMem >= pCur->m_Data + pCur->m_NumBytes ) ) |
|
continue; |
|
|
|
// Make sure the allocation is on a block boundary |
|
intp pFirstAllocation = AlignValue( ( intp ) pCur->m_Data, m_nAlignment ); |
|
|
|
intp nOffset = (intp)pMem - pFirstAllocation; |
|
return ( nOffset % m_BlockSize ) == 0; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Reports memory leaks |
|
//----------------------------------------------------------------------------- |
|
void CUtlMemoryPool::ReportLeaks() |
|
{ |
|
#ifdef _DEBUG |
|
if (!g_ReportFunc) |
|
return; |
|
|
|
g_ReportFunc("Memory leak: mempool blocks left in memory: %d\n", m_BlocksAllocated); |
|
|
|
// walk and destroy the free list so it doesn't intefere in the scan |
|
while (m_pHeadOfFreeList != NULL) |
|
{ |
|
void *next = *((void**)m_pHeadOfFreeList); |
|
memset(m_pHeadOfFreeList, 0, m_BlockSize); |
|
m_pHeadOfFreeList = next; |
|
} |
|
|
|
g_ReportFunc("Dumping memory: \'"); |
|
|
|
for( CBlob *pCur=m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur=pCur->m_pNext ) |
|
{ |
|
// scan the memory block and dump the leaks |
|
char *scanPoint = (char *)pCur->m_Data; |
|
char *scanEnd = pCur->m_Data + pCur->m_NumBytes; |
|
bool needSpace = false; |
|
|
|
while (scanPoint < scanEnd) |
|
{ |
|
// search for and dump any strings |
|
if ((unsigned)(*scanPoint + 1) <= 256 && V_isprint(*scanPoint)) |
|
{ |
|
g_ReportFunc("%c", *scanPoint); |
|
needSpace = true; |
|
} |
|
else if (needSpace) |
|
{ |
|
needSpace = false; |
|
g_ReportFunc(" "); |
|
} |
|
|
|
scanPoint++; |
|
} |
|
} |
|
|
|
g_ReportFunc("\'\n"); |
|
#endif // _DEBUG |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CUtlMemoryPool::AddNewBlob() |
|
{ |
|
MEM_ALLOC_CREDIT_(m_pszAllocOwner); |
|
|
|
int sizeMultiplier; |
|
|
|
if( m_GrowMode == GROW_SLOW ) |
|
{ |
|
sizeMultiplier = 1; |
|
} |
|
else |
|
{ |
|
if ( m_GrowMode == GROW_NONE ) |
|
{ |
|
// Can only have one allocation when we're in this mode |
|
if( m_NumBlobs != 0 ) |
|
{ |
|
Assert( !"CUtlMemoryPool::AddNewBlob: mode == GROW_NONE" ); |
|
return; |
|
} |
|
} |
|
|
|
// GROW_FAST and GROW_NONE use this. |
|
sizeMultiplier = m_NumBlobs + 1; |
|
} |
|
|
|
// maybe use something other than malloc? |
|
int nElements = m_BlocksPerBlob * sizeMultiplier; |
|
int blobSize = m_BlockSize * nElements; |
|
CBlob *pBlob = (CBlob*)malloc( sizeof(CBlob) - 1 + blobSize + ( m_nAlignment - 1 ) ); |
|
Assert( pBlob ); |
|
|
|
// Link it in at the end of the blob list. |
|
pBlob->m_NumBytes = blobSize; |
|
pBlob->m_pNext = &m_BlobHead; |
|
pBlob->m_pPrev = pBlob->m_pNext->m_pPrev; |
|
pBlob->m_pNext->m_pPrev = pBlob->m_pPrev->m_pNext = pBlob; |
|
|
|
// setup the free list |
|
m_pHeadOfFreeList = AlignValue( pBlob->m_Data, m_nAlignment ); |
|
Assert (m_pHeadOfFreeList); |
|
|
|
void **newBlob = (void**)m_pHeadOfFreeList; |
|
for (int j = 0; j < nElements-1; j++) |
|
{ |
|
newBlob[0] = (char*)newBlob + m_BlockSize; |
|
newBlob = (void**)newBlob[0]; |
|
} |
|
|
|
// null terminate list |
|
newBlob[0] = NULL; |
|
m_NumBlobs++; |
|
} |
|
|
|
|
|
void* CUtlMemoryPool::Alloc() |
|
{ |
|
return Alloc( m_BlockSize ); |
|
} |
|
|
|
|
|
void* CUtlMemoryPool::AllocZero() |
|
{ |
|
return AllocZero( m_BlockSize ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Allocs a single block of memory from the pool. |
|
// Input : amount - |
|
//----------------------------------------------------------------------------- |
|
void *CUtlMemoryPool::Alloc( size_t amount ) |
|
{ |
|
void *returnBlock; |
|
|
|
if ( amount > (size_t)m_BlockSize ) |
|
return NULL; |
|
|
|
if ( !m_pHeadOfFreeList ) |
|
{ |
|
// returning NULL is fine in GROW_NONE |
|
if ( m_GrowMode == GROW_NONE && m_NumBlobs > 0 ) |
|
{ |
|
//Assert( !"CUtlMemoryPool::Alloc: tried to make new blob with GROW_NONE" ); |
|
return NULL; |
|
} |
|
|
|
// overflow |
|
AddNewBlob(); |
|
|
|
// still failure, error out |
|
if ( !m_pHeadOfFreeList ) |
|
{ |
|
Assert( !"CUtlMemoryPool::Alloc: ran out of memory" ); |
|
return NULL; |
|
} |
|
} |
|
m_BlocksAllocated++; |
|
m_PeakAlloc = MAX(m_PeakAlloc, m_BlocksAllocated); |
|
|
|
returnBlock = m_pHeadOfFreeList; |
|
|
|
// move the pointer the next block |
|
m_pHeadOfFreeList = *((void**)m_pHeadOfFreeList); |
|
|
|
return returnBlock; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Allocs a single block of memory from the pool, zeroes the memory before returning |
|
// Input : amount - |
|
//----------------------------------------------------------------------------- |
|
void *CUtlMemoryPool::AllocZero( size_t amount ) |
|
{ |
|
void *mem = Alloc( amount ); |
|
if ( mem ) |
|
{ |
|
V_memset( mem, 0x00, ( int )amount ); |
|
} |
|
return mem; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Frees a block of memory |
|
// Input : *memBlock - the memory to free |
|
//----------------------------------------------------------------------------- |
|
void CUtlMemoryPool::Free( void *memBlock ) |
|
{ |
|
if ( !memBlock ) |
|
return; // trying to delete NULL pointer, ignore |
|
|
|
#ifdef _DEBUG |
|
// check to see if the memory is from the allocated range |
|
bool bOK = false; |
|
for( CBlob *pCur=m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur=pCur->m_pNext ) |
|
{ |
|
if (memBlock >= pCur->m_Data && (char*)memBlock < (pCur->m_Data + pCur->m_NumBytes)) |
|
{ |
|
bOK = true; |
|
} |
|
} |
|
Assert (bOK); |
|
#endif // _DEBUG |
|
|
|
#ifdef _DEBUG |
|
// invalidate the memory |
|
memset( memBlock, 0xDD, m_BlockSize ); |
|
#endif |
|
|
|
m_BlocksAllocated--; |
|
|
|
// make the block point to the first item in the list |
|
*((void**)memBlock) = m_pHeadOfFreeList; |
|
|
|
// the list head is now the new block |
|
m_pHeadOfFreeList = memBlock; |
|
} |
|
|
|
int CUtlMemoryPool::Size() const |
|
{ |
|
uint32 size = 0; |
|
|
|
for( CBlob *pCur=m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur=pCur->m_pNext ) |
|
{ |
|
size += pCur->m_NumBytes; |
|
} |
|
return size; |
|
} |
|
|
|
|