Skip to main content
edited title
Link
Deduplicator
  • 19.3k
  • 1
  • 31
  • 65

Immutable Fixed-Size Memory Pool

edited tags
Link
200_success
  • 144.2k
  • 22
  • 188
  • 473
Source Link
Acorn
  • 283
  • 1
  • 7

Immutable Memory Pool

I've been reading about Memory Pools since I came across them in the book Game Programming Complete 4th Edition, and I decided to try and spin my own since I didn't quite understand how the one they presented in the book worked at the time.

One thing I'm uncertain of is defining the deleter in the shared_ptr. I don't like using delegates when they aren't needed, but if I try to pass in the address to the function pointer as the deleter then the program will not compile.

Memory chunks are stored in a singly-linked list, with the first N bytes of each entry in the list being a pointer to the next entry.

While it's nifty using pointer majicks to store the pointer in the head of each chunk, it is scary to look at when you don't understand pointer-to-pointers (I know I didn't when I first tried to roll it out!).

Any suggestions on changes? Improvements? Bugs I didn't see?

#include <memory>
namespace acorn
{

    template<class Object>
    //An immutable memory pool does not grow or shrink in size.
    class ImmutableMemoryPool
    {
        typedef unsigned char byte;

        //A pointer to the block of data we will be allocating.
        byte* m_pBlock;

        //A pointer to the current chunk
        byte* m_pCurrentChunk;

        //A pointer to the last chunk
        byte* m_pLastChunk;

        unsigned int m_NumberOfChunks;
        unsigned int m_SizeOfChunk;
        unsigned int m_SizeOfChunkData;
        unsigned int m_SizeOfChunkHeader;

        unsigned int m_SizeOfBlock;




    public:
        ImmutableMemoryPool(unsigned int numberOfChunks) : m_NumberOfChunks(numberOfChunks), m_SizeOfChunkData(sizeof(Object))
        {

            //Set the size of the header of each chunk
            m_SizeOfChunkHeader = sizeof(byte*);

            //Calculate the size of a whole chunk
            m_SizeOfChunk = m_SizeOfChunkHeader + m_SizeOfChunkData;

            //Calculate the size of the block of data
            m_SizeOfBlock = m_SizeOfChunk * m_NumberOfChunks;


            //Allocate a new block of memory
            m_pBlock = new byte[m_SizeOfBlock];

            //Initialize the chunk header data
            InitializeHeaders();

            //Set the current chunk pointer to point at the start of the block
            m_pCurrentChunk = m_pBlock;

            //Set the last chunk poitner to point at the last chunk
            m_pLastChunk = m_pBlock + m_SizeOfBlock - m_SizeOfChunk;

        }

        ~ImmutableMemoryPool()
        {
            delete[] m_pBlock;
        }


        //Places a chunk of memory back into the pool
        void Dealloc(Object* pData)
        {
            byte* pConvertedData = (byte*)pData;
            //Sure, call it on a nullptr - I don't care I'll just ignore it HA
            if (pConvertedData != nullptr) {
                //Make sure this pointer belongs to this pool
                if (pConvertedData < (m_pBlock + m_SizeOfBlock) && pConvertedData > m_pBlock)
                {

                    //Get the header
                    byte* pHeader = (byte*)pConvertedData - m_SizeOfChunkHeader;

                    //Set the tail chunk to now point to this returned chunk instead of a nullptr
                    byte** ppHeaderData = (byte**)m_pLastChunk;
                    ppHeaderData[0] = pHeader;

                    //Set the new tail as the returned chunk
                    m_pLastChunk = pHeader;

                    //Set the header data to point to a nullptr
                    ppHeaderData = (byte**)pHeader;
                    ppHeaderData[0] = nullptr;


                    //If the current chunk was pointing to a nullptr, point it to the new tail instead
                    if (m_pCurrentChunk == nullptr)
                    {
                        m_pCurrentChunk = pHeader;
                    }

                }
                else
                {
                    std::cout << "This data isn't mine!" << std::endl;
                }
            }
        }

        //Returns a single chunk of memory
        std::shared_ptr<Object> Alloc()
        {

            //If there are no available chunks, return a nullptr
            if (m_pCurrentChunk == nullptr)
            {
                std::cout << "Sorry! No more chunks to be given out. Come back later, but for now have this nullptr!" << std::endl;
                return nullptr;
            }

            //Get the pointer to the next chunk and set it to pNext
            byte** ppChunkHeaderData = (byte**)m_pCurrentChunk;
            byte* pNext = ppChunkHeaderData[0];

            std::cout << (void*)m_pCurrentChunk << std::endl;
            std::cout << (void*)pNext << std::endl;


            //Get the data to return
            void* rawReturnData = m_pCurrentChunk + m_SizeOfChunkHeader;

            //Stick it into a shared pointer that automatically puts the object back into the pool when it is time.
            std::shared_ptr<Object> returnPtr((Object*)rawReturnData, [=](Object* pRawData){
                Dealloc(pRawData);
            
            });

            //Set the next chunk
            m_pCurrentChunk = pNext;

            return returnPtr;
        }

    protected:
        void InitializeHeaders()
        {
            byte* pHeader = nullptr;
            byte* pEnd = nullptr;
            byte* pNext = nullptr;
            byte** ppChunkHeader;


            //Set the header and end of the block
            pHeader = m_pBlock;
            pEnd = m_pBlock + m_SizeOfBlock;

            //Loop through and set the header of each chunk to point to the next chunk
            while (pHeader < pEnd)
            {
                pNext = pHeader + m_SizeOfChunk;
                ppChunkHeader = (byte**)pHeader;
                ppChunkHeader[0] = (pNext < pEnd ? pNext : nullptr);


                std::cout << "Header: " << (void*)pHeader << std::endl;
                std::cout << "Next: " << (void*)pNext << std::endl;
                pHeader = pNext;
            }


            std::cout << std::endl;
        }

    };