#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(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; }