Skip to content

Instantly share code, notes, and snippets.

@silverweed
Last active November 15, 2017 12:12
Show Gist options
  • Save silverweed/77f021c7e844262ff57bdbc302375621 to your computer and use it in GitHub Desktop.
Save silverweed/77f021c7e844262ff57bdbc302375621 to your computer and use it in GitHub Desktop.
Pool allocator
#pragma once
#include <cstdlib>
#include <cstdio>
#include <cstdint>
#include <cassert>
#include <utility>
#define ABORT(x) do { \
fprintf(stderr, "%s\n", x); \
std::abort(); \
} while (false)
/* A simple pool allocator type-safe class.
* Internally uses a contiguous memory block organized as a linked list.
* Alloc and dealloc operations are O(1).
* Capacity is decided on creation and cannot be changed afterwards.
* Requires explicit startup and shutdown.
*/
template <class T>
class PoolAllocator {
public:
explicit PoolAllocator(size_t maxElems = 1024) : capacity(maxElems + 1) {
static_assert(sizeof(T) >= sizeof(uintptr_t), "sizeof(T) < sizeof(uintptr_t)!");
}
PoolAllocator(const PoolAllocator&) = delete;
PoolAllocator(PoolAllocator&&) = delete;
void startup() {
if (active)
ABORT("PoolAllocator initialized twice!");
pool = reinterpret_cast<T*>(malloc(capacity * sizeof(T)));
_fillPool();
active = true;
}
void shutdown() {
if (!active)
ABORT("PoolAllocator shut down twice!");
free(pool);
active = false;
}
T* alloc() {
if (!active)
ABORT("Tried to allocate memory on uninitialized allocator!");
const auto firstFreeAddr = *reinterpret_cast<uintptr_t*>(pool);
if (firstFreeAddr == 0)
ABORT("PoolAllocator: Out of memory");
const auto next = *reinterpret_cast<uintptr_t*>(firstFreeAddr);
*reinterpret_cast<uintptr_t*>(pool) = reinterpret_cast<uintptr_t>(next);
return reinterpret_cast<T*>(firstFreeAddr);
}
void dealloc(T *mem) {
if (!active) {
// Silently fail, memory was already freed
return;
}
assert(mem > pool && mem < pool + capacity + sizeof(T)
&& "Tried to deallocate memory not belonging to the pool!");
const auto firstFreeAddr = *reinterpret_cast<uintptr_t*>(pool);
*reinterpret_cast<uintptr_t*>(mem) = firstFreeAddr;
*reinterpret_cast<uintptr_t*>(pool) = reinterpret_cast<uintptr_t>(mem);
}
template <class... Args>
T* create(Args&&... args) {
T *mem = alloc();
return new (mem) T(std::forward<Args>(args)...);
}
void destroy(T *obj) {
obj->~T();
dealloc(obj);
}
#ifdef DEBUG
void dump() const {
for (size_t i = 0; i < capacity; ++i) {
printf("(%p): %p | ", pool + i, *(reinterpret_cast<uintptr_t*>(pool + i)));
}
printf("\n");
}
/** Calculates the remaining capacity of the pool "the hard way". Used for debugging */
size_t realRemainingCapacity() const {
size_t c = 0;
auto nxt = *reinterpret_cast<uintptr_t*>(pool);
while (nxt != 0) {
++c;
nxt = *reinterpret_cast<uintptr_t*>(nxt);
}
return c;
}
size_t totMem() const { return capacity * sizeof(T); }
#endif
private:
T *pool;
size_t capacity;
bool active = false;
/** Fills the pool with a linked list of pointers */
void _fillPool() {
for (size_t i = 0; i < capacity - 1; ++i) {
const auto curAddr = pool + i;
*reinterpret_cast<uintptr_t*>(curAddr) = reinterpret_cast<uintptr_t>(curAddr + 1);
}
*reinterpret_cast<uintptr_t*>(pool + capacity - 1) = 0x0;
}
};
#undef ABORT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment