Gaia-ECS v1.0.0
A simple and powerful entity component system
Loading...
Searching...
No Matches
stack_allocator.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cinttypes>
5
6#include "gaia/mem/raw_data_holder.h"
7
8namespace gaia {
9 namespace mem {
11 namespace detail {
12 struct AllocationInfo {
14 uint32_t prev;
16 uint32_t off : 8;
18 uint32_t cnt : 24;
19 void (*dtor)(void*, uint32_t);
20 };
21 } // namespace detail
23
24 // MSVC might warn about applying additional padding to an instance of StackAllocator.
25 // This is perfectly fine, but might make builds with warning-as-error turned on to fail.
26 GAIA_MSVC_WARNING_PUSH()
27 GAIA_MSVC_WARNING_DISABLE(4324)
28
32 template <uint32_t CapacityInBytes = 1024>
34 using alloc_info = detail::AllocationInfo;
35
37 detail::raw_data_holder<CapacityInBytes, 16> m_buffer;
39 uint32_t m_pos = 0;
41 uint32_t m_posPrev = 0;
43 uint32_t m_allocs = 0;
44
45 public:
47 // Aligned used so the sentinel object can be stored properly
48 const auto bufferMemAddr = (uintptr_t)((uint8_t*)m_buffer);
49 m_posPrev = m_pos = padding<alignof(alloc_info)>(bufferMemAddr);
50 }
51
53 reset();
54 }
55
56 StackAllocator(const StackAllocator&) = delete;
58 StackAllocator& operator=(const StackAllocator&) = delete;
59 StackAllocator& operator=(StackAllocator&&) = delete;
60
67 template <typename T>
68 GAIA_NODISCARD T* alloc(uint32_t cnt) {
69 constexpr auto sizeT = (uint32_t)sizeof(T);
70 const auto addrBuff = (uintptr_t)((uint8_t*)m_buffer);
71 const auto addrAllocInfo = align<alignof(alloc_info)>(addrBuff + m_pos);
72 const auto addrAllocData = align<alignof(T)>(addrAllocInfo + sizeof(alloc_info));
73 const auto off = (uint32_t)(addrAllocData - addrAllocInfo);
74
75 // There has to be some space left in the buffer
76 const bool isFull = (uint32_t)(addrAllocData - addrBuff) + sizeT * cnt >= CapacityInBytes;
77 if GAIA_UNLIKELY (isFull) {
78 GAIA_ASSERT(!isFull && "Allocation space exceeded on StackAllocator");
79 return nullptr;
80 }
81
82 // Memory sentinel
83 auto* pInfo = (alloc_info*)addrAllocInfo;
84 pInfo->prev = m_posPrev;
85 pInfo->off = off;
86 pInfo->cnt = cnt;
87 pInfo->dtor = [](void* ptr, uint32_t cnt) {
88 core::call_dtor_n((T*)ptr, cnt);
89 };
90
91 // Constructing the object is necessary
92 auto* pData = (T*)addrAllocData;
93 core::call_ctor_raw_n(pData, cnt);
94
95 // Allocation start offset
96 m_posPrev = (uint32_t)(addrAllocInfo - addrBuff);
97 // Point to the next free space (not necessary aligned yet)
98 m_pos = m_posPrev + pInfo->off + sizeT * cnt;
99
100 ++m_allocs;
101 return pData;
102 }
103
107 void free([[maybe_unused]] void* pData, [[maybe_unused]] uint32_t cnt) {
108 GAIA_ASSERT(pData != nullptr);
109 GAIA_ASSERT(cnt > 0);
110 GAIA_ASSERT(m_allocs > 0);
111
112 const auto addrBuff = (uintptr_t)((uint8_t*)m_buffer);
113
114 // Destroy the last allocated object
115 const auto addrAllocInfo = addrBuff + m_posPrev;
116 auto* pInfo = (alloc_info*)addrAllocInfo;
117 const auto addrAllocData = addrAllocInfo + pInfo->off;
118 void* pInfoData = (void*)addrAllocData;
119 GAIA_ASSERT(pData == pInfoData);
120 GAIA_ASSERT(pInfo->cnt == cnt);
121 pInfo->dtor(pInfoData, pInfo->cnt);
122
123 m_pos = m_posPrev;
124 m_posPrev = pInfo->prev;
125 --m_allocs;
126 }
127
129 void reset() {
130 const auto addrBuff = (uintptr_t)((uint8_t*)m_buffer);
131
132 // Destroy allocated objects back-to-front
133 auto pos = m_posPrev;
134 while (m_allocs > 0) {
135 const auto addrAllocInfo = addrBuff + pos;
136 auto* pInfo = (alloc_info*)addrAllocInfo;
137 const auto addrAllocData = addrAllocInfo + pInfo->off;
138 pInfo->dtor((void*)addrAllocData, pInfo->cnt);
139 pos = pInfo->prev;
140
141 --m_allocs;
142 }
143
144 GAIA_ASSERT(m_allocs == 0);
145
146 m_pos = 0;
147 m_posPrev = 0;
148 m_allocs = 0;
149 }
150
153 GAIA_NODISCARD constexpr uint32_t capacity() {
154 return CapacityInBytes;
155 }
156 };
157
158 GAIA_MSVC_WARNING_POP()
159 } // namespace mem
160} // namespace gaia
Stack allocator capable of instantiating any default-constructible object on stack....
Definition stack_allocator.h:33
void reset()
Frees all allocated objects from the buffer.
Definition stack_allocator.h:129
GAIA_NODISCARD T * alloc(uint32_t cnt)
Allocates objects inside the buffer. No default initialization is done so the object is returned in a...
Definition stack_allocator.h:68
void free(void *pData, uint32_t cnt)
Frees the last allocated object from the stack.
Definition stack_allocator.h:107
GAIA_NODISCARD constexpr uint32_t capacity()
Returns the fixed buffer capacity.
Definition stack_allocator.h:153