/******************
CMemoryPool.cpp
******************/
/*!\file CMemoryPool.cpp
* \brief CMemoryPool implementation.
*/
#include "../BasicIncludes.h"
#include "SMemoryChunk.h"
#include "CMemoryPool.h"
namespace MemPool
{
static const int FREEED_MEMORY_CONTENT = 0xAA ; //!< Value for freed memory
static const int NEW_ALLOCATED_MEMORY_CONTENT = 0xFF ; //!< Initial Value for new allocated memory
/******************
Constructor
******************/
CMemoryPool::CMemoryPool(const std::size_t &sInitialMemoryPoolSize,
const std::size_t &sMemoryChunkSize,
const std::size_t &sMinimalMemorySizeToAllocate,
bool bSetMemoryData)
{
m_ptrFirstChunk = NULL ;
m_ptrLastChunk = NULL ;
m_ptrCursorChunk = NULL ;
m_sTotalMemoryPoolSize = 0 ;
m_sUsedMemoryPoolSize = 0 ;
m_sFreeMemoryPoolSize = 0 ;
m_sMemoryChunkSize = sMemoryChunkSize ;
m_uiMemoryChunkCount = 0 ;
m_uiObjectCount = 0 ;
m_bSetMemoryData = bSetMemoryData ;
m_sMinimalMemorySizeToAllocate = sMinimalMemorySizeToAllocate ;
// Allocate the Initial amount of Memory from the Operating-System...
AllocateMemory(sInitialMemoryPoolSize) ;
}
/******************
Destructor
******************/
CMemoryPool::~CMemoryPool()
{
FreeAllAllocatedMemory() ;
DeallocateAllChunks() ;
// Check for possible Memory-Leaks...
assert((m_uiObjectCount == 0) && "WARNING : Memory-Leak : You have not freed all allocated Memory") ;
}
/******************
GetMemory
******************/
void *CMemoryPool::GetMemory(const std::size_t &sMemorySize)
{
std::size_t sBestMemBlockSize = CalculateBestMemoryBlockSize(sMemorySize) ;
SMemoryChunk *ptrChunk = NULL ;
while(!ptrChunk)
{
// Is a Chunks available to hold the requested amount of Memory ?
ptrChunk = FindChunkSuitableToHoldMemory(sBestMemBlockSize) ;
if(!ptrChunk)
{
// No chunk can be found
// => Memory-Pool is to small. We have to request
// more Memory from the Operating-System....
sBestMemBlockSize = MaxValue(sBestMemBlockSize, CalculateBestMemoryBlockSize(m_sMinimalMemorySizeToAllocate)) ;
AllocateMemory(sBestMemBlockSize) ;
}
}
// Finally, a suitable Chunk was found.
// Adjust the Values of the internal "TotalSize"/"UsedSize" Members and
// the Values of the MemoryChunk itself.
m_sUsedMemoryPoolSize += sBestMemBlockSize ;
m_sFreeMemoryPoolSize -= sBestMemBlockSize ;
m_uiObjectCount++ ;
SetMemoryChunkValues(ptrChunk, sBestMemBlockSize) ;
// eventually, return the Pointer to the User
return ((void *) ptrChunk->Data) ;
}
/******************
FreeMemory
******************/
void CMemoryPool::FreeMemory(void *ptrMemoryBlock, const std::size_t &sMemoryBlockSize)
{
// Search all Chunks for the one holding the "ptrMemoryBlock"-Pointer
// ("SMemoryChunk->Data == ptrMemoryBlock"). Eventually, free that Chunks,
// so it beecomes available to the Memory-Pool again...
SMemoryChunk *ptrChunk = FindChunkHoldingPointerTo(ptrMemoryBlock) ;
if(ptrChunk)
{
//std::cerr << "Freed Chunks OK (Used memPool Size : " << m_sUsedMemoryPoolSize << ")" << std::endl ;
FreeChunks(ptrChunk) ;
}
else
{
assert(false && "ERROR : Requested Pointer not in Memory Pool") ;
}
assert((m_uiObjectCount > 0) && "ERROR : Request to delete more Memory then allocated.") ;
m_uiObjectCount-- ;
}
/******************
AllocateMemory
******************/
bool CMemoryPool::AllocateMemory(const std::size_t &sMemorySize)
{
// This function will allocate *at least* "sMemorySize"-Bytes from the Operating-System.
// How it works :
// Calculate the amount of "SMemoryChunks" needed to manage the requested MemorySize.
// Every MemoryChunk can manage only a certain amount of Memory
// (set by the "m_sMemoryChunkSize"-Member of the Memory-Pool).
//
// Also, calculate the "Best" Memory-Block size to allocate from the
// Operating-System, so that all allocated Memory can be assigned to a
// Memory Chunk.
// Example :
// You want to Allocate 120 Bytes, but every "SMemoryChunk" can only handle
// 50 Bytes ("m_sMemoryChunkSize = 50").
// So, "CalculateNeededChunks()" will return the Number of Chunks needed to
// manage 120 Bytes. Since it is not possible to divide 120 Bytes in to
// 50 Byte Chunks, "CalculateNeededChunks()" returns 3.
// ==> 3 Chunks can Manage 150 Bytes of data (50 * 3 = 150), so
// the requested 120 Bytes will fit into this Block.
// "CalculateBestMemoryBlockSize()" will return the amount of memory needed
// to *perfectly* subdivide the allocated Memory into "m_sMemoryChunkSize" (= 50) Byte
// pieces. -> "CalculateBestMemoryBlockSize()" returns 150.
// So, 150 Bytes of memory are allocated from the Operating-System and
// subdivided into 3 Memory-Chunks (each holding a Pointer to 50 Bytes of the allocated memory).
// Since only 120 Bytes are requested, we have a Memory-Overhead of
// 150 - 120 = 30 Bytes.
// Note, that the Memory-overhead is not a bad thing, because we can use
// that memory later (it remains in the Memory-Pool).
//
unsigned int uiNeededChunks = CalculateNeededChunks(sMemorySize) ;
std::size_t sBestMemBlockSize = CalculateBestMemoryBlockSize(sMemorySize) ;
TByte *ptrNewMemBlock = (TByte *) malloc(sBestMemBlockSize) ; // allocate from Operating System
SMemoryChunk *ptrNewChunks = (SMemoryChunk *) malloc((uiNeededChunks * sizeof(SMemoryChunk))) ; // allocate Chunk-Array to Manage the Memory
assert(((ptrNewMemBlock) && (ptrNewChunks)) && "Error : System ran out of Memory") ;
// Adjust internal Values (Total/Free Memory, etc.)
m_sTotalMemoryPoolSize += sBestMemBlockSize ;
m_sFreeMemoryPoolSize += sBestMemBlockSize ;
m_uiMemoryChunkCount += uiNeededChunks ;
// Usefull for Debugging : Set the Memory-Content to a defined Value
if(m_bSetMemoryData)
{
memset(((void *) ptrNewMemBlock), NEW_ALLOCATED_MEMORY_CONTENT, sBestMemBlockSize) ;
}
// Associate the allocated Memory-Block with the Linked-List of MemoryChunks
return LinkChunksToData(ptrNewChunks, uiNeededChunks, ptrNewMemBlock) ; ;
}
/******************
CalculateNeededChunks
******************/
unsigned int CMemoryPool::CalculateNeededChunks(const std::size_t &sMemorySize)
{
float f = (float) (((float)sMemorySize) / ((float)m_sMemoryChunkSize)) ;
return ((unsigned int) ceil(f)) ;
}
/******************
CalculateBestMemoryBlockSize
******************/
std::size_t CMemoryPool::CalculateBestMemoryBlockSize(const std::size_t &sRequestedMemoryBlockSize)
{
unsigned int uiNeededChunks = CalculateNeededChunks(sRequestedMemoryBlockSize) ;
return std::size_t((uiNeededChunks * m_sMemoryChunkSize)) ;
}
/******************
FreeChunks
******************/
void CMemoryPool::FreeChunks(SMemoryChunk *ptrChunk)
{
// Make the Used Memory of the given Chunk available
// to the Memory Pool again.
SMemoryChunk *ptrCurrentChunk = ptrChunk ;
unsigned int uiChunkCount = CalculateNeededChunks(ptrCurrentChunk->UsedSize);
for(unsigned int i = 0; i < uiChunkCount; i++)
{
if(ptrCurrentChunk)
{
// Step 1 : Set the allocated Memory to 'FREEED_MEMORY_CONTENT'
// Note : This is fully Optional, but usefull for debugging
if(m_bSetMemoryData)
{
memset(((void *) ptrCurrentChunk->Data), FREEED_MEMORY_CONTENT, m_sMemoryChunkSize) ;
}
// Step 2 : Set the Used-Size to Zero
ptrCurrentChunk->UsedSize = 0 ;
// Step 3 : Adjust Memory-Pool Values and goto next Chunk
m_sUsedMemoryPoolSize -= m_sMemoryChunkSize ;
ptrCurrentChunk = ptrCurrentChunk->Next ;
}
}
}
/******************
FindChunkSuitableToHoldMemory
******************/
SMemoryChunk *CMemoryPo