Skip to content

Instantly share code, notes, and snippets.

@dwilliamson
Created February 29, 2020 14:06
Show Gist options
  • Save dwilliamson/b3d54848a6cdff744d7a2d985f5e76fb to your computer and use it in GitHub Desktop.
Save dwilliamson/b3d54848a6cdff744d7a2d985f5e76fb to your computer and use it in GitHub Desktop.
struct GlobalHeapAllocator : public memAllocator
{
void* malloc(size_t size) override
{
return ::malloc(size);
}
void free(void* ptr) override
{
return ::free(ptr);
}
void* realloc(void* ptr, size_t size) override
{
return ::realloc(ptr, size);
}
};
MEMORY_API memAllocator* memGlobalHeapAllocator()
{
static GlobalHeapAllocator allocator;
return &allocator;
}
memDynamicBuffer::memDynamicBuffer(memAllocator* allocator)
: m_Allocator(allocator)
{
if (m_Allocator == nullptr)
m_Allocator = memGlobalHeapAllocator();
}
memDynamicBuffer::memDynamicBuffer(uint32_t size, memAllocator* allocator)
: memDynamicBuffer(allocator)
{
resize(size);
}
memDynamicBuffer::memDynamicBuffer(const memDynamicBuffer& rhs)
: m_Allocator(rhs.m_Allocator)
, m_Capacity(rhs.m_Capacity)
, m_Size(rhs.m_Size)
{
if (rhs.m_Data != nullptr)
{
m_Data = (uint8_t*)m_Allocator->malloc(m_Capacity);
memcpy(m_Data, rhs.m_Data, m_Size);
}
}
memDynamicBuffer::memDynamicBuffer(memDynamicBuffer&& rhs)
: m_Allocator(rhs.m_Allocator)
, m_Data(rhs.m_Data)
, m_Capacity(rhs.m_Capacity)
, m_Size(rhs.m_Size)
{
rhs.m_Allocator = nullptr;
rhs.m_Data = nullptr;
rhs.m_Capacity = 0;
rhs.m_Size = 0;
}
memDynamicBuffer::~memDynamicBuffer()
{
if (m_Data != nullptr)
m_Allocator->free(m_Data);
}
void memDynamicBuffer::resize(uint32_t new_size, uint32_t alignment)
{
if (new_size > m_Capacity)
{
// Calculate size rounded up to the requested alignment
assert(alignment > 0);
m_Capacity = new_size + ((alignment - 1) - ((new_size - 1) % alignment));
m_Data = m_Allocator->realloc(m_Data, m_Capacity);
}
m_Size = new_size;
}
void memDynamicBuffer::growSize(int32_t nb_bytes, uint32_t alignment)
{
assert((int32_t)m_Size + nb_bytes >= 0);
resize(m_Size + nb_bytes, alignment);
}
void memDynamicBuffer::resetSize()
{
m_Size = 0;
}
// Optional runtime-swappable interface to allocators
// The implemented functions are optional to call but the cost of the vtable will stick be present in each allocator
struct clcpp_attr(reflect) memAllocator
{
virtual void* malloc(size_t size) = 0;
virtual void free(void* ptr) = 0;
// Allocators are not obliged to implement this and some will assert if called
virtual void* realloc(void* ptr, size_t size) = 0;
};
// Return pointer to the global heap allocator
MEMORY_API memAllocator* memGlobalHeapAllocator();
// This dynamic buffer separates size from capacity so that buffer memory can be reused without
// needing to release the allocation; calling `resetSize` will merely set the size to zero.
class MEMORY_API memDynamicBuffer
{
public:
// Empty memory buffer
explicit memDynamicBuffer(memAllocator* allocator = nullptr);
// Pre-allocate
explicit memDynamicBuffer(uint32_t size, memAllocator* allocator = nullptr);
// Make copy construction explicit
explicit memDynamicBuffer(const memDynamicBuffer& rhs);
// Moves for returning from creation functions
memDynamicBuffer(memDynamicBuffer&& rhs);
~memDynamicBuffer();
// No assignment copies
memDynamicBuffer& operator = (memDynamicBuffer&) = delete;
void resize(uint32_t new_size, uint32_t alignment = MEMORY_NATURAL_ALIGNMENT);
void growSize(int32_t nb_bytes, uint32_t alignment = MEMORY_NATURAL_ALIGNMENT);
void resetSize();
void* data() const { return m_Data; }
void* dataAtOffset(uint32_t offset) const { return (uint8_t*)m_Data + offset; }
void* dataAtEnd() const { return (uint8_t*)m_Data + m_Size; }
uint32_t size() const { return m_Size; }
private:
// Inputs
memAllocator* m_Allocator = nullptr;
// Allocated data
void* m_Data = nullptr;
// Allocated capacity and current size within that capacity
uint32_t m_Capacity = 0;
uint32_t m_Size = 0;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment