Last active
February 29, 2020 14:01
-
-
Save dwilliamson/c1842a98b1e7cfd8c9724756c1ccec77 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ----------------------------------------------------------------------------------------------------------------- | |
// 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