// Code by Frederik Omlor (2023) // ---------------------------- HEADER -------------------------------- #pragma once #include "Allocator.h" #include #include "../Analysis/Debug.h" namespace CCMemory { struct CCE_API PoolAllocator : public Allocator { //TODO: Align!! PoolAllocator(unsigned int elements, unsigned long _poolSize) { DASSERT(elements >= 0, "The element length must be at least 1!"); DASSERT(_poolSize >= 0, "The pool must be at least 1!"); totalSpace = _poolSize * elements; this->poolSize = _poolSize; freeSpace = totalSpace; numPoolElements = elements; freePoolElements = elements; // malloc [ poolIndices | pe0 | pe1 | pe2 | ... | pen ] bottom = (intptr_t) malloc((sizeof(bool) * elements) + totalSpace); pool = new (reinterpret_cast(bottom)) bool[elements]; memset(pool, FALSE, elements); allocatableMemBottom = bottom + ((sizeof(bool) * numPoolElements)); } ~PoolAllocator() { poolSize = 0; totalSpace = 0; freeSpace = 0; usedSpace = 0; numAllocs = 0; numFrees = 0; free((void*)bottom); numPoolElements = 0; bottom = 0; } template T* Alloc() { // check DASSERT(sizeof(T) <= poolSize, "The size of an allocated chunk of memory must be smaller than the specified pool size."); if (freePoolElements <= 0) { return nullptr; } // allocate unsigned int elementIndex = 0; T* ptr = 0; for (intptr_t elementStatus = (intptr_t)pool; elementStatus < (intptr_t)pool + numPoolElements; elementStatus += sizeof(bool)) { if (*(bool*)elementStatus == false) { // element not used yet intptr_t adress = (allocatableMemBottom + poolSize * elementIndex); ptr = new (reinterpret_cast (adress)) T(); *(bool*)elementStatus = true; break; } elementIndex++; } if (ptr == 0) { // probably no pool element free DWARNING("Pool Allocator ran out of pool elements!"); return nullptr; } // update numAllocs++; freePoolElements--; usedSpace += sizeof(T); freeSpace = totalSpace - usedSpace; return ptr; } void Free(intptr_t addr, unsigned int size); void Clear(); const unsigned long GetPoolSize(); const unsigned int GetNumPoolElements(); const unsigned int GetNumFreePoolElements(); private: unsigned long poolSize = 0; unsigned int freePoolElements = 0; unsigned int numPoolElements = 0; bool* pool = nullptr; intptr_t bottom = 0; intptr_t allocatableMemBottom = 0; }; } // ---------------------------- DEFINITION ---------------------------- #include "PoolAllocator.h" #include #include "../Analysis/Debug.h" #include namespace CCMemory { /// /// Gets the pool size. /// /// Returns the size of the pools of the allocator. const unsigned long PoolAllocator::GetPoolSize() { return poolSize; } /// /// Gets the number of all pool elements. /// /// Returns the number of all available pool elements of the allocator. const unsigned int PoolAllocator::GetNumPoolElements() { return numPoolElements; } /// /// Gets the number of the free pool elements. /// /// Returns the number of free pool elements of the allocator. const unsigned int PoolAllocator::GetNumFreePoolElements() { return freePoolElements; } //TODO: Add size into alignment buffer (?) or remove it so that // it's not nessecary to give the explicit size of the object to // free. This is still error prone since you might give arbitrary // values to the function which in consequence breaks the memory // counting (free mem / used mem). void PoolAllocator::Free(intptr_t addr, unsigned int size) { // check DASSERT(addr < allocatableMemBottom + totalSpace, "Trying to free a memory adress which is not part of the allocated memory!"); // free intptr_t poolIndex = (addr - allocatableMemBottom) / poolSize; if (pool[poolIndex]) { pool[poolIndex] = false; // update freePoolElements++; numFrees++; usedSpace -= size; freeSpace += size; } } /// /// Clears all of the elements of the pool allocator. /// void PoolAllocator::Clear() { memset(pool, FALSE, numPoolElements); freePoolElements = numPoolElements; usedSpace = 0; freeSpace = totalSpace; } }