After taking a break and reviewing the comments on my first attempt at creating a fixed-size memory pool, this is my second attempt.
The memory pool should allocate each block correctly aligned in memory, so that when I call placement new on the returned memory, it isn't misaligned. This is my main goal with this implementation.
The pool is implemented as a free-list, with each block looking something like this in memory:
------------------------------------------- | Next Block Index | Padding | Object Data | -------------------------------------------
Here's my .h
#include <cstdint> class FixedSizeMemoryPool { public: FixedSizeMemoryPool(const uint32_t objectSize, const uint32_t nObjects); ~FixedSizeMemoryPool(); FixedSizeMemoryPool(const FixedSizeMemoryPool&)=delete; FixedSizeMemoryPool&operator=(const FixedSizeMemoryPool&)=delete; FixedSizeMemoryPool(FixedSizeMemoryPool&&)=delete; FixedSizeMemoryPool&operator=(FixedSizeMemoryPool&&)=delete; uint8_t* Alloc(); void Free(void* data); const uint32_t GetCapacity() const; const uint32_t GetFreeBlockCount() const; private: const uint32_t GetIndex(void* data) const; const uint32_t CalculatePadding(const uint32_t objectSize) const; uint32_t m_nFreeBlocks; uint32_t m_Capacity; uint32_t m_BlockSize; uint32_t m_PaddingSize; uint32_t m_NextFreeBlock; uint8_t* m_pData; };
And the .cpp:
FixedSizeMemoryPool::FixedSizeMemoryPool(const uint32_t objectSize, const uint32_t nObjects) : m_pData(nullptr), m_BlockSize(0), m_PaddingSize(0), m_Capacity(nObjects), m_nFreeBlocks(nObjects), m_NextFreeBlock(0) { const uint32_t headerSize = sizeof(uint32_t); const uint32_t totalObjectSize = headerSize + objectSize; m_PaddingSize = CalculatePadding(totalObjectSize); m_BlockSize = totalObjectSize + m_PaddingSize; m_pData = new uint8_t[m_BlockSize * nObjects]; //Set up the next free block for(unsigned int i = 0; i < nObjects; ++i) { uint8_t* hPtr = m_pData + (i * m_BlockSize); *((uint32_t*)hPtr) = i + 1; } } FixedSizeMemoryPool::~FixedSizeMemoryPool() { delete[] m_pData; } uint8_t *FixedSizeMemoryPool::Alloc() { if(m_NextFreeBlock == m_Capacity) { return nullptr; } uint8_t* ptr = m_pData + (m_NextFreeBlock * m_BlockSize); m_NextFreeBlock = *((uint32_t*)ptr); ptr += sizeof(uint32_t); ptr += m_PaddingSize; --m_nFreeBlocks; return ptr; } void FixedSizeMemoryPool::Free(void* data) { uint8_t* pData = (uint8_t*)data - m_PaddingSize - sizeof(uint32_t); *((uint32_t*)pData) = m_NextFreeBlock; m_NextFreeBlock = GetIndex(data); ++m_nFreeBlocks; } const uint32_t FixedSizeMemoryPool::GetIndex(void *data) const { return ((uint8_t*)data - m_pData) / m_BlockSize; } const uint32_t FixedSizeMemoryPool::CalculatePadding( const uint32_t objectSize) const { //Check the object size is a power of 2 if((objectSize & (objectSize - 1)) != 0) { //Get next highest power of 2 uint32_t paddingSize = 0; paddingSize = objectSize - 1; paddingSize |= m_PaddingSize >> 1; paddingSize |= m_PaddingSize >> 2; paddingSize |= m_PaddingSize >> 4; paddingSize |= m_PaddingSize >> 8; paddingSize |= m_PaddingSize >> 16; ++paddingSize ; paddingSize = paddingSize - objectSize; return paddingSize; } return 0; } const uint32_t FixedSizeMemoryPool::GetCapacity() const { return m_Capacity; } const uint32_t FixedSizeMemoryPool::GetFreeBlockCount() const { return m_nFreeBlocks; }
uint32_t
(presumably intended to bestd::uint32_t
, though that's not a given) for the object size and count, instead ofstd::size_t
? Is there a good reason to limit objects to be less than 4GB, or to hold more than 4 billion of them?\$\endgroup\$