WIP Block allocator implementation
Some checks failed
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, macos-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Has been cancelled
Some checks failed
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, macos-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Has been cancelled
This commit is contained in:
@@ -78,6 +78,12 @@ private:
|
||||
|
||||
// Remove a free block from the index
|
||||
void removeFreeBlock(TLSFBlock* block);
|
||||
|
||||
// Find a free block that can hold at least size bytes
|
||||
TLSFBlock* findFreeBlock(size_t size) const;
|
||||
|
||||
// Get the usable size of a block
|
||||
size_t getBlockUsableSize(const TLSFBlock* block) const;
|
||||
};
|
||||
|
||||
} // namespace mimir
|
||||
|
||||
125
src/heap.cpp
125
src/heap.cpp
@@ -41,7 +41,7 @@ namespace mimir {
|
||||
|
||||
// Two-Level Segregated Fit (TLSF): http://www.gii.upv.es/tlsf/files/papers/ecrts04_tlsf.pdf
|
||||
|
||||
const size_t kMinblockSize = 16;
|
||||
const size_t kMinBlockSize = 16;
|
||||
|
||||
struct TLSFBlock
|
||||
{
|
||||
@@ -123,13 +123,13 @@ bool Heap::init(size_t minSize, size_t maxSize)
|
||||
|
||||
// Start with one free block, filling the entire Region
|
||||
TLSFBlock* block = (TLSFBlock*)(m_region.getAddress() + sizeof(TLSFIndex));
|
||||
block->prevPhys = nullptr;
|
||||
block->nextFree = nullptr;
|
||||
block->prevFree = nullptr;
|
||||
block->size = m_region.getMaxSize() - sizeof(TLSFIndex);
|
||||
block->size |= 0b11; // T=1: Last Block, F=1: Free Block
|
||||
block->size = m_region.getMaxSize();
|
||||
block->size |= 0b10; // T=1: Last Block, F=1: Free Block
|
||||
|
||||
insertFreeBlock(block);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -147,18 +147,26 @@ size_t Heap::getUsed() const
|
||||
return m_usedSize;
|
||||
}
|
||||
|
||||
size_t Heap::getBlockUsableSize(const TLSFBlock* block) const
|
||||
{
|
||||
return (block->size & ~0b11) - 16;
|
||||
}
|
||||
|
||||
void Heap::insertFreeBlock(TLSFBlock* block)
|
||||
{
|
||||
// Add the block to the index
|
||||
|
||||
TLSFIndex* index = (TLSFIndex*)m_region.getAddress();
|
||||
|
||||
size_t usableSize = (block->size & ~0b11) - 16;
|
||||
|
||||
size_t firstLevelIndex = std::bit_width(usableSize) - 5;
|
||||
size_t usableSize = getBlockUsableSize(block);
|
||||
size_t firstLevel = std::bit_width(usableSize);
|
||||
size_t firstLevelIndex = firstLevel - 5;
|
||||
size_t secondLevelIndex = (usableSize >> firstLevelIndex) & 0b1111ULL;
|
||||
|
||||
// Update free bitmaps
|
||||
index->firstLevelFreeBitMap |= std::bit_floor(usableSize);
|
||||
index->secondLevelFreeBitMap[firstLevelIndex] |= secondLevelIndex & 0b1111ULL;
|
||||
index->secondLevelFreeBitMap[firstLevelIndex] |= secondLevelIndex;
|
||||
|
||||
TLSFBlock* prevFirstFreeBlock = index->secondLevelFreeBlocks[firstLevelIndex][secondLevelIndex];
|
||||
index->secondLevelFreeBlocks[firstLevelIndex][secondLevelIndex] = block;
|
||||
|
||||
@@ -167,6 +175,9 @@ void Heap::insertFreeBlock(TLSFBlock* block)
|
||||
block->nextFree = prevFirstFreeBlock;
|
||||
prevFirstFreeBlock->prevFree = block;
|
||||
}
|
||||
|
||||
// Mark block as free
|
||||
block->size |= 0b01; // T=1: Last Block, F=1: Free Block
|
||||
}
|
||||
|
||||
void Heap::removeFreeBlock(TLSFBlock* block)
|
||||
@@ -174,24 +185,25 @@ void Heap::removeFreeBlock(TLSFBlock* block)
|
||||
// Remove the block from the index
|
||||
TLSFIndex* index = (TLSFIndex*)m_region.getAddress();
|
||||
|
||||
size_t usableSize = (block->size & ~0b11) - 16;
|
||||
|
||||
size_t usableSize = getBlockUsableSize(block);
|
||||
size_t firstLevel = std::bit_width(usableSize);
|
||||
size_t firstLevelIndex = firstLevel - 5;
|
||||
size_t secondLevelIndex = (usableSize >> (firstLevel - 5)) & 0b1111ULL;
|
||||
size_t secondLevelIndex = (usableSize >> (firstLevelIndex)) & 0b1111ULL;
|
||||
|
||||
if (block->prevFree != nullptr) {
|
||||
// Link the neighboring free blocks together
|
||||
block->prevFree = block->nextFree;
|
||||
block->prevFree->nextFree = block->nextFree;
|
||||
if (block->nextFree) {
|
||||
block->nextFree = block->prevFree;
|
||||
block->nextFree->prevFree = block->prevFree;
|
||||
}
|
||||
} else {
|
||||
// This block was the first for this level
|
||||
index->secondLevelFreeBlocks[firstLevelIndex][secondLevelIndex] = block->nextFree;
|
||||
|
||||
if (block->nextFree == nullptr) {
|
||||
// We have removed all the blocks at this level.
|
||||
if (block->nextFree != nullptr) {
|
||||
block->nextFree->prevFree = nullptr;
|
||||
} else {
|
||||
// We have removed all the free blocks at this level.
|
||||
|
||||
// Update second level bitmask.
|
||||
index->secondLevelFreeBitMap[firstLevelIndex] &= ~(1ULL << secondLevelIndex);
|
||||
@@ -205,20 +217,15 @@ void Heap::removeFreeBlock(TLSFBlock* block)
|
||||
}
|
||||
}
|
||||
|
||||
if (block->size & 0b10ULL) {
|
||||
// This block was the last in physical order.
|
||||
if (block->prevPhys) {
|
||||
// Update the prior block in physical order to mark it as the last block.
|
||||
block->prevPhys->size &= 0b10ULL;
|
||||
}
|
||||
}
|
||||
// Mark block as non-free
|
||||
block->size &= ~0b01; // T=1: Last Block, F=1: Free Block
|
||||
}
|
||||
|
||||
// Allocate `size` bytes
|
||||
std::byte* Heap::alloc(size_t size)
|
||||
// Find a free block that can hold at least size bytes
|
||||
TLSFBlock* Heap::findFreeBlock(size_t size) const
|
||||
{
|
||||
if (size < kMinblockSize) {
|
||||
size = kMinblockSize;
|
||||
if (size < kMinBlockSize) {
|
||||
size = kMinBlockSize;
|
||||
}
|
||||
TLSFIndex* index = (TLSFIndex*)m_region.getAddress();
|
||||
|
||||
@@ -258,52 +265,50 @@ std::byte* Heap::alloc(size_t size)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Any buffer at the second level will fit this allocation.
|
||||
// Any buffer at the second level will fit this allocation. Pick the smallest buffer.
|
||||
selectedSecondLevel = std::countr_zero(index->secondLevelFreeBitMap[selectedFirstLevel - 4]);
|
||||
}
|
||||
|
||||
// Take the first free block
|
||||
TLSFBlock* block = index->secondLevelFreeBlocks[selectedFirstLevel - 4][selectedSecondLevel];
|
||||
|
||||
size_t oldBlockSize = block->size & ~0b11ULL;
|
||||
bool oldBlockWasLastPhysBlock = (block->size & ~0b10ULL) != 0;
|
||||
|
||||
TLSFBlock* prevFreeBlock = block->prevFree;
|
||||
TLSFBlock* nextFreeBlock = block->nextFree;
|
||||
TLSFBlock* prevPhysBlock = block->prevPhys;
|
||||
TLSFBlock* nextPhysBlock = nullptr;
|
||||
if (!oldBlockWasLastPhysBlock) {
|
||||
nextPhysBlock = (TLSFBlock*)(((std::byte*)block) + oldBlockSize + 16);
|
||||
return block;
|
||||
}
|
||||
|
||||
// The next free block at this level replaces this block in the index.
|
||||
index->secondLevelFreeBlocks[selectedFirstLevel - 4][selectedSecondLevel] = nextFreeBlock;
|
||||
|
||||
if (nextFreeBlock == nullptr) {
|
||||
// This was the last free block at this level.
|
||||
// Update the bitmap for the second level...
|
||||
index->secondLevelFreeBitMap[selectedFirstLevel - 4] &= ~(1ULL << selectedSecondLevel);
|
||||
|
||||
// Check if there are any remaining free blocks within the first level
|
||||
if (index->secondLevelFreeBitMap[selectedFirstLevel - 4] == 0) {
|
||||
|
||||
// This was the last one. Clear the first level bit as well.
|
||||
index->firstLevelFreeBitMap &= ~(1ULL << selectedFirstLevel);
|
||||
}
|
||||
} else {
|
||||
// The next free block is now the first free block at this level.
|
||||
nextFreeBlock->prevFree = nullptr;
|
||||
// Allocate `size` bytes
|
||||
std::byte* Heap::alloc(size_t size)
|
||||
{
|
||||
if (size < kMinBlockSize) {
|
||||
size = kMinBlockSize;
|
||||
}
|
||||
|
||||
if (nextPhysBlock) {
|
||||
// Link the next physical block to this one.
|
||||
nextPhysBlock->prevPhys = block;
|
||||
} else {
|
||||
// This is the last physical block, so set the last block bit.
|
||||
block->size &= 0b10ULL;
|
||||
TLSFBlock* block = findFreeBlock(size);
|
||||
if (block == nullptr) {
|
||||
// No free block found
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
removeFreeBlock(block);
|
||||
|
||||
size_t usableSize = getBlockUsableSize(block);
|
||||
|
||||
if (size - usableSize >= sizeof(TLSFBlock)) {
|
||||
// Shrink the block to size; allocate a new free block for the remainder
|
||||
|
||||
TLSFBlock* freeBlock = (TLSFBlock*)((std::byte*)block + size + 16ULL);
|
||||
freeBlock->size = size - usableSize;
|
||||
freeBlock->prevPhys = block;
|
||||
freeBlock->prevFree = nullptr;
|
||||
freeBlock->nextFree = nullptr;
|
||||
if (block->size & 0b10) { // LSB: T, F: T = Last physical block, F = Free block
|
||||
// Block was the last physical block
|
||||
block->size &= ~0b10;
|
||||
freeBlock->size &= 0b10;
|
||||
}
|
||||
block->size -= freeBlock->size & ~0b10;
|
||||
insertFreeBlock(freeBlock);
|
||||
}
|
||||
|
||||
block->size = size;
|
||||
m_usedSize += size;
|
||||
return (std::byte*)block + 16;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user