From c979a925d87e67bd666e8456b8d7560285e9f731 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Sun, 16 Jan 2022 18:30:47 -0800 Subject: [PATCH] Implement CMediumAllocPool --- Runtime/CMediumAllocPool.cpp | 223 ++++++++++++++++++++++++++++++++++- Runtime/CMediumAllocPool.hpp | 48 +++++--- 2 files changed, 251 insertions(+), 20 deletions(-) diff --git a/Runtime/CMediumAllocPool.cpp b/Runtime/CMediumAllocPool.cpp index 917c831de..b5da5c1e3 100644 --- a/Runtime/CMediumAllocPool.cpp +++ b/Runtime/CMediumAllocPool.cpp @@ -1,8 +1,227 @@ #include "CMediumAllocPool.hpp" +#include namespace metaforce { +namespace { +CMediumAllocPool* gMediumAllocPtr = nullptr; +void InitBookKeeping(void* bookKeeping, u32 count) { + u32 roundedLen = count & 0xFFFF; + u8 header = 0; + if (roundedLen > 3) { + header = (count >> 8) | 0x80; + u8* bookKeepingIter = static_cast(bookKeeping); + bookKeepingIter[0] = header; + bookKeepingIter[1] = static_cast(count); + bookKeepingIter[roundedLen - 2] = static_cast(count); + bookKeepingIter[roundedLen - 1] = header; + return; + } + + if (roundedLen == 3) { + header = 0x60; + } else if (roundedLen == 2) { + header = 0x40; + } else { + header = 0x20; + } + + static_cast(bookKeeping)[0] = header; + if ((count & 0xFFFF) >= 2) { + static_cast(bookKeeping)[(count & 0xFFFF) - 1] = header | 0x80; + } +} + +u32 GetBlockOffset(u8* startPtr, u8* endPtr) { + u32 numBlocks = (endPtr - startPtr) < 2 ? 0 : *startPtr; + + numBlocks += (*startPtr & 0x7f) * 0x100; + u32 flags = numBlocks & 0x6000; + if (flags == 0) { + return numBlocks; + } + + if (flags == 0x6000) { + numBlocks = 3; + } else { + numBlocks = (std::countl_zero(0x4000 - flags) / 32) + 1; + } + + return numBlocks & 0xFFFF; +} +} // namespace + +SMediumAllocPuddle::SMediumAllocPuddle(u32 numBlocks, void* ptr, bool canErase) +: x0_hasBuffer(ptr != nullptr) +, x4_mainData(ptr) +, x8_bookKeeping(reinterpret_cast(reinterpret_cast(ptr) + numBlocks * 32)) +, x14_numBlocks(numBlocks) +, x1c_numEntries(numBlocks) +, x20_24_canErase(canErase) { + InitBookKeeping(x8_bookKeeping, numBlocks & 0xFFFF); +} + +void* SMediumAllocPuddle::FindFreeEntry(u32 blockCount) { + if (blockCount > x14_numBlocks) + return nullptr; + + u8* pbVar3 = reinterpret_cast(x8_bookKeeping); + u8* pbVar4 = reinterpret_cast(xc_cachedBookKeepingAddr); + if (xc_cachedBookKeepingAddr == nullptr) { + pbVar4 = pbVar3; + } + u8* ptr2 = pbVar3 + x1c_numEntries; + u8* ptr1 = pbVar4; + + u8* pbVar2 = nullptr; + while (pbVar2 != pbVar4) { + if (((*ptr1) & 0x80) == 0 || ptr1 == ptr2) { + pbVar2 = pbVar3; + if (ptr1 != ptr2) { + pbVar2 = ptr1 + *ptr1; + } + } else { + u32 uVar1 = GetBlockOffset(ptr1, ptr2) & 0xFFFF; + if (blockCount <= uVar1) { + uVar1 = (uVar1 - blockCount) & 0xFFFF; + if (uVar1 != 0) { + InitBookKeeping(ptr1 + blockCount, uVar1); + } + xc_cachedBookKeepingAddr = ptr1; + return ptr1; + } + + pbVar2 = ptr1 + uVar1; + if (pbVar2 == pbVar4) { + return nullptr; + } + + if (pbVar2 == ptr2) { + pbVar2 = reinterpret_cast(pbVar3); + } + } + ptr1 = pbVar2; + } + return nullptr; +} + +void* SMediumAllocPuddle::FindFree(u32 blockCount) { + u8* ptr = reinterpret_cast(FindFreeEntry(blockCount)); + + if (ptr == nullptr) { + return nullptr; + } + + u8* bookKeepingPtr = reinterpret_cast(x8_bookKeeping); + u8* mainDataPtr = reinterpret_cast(x4_mainData); + + mainDataPtr = mainDataPtr + (ptr - bookKeepingPtr) * 32; + *(ptr + blockCount - 1) = static_cast(blockCount); + x14_numBlocks -= blockCount; + ++x18_numAllocs; + return mainDataPtr; +} + +void SMediumAllocPuddle::Free(void* ptr) { + u32 uVar3 = reinterpret_cast(ptr) - reinterpret_cast(x4_mainData); + u32 uVar4 = (reinterpret_cast(x8_bookKeeping) + uVar3)[0]; + x14_numBlocks += uVar4; + --x18_numAllocs; + u8* bookKeepingStart = reinterpret_cast(x8_bookKeeping); + u8* pvVar2 = reinterpret_cast(xc_cachedBookKeepingAddr); + u8* pvVar5 = bookKeepingStart + uVar3; + u8* ptr2 = bookKeepingStart + x1c_numEntries; + + uVar3 = uVar4; + u8* bookKeepingPtr = pvVar5; + if (bookKeepingStart < pvVar5) { + u8 bVar1 = *(pvVar5 - 1); + if ((bVar1 & 0x80) != 0) { + if ((bVar1 & 0x60) == 0) { + uVar3 = *(pvVar5 - 2) + (bVar1 & 0x7f) * 0x100; + } else if ((bVar1 & 0x60) == 0x60) { + uVar3 = 3; + } else { + uVar3 = (std::countl_zero(0x40 - (bVar1 & 0x60)) / 32) + 1; + } + + bookKeepingPtr = pvVar5 - (uVar3 & 0xFFFF); + uVar3 = (uVar4 + (uVar3 & 0xFFFF)) & 0xFFFF; + } + } + + u8* ptr1 = pvVar5 + uVar4; + if ((ptr1 < ptr2) && (*ptr1 & 0x80) != 0) { + uVar4 = GetBlockOffset(ptr1, ptr2); + uVar3 += uVar4 & 0xFFFF; + } + + InitBookKeeping(bookKeepingPtr, uVar3); + if (pvVar2 == pvVar5) { + if (bookKeepingPtr == bookKeepingStart) { + xc_cachedBookKeepingAddr = nullptr; + } else { + xc_cachedBookKeepingAddr = reinterpret_cast(bookKeepingPtr - (bookKeepingPtr - 1)); + } + } +} + +CMediumAllocPool::CMediumAllocPool() { + x18_lastPuddle = x0_puddles.begin(); + gMediumAllocPtr = this; +} + +void* CMediumAllocPool::Alloc(u32 len) { + u32 blockCount = 1; + if (len > 16) { + blockCount = (len + 16) / 32; + } + SMediumAllocPuddle& puddle = *x18_lastPuddle; + void* free = puddle.FindFree(blockCount); + + if (free == nullptr) { + for (auto it = x0_puddles.begin(); it != x0_puddles.end(); ++it) { + if (it != x18_lastPuddle) { + free = it->FindFreeEntry(blockCount); + if (free != nullptr) { + x18_lastPuddle = it; + } + } + } + } + + return free; +} + +bool CMediumAllocPool::Free(void* ptr) { + auto node = x0_puddles.begin(); + while (true) { + if (node == x0_puddles.end()) { + return true; + } + + if ((reinterpret_cast(ptr) - reinterpret_cast(node->x4_mainData)) < node->x1c_numEntries / 32) + break; + + ++node; + } + + node->Free(ptr); + if (node->x18_numAllocs == 0 && node->x20_24_canErase) { + if (x18_lastPuddle == node) { + x18_lastPuddle = x0_puddles.begin(); + } + + x0_puddles.erase(node); + } + return true; +} + +void CMediumAllocPool::AddPuddle(u32 numBlocks, void* ptr, bool v) { + x0_puddles.emplace_back(numBlocks, ptr, v); + x18_lastPuddle = x0_puddles.end(); + x18_lastPuddle = --x18_lastPuddle; +} -//CMediumAllocPool::CMediumAllocPool() u32 CMediumAllocPool::GetTotalEntries() const { u32 ret = 0; for (const auto& puddle : x0_puddles) { @@ -29,4 +248,4 @@ u32 CMediumAllocPool::GetNumAllocs() const { return ret; } -} +} // namespace metaforce diff --git a/Runtime/CMediumAllocPool.hpp b/Runtime/CMediumAllocPool.hpp index 631b50d09..f345e921e 100644 --- a/Runtime/CMediumAllocPool.hpp +++ b/Runtime/CMediumAllocPool.hpp @@ -1,29 +1,41 @@ #pragma once #include -#include +#include "Runtime/GCNTypes.hpp" + namespace metaforce { -class CMediumAllocPool { - struct SMediumAllocPuddle { - u8 x0_; - void* x4_; - void* x8_; - void* xc_; - u32 x10_; - u32 x14_numBlocks; - u32 x18_numAllocs; - u32 x1c_numEntries; - u8 x20_; - }; - std::list x0_puddles; - std::list::iterator x18_lastAlloc; -public: - //CMediumAllocPool(); +struct SMediumAllocPuddle { + bool x0_hasBuffer; // was an auto_ptr + void* x4_mainData; // "" + void* x8_bookKeeping; + void* xc_cachedBookKeepingAddr = nullptr; + s32 x10_ = -1; + u32 x14_numBlocks; + u32 x18_numAllocs = 0; + u32 x1c_numEntries; + bool x20_24_canErase : 1; + void* FindFreeEntry(u32 blockCount); +public: + SMediumAllocPuddle(u32 numBlocks, void* ptr, bool canErase); + void* FindFree(u32 blockCount); + + void Free(void* ptr); +}; + +class CMediumAllocPool { + std::list x0_puddles; + std::list::iterator x18_lastPuddle; +public: + CMediumAllocPool(); + + void* Alloc(u32 len); + bool Free(void* ptr); + void AddPuddle(u32 numBlocks, void* ptr, bool v); u32 GetTotalEntries() const; u32 GetNumBlocksAvailable() const; u32 GetNumAllocs() const; - + bool HasPuddles() const { return !x0_puddles.empty(); } }; }