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;
    		}
    
    	};