Releasing memory

The function to release memory will mark the size of each allocation header as 0. This way, we can check the size of the memory being released, and if the size is 0 we know the memory was double freed. The allocation size being set to 0 will also be used later when we implement sub and super allocators. Let's start implementing the free function by retrieving the size and alignment meta-data from the allocation header. Knowing this information, we can set the memory pointer to be at the start of the page (or set of pages) that is being released.

void Release(void* memory, const char* location, Allocator* allocator) {
    if (allocator == 0) {
        allocator = GlobalAllocator;
    }

    // Retrieve allocation information from header. The allocation header always
    // preceeds the allocation.
    u8* mem = (u8*)memory;
    mem -= sizeof(Allocation);
    Allocation* allocation = (Allocation*)mem;
    u32 alignment = allocation->alignment;
    
    u32 allocationSize = allocation->size; // Add enough space to pad out for alignment
    
    u32 allocationHeaderPadding = 0;
    if (alignment != 0) {  // Add padding to the header to compensate for alignment
        allocationHeaderPadding = alignment - 1; // Somewhere in this range, we will be aligned
    }
    u32 paddedAllocationSize = allocationSize + allocationHeaderPadding + sizeof(Allocation);
    allocator->requested -= allocation->size;
    
    // Implement sub-allocator here

Now that we know the starting page of this allocation, we can figure out the number of pages, and mark all the pages that where used for this allocation as free.

    // Clear the bits that where tracking this memory
    u8* firstMemory = (u8*)allocator;
    u64 address = (u64)(mem - firstMemory);
    u32 firstPage = address / allocator->pageSize;
    u32 numPages = paddedAllocationSize / allocator->pageSize + (paddedAllocationSize % allocator->pageSize ? 1 : 0);
    ClearRange(allocator, firstPage, numPages);

Finally, we unlink the allocation from the allocators list of active allocations and set the size of the allocation to zero.

    // Unlink tracking
    if (allocation->next != 0) {
        allocation->next->prev = allocation->prev;
    }
    if (allocation->prev != 0) {
        allocation->prev->next = allocation->next;
    }
    if (allocation == allocator->active) {
        allocator->active = allocation->next;
    }

    // Set the size to 0, to indicate that this header has been released
    u32 oldAllocSize = allocation->size;
    allocation->size = 0;

    if (allocator->releaseCallback != 0) {
        allocator->releaseCallback(allocator, allocation, oldAllocSize, paddedAllocationSize, firstPage, numPages);
    }
}