From 54a78ff04e6808340292c989970774566588013e Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Fri, 31 Jan 2020 23:53:20 -0600 Subject: [PATCH] [nostalgia/core/gba] Rewrite heap allocator --- src/nostalgia/core/gba/mem.cpp | 166 ++++++++++++++++----------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/src/nostalgia/core/gba/mem.cpp b/src/nostalgia/core/gba/mem.cpp index 81bacdfe..a2d351d3 100644 --- a/src/nostalgia/core/gba/mem.cpp +++ b/src/nostalgia/core/gba/mem.cpp @@ -7,138 +7,138 @@ */ #include "addresses.hpp" +#include "ox/std/bit.hpp" +#include "ox/std/types.hpp" #include "panic.hpp" #include +// this warning is too dumb to realize that it can actually confirm the hard +// coded address aligns with the requirement of HeapSegment, so it must be +// suppressed #pragma GCC diagnostic ignored "-Wcast-align" +#define HEAP_BEGIN reinterpret_cast(MEM_WRAM_BEGIN) +// set size to half of WRAM +#define HEAP_SIZE ((MEM_WRAM_END - MEM_WRAM_BEGIN) / 2) +#define HEAP_END reinterpret_cast(MEM_WRAM_BEGIN + HEAP_SIZE) + namespace nostalgia::core { +static constexpr std::size_t alignedSize(std::size_t sz) { + return sz + (sz & 7); +} + +template +static constexpr std::size_t alignedSize(T = {}) { + return alignedSize(sizeof(T)); +} + struct HeapSegment { std::size_t size; uint8_t inUse; - HeapSegment *next; - uint8_t *end() { - return reinterpret_cast(this) + this->size; + void init(std::size_t maxSize = ox::bit_cast(HEAP_END)) { + this->size = maxSize - reinterpret_cast(this); + this->inUse = false; } + + template + T *data() { + return reinterpret_cast(reinterpret_cast(this) + alignedSize(this)); + } + + template + T *end() { + const auto size = alignedSize(this) + alignedSize(this->size); + auto e = reinterpret_cast(reinterpret_cast(this) + size); + return reinterpret_cast(e); + } + }; -static HeapSegment *volatile _heapIdx = nullptr; +static HeapSegment *volatile heapIdx = nullptr; void initHeap() { - _heapIdx = reinterpret_cast(MEM_WRAM_END) - 1; - // set size to half of WRAM - _heapIdx->size = (MEM_WRAM_END - MEM_WRAM_BEGIN) / 2; - _heapIdx->next = nullptr; - _heapIdx->inUse = false; + heapIdx = HEAP_BEGIN; + heapIdx->init(); } +static HeapSegment *findSegmentFor(std::size_t sz) { + for (auto s = HEAP_BEGIN; s + sz < HEAP_END; s = s->end()) { + if (s->size >= sz && !s->inUse) { + return s; + } + } + return nullptr; } -using namespace nostalgia::core; +struct SegmentPair { + HeapSegment *anteSegment = nullptr; + HeapSegment *segment = nullptr; +}; -void *malloc(std::size_t allocSize) { - // add space for heap segment header data - const auto fullSize = allocSize + sizeof(HeapSegment); - auto seg = _heapIdx; +static SegmentPair findSegmentOf(void *ptr) { HeapSegment *prev = nullptr; - while (seg && (seg->inUse || seg->size < fullSize)) { + for (auto seg = HEAP_BEGIN; seg < HEAP_END;) { + if (seg->data() == ptr) { + return {prev, seg}; + } prev = seg; - seg = seg->next; + seg = seg->end(); } + return {}; +} - // panic if the allocation failed +[[nodiscard]] void *malloc(std::size_t allocSize) { + const auto targetSize = alignedSize(sizeof(HeapSegment)) + alignedSize(allocSize); + auto seg = findSegmentFor(targetSize); if (seg == nullptr) { - panic("Heap allocation failed"); + return nullptr; } - - seg = reinterpret_cast(reinterpret_cast(seg) - allocSize); - if (prev) { - prev->next = seg; - } - // update size for the heap segment now that it is to be considered - // allocated - seg->size = fullSize; - seg->next = reinterpret_cast(reinterpret_cast(seg) + fullSize); + const auto bytesRemaining = seg->size - targetSize; + seg->size = targetSize; seg->inUse = true; - auto out = seg + 1; - - auto hs = *_heapIdx; - hs.size -= fullSize; - if (hs.size == 0) { - _heapIdx = hs.next; - } else { - _heapIdx = reinterpret_cast((reinterpret_cast(_heapIdx)) - fullSize); - *_heapIdx = hs; - } - + auto out = seg->data(); + seg->end()->init(bytesRemaining); return out; } -void free(void *ptrIn) { - // get ptr back down to the meta data - auto *ptr = reinterpret_cast(ptrIn) - 1; - HeapSegment *prev = nullptr; - auto current = _heapIdx; - while (current && current != ptr) { - prev = current; - current = current->next; - } - - // ptr was found as a valid memory allocation, deallocate it - if (current) { - // move header back to end of segment - auto newCurrent = reinterpret_cast(current->end() - sizeof(HeapSegment)); - *newCurrent = *current; - current = newCurrent; - prev->next = current; - - // mark as not in use - current->inUse = false; - - // join with next if next is also unused - if (current->next && !current->next->inUse) { - current->size += current->next->size; - current->next = current->next->next; - } - - // join with prev if prev is also unused - if (prev && !prev->inUse) { - prev->size += current->size; - prev->next = current->next; - current = prev; - } - - // if current is closer heap start than _heapIdx, _heapIdx becomes - // current - if (current > _heapIdx) { - _heapIdx = current; - } +void free(void *ptr) { + auto p = findSegmentOf(ptr); + if (p.anteSegment) { + p.anteSegment->size += p.segment->size; + } else if (p.segment) { + p.segment->inUse = false; + } else { + panic("Bad heap free"); } } +} + +using namespace nostalgia; + void *operator new(std::size_t allocSize) { - return malloc(allocSize); + return core::malloc(allocSize); } void *operator new[](std::size_t allocSize) { - return malloc(allocSize); + return core::malloc(allocSize); } void operator delete(void *ptr) { - free(ptr); + core::free(ptr); } void operator delete[](void *ptr) { - free(ptr); + core::free(ptr); } void operator delete(void *ptr, unsigned) { - free(ptr); + core::free(ptr); } void operator delete[](void *ptr, unsigned) { - free(ptr); + core::free(ptr); }