mirror of https://github.com/AxioDL/metaforce.git
252 lines
6.2 KiB
C++
252 lines
6.2 KiB
C++
#include "CMediumAllocPool.hpp"
|
|
#include <bit>
|
|
|
|
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<u8*>(bookKeeping);
|
|
bookKeepingIter[0] = header;
|
|
bookKeepingIter[1] = static_cast<u8>(count);
|
|
bookKeepingIter[roundedLen - 2] = static_cast<u8>(count);
|
|
bookKeepingIter[roundedLen - 1] = header;
|
|
return;
|
|
}
|
|
|
|
if (roundedLen == 3) {
|
|
header = 0x60;
|
|
} else if (roundedLen == 2) {
|
|
header = 0x40;
|
|
} else {
|
|
header = 0x20;
|
|
}
|
|
|
|
static_cast<u8*>(bookKeeping)[0] = header;
|
|
if ((count & 0xFFFF) >= 2) {
|
|
static_cast<u8*>(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<u32>(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<void*>(reinterpret_cast<uintptr_t>(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<u8*>(x8_bookKeeping);
|
|
u8* pbVar4 = reinterpret_cast<u8*>(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<u8*>(pbVar3);
|
|
}
|
|
}
|
|
ptr1 = pbVar2;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void* SMediumAllocPuddle::FindFree(u32 blockCount) {
|
|
u8* ptr = reinterpret_cast<u8*>(FindFreeEntry(blockCount));
|
|
|
|
if (ptr == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
u8* bookKeepingPtr = reinterpret_cast<u8*>(x8_bookKeeping);
|
|
u8* mainDataPtr = reinterpret_cast<u8*>(x4_mainData);
|
|
|
|
mainDataPtr = mainDataPtr + (ptr - bookKeepingPtr) * 32;
|
|
*(ptr + blockCount - 1) = static_cast<u8>(blockCount);
|
|
x14_numBlocks -= blockCount;
|
|
++x18_numAllocs;
|
|
return mainDataPtr;
|
|
}
|
|
|
|
void SMediumAllocPuddle::Free(void* ptr) {
|
|
u32 uVar3 = reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(x4_mainData);
|
|
u32 uVar4 = (reinterpret_cast<u8*>(x8_bookKeeping) + uVar3)[0];
|
|
x14_numBlocks += uVar4;
|
|
--x18_numAllocs;
|
|
u8* bookKeepingStart = reinterpret_cast<u8*>(x8_bookKeeping);
|
|
u8* pvVar2 = reinterpret_cast<u8*>(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<u32>(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<void*>(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<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(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;
|
|
}
|
|
|
|
u32 CMediumAllocPool::GetTotalEntries() const {
|
|
u32 ret = 0;
|
|
for (const auto& puddle : x0_puddles) {
|
|
ret += puddle.x1c_numEntries;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
u32 CMediumAllocPool::GetNumBlocksAvailable() const {
|
|
u32 ret = 0;
|
|
for (const auto& puddle : x0_puddles) {
|
|
ret += puddle.x14_numBlocks;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
u32 CMediumAllocPool::GetNumAllocs() const {
|
|
u32 ret = 0;
|
|
for (const auto& puddle : x0_puddles) {
|
|
ret += puddle.x18_numAllocs;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
} // namespace metaforce
|