Files
MontaukOS/kernel/src/Memory/Heap.cpp
T

145 lines
3.8 KiB
C++

/*
* MemoryAllocator.cpp
* Heap memory allocator
* Copyright (c) 2025 Daniel Hammer
*/
#include "Heap.hpp"
#include "Memmap.hpp"
#include "HHDM.hpp"
#include <Libraries/Memory.hpp>
#include <Terminal/Terminal.hpp>
#include <Common/Panic.hpp>
#include <Platform/Util.hpp>
#include <Common/Panic.hpp>
#include <CppLib/Vector.hpp>
#include "PageFrameAllocator.hpp"
namespace Memory
{
HeapAllocator::Header* HeapAllocator::GetHeader(void* block) {
return (Header*)(block - sizeof(Header));
}
size_t HeapAllocator::GetAllocatedBlockSize(void* ptr) {
Header* header = GetHeader(ptr);
return header->size;
}
void HeapAllocator::InsertToFreelist(void* ptr, std::size_t size) {
auto prev_next = head.next;
head.next = (Node*)ptr;
head.next->next = prev_next;
head.next->size = size;
}
void HeapAllocator::InsertPageToFreelist() {
InsertToFreelist(Memory::g_pfa->Allocate(), 0x1000);
}
void HeapAllocator::InsertPagesToFreelist(std::size_t n) {
auto ptr = Memory::g_pfa->ReallocConsecutive(nullptr, n);
size_t size = 0x1000 * n;
InsertToFreelist(ptr, size);
}
HeapAllocator::HeapAllocator()
{
InsertPagesToFreelist(0x32);
}
void* HeapAllocator::Request(size_t size) {
Lock.Acquire();
Node* current = head.next;
Node* prev = &head;
size_t sizeNeeded = size + sizeof(Header);
while (current != nullptr) {
if (current->size >= sizeNeeded) {
// Unlink the node
auto locatedBlockSize = current->size;
prev->next = current->next;
Header* header = (Header*)current;
header->magic = headerMagic;
header->size = size;
void* block = (void*)((uintptr_t)header + sizeof(Header));
if (locatedBlockSize > sizeNeeded) {
void* rest = (void*)((uintptr_t)header + sizeNeeded);
auto newBlockSize = locatedBlockSize - sizeNeeded;
InsertToFreelist(rest, newBlockSize);
}
Lock.Release();
return block;
}
prev = current;
current = current->next;
}
Lock.Release();
// First pass allocation failed -- grow the heap
size_t pagesNeeded = (sizeNeeded + 0xFFF) / 0x1000;
InsertPagesToFreelist(pagesNeeded);
return Request(size);
}
void* HeapAllocator::Realloc(void* ptr, size_t size) {
auto new_block = Request(size);
if (ptr != nullptr && new_block != nullptr) {
size_t oldSize = GetAllocatedBlockSize(ptr);
size_t copySize = (oldSize < size) ? oldSize : size;
memcpy(new_block, ptr, copySize);
Free(ptr);
}
return new_block;
}
void HeapAllocator::Free(void* ptr) {
Lock.Acquire();
Header* header = GetHeader(ptr);
auto size = header->size;
if (header->magic != headerMagic) {
Panic("Bad magic in HeapAllocator header", nullptr);
return;
}
auto actualSize = size + sizeof(Header);
void* actualBlock = (void*)header;
InsertToFreelist(actualBlock, actualSize);
Lock.Release();
}
// Traverses the Allocator's linked list for debugging
void HeapAllocator::Walk() {
Node* current = {head.next};
size_t i{0};
while (current != nullptr) {
Kt::KernelLogStream(Kt::DEBUG, "HeapAllocator") << base::dec << i << " " << current->size << " bytes & address 0x" << base::hex << (uint64_t)current;
current = current->next;
i++;
}
}
};