r5sdk/r5dev/tier1/mempool.cpp
Kawe Mazidjatari 654da2c816 Tier1: fix CUtlMemoryPool::CBlob structure
Only contains the 'previous' pointer, freeing memory now works.
2024-04-05 17:31:35 +02:00

348 lines
8.2 KiB
C++

//===== Copyright (c) 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
#include <malloc.h>
#endif
// Should be last include
#include "tier0/memdbgon.h"
#include <public/tier0/imemalloc.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, unsigned short nAlignment)
{
#ifdef _X360
if (numElements > 0 && growMode != GROW_NONE)
{
numElements = 1;
}
#endif
m_nAlignment = (nAlignment != 0) ? nAlignment : 1;
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_pNext = m_pPrev = NULL;
}
//-----------------------------------------------------------------------------
// Frees everything
//-----------------------------------------------------------------------------
void CUtlMemoryPool::Clear()
{
// Free everything..
CBlob* pPrev;
for (CBlob* pCur = m_pNext; pCur; pCur = pPrev)
{
pPrev = pCur->m_pPrev;
free(pCur);
}
Init();
}
//-----------------------------------------------------------------------------
// Is an allocation within the pool?
//-----------------------------------------------------------------------------
bool CUtlMemoryPool::IsAllocationWithinPool(void* pMem) const
{
for (CBlob* pCur = m_pNext; pCur; pCur = pCur->m_pPrev)
{
// 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_pPrev != NULL)
{
void* next = *((void**)m_pPrev);
memset(m_pPrev, 0, m_BlockSize);
m_pPrev = (CBlob*)next;
}
g_ReportFunc("Dumping memory: \'");
for (CBlob* pCur = m_pNext; pCur; pCur = pCur->m_pPrev)
{
// 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 && 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_pPrev = m_pNext;
m_pNext = pBlob;
m_pPrev = (CBlob*)AlignValue(pBlob->m_Data, m_nAlignment);
Assert(m_pPrev);
void** newBlob = (void**)m_pPrev;
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)
{
if (amount > (size_t)m_BlockSize)
return NULL;
if (!m_pPrev)
{
// 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_pPrev)
{
Assert(!"CUtlMemoryPool::Alloc: ran out of memory");
return NULL;
}
}
m_BlocksAllocated++;
m_PeakAlloc = MAX(m_PeakAlloc, m_BlocksAllocated);
void* returnBlock = m_pPrev;
// move the pointer the next block
m_pPrev = m_pPrev->m_pPrev;
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)
{
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_pNext; pCur; pCur = pCur->m_pPrev)
{
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_pPrev;
// the list head is now the new block
m_pPrev = (CBlob*)memBlock;
}
int CUtlMemoryPool::Size() const
{
uint32 size = 0;
for (CBlob* pCur = m_pNext; pCur; pCur = pCur->m_pPrev)
{
size += pCur->m_NumBytes;
}
return size;
}