From f211d2245522d328bc034f868f1285303a930a8f Mon Sep 17 00:00:00 2001 From: YasInvolved Date: Wed, 6 May 2026 22:03:11 +0200 Subject: [PATCH 1/4] introduce metadata --- Engine/src/delta/core/FreeListAllocator.cpp | 10 ++++++++-- Engine/src/delta/core/FreeListAllocator.h | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Engine/src/delta/core/FreeListAllocator.cpp b/Engine/src/delta/core/FreeListAllocator.cpp index 3a07bc0..7a6eea1 100644 --- a/Engine/src/delta/core/FreeListAllocator.cpp +++ b/Engine/src/delta/core/FreeListAllocator.cpp @@ -23,13 +23,18 @@ using namespace delta::core; using MemoryState = MemoryManager::MemoryState; using Node = FreeListAllocator::Node; +using BucketMetadata = FreeListAllocator::BucketMetadata; DLT_FORCE_INLINE void* AllocatePageForBucket(FreeListAllocator* allocator, uint32_t bucketIx, uint64_t size) { void* rawPage = MemoryManager::AllocatePageLockFree(allocator->memState); - uint64_t numChunks = allocator->pageSize / size; - uint8_t* walker = static_cast(rawPage); + BucketMetadata* meta = reinterpret_cast(rawPage); + meta->bucketIx = bucketIx; + meta->bitmask = 0; + + uint64_t numChunks = allocator->remaining / size; + uint8_t* walker = static_cast(rawPage) + sizeof(BucketMetadata); Node* head = reinterpret_cast(walker); Node* current = head; @@ -50,6 +55,7 @@ void delta::core::FreeList_Init(FreeListAllocator* allocator, MemoryState* memSt { allocator->memState = memState; allocator->pageSize = memState->pageSize; + allocator->remaining = memState->pageSize - sizeof(BucketMetadata); } void* delta::core::FreeList_Allocate(FreeListAllocator* allocator, uint64_t size, uint64_t alignment) diff --git a/Engine/src/delta/core/FreeListAllocator.h b/Engine/src/delta/core/FreeListAllocator.h index 8c115b3..93950a4 100644 --- a/Engine/src/delta/core/FreeListAllocator.h +++ b/Engine/src/delta/core/FreeListAllocator.h @@ -28,12 +28,18 @@ namespace delta::core static constexpr uint32_t BUCKET_COUNT = 7ull; // log2(2048) - log2(16) = 7, calculated manually for simplicity struct Node { Node* next; }; + struct alignas(16) BucketMetadata + { + uint32_t bucketIx; + uint32_t bitmask; + }; MemoryManager::MemoryState* memState; Node* buckets[BUCKET_COUNT]; uint64_t pageSize; + uint64_t remaining; // pageSize - metadata size }; void FreeList_Init(FreeListAllocator* allocator, MemoryManager::MemoryState* memState); From ef244c9575b2fb0a5e8a23a01ded8f1e2f10587a Mon Sep 17 00:00:00 2001 From: YasInvolved Date: Wed, 6 May 2026 22:30:45 +0200 Subject: [PATCH 2/4] O(1) free mechanism, bug fixes --- Engine/src/delta/core/FreeListAllocator.cpp | 16 ++++++++++++++++ Engine/src/delta/core/FreeListAllocator.h | 1 + 2 files changed, 17 insertions(+) diff --git a/Engine/src/delta/core/FreeListAllocator.cpp b/Engine/src/delta/core/FreeListAllocator.cpp index 7a6eea1..41ffaba 100644 --- a/Engine/src/delta/core/FreeListAllocator.cpp +++ b/Engine/src/delta/core/FreeListAllocator.cpp @@ -48,6 +48,7 @@ DLT_FORCE_INLINE void* AllocatePageForBucket(FreeListAllocator* allocator, uint3 } current->next = nullptr; + allocator->buckets[bucketIx] = head; return head; } @@ -82,3 +83,18 @@ void* delta::core::FreeList_Allocate(FreeListAllocator* allocator, uint64_t size return MemoryManager::AllocatePageLockFree(allocator->memState); } + +void delta::core::FreeList_Free(FreeListAllocator* allocator, void* ptr) +{ + if (!ptr) + return; + + uintptr_t address = reinterpret_cast(ptr); + uintptr_t basePage = address & ~0xfffull; + + const BucketMetadata* metadata = reinterpret_cast(basePage); + uint32_t bucketIx = metadata->bucketIx; + Node* freeNode = reinterpret_cast(ptr); + freeNode->next = allocator->buckets[bucketIx]; + allocator->buckets[bucketIx] = freeNode; +} diff --git a/Engine/src/delta/core/FreeListAllocator.h b/Engine/src/delta/core/FreeListAllocator.h index 93950a4..eec3ad7 100644 --- a/Engine/src/delta/core/FreeListAllocator.h +++ b/Engine/src/delta/core/FreeListAllocator.h @@ -44,4 +44,5 @@ namespace delta::core void FreeList_Init(FreeListAllocator* allocator, MemoryManager::MemoryState* memState); void* FreeList_Allocate(FreeListAllocator* allocator, uint64_t size, uint64_t alignment); + void FreeList_Free(FreeListAllocator* allocator, void* ptr); } From c52a037fb5306d27e91b298402be30e312d80141 Mon Sep 17 00:00:00 2001 From: YasInvolved Date: Wed, 6 May 2026 22:49:53 +0200 Subject: [PATCH 3/4] introduce magic value to check for region corruption or invalid pointer passed in --- Engine/src/delta/core/FreeListAllocator.cpp | 4 +++- Engine/src/delta/core/FreeListAllocator.h | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Engine/src/delta/core/FreeListAllocator.cpp b/Engine/src/delta/core/FreeListAllocator.cpp index 41ffaba..604344f 100644 --- a/Engine/src/delta/core/FreeListAllocator.cpp +++ b/Engine/src/delta/core/FreeListAllocator.cpp @@ -30,8 +30,8 @@ DLT_FORCE_INLINE void* AllocatePageForBucket(FreeListAllocator* allocator, uint3 void* rawPage = MemoryManager::AllocatePageLockFree(allocator->memState); BucketMetadata* meta = reinterpret_cast(rawPage); + meta->magic = BucketMetadata::MAGIC_VALUE; meta->bucketIx = bucketIx; - meta->bitmask = 0; uint64_t numChunks = allocator->remaining / size; uint8_t* walker = static_cast(rawPage) + sizeof(BucketMetadata); @@ -93,6 +93,8 @@ void delta::core::FreeList_Free(FreeListAllocator* allocator, void* ptr) uintptr_t basePage = address & ~0xfffull; const BucketMetadata* metadata = reinterpret_cast(basePage); + assert(metadata->magic == BucketMetadata::MAGIC_VALUE); + uint32_t bucketIx = metadata->bucketIx; Node* freeNode = reinterpret_cast(ptr); freeNode->next = allocator->buckets[bucketIx]; diff --git a/Engine/src/delta/core/FreeListAllocator.h b/Engine/src/delta/core/FreeListAllocator.h index eec3ad7..47dec73 100644 --- a/Engine/src/delta/core/FreeListAllocator.h +++ b/Engine/src/delta/core/FreeListAllocator.h @@ -30,8 +30,11 @@ namespace delta::core struct Node { Node* next; }; struct alignas(16) BucketMetadata { + static constexpr char MAGIC_VALUE_STR[sizeof(uint64_t)] = "DLT_SFA"; + static constexpr uint64_t MAGIC_VALUE = std::bit_cast(MAGIC_VALUE_STR); + + uint64_t magic; uint32_t bucketIx; - uint32_t bitmask; }; MemoryManager::MemoryState* memState; From d680a88dcaf87427125a2911d079895dd0d9bc7c Mon Sep 17 00:00:00 2001 From: YasInvolved Date: Wed, 13 May 2026 17:26:44 +0200 Subject: [PATCH 4/4] Removed unnecessary metadata, fixed a bug in bucket carving mechanism --- Engine/src/delta/core/FreeListAllocator.cpp | 33 +++++++-------------- Engine/src/delta/core/FreeListAllocator.h | 10 +------ 2 files changed, 12 insertions(+), 31 deletions(-) diff --git a/Engine/src/delta/core/FreeListAllocator.cpp b/Engine/src/delta/core/FreeListAllocator.cpp index 604344f..5b8c3b4 100644 --- a/Engine/src/delta/core/FreeListAllocator.cpp +++ b/Engine/src/delta/core/FreeListAllocator.cpp @@ -23,23 +23,20 @@ using namespace delta::core; using MemoryState = MemoryManager::MemoryState; using Node = FreeListAllocator::Node; -using BucketMetadata = FreeListAllocator::BucketMetadata; DLT_FORCE_INLINE void* AllocatePageForBucket(FreeListAllocator* allocator, uint32_t bucketIx, uint64_t size) { void* rawPage = MemoryManager::AllocatePageLockFree(allocator->memState); - BucketMetadata* meta = reinterpret_cast(rawPage); - meta->magic = BucketMetadata::MAGIC_VALUE; - meta->bucketIx = bucketIx; - - uint64_t numChunks = allocator->remaining / size; - uint8_t* walker = static_cast(rawPage) + sizeof(BucketMetadata); + uint64_t numChunks = allocator->pageSize / size; + uint8_t* walker = static_cast(rawPage); + // first step Node* head = reinterpret_cast(walker); Node* current = head; - for (uint32_t i = 0; i < numChunks; i++) + // next n - 1 steps + for (uint32_t i = 0; i < numChunks - 2; i++) { walker += size; Node* next = reinterpret_cast(walker); @@ -47,16 +44,15 @@ DLT_FORCE_INLINE void* AllocatePageForBucket(FreeListAllocator* allocator, uint3 current = next; } - current->next = nullptr; - allocator->buckets[bucketIx] = head; - return head; + walker += size; + Node* next = reinterpret_cast(walker); + return next; } void delta::core::FreeList_Init(FreeListAllocator* allocator, MemoryState* memState) { allocator->memState = memState; allocator->pageSize = memState->pageSize; - allocator->remaining = memState->pageSize - sizeof(BucketMetadata); } void* delta::core::FreeList_Allocate(FreeListAllocator* allocator, uint64_t size, uint64_t alignment) @@ -86,17 +82,10 @@ void* delta::core::FreeList_Allocate(FreeListAllocator* allocator, uint64_t size void delta::core::FreeList_Free(FreeListAllocator* allocator, void* ptr) { - if (!ptr) - return; - uintptr_t address = reinterpret_cast(ptr); - uintptr_t basePage = address & ~0xfffull; +} - const BucketMetadata* metadata = reinterpret_cast(basePage); - assert(metadata->magic == BucketMetadata::MAGIC_VALUE); +void delta::core::FreeList_Destroy(FreeListAllocator* allocator) +{ - uint32_t bucketIx = metadata->bucketIx; - Node* freeNode = reinterpret_cast(ptr); - freeNode->next = allocator->buckets[bucketIx]; - allocator->buckets[bucketIx] = freeNode; } diff --git a/Engine/src/delta/core/FreeListAllocator.h b/Engine/src/delta/core/FreeListAllocator.h index 47dec73..604144a 100644 --- a/Engine/src/delta/core/FreeListAllocator.h +++ b/Engine/src/delta/core/FreeListAllocator.h @@ -28,24 +28,16 @@ namespace delta::core static constexpr uint32_t BUCKET_COUNT = 7ull; // log2(2048) - log2(16) = 7, calculated manually for simplicity struct Node { Node* next; }; - struct alignas(16) BucketMetadata - { - static constexpr char MAGIC_VALUE_STR[sizeof(uint64_t)] = "DLT_SFA"; - static constexpr uint64_t MAGIC_VALUE = std::bit_cast(MAGIC_VALUE_STR); - - uint64_t magic; - uint32_t bucketIx; - }; MemoryManager::MemoryState* memState; Node* buckets[BUCKET_COUNT]; uint64_t pageSize; - uint64_t remaining; // pageSize - metadata size }; void FreeList_Init(FreeListAllocator* allocator, MemoryManager::MemoryState* memState); void* FreeList_Allocate(FreeListAllocator* allocator, uint64_t size, uint64_t alignment); void FreeList_Free(FreeListAllocator* allocator, void* ptr); + void FreeList_Destroy(FreeListAllocator* allocator); }