Skip to content

Instantly share code, notes, and snippets.

@dwilliamson
Last active February 29, 2020 14:01
Show Gist options
  • Save dwilliamson/c1842a98b1e7cfd8c9724756c1ccec77 to your computer and use it in GitHub Desktop.
Save dwilliamson/c1842a98b1e7cfd8c9724756c1ccec77 to your computer and use it in GitHub Desktop.
struct MEMORY_API memStackAllocatorBlock
{
memStackAllocatorBlock(uint32_t size)
: data_start(nullptr)
, data_end(nullptr)
, next(nullptr)
{
// Allocate block memory
data_start = (uint8_t*)::malloc(size);
data_end = data_start + size;
}
~memStackAllocatorBlock()
{
if (data_start != nullptr)
::free(data_start);
}
// Range of allocated data for this block
uint8_t* data_start = nullptr;
uint8_t* data_end = nullptr;
// Intrusive link within block link list belonging to an allocator
memStackAllocatorBlock* next = nullptr;
};
memStackAllocator::memStackAllocator(uint32_t block_size)
: block_size(block_size)
{
}
memStackAllocator::~memStackAllocator()
{
// Release all memory blocks
while (first_block != nullptr)
{
memStackAllocatorBlock* next_block = first_block->next;
delete first_block;
first_block = next_block;
}
}
void* memStackAllocator::malloc(size_t size)
{
return ::malloc(*this, size);
}
void memStackAllocator::free(void*)
{
// Can't do out-of-order frees from a stack allocator
assert(false);
}
void* memStackAllocator::realloc(void*, size_t)
{
// Unsupported
assert(false);
return nullptr;
}
MEMORY_API void* malloc(memStackAllocator& allocator, uint32_t size)
{
// No more free space?
if (allocator.data_alloc + size > allocator.data_end)
{
// Has a block already been allocated for reuse?
if (allocator.next_alloc_block != nullptr)
{
// Set it up ready to pull allocations from
allocator.data_alloc = allocator.next_alloc_block->data_start;
allocator.data_end = allocator.next_alloc_block->data_end;
// Move the next alloc block along the linked list
allocator.next_alloc_block = allocator.next_alloc_block->next;
}
else
{
// Allocate blocks on-demand
memStackAllocatorBlock* new_block = new memStackAllocatorBlock(allocator.block_size);
allocator.data_alloc = new_block->data_start;
allocator.data_end = new_block->data_end;
// Add to linked list
if (allocator.first_block == nullptr)
{
allocator.first_block = new_block;
allocator.last_block = new_block;
}
else
{
allocator.last_block->next = new_block;
allocator.last_block = new_block;
}
}
}
// Pull from the current block
void* data = allocator.data_alloc;
allocator.data_alloc += size;
allocator.allocated_bytes += size;
return data;
}
MEMORY_API void reset(memStackAllocator& allocator)
{
// Point allocator at the first block, discarding the old allocation position and allowing
// new allocations to reuse existing blocks
if (allocator.first_block != nullptr)
{
allocator.data_alloc = allocator.first_block->data_start;
allocator.data_end = allocator.first_block->data_end;
allocator.next_alloc_block = allocator.first_block->next;
}
}
// -----------------------------------------------------------------------------------------------------------------
// Stack allocator that can only be allocated from and reset.
// Memory grows linearly in chunks of the block size to reduce virtual memory fragmentation.
// Also removes need to copy existing data to newly allocated blocks.
// Any allocations that span a block edge require allocation of a new block where they get placed at
// the front.
// -----------------------------------------------------------------------------------------------------------------
struct memStackAllocatorBlock;
struct MEMORY_API memStackAllocator : public memAllocator
{
memStackAllocator(uint32_t block_size);
~memStackAllocator();
// Optional allocator interface
// Use global functions to guarantee faster calls
void* malloc(size_t size) override;
void free(void* ptr) override;
void* realloc(void* ptr, size_t size) override;
// Size data stored to allow further blocks to be allocated
const uint32_t block_size;
// Currently allocated position and limit to its allocation
uint8_t* data_alloc = nullptr;
uint8_t* data_end = nullptr;
// Keeps track of the total number bytes allocated
uint32_t allocated_bytes = 0;
// Linked list of on-demand allocated blocks
memStackAllocatorBlock* first_block = nullptr;
memStackAllocatorBlock* last_block = nullptr;
// Next free block to allocate from (if any)
memStackAllocatorBlock* next_alloc_block = nullptr;
};
MEMORY_API void* malloc(memStackAllocator& allocator, uint32_t size);
MEMORY_API void reset(memStackAllocator& allocator);
// New/delete operators for stack allocator
inline void* operator new (size_t size, memStackAllocator& allocator)
{
return malloc(allocator, size);
}
inline void operator delete(void* ptr, memStackAllocator& allocator)
{
// Not supported
assert(false);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment