Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
smallblock_allocator.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cinttypes>
5#include <cstddef>
6#include <cstdint>
7#include <cstring>
8
9#include "gaia/cnt/fwd_llist.h"
10#include "gaia/cnt/sarray.h"
11#include "gaia/core/bit_utils.h"
12#include "gaia/core/dyn_singleton.h"
13#include "gaia/core/utility.h"
14#include "gaia/mem/mem_alloc.h"
15#include "gaia/util/logging.h"
16
17namespace gaia {
18 namespace mem {
19 static constexpr uint32_t SmallBlockAlignment = (uint32_t)alignof(std::max_align_t);
20 static constexpr uint32_t SmallBlockGranularity = SmallBlockAlignment;
21 static constexpr uint32_t SmallBlockMaxSize = 512;
22 static constexpr uint32_t SmallBlockSizeTypeCount = SmallBlockMaxSize / SmallBlockGranularity;
23
27 constexpr uint32_t small_block_size(uint32_t sizeType) {
28 GAIA_ASSERT(sizeType < SmallBlockSizeTypeCount);
29 return (sizeType + 1) * SmallBlockGranularity;
30 }
31
35 constexpr uint8_t small_block_size_type(uint32_t sizeBytes) {
36 GAIA_ASSERT(sizeBytes > 0);
37 GAIA_ASSERT(sizeBytes <= SmallBlockMaxSize);
38 return (uint8_t)((align(sizeBytes, SmallBlockGranularity) / SmallBlockGranularity) - 1);
39 }
40
41 namespace detail {
42 struct SmallBlockHeader final {
43 uintptr_t m_pageAddr = 0;
44#if GAIA_DEBUG
45 uint32_t m_requestedBytes = 0;
46 uint32_t m_reserved = 0;
47#else
48 uint64_t m_reserved = 0;
49#endif
50 };
51 } // namespace detail
52
53 static constexpr uint32_t SmallBlockUsableOffset =
54 align((uint32_t)sizeof(detail::SmallBlockHeader), SmallBlockAlignment);
55
56 struct GAIA_API SmallBlockAllocatorPageStats final {
58 uint64_t mem_total;
60 uint64_t mem_used;
62 uint32_t num_pages;
65#if GAIA_DEBUG
67 uint64_t mem_requested;
69 uint32_t num_pages_empty;
70#endif
71 };
72
73 struct GAIA_API SmallBlockAllocatorStats final {
74 SmallBlockAllocatorPageStats stats[SmallBlockSizeTypeCount];
75 };
76
77 namespace detail {
78 static_assert(sizeof(SmallBlockHeader) <= SmallBlockUsableOffset);
79
80 constexpr uint32_t small_block_stride(uint32_t sizeType) {
81 return SmallBlockUsableOffset + small_block_size(sizeType);
82 }
83
84 class SmallBlockAllocatorImpl;
85
86 struct SmallBlockPage final: cnt::fwd_llist_base<SmallBlockPage> {
87 static constexpr uint16_t NBlocks = 64;
88 static constexpr uint16_t NBlocks_Bits = (uint16_t)core::count_bits(NBlocks);
89 static constexpr uint16_t SizeTypeBits = (uint16_t)core::count_bits(SmallBlockSizeTypeCount - 1);
90 static constexpr uint32_t InvalidBlockId = NBlocks + 1;
91#if GAIA_DEBUG
92 static constexpr uint8_t FreedBlockPattern = 0xDD;
93#endif
94 static constexpr uint32_t BlockArrayBytes = ((uint32_t)NBlocks_Bits * (uint32_t)NBlocks + 7) / 8;
95
98
99 void* m_data;
100 BlockArray m_blocks;
101
102 uint32_t m_sizeType : SizeTypeBits;
103 uint32_t m_blockCnt : NBlocks_Bits;
104 uint32_t m_usedBlocks : NBlocks_Bits;
105 uint32_t m_nextFreeBlock : NBlocks_Bits;
106 uint32_t m_freeBlocks : NBlocks_Bits;
107
108#if GAIA_ASSERT_ENABLED
109 uint64_t m_usedMask = 0;
110#endif
111
115 SmallBlockPage(void* ptr, uint8_t sizeType):
116 m_data(ptr), m_sizeType(sizeType), m_blockCnt(0), m_usedBlocks(0), m_nextFreeBlock(0), m_freeBlocks(0) {}
117
119 GAIA_NODISCARD uint32_t block_stride() const {
120 return small_block_stride(m_sizeType);
121 }
122
123 void write_block_idx(uint32_t blockIdx, uint32_t value) {
124 const uint32_t bitPosition = blockIdx * NBlocks_Bits;
125
126 GAIA_ASSERT(bitPosition < NBlocks * NBlocks_Bits);
127 GAIA_ASSERT(value <= InvalidBlockId);
128
129 BitView{{(uint8_t*)m_blocks.data(), BlockArrayBytes}}.set(bitPosition, (uint8_t)value);
130 }
131
132 GAIA_NODISCARD uint8_t read_block_idx(uint32_t blockIdx) const {
133 const uint32_t bitPosition = blockIdx * NBlocks_Bits;
134
135 GAIA_ASSERT(bitPosition < NBlocks * NBlocks_Bits);
136
137 return BitView{{(uint8_t*)m_blocks.data(), BlockArrayBytes}}.get(bitPosition);
138 }
139
140#if GAIA_DEBUG
144 GAIA_NODISCARD void* alloc_block(uint32_t bytesWanted){
145#else
148 GAIA_NODISCARD void* alloc_block() {
149#endif
150 auto store_block_address = [&](uint32_t index) {
151 auto* pMemoryBlock = (uint8_t*)m_data + (index * block_stride());
152 GAIA_ASSERT((uintptr_t)pMemoryBlock % SmallBlockAlignment == 0);
153 auto& header = block_header(pMemoryBlock);
154 header.m_pageAddr = (uintptr_t)this;
155#if GAIA_DEBUG
156 header.m_requestedBytes = bytesWanted;
157 header.m_reserved = 0;
158#else
159 header.m_reserved = 0;
160#endif
161 auto* pData = pMemoryBlock + SmallBlockUsableOffset;
162 GAIA_ASSERT((uintptr_t)pData % SmallBlockAlignment == 0);
163 return (void*)pData;
164 };
165
166 GAIA_ASSERT(!full() && "Trying to allocate too many blocks!");
167
168 uint32_t index = 0;
169 if (m_freeBlocks == 0U) {
170 index = m_blockCnt;
171 ++m_usedBlocks;
172 ++m_blockCnt;
173 write_block_idx(index, index);
174 } else {
175 GAIA_ASSERT(m_nextFreeBlock < m_blockCnt && "Block allocator recycle list broken!");
176
177 ++m_usedBlocks;
178 --m_freeBlocks;
179
180 index = m_nextFreeBlock;
181 m_nextFreeBlock = read_block_idx(m_nextFreeBlock);
182 }
183
184#if GAIA_ASSERT_ENABLED
185 GAIA_ASSERT((m_usedMask & (uint64_t(1) << index)) == 0 && "Block already marked as live");
186 m_usedMask |= uint64_t(1) << index;
187#endif
188
189 return store_block_address(index);
190 }
191
194 void free_block(void* pBlock) {
195 GAIA_ASSERT(pBlock != nullptr);
196 GAIA_ASSERT(m_usedBlocks > 0);
197 GAIA_ASSERT(m_freeBlocks <= NBlocks);
198
199 const auto* pMemoryBlock = (uint8_t*)pBlock - SmallBlockUsableOffset;
200 const auto blckAddr = (uintptr_t)pMemoryBlock;
201 GAIA_ASSERT(blckAddr % SmallBlockAlignment == 0);
202 const auto dataAddr = (uintptr_t)m_data;
203 GAIA_ASSERT(blckAddr >= dataAddr);
204 const auto blockStride = (uintptr_t)block_stride();
205#if GAIA_ASSERT_ENABLED
206 const auto pageSize = blockStride * NBlocks;
207 GAIA_ASSERT(blckAddr < dataAddr + pageSize);
208#endif
209 GAIA_ASSERT((blckAddr - dataAddr) % blockStride == 0);
210 const auto blockIdx = (uint32_t)((blckAddr - dataAddr) / blockStride);
211 GAIA_ASSERT(blockIdx < m_blockCnt);
212
213#if GAIA_DEBUG
214 auto& header = block_header((void*)pMemoryBlock);
215 GAIA_ASSERT(header.m_requestedBytes > 0);
216#endif
217#if GAIA_ASSERT_ENABLED
218 GAIA_ASSERT((m_usedMask & (uint64_t(1) << blockIdx)) != 0 && "Double free or corrupted block state");
219 m_usedMask &= ~(uint64_t(1) << blockIdx);
220#endif
221
222#if GAIA_DEBUG
223 header.m_requestedBytes = 0;
224 std::memset(pBlock, FreedBlockPattern, small_block_size(m_sizeType));
225#endif
226
227 if (m_freeBlocks == 0U)
228 write_block_idx(blockIdx, InvalidBlockId);
229 else
230 write_block_idx(blockIdx, m_nextFreeBlock);
231 m_nextFreeBlock = blockIdx;
232
233 ++m_freeBlocks;
234 --m_usedBlocks;
235 }
236
238 GAIA_NODISCARD uint32_t used_blocks_cnt() const {
239 return m_usedBlocks;
240 }
241
243 GAIA_NODISCARD bool full() const {
244 return used_blocks_cnt() >= NBlocks;
245 }
246
248 GAIA_NODISCARD bool empty() const {
249 return used_blocks_cnt() == 0;
250 }
251
253 void verify() const {
254#if GAIA_ASSERT_ENABLED
255 GAIA_ASSERT(m_sizeType < SmallBlockSizeTypeCount);
256 GAIA_ASSERT(m_blockCnt <= NBlocks);
257 GAIA_ASSERT(m_usedBlocks <= m_blockCnt);
258 GAIA_ASSERT(m_freeBlocks <= m_blockCnt);
259 GAIA_ASSERT(m_usedBlocks + m_freeBlocks == m_blockCnt);
260 GAIA_ASSERT(((uintptr_t)m_data % SmallBlockAlignment) == 0);
261
262 [[maybe_unused]] uint64_t freeMask = 0;
263
264 if (m_freeBlocks != 0) {
265 uint32_t next = m_nextFreeBlock;
266 GAIA_FOR(m_freeBlocks) {
267 GAIA_ASSERT(next < m_blockCnt);
268 #if GAIA_DEBUG
269 const auto bit = uint64_t(1) << next;
270 GAIA_ASSERT((freeMask & bit) == 0 && "Free list contains a cycle");
271 freeMask |= bit;
272 #endif
273 next = read_block_idx(next);
274 }
275
276 GAIA_ASSERT(next == InvalidBlockId);
277 }
278
279 GAIA_FOR(m_blockCnt) {
280 const auto* pMemoryBlock = (const uint8_t*)m_data + (i * block_stride());
281 const auto& header = block_header(pMemoryBlock);
282 GAIA_ASSERT(header.m_pageAddr == (uintptr_t)this);
283 GAIA_ASSERT(((uintptr_t)pMemoryBlock % SmallBlockAlignment) == 0);
284
285 #if GAIA_DEBUG
286 const bool isFree = (freeMask & (uint64_t(1) << i)) != 0;
287 GAIA_ASSERT((header.m_requestedBytes == 0) == isFree);
288 #endif
289 }
290
291 #if GAIA_DEBUG
292 GAIA_ASSERT((m_usedMask & freeMask) == 0);
293 const auto liveMask = m_blockCnt == 64 ? ~uint64_t(0) : ((uint64_t(1) << m_blockCnt) - 1);
294 GAIA_ASSERT((m_usedMask | freeMask) == liveMask);
295 #endif
296#endif
297 }
298
299#if GAIA_DEBUG
301 GAIA_NODISCARD uint64_t requested_bytes() const {
302 if (m_usedBlocks == 0)
303 return 0;
304
305 uint64_t freeMask = 0;
306 uint32_t next = m_nextFreeBlock;
307 GAIA_FOR(m_freeBlocks) {
308 GAIA_ASSERT(next < m_blockCnt);
309 const auto bit = uint64_t(1) << next;
310 GAIA_ASSERT((freeMask & bit) == 0 && "Free list contains a cycle");
311 freeMask |= bit;
312 next = read_block_idx(next);
313 }
314
315 uint64_t requested = 0;
316 GAIA_FOR(m_blockCnt) {
317 if ((freeMask & (uint64_t(1) << i)) != 0)
318 continue;
319
320 const auto* pMemoryBlock = (const uint8_t*)m_data + (i * block_stride());
321 requested += block_header(pMemoryBlock).m_requestedBytes;
322 }
323
324 return requested;
325 }
326#endif
327
328 private:
329 static SmallBlockHeader& block_header(void* pMemoryBlock) {
330 return *(SmallBlockHeader*)pMemoryBlock;
331 }
332
333 static const SmallBlockHeader& block_header(const void* pMemoryBlock) {
334 return *(const SmallBlockHeader*)pMemoryBlock;
335 }
336 }; // namespace detail
337
338 enum class SmallBlockPageState : uint8_t { Detached, Empty, Partial, Full };
339
345 } // namespace mem
346
348
349 namespace detail {
352 friend ::gaia::mem::SmallBlockAllocator;
353
354 SmallBlockPageContainer m_pages[SmallBlockSizeTypeCount];
355 bool m_isDone = false;
356
357 SmallBlockAllocatorImpl() = default;
358
359 public:
360 static constexpr uint32_t MAX_SIZE = SmallBlockMaxSize;
361
363 flush(true);
364
365#if GAIA_ASSERT_ENABLED
366 for (const auto& container: m_pages) {
367 const bool hasPages = container.pagesEmpty.first != nullptr || container.pagesPartial.first != nullptr ||
368 container.pagesFull.first != nullptr;
369 GAIA_ASSERT(!hasPages && "SmallBlockAllocator leaking memory");
370 }
371#endif
372 }
373
377 SmallBlockAllocatorImpl& operator=(const SmallBlockAllocatorImpl&) = delete;
378
382 GAIA_NODISCARD void* alloc(uint32_t bytesWanted) {
383 GAIA_ASSERT(bytesWanted > 0);
384 GAIA_ASSERT(bytesWanted <= MAX_SIZE);
385 if (bytesWanted == 0 || bytesWanted > MAX_SIZE)
386 return nullptr;
387
388 const auto sizeType = small_block_size_type(bytesWanted);
389 auto& container = m_pages[sizeType];
390
391 SmallBlockPageState prevState = SmallBlockPageState::Partial;
392 auto* pPage = container.pagesPartial.first;
393 if (pPage == nullptr) {
394 prevState = SmallBlockPageState::Empty;
395 pPage = container.pagesEmpty.first;
396 if (pPage == nullptr) {
397 prevState = SmallBlockPageState::Detached;
398 pPage = alloc_page(sizeType);
399 }
400 }
401
402#if GAIA_DEBUG
403 void* pBlock = pPage->alloc_block(bytesWanted);
404#else
405 void* pBlock = pPage->alloc_block();
406#endif
407 GAIA_PROF_ALLOC(pBlock, bytesWanted);
408 move_page(container, pPage, prevState, state_for(*pPage));
409 verify();
410 return pBlock;
411 }
412
415 void free(void* pBlock) {
416 GAIA_ASSERT(pBlock != nullptr);
417 if (pBlock == nullptr)
418 return;
419
420 const auto& header = *(const SmallBlockHeader*)((uint8_t*)pBlock - SmallBlockUsableOffset);
421 const auto pageAddr = header.m_pageAddr;
422 GAIA_ASSERT(pageAddr % sizeof(uintptr_t) == 0);
423#if GAIA_DEBUG
424 GAIA_ASSERT(header.m_requestedBytes > 0);
425#endif
426 auto* pPage = (SmallBlockPage*)pageAddr;
427 const auto prevState = state_for(*pPage);
428 auto& container = m_pages[pPage->m_sizeType];
429
430 GAIA_PROF_FREE(pBlock);
431 pPage->free_block(pBlock);
432 move_page(container, pPage, prevState, state_for(*pPage));
433 verify();
434
435 if (m_isDone) {
436 if (pPage->empty()) {
437 container.pagesEmpty.unlink(pPage);
438 free_page(pPage);
439 }
440
441 try_delete_this();
442 }
443 }
444
447 void flush(bool releaseAll = false) {
448 for (uint32_t i = 0; i < SmallBlockSizeTypeCount; ++i)
449 flush_pages(m_pages[i], releaseAll);
450 verify();
451 }
452
454 GAIA_NODISCARD SmallBlockAllocatorStats stats() const {
456 for (uint32_t sizeType = 0; sizeType < SmallBlockSizeTypeCount; ++sizeType)
457 stats.stats[sizeType] = page_stats(sizeType);
458 return stats;
459 }
460
462 void diag() const {
463 const auto allStats = stats();
464 for (uint32_t sizeType = 0; sizeType < SmallBlockSizeTypeCount; ++sizeType) {
465 const auto& stats = allStats.stats[sizeType];
466 if (stats.num_pages == 0)
467 continue;
468
469 GAIA_LOG_N("SmallBlockAllocator %u B stats", small_block_size(sizeType));
470 GAIA_LOG_N(" Allocated: %" PRIu64 " B", stats.mem_total);
471 GAIA_LOG_N(" Reserved by live blocks: %" PRIu64 " B", stats.mem_used);
472 GAIA_LOG_N(" Pages: %u", stats.num_pages);
473 GAIA_LOG_N(" Reusable pages: %u", stats.num_pages_free);
474#if !GAIA_DEBUG
475 GAIA_LOG_N(
476 " Utilization: %.1f%%",
477 stats.mem_total ? 100.0 * ((double)stats.mem_used / (double)stats.mem_total) : 0.0);
478#else
479 GAIA_LOG_N(" Requested: %" PRIu64 " B", stats.mem_requested);
480 GAIA_LOG_N(" Free capacity: %" PRIu64 " B", stats.mem_total - stats.mem_used);
481 GAIA_LOG_N(" Internal slack: %" PRIu64 " B", stats.mem_used - stats.mem_requested);
482 GAIA_LOG_N(
483 " Utilization: %.1f%%",
484 stats.mem_total ? 100.0 * ((double)stats.mem_requested / (double)stats.mem_total) : 0.0);
485 GAIA_LOG_N(" Empty pages: %u", stats.num_pages_empty);
486#endif
487 }
488 }
489
491 void verify() const {
492#if GAIA_ASSERT_ENABLED
493 for (uint32_t sizeType = 0; sizeType < SmallBlockSizeTypeCount; ++sizeType)
494 verify_container(m_pages[sizeType], sizeType);
495#endif
496 }
497
498 private:
499 static constexpr const char* s_strSmallBlockData = "SmallBlockData";
500 static constexpr const char* s_strSmallBlockPage = "SmallBlockPage";
501
502 static SmallBlockPage* alloc_page(uint8_t sizeType) {
503 const uint32_t size = small_block_stride(sizeType) * SmallBlockPage::NBlocks;
504 auto* pPageData = AllocHelper::alloc_alig<uint8_t>(s_strSmallBlockData, SmallBlockAlignment, size);
505 auto* pMemoryPage = AllocHelper::alloc<SmallBlockPage>(s_strSmallBlockPage);
506 return new (pMemoryPage) SmallBlockPage(pPageData, sizeType);
507 }
508
509 static void free_page(SmallBlockPage* pPage) {
510 GAIA_ASSERT(pPage != nullptr);
511
512 AllocHelper::free_alig(s_strSmallBlockData, pPage->m_data);
513 pPage->~SmallBlockPage();
514 AllocHelper::free(s_strSmallBlockPage, pPage);
515 }
516
517 void done() {
518 m_isDone = true;
519 }
520
521 void try_delete_this() {
522 bool allEmpty = true;
523 for (const auto& container: m_pages) {
524 const bool hasPages = container.pagesEmpty.first != nullptr || container.pagesPartial.first != nullptr ||
525 container.pagesFull.first != nullptr;
526 allEmpty = allEmpty && !hasPages;
527 }
528
529 if (allEmpty)
530 delete this;
531 }
532
533 static constexpr uint32_t warm_pages_to_keep() {
534 return 0;
535 }
536
537 static SmallBlockPageState state_for(const SmallBlockPage& page) {
538 if (page.empty())
539 return SmallBlockPageState::Empty;
540 if (page.full())
541 return SmallBlockPageState::Full;
542 return SmallBlockPageState::Partial;
543 }
544
545 static cnt::fwd_llist<SmallBlockPage>& page_list(SmallBlockPageContainer& container, SmallBlockPageState state) {
546 switch (state) {
547 case SmallBlockPageState::Empty:
548 return container.pagesEmpty;
549 case SmallBlockPageState::Partial:
550 return container.pagesPartial;
551 default:
552 GAIA_ASSERT(state == SmallBlockPageState::Full);
553 return container.pagesFull;
554 }
555 }
556
557 static void move_page(
558 SmallBlockPageContainer& container, SmallBlockPage* pPage, SmallBlockPageState fromState,
559 SmallBlockPageState toState) {
560 if (fromState == toState)
561 return;
562
563 if (fromState != SmallBlockPageState::Detached)
564 page_list(container, fromState).unlink(pPage);
565 page_list(container, toState).link(pPage);
566 }
567
568 [[maybe_unused]] static void verify_page_membership(
569 [[maybe_unused]] const SmallBlockPage& page, //
570 [[maybe_unused]] uint32_t sizeType, //
571 [[maybe_unused]] SmallBlockPageState expectedState //
572 ) {
573 GAIA_ASSERT(page.m_sizeType == sizeType);
574 GAIA_ASSERT(state_for(page) == expectedState);
575 GAIA_ASSERT(page.get_fwd_llist_link().linked());
576 }
577
578 static void verify_container(const SmallBlockPageContainer& container, uint32_t sizeType) {
579 for (const auto& page: container.pagesEmpty) {
580 verify_page_membership(page, sizeType, SmallBlockPageState::Empty);
581 page.verify();
582 }
583
584 for (const auto& page: container.pagesPartial) {
585 verify_page_membership(page, sizeType, SmallBlockPageState::Partial);
586 page.verify();
587 }
588
589 for (const auto& page: container.pagesFull) {
590 verify_page_membership(page, sizeType, SmallBlockPageState::Full);
591 page.verify();
592 }
593 }
594
595 GAIA_NODISCARD SmallBlockAllocatorPageStats page_stats(uint32_t sizeType) const {
596 SmallBlockAllocatorPageStats stats{};
597 const auto& container = m_pages[sizeType];
598 const auto blockStride = (uint64_t)small_block_stride(sizeType);
599 const auto pageSize = blockStride * SmallBlockPage::NBlocks;
600
601 stats.num_pages = (uint32_t)container.pagesEmpty.size() + (uint32_t)container.pagesPartial.size() +
602 (uint32_t)container.pagesFull.size();
603 stats.num_pages_free = (uint32_t)container.pagesEmpty.size() + (uint32_t)container.pagesPartial.size();
604 stats.mem_total = stats.num_pages * pageSize;
605 stats.mem_used = container.pagesFull.size() * pageSize;
606
607#if GAIA_DEBUG
608 stats.num_pages_empty = (uint32_t)container.pagesEmpty.size();
609
610 for (const auto& page: container.pagesFull)
611 stats.mem_requested += page.requested_bytes();
612
613 for (const auto& page: container.pagesPartial) {
614 stats.mem_used += page.used_blocks_cnt() * blockStride;
615 stats.mem_requested += page.requested_bytes();
616 }
617#else
618 for (const auto& page: container.pagesPartial)
619 stats.mem_used += page.used_blocks_cnt() * blockStride;
620#endif
621
622 return stats;
623 }
624
625 static void flush_pages(SmallBlockPageContainer& container, bool releaseAll) {
626 const bool keepWarmPage = !releaseAll && warm_pages_to_keep() != 0;
627 bool keptWarmPage = false;
628
629 for (auto it = container.pagesEmpty.begin(); it != container.pagesEmpty.end();) {
630 auto* pPage = &(*it);
631 ++it;
632
633 if (!pPage->empty())
634 continue;
635
636 if (keepWarmPage && !keptWarmPage) {
637 keptWarmPage = true;
638 continue;
639 }
640
641 container.pagesEmpty.unlink(pPage);
642 free_page(pPage);
643 }
644 }
645 };
646
647 } // namespace detail
648} // namespace gaia
649} // namespace gaia
650
652#define GAIA_USE_SMALLBLOCK(name) \
653 void _smallblockallocator_verify_size() { \
654 static_assert( \
655 sizeof(*this) <= ::gaia::mem::SmallBlockMaxSize, "Object is too large to be used with SmallBlockAllocator"); \
656 } \
657 static void* operator new(size_t size) { \
658 return ::gaia::mem::SmallBlockAllocator::get().alloc((uint32_t)size); \
659 } \
660 static void* operator new[](size_t size) { \
661 return ::gaia::mem::mem_alloc(#name, (uint32_t)size); \
662 } \
663 static void operator delete(void* p) noexcept { \
664 ::gaia::mem::SmallBlockAllocator::get().free(p); \
665 } \
666 static void operator delete[](void* p) { \
667 return ::gaia::mem::mem_free(#name, p); \
668 }
Array with variable size of elements of type.
Definition darray_impl.h:25
Gaia-ECS is a header-only library which means we want to avoid using global static variables because ...
Definition dyn_singleton.h:21
General-purpose allocator for small, variable-sized allocations up to 512 bytes.
Definition smallblock_allocator.h:351
GAIA_NODISCARD SmallBlockAllocatorStats stats() const
Returns allocator statistics per size class.
Definition smallblock_allocator.h:454
void verify() const
Verifies allocator invariants.
Definition smallblock_allocator.h:491
void free(void *pBlock)
Releases storage allocated for the given pointer.
Definition smallblock_allocator.h:415
GAIA_NODISCARD void * alloc(uint32_t bytesWanted)
Allocates storage for up to 512 bytes.
Definition smallblock_allocator.h:382
void diag() const
Performs diagnostics of allocator memory usage.
Definition smallblock_allocator.h:462
void flush(bool releaseAll=false)
Flushes unused pages.
Definition smallblock_allocator.h:447
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Each fwd_llist node either has to inherit from fwd_llist_base or it has to provide get_fwd_llist_link...
Definition fwd_llist.h:26
Definition bit_utils.h:11
Definition smallblock_allocator.h:56
uint32_t num_pages_free
Number of reusable pages (partial + empty).
Definition smallblock_allocator.h:64
uint64_t mem_used
Memory reserved by live blocks for this size class.
Definition smallblock_allocator.h:60
uint32_t num_pages
Number of allocated pages.
Definition smallblock_allocator.h:62
uint64_t mem_total
Total allocated memory for this size class.
Definition smallblock_allocator.h:58
Definition smallblock_allocator.h:73
Definition smallblock_allocator.h:42
Definition smallblock_allocator.h:340
Definition smallblock_allocator.h:86
GAIA_NODISCARD uint32_t block_stride() const
Returns the stride of one block in this page.
Definition smallblock_allocator.h:119
GAIA_NODISCARD void * alloc_block()
Allocates one block from this page.
Definition smallblock_allocator.h:148
GAIA_NODISCARD bool empty() const
Returns true when the page is empty.
Definition smallblock_allocator.h:248
void free_block(void *pBlock)
Frees one block back to this page.
Definition smallblock_allocator.h:194
void verify() const
Verifies internal page invariants.
Definition smallblock_allocator.h:253
SmallBlockPage(void *ptr, uint8_t sizeType)
Constructs a page for the given size class.
Definition smallblock_allocator.h:115
GAIA_NODISCARD bool full() const
Returns true when the page is full.
Definition smallblock_allocator.h:243
GAIA_NODISCARD uint32_t used_blocks_cnt() const
Returns the number of live blocks.
Definition smallblock_allocator.h:238