Musa-Cpp-Lib-V2/lib/Base/General_Purpose_Allocator.cpp

169 lines
5.3 KiB
C++

#if GP_ALLOCATOR_TRACK_ALLOCATIONS
global General_Allocator gAllocator; // @Shared
global Mutex allocator_mutex;
#endif
#if !COMPILER_MSVC
// Note: There is *no* std::aligned_realloc. Must implement manually if needed.
force_inline void* gp_aligned_realloc(u64 old_size, void* ptr, u64 new_size, u64 alignment) {
if (!ptr || old_size == 0) return std::aligned_alloc(alignment, new_size);
if (new_size == 0) { std::free(ptr); return nullptr; }
// Allocate new block
void* new_ptr = std::aligned_alloc(alignment, new_size);
if (!new_ptr) return nullptr;
u64 copy_size = old_size < new_size ? old_size : new_size;
memcpy(new_ptr, ptr, copy_size);
std::free(ptr);
return new_ptr;
}
#endif
General_Allocator* get_general_allocator_data() {
#if GP_ALLOCATOR_TRACK_ALLOCATIONS
return &gAllocator;
#else
return (General_Allocator*)nullptr;
#endif
}
constexpr s64 Allocation_Tracking_Is_Enabled = GP_ALLOCATOR_TRACK_ALLOCATIONS;
bool GPAllocator_Tracking_Enabled () {
return Allocation_Tracking_Is_Enabled != 0;
}
void GPAllocator_Initialize_Allocation_Tracker () {
#if GP_ALLOCATOR_TRACK_ALLOCATIONS
mutex_init(&allocator_mutex);
constexpr s64 alignment = 64;
s64 item_count_max = 64 * 4096;
s64 total_allocation_size = item_count_max * sizeof(Allocation);
auto memory = Aligned_Alloc(total_allocation_size, alignment); // @MemoryLeak (intentional)
gAllocator.allocations = Array<Allocation>(item_count_max, memory, item_count_max, GPAllocator());
gAllocator.allocations.count = 0; // Init to zero.
#endif
}
bool GPAllocator_Is_This_Yours (void* old_memory) {
#if GP_ALLOCATOR_TRACK_ALLOCATIONS
lock_guard(&allocator_mutex);
s64 old_size = 0;
for (s64 i = 0; i < gAllocator.allocations.count; i += 1) {
if (gAllocator.allocations[i].memory != old_memory)
continue;
return true;
}
#endif
return false;
}
void Add_Allocation(s64 new_size, void* new_memory_address, s32 alignment) {
#if GP_ALLOCATOR_TRACK_ALLOCATIONS
if (new_memory_address == nullptr) return;
lock_guard(&allocator_mutex);
Allocation allocation = {new_size, new_memory_address, alignment};
array_add(gAllocator.allocations, allocation);
gAllocator.total_bytes_allocated += new_size;
#endif
}
void Remove_Allocation(void* old_memory) {
#if GP_ALLOCATOR_TRACK_ALLOCATIONS
lock_guard(&allocator_mutex);
s64 old_size = 0;
for (s64 i = 0; i < gAllocator.allocations.count; i += 1) {
if (gAllocator.allocations[i].memory != old_memory)
continue;
old_size = gAllocator.allocations[i].size;
array_unordered_remove_by_index(gAllocator.allocations, i);
gAllocator.total_bytes_allocated -= old_size;
return;
}
Assert(false); // "Did not find allocation in Array"
#endif
}
void* GPAllocator_New (s64 new_size, s64 alignment, bool initialize) {
// Fallback allocator: _aligned_malloc, which is MSVC's version of std::aligned_alloc
auto memory = Aligned_Alloc(new_size, alignment);
// _aligned_malloc does not zero memory, so we can zero it here
if (initialize && memory) { memset(memory, ALLOCATOR_INIT_VALUE, new_size); }
Add_Allocation(new_size, memory, (s32)alignment);
// printf("[GP] Allocating memory %p of size %llu\n", memory, new_size);
return memory;
}
void* GPAllocator_Resize (s64 old_size, void* old_memory, s64 new_size, s64 alignment, bool initialize) {
Assert((alignment % 8) == 0 && (alignment != 0));
if (old_memory == nullptr) {
return GPAllocator_New(new_size, alignment);
}
// Debug version: _aligned_realloc_dbg
auto new_memory_address = Aligned_Realloc(old_size, old_memory, new_size, alignment);
if (initialize && new_memory_address && new_size > old_size) {
memset((u8*)new_memory_address + old_size, ALLOCATOR_INIT_VALUE, new_size - old_size);
}
Remove_Allocation(old_memory);
Add_Allocation(new_size, new_memory_address, (s32)alignment);
// printf("[GP] Rellocating memory %p of size %llu\n", new_memory_address, new_size);
return new_memory_address;
}
void GPAllocator_Delete (void* memory) {
if (memory == nullptr) return;
Aligned_Free(memory);
Remove_Allocation(memory);
// printf("[GP] Deleting memory %p\n", memory);
}
Allocator GPAllocator () {
return { GPAllocator_Proc, nullptr };
}
void* GPAllocator_Proc (Allocator_Mode mode, s64 requested_size, s64 old_size, void* old_memory, void* allocator_data) {
u16 alignment = 16; // default alignment
Thread_Context* context = thread_context();
if (context) alignment = context->GPAllocator_alignment;
switch (mode) {
case Allocator_Mode::ALLOCATE: {
return GPAllocator_New(requested_size, alignment);
} break;
case Allocator_Mode::RESIZE: {
void* result = GPAllocator_Resize(old_size, old_memory, requested_size, alignment);
// NOTE: The _aligned_realloc function already copies the old memory, so there's
// no need to copy the old memory block here.
return result;
} break;
case Allocator_Mode::DEALLOCATE: {
GPAllocator_Delete(old_memory); // unused
} break;
case Allocator_Mode::DETAILS: {
Assert(allocator_data == nullptr);
return "GPAllocator";
} break;
}
return nullptr;
}