Program Listing for File LinkedListAllocator.cpp#
↰ Return to documentation for file (LinkedListAllocator.cpp)
#define DEBUG_COLLECT_AFTER_FREE
#define DEBUG_SHOW_MEMORY_INFORMATION
//#define DEBUG_CLEAR_MEMORY_AFTER_COLLECT
//#define DEBUG_CLEAR_MEMORY_ON_DESTROY
//#define DEBUG_USE_IOSTREAM_FOR_DISPLAY
#define DEBUG_USE_STDIO_FOR_DISPLAY
#include "LinkedListAllocator.h"
#include <Windows.h>
#include <cstdio>
#ifdef DEBUG_USE_IOSTREAM_FOR_DISPLAY
#include <iostream>
#include <iomanip>
#endif
#ifdef DEBUG_USE_STDIO_FOR_DISPLAY
#include <cinttypes>
#endif
#include "MemoryAlignmentHelper.h"
uintptr_t LinkedListAllocator::sBaseAddressOfLinkedListAllocator = 0;
bool LinkedListAllocator::bIsVirtualMemoryAllocated = false;
LinkedListAllocator* LinkedListAllocator::Get()
{
assert(sBaseAddressOfLinkedListAllocator != 0);
return reinterpret_cast<LinkedListAllocator*>(sBaseAddressOfLinkedListAllocator);
}
LinkedListAllocator* LinkedListAllocator::Create(void* i_pMemoryAddress, size_t i_bytes, unsigned i_numDescriptors)
{
#ifdef DEBUG_SHOW_MEMORY_INFORMATION
void* pDebugMemoryStart = i_pMemoryAddress;
printf("========================================LINKEDLIST ALLOCATOR=======================================\n");
printf("pLinkedListAllocator Start Address: %zX\n", reinterpret_cast<uintptr_t>(pDebugMemoryStart));
//printf("pLinkedListAllocator End Address: %zX\n", reinterpret_cast<uintptr_t>(pDebugMemoryStart) + i_bytes);
printf("Total Size of Heap: %zu bytes\n", i_bytes);
printf("===================================================================================================\n");
/*for (size_t index = 0; index < i_bytes; index++)
{
*static_cast<char*>(pDebugMemoryStart) = '~';
pDebugMemoryStart = static_cast<char*>(pDebugMemoryStart) + 1;
}*/
#endif
auto pLinkedListAllocator = static_cast<LinkedListAllocator*>(i_pMemoryAddress);
assert(pLinkedListAllocator);
return pLinkedListAllocator->Initialize(i_pMemoryAddress, i_bytes, i_numDescriptors);
}
LinkedListAllocator* LinkedListAllocator::Create(size_t i_bytes, unsigned int i_numDescriptors)
{
assert((i_bytes) > sizeof(LinkedListAllocator));
SYSTEM_INFO SysInfo;
GetSystemInfo(&SysInfo);
// round our size to a multiple of memory page size
assert(SysInfo.dwPageSize > 0);
const size_t sizeHeapInPageMultiples = SysInfo.dwPageSize * ((i_bytes + SysInfo.dwPageSize) / SysInfo.dwPageSize);
assert((sizeHeapInPageMultiples) > sizeof(LinkedListAllocator));
void* pMemoryAddress = VirtualAlloc(NULL, sizeHeapInPageMultiples, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
assert(pMemoryAddress);
bIsVirtualMemoryAllocated = true;
#ifdef DEBUG_SHOW_MEMORY_INFORMATION
void* pDebugMemoryStart = pMemoryAddress;
printf("========================================LINKEDLIST ALLOCATOR=======================================\n");
printf("pLinkedListAllocator Start Address: %zX\n", reinterpret_cast<uintptr_t>(pDebugMemoryStart));
//printf("pLinkedListAllocator End Address: %zX\n", reinterpret_cast<uintptr_t>(pDebugMemoryStart) + i_bytes);
printf("Total Size of Heap: %zu bytes\n", i_bytes);
printf("===========================================================================================\n");
/*for (size_t index = 0; index < i_bytes; index++)
{
*static_cast<char*>(pDebugMemoryStart) = '~';
pDebugMemoryStart = static_cast<char*>(pDebugMemoryStart) + 1;
}*/
#endif
auto pLinkedListAllocator = static_cast<LinkedListAllocator*>(pMemoryAddress);
assert(pLinkedListAllocator);
return pLinkedListAllocator->Initialize(pMemoryAddress, i_bytes, i_numDescriptors);
}
void LinkedListAllocator::Destroy()
{
#ifdef DEBUG_CLEAR_MEMORY_ON_DESTROY
void* pMemoryAddressStart = reinterpret_cast<void*>(pRoot);
const size_t totalSize = totalSizeOfLinkedListAllocator;
for(size_t index = 0; index < totalSize; index++)
{
*static_cast<char*>(pMemoryAddressStart) = NULL;
pMemoryAddressStart = static_cast<char*>(pMemoryAddressStart) + 1;
}
#endif
if(bIsVirtualMemoryAllocated)
{
VirtualFree(reinterpret_cast<void*>(pRoot), 0, MEM_RELEASE);
}
}
LinkedListAllocator* LinkedListAllocator::Initialize(void* i_pMemory, size_t i_bytes, unsigned int i_numDescriptors)
//LinkedListAllocator* LinkedListAllocator::Initialize(size_t i_bytes)
{
sBaseAddressOfLinkedListAllocator = reinterpret_cast<uintptr_t>(i_pMemory);
//Initialize LinkedListAllocator
pRoot = reinterpret_cast<uintptr_t>(i_pMemory);
pHead = reinterpret_cast<MemoryBlock*>(pRoot + sizeof(LinkedListAllocator));
pTail = reinterpret_cast<MemoryBlock*>(pRoot + i_bytes - sizeof(MemoryBlock));
totalSizeOfLinkedListAllocator = i_bytes;
//Initialize Head
pHead->pBaseAddress = reinterpret_cast<uintptr_t>(pHead);
pHead->BlockSize = 0;
pHead->pPreviousBlock = nullptr;
pHead->pNextBlock = nullptr;
pHead->bIsAllocated = true;
//Initialize Tail
pTail->pBaseAddress = reinterpret_cast<uintptr_t>(pTail);
pTail->BlockSize = 0;
pTail->pPreviousBlock = nullptr;
pTail->pNextBlock = nullptr;
pTail->bIsAllocated = true;
//Create the InitialMemoryBlock i.e. Heap Memory Available to the User
const size_t AvailableMemory = i_bytes - sizeof(LinkedListAllocator) - sizeof(MemoryBlock) - sizeof(MemoryBlock);
assert(AvailableMemory > 0);
MemoryBlock* pNewMemoryBlock = reinterpret_cast<MemoryBlock*>(pTail->pBaseAddress - AvailableMemory);
pNewMemoryBlock->pBaseAddress = reinterpret_cast<uintptr_t>(pNewMemoryBlock) + sizeof(MemoryBlock);
const size_t AvailableBlockMemory = AvailableMemory - sizeof(MemoryBlock);
assert(AvailableBlockMemory > 0);
pNewMemoryBlock->BlockSize = AvailableBlockMemory;
pNewMemoryBlock->pPreviousBlock = pHead;
pNewMemoryBlock->pNextBlock = pTail;
pNewMemoryBlock->bIsAllocated = false;
//Link the Head, Tail & the InitialMemoryBlock
pHead->pNextBlock = pNewMemoryBlock;
pTail->pPreviousBlock = pNewMemoryBlock;
#ifdef DEBUG_SHOW_MEMORY_INFORMATION
ShowFreeBlocks();
#endif
return reinterpret_cast<LinkedListAllocator*>(pRoot);
}
size_t LinkedListAllocator::GetSizeRequiredForANewBlock(size_t i_size, size_t i_align)
{
const size_t alignPadding = MemoryAlignmentHelper::AlignPadding(i_size, i_align);
//const size_t requiredSize = (2 * sizeof(MemoryBlock)) + alignPadding + i_size;
const size_t requiredSize = sizeof(MemoryBlock) + alignPadding + i_size;
return requiredSize;
}
MemoryBlock* LinkedListAllocator::FindFirstFit(size_t i_size, size_t i_align) const
{
const size_t SizeRequired = GetSizeRequiredForANewBlock(i_size, i_align);
MemoryBlock* IteratorBlock = pTail;
while ((nullptr != IteratorBlock))
{
//Find First Fit with alignment
if(IteratorBlock->BlockSize >= SizeRequired && IteratorBlock->bIsAllocated == false)
{
MemoryBlock* pPotentialAvailableBlock = IteratorBlock;
uintptr_t pNextBlockToAvailableBlockAddress = reinterpret_cast<uintptr_t>(pPotentialAvailableBlock->pNextBlock);
//uintptr_t EndOfAvailableBlock = reinterpret_cast<uintptr_t>(pAvailableBlock) + sizeof(MemoryBlock) + pAvailableBlock->BlockSize;
uintptr_t EndOfAvailableBlock = reinterpret_cast<uintptr_t>(pPotentialAvailableBlock->pNextBlock);
uintptr_t PotentialUserAddress = EndOfAvailableBlock - i_size;
uintptr_t AlignedUserAddress = MemoryAlignmentHelper::AlignDown(PotentialUserAddress, i_align);
uintptr_t StartOfNewMemoryBlock = AlignedUserAddress - sizeof(MemoryBlock);
if (!(StartOfNewMemoryBlock < pPotentialAvailableBlock->pBaseAddress))
{
break;
}
}
IteratorBlock = IteratorBlock->pPreviousBlock;
}
return IteratorBlock;
}
void LinkedListAllocator::PrintDisplayHeading() const
{
#ifdef DEBUG_USE_IOSTREAM_FOR_DISPLAY
const char separator = ' ';
std::cout << " | ";
std::cout << std::left << std::setw(sizeof(size_t)) << std::setfill(separator) << "MemoryBlockAt" << " | ";
std::cout << std::left << std::setw(sizeof(size_t)) << std::setfill(separator) << "BaseAddress" << " | ";
std::cout << std::left << std::setw(sizeof(size_t)) << std::setfill(separator) << "SizeOfBlock" << " | ";
std::cout << std::left << std::setw(sizeof(size_t)) << std::setfill(separator) << "PreviousAddress" << " | ";
std::cout << std::left << std::setw(sizeof(size_t)) << std::setfill(separator) << "NextAddress" << " | ";
std::cout << std::left << std::setw(sizeof(size_t)) << std::setfill(separator) << "IsAllocated" << " | ";
/*std::cout << std::left << std::setw(18) << std::setfill(separator) << "MemoryBlockAt" << " | ";
std::cout << std::left << std::setw(18) << std::setfill(separator) << "BaseAddress" << " | ";
std::cout << std::left << std::setw(18) << std::setfill(separator) << "SizeOfBlock" << " | ";
std::cout << std::left << std::setw(18) << std::setfill(separator) << "PreviousAddress" << " | ";
std::cout << std::left << std::setw(18) << std::setfill(separator) << "NextAddress" << " | ";
std::cout << std::left << std::setw(11) << std::setfill(separator) << "IsAllocated" << " | ";*/
std::cout << std::endl;
#endif
#ifdef DEBUG_USE_STDIO_FOR_DISPLAY
#endif
}
bool LinkedListAllocator::canCoalesceWithNextMemoryBlock(MemoryBlock* i_pMemoryBlock)
{
if(i_pMemoryBlock == nullptr)
{
return false;
}
return !i_pMemoryBlock->bIsAllocated && i_pMemoryBlock->pNextBlock && !i_pMemoryBlock->pNextBlock->bIsAllocated;
}
MemoryBlock* LinkedListAllocator::CoalesceWithNextBlock(MemoryBlock* i_pMemoryBlock)
{
MemoryBlock* pNextMemoryBlock = i_pMemoryBlock->pNextBlock;
if(pNextMemoryBlock->pNextBlock != nullptr)
{
uintptr_t EndAddressOfNextBlock = reinterpret_cast<uintptr_t>(pNextMemoryBlock->pNextBlock);
size_t SizeFromNextBlock = EndAddressOfNextBlock - reinterpret_cast<uintptr_t>(pNextMemoryBlock);
uintptr_t EndAddressOfCurrentMemoryBlock = reinterpret_cast<uintptr_t>(pNextMemoryBlock);
size_t SizeFromCurrentBlock = EndAddressOfCurrentMemoryBlock - i_pMemoryBlock->pBaseAddress;
i_pMemoryBlock->BlockSize = SizeFromCurrentBlock + SizeFromNextBlock;
i_pMemoryBlock->pNextBlock = pNextMemoryBlock->pNextBlock;
i_pMemoryBlock->pNextBlock->pPreviousBlock = i_pMemoryBlock;
#ifdef DEBUG_CLEAR_MEMORY_AFTER_COLLECT
//Clear the Memory and initialize the collected memory with null values
void* pUserMemoryStart = reinterpret_cast<void*>(i_pMemoryBlock->pBaseAddress);
for(size_t index = 0; index < i_pMemoryBlock->BlockSize; index++)
{
*static_cast<char*>(pUserMemoryStart) = NULL;
pUserMemoryStart = static_cast<char*>(pUserMemoryStart) + 1;
}
#endif
return i_pMemoryBlock;
}
return nullptr;
}
bool LinkedListAllocator::CheckAddressWithinHeap(uintptr_t i_AddressToCheck)
{
//std::cout << "CheckAddressWithinHeap: " << std::hex << i_AddressToCheck << std::dec << std::endl;
if(i_AddressToCheck > pRoot && i_AddressToCheck < (pRoot+totalSizeOfLinkedListAllocator))
{
return true;
}
return false;
}
void* LinkedListAllocator::Alloc(size_t i_size)
{
return Alloc(i_size, MemoryAlignmentHelper::sDefaultAlignment);
}
void* LinkedListAllocator::Alloc(size_t i_size, size_t i_align)
{
MemoryBlock* pAvailableBlock = FindFirstFit(i_size, i_align);
if(pAvailableBlock == nullptr)
{
return nullptr;
}
uintptr_t pNextBlockToAvailableBlockAddress = reinterpret_cast<uintptr_t>(pAvailableBlock->pNextBlock);
//uintptr_t EndOfAvailableBlock = reinterpret_cast<uintptr_t>(pAvailableBlock) + sizeof(MemoryBlock) + pAvailableBlock->BlockSize;
uintptr_t EndOfAvailableBlock = reinterpret_cast<uintptr_t>(pAvailableBlock->pNextBlock);
uintptr_t PotentialUserAddress = EndOfAvailableBlock - i_size;
uintptr_t AlignedUserAddress = MemoryAlignmentHelper::AlignDown(PotentialUserAddress, i_align);
uintptr_t StartOfNewMemoryBlock = AlignedUserAddress - sizeof(MemoryBlock);
if(StartOfNewMemoryBlock < pAvailableBlock->pBaseAddress)
{
return nullptr;
}
MemoryBlock* pNewMemoryBlock = reinterpret_cast<MemoryBlock*>(StartOfNewMemoryBlock);
pNewMemoryBlock->pBaseAddress = AlignedUserAddress;
pNewMemoryBlock->BlockSize = i_size;
pNewMemoryBlock->pPreviousBlock = pAvailableBlock;
pNewMemoryBlock->pNextBlock = pAvailableBlock->pNextBlock;
pNewMemoryBlock->bIsAllocated = true;
pAvailableBlock->pNextBlock = pNewMemoryBlock;
MemoryBlock* pNextBlockToAvailableBlock = reinterpret_cast<MemoryBlock*>(pNextBlockToAvailableBlockAddress);
assert(pNextBlockToAvailableBlock != nullptr);
pNextBlockToAvailableBlock->pPreviousBlock = pNewMemoryBlock;
pAvailableBlock->BlockSize = StartOfNewMemoryBlock - pAvailableBlock->pBaseAddress;
//DisplayMemoryBlock(pNewMemoryBlock);
assert(CheckAddressWithinHeap(pNewMemoryBlock->pBaseAddress));
return reinterpret_cast<void*>(pNewMemoryBlock->pBaseAddress);
}
void LinkedListAllocator::Collect()
{
MemoryBlock* IteratorBlock = pHead->pNextBlock;
while (IteratorBlock)
{
if(canCoalesceWithNextMemoryBlock(IteratorBlock))
{
MemoryBlock* CoalescedBlock = CoalesceWithNextBlock(IteratorBlock);
if (CoalescedBlock == nullptr)
{
break;
}
IteratorBlock = CoalescedBlock;
}
else
{
IteratorBlock = IteratorBlock->pNextBlock;
}
}
}
bool LinkedListAllocator::Contains(void* i_pMemory) const
{
MemoryBlock* IteratorBlock = pTail->pPreviousBlock;
while ((nullptr != IteratorBlock))
{
if (IteratorBlock->pBaseAddress == reinterpret_cast<uintptr_t>(i_pMemory))
{
return true;
}
IteratorBlock = IteratorBlock->pPreviousBlock;
}
return false;
}
bool LinkedListAllocator::IsAllocated(void* i_pMemory) const
{
if (i_pMemory == nullptr)
{
return false;
}
const uintptr_t MemoryBlockAddressForUserPointer = reinterpret_cast<uintptr_t>(i_pMemory) - sizeof(MemoryBlock);
MemoryBlock* MemoryBlockToCheck = reinterpret_cast<MemoryBlock*>(MemoryBlockAddressForUserPointer);
assert(MemoryBlockToCheck);
return MemoryBlockToCheck->bIsAllocated;
}
size_t LinkedListAllocator::GetLargestFreeBlock() const
{
return 0;
}
size_t LinkedListAllocator::GetTotalFreeMemory() const
{
return 0;
}
void LinkedListAllocator::ShowFreeBlocks() const
{
#ifdef DEBUG_USE_IOSTREAM_FOR_DISPLAY
std::cout << "================ START Free Blocks ================" << std::endl;
#endif
#ifdef DEBUG_USE_STDIO_FOR_DISPLAY
printf("========================================================================== START Free Blocks ==========================================================================\n");
#endif
MemoryBlock* IteratorBlock = pHead;
#ifdef DEBUG_USE_IOSTREAM_FOR_DISPLAY
if (nullptr != IteratorBlock)
{
PrintDisplayHeading();
}
#endif
while ((nullptr != IteratorBlock))
{
if ((IteratorBlock != pHead)
&& (IteratorBlock != pTail)
&& (!IteratorBlock->bIsAllocated))
{
DisplayMemoryBlock(IteratorBlock);
}
IteratorBlock = IteratorBlock->pNextBlock;
}
#ifdef DEBUG_USE_IOSTREAM_FOR_DISPLAY
std::cout << "================ END Free Blocks ================" << std::endl << std::endl;
#endif
#ifdef DEBUG_USE_STDIO_FOR_DISPLAY
printf("============================================================================ END Free Blocks ============================================================================\n\n");
#endif
}
void LinkedListAllocator::DisplayMemoryBlock(MemoryBlock* i_pMemoryBlock) const
{
assert(i_pMemoryBlock);
const char separator = ' ';
uintptr_t MemoryBlockAddress = reinterpret_cast<uintptr_t>(i_pMemoryBlock);
uintptr_t BaseAddress = i_pMemoryBlock->pBaseAddress;
size_t SizeOfBlock = i_pMemoryBlock->BlockSize;
uintptr_t PreviousAddress = reinterpret_cast<uintptr_t>(i_pMemoryBlock->pPreviousBlock);
uintptr_t NextAddress = reinterpret_cast<uintptr_t>(i_pMemoryBlock->pNextBlock);
bool IsBlockAllocated = i_pMemoryBlock->bIsAllocated;
#ifdef DEBUG_USE_IOSTREAM_FOR_DISPLAY
std::cout << " | ";
std::cout << std::left << std::setw(sizeof(size_t)) << std::setfill(separator) << std::hex << std::uppercase << MemoryBlockAddress << " | " << std::dec;
std::cout << std::left << std::setw(sizeof(size_t)) << std::setfill(separator) << std::hex << std::uppercase << BaseAddress << " | " << std::dec;
std::cout << std::left << std::setw(sizeof(size_t)) << std::setfill(separator) << SizeOfBlock << " | " << std::dec;
std::cout << std::left << std::setw(sizeof(size_t)) << std::setfill(separator) << std::hex << std::uppercase << PreviousAddress << " | " << std::dec;
std::cout << std::left << std::setw(sizeof(size_t)) << std::setfill(separator) << std::hex << std::uppercase << NextAddress << " | " << std::dec;
std::cout << std::left << std::setw(sizeof(size_t)) << std::setfill(separator) << IsBlockAllocated << " | ";
std::cout << std::endl;
#endif
#ifdef DEBUG_USE_STDIO_FOR_DISPLAY
printf(
" | MemoryBlockAt: 0x%" PRIXPTR
" | BaseAddress: 0x%" PRIXPTR
" | SizeOfBlock: %10zu | PreviousAddress: 0x%" PRIXPTR
" | NextAddress: 0x%" PRIXPTR
" | IsFreeBlock: %s |\n"
, MemoryBlockAddress
, BaseAddress
, SizeOfBlock
, PreviousAddress
, NextAddress
, (IsBlockAllocated ? "false" : "true")
);
#endif
}
void LinkedListAllocator::ShowOutstandingAllocations() const
{
#ifdef DEBUG_USE_IOSTREAM_FOR_DISPLAY
std::cout << "================ START Outstanding Allocations ================" << std::endl;
#endif
#ifdef DEBUG_USE_STDIO_FOR_DISPLAY
printf("==================================================================== START Outstanding Allocations ====================================================================\n");
#endif
MemoryBlock* IteratorBlock = pHead;
#ifdef DEBUG_USE_IOSTREAM_FOR_DISPLAY
if (nullptr != IteratorBlock)
{
PrintDisplayHeading();
}
#endif
while ((nullptr != IteratorBlock))
{
if((IteratorBlock != pHead)
&& (IteratorBlock != pTail)
&& (IteratorBlock->bIsAllocated))
{
DisplayMemoryBlock(IteratorBlock);
}
IteratorBlock = IteratorBlock->pNextBlock;
}
#ifdef DEBUG_USE_IOSTREAM_FOR_DISPLAY
std::cout << "================ END Outstanding Allocations ================" << std::endl << std::endl;
#endif
#ifdef DEBUG_USE_STDIO_FOR_DISPLAY
printf("==================================================================== END Outstanding Allocations ====================================================================\n\n");
#endif
}
bool LinkedListAllocator::Free(void* i_pMemory)
{
if(i_pMemory == nullptr)
{
return true;
}
assert(Contains(i_pMemory));
const uintptr_t MemoryBlockAddressForUserPointer = reinterpret_cast<uintptr_t>(i_pMemory) - sizeof(MemoryBlock);
MemoryBlock* MemoryBlockToFree = reinterpret_cast<MemoryBlock*>(MemoryBlockAddressForUserPointer);
assert(MemoryBlockToFree);
MemoryBlockToFree->bIsAllocated = false;
uintptr_t AvailableBytes = reinterpret_cast<uintptr_t>(MemoryBlockToFree->pNextBlock) - MemoryBlockToFree->pBaseAddress;
MemoryBlockToFree->BlockSize = AvailableBytes;
#ifdef DEBUG_COLLECT_AFTER_FREE
Collect();
#endif
return true;
}