Implement MxMemoryPool (#671)

* Implement MxMemoryPool

* Naming fix

* Annotations and size asserts

* hex padding
This commit is contained in:
MS
2024-03-13 21:44:07 -04:00
committed by GitHub
parent 7edad07d18
commit 331aac73f2
6 changed files with 252 additions and 132 deletions

View File

@@ -0,0 +1,95 @@
#ifndef MXBITSET_H
#define MXBITSET_H
#pragma warning(disable : 4237)
#include "mxtypes.h"
#include <assert.h>
#include <limits.h> // CHAR_BIT
template <size_t N>
class MxBitset {
public:
MxBitset() { Tidy(); }
// SIZE 0x08
class Reference {
friend class MxBitset<N>;
public:
Reference& Flip()
{
m_bitset->Flip(m_offset);
return (*this);
}
bool operator~() const { return (!m_bitset->Test(m_offset)); }
operator bool() const { return (m_bitset->Test(m_offset)); }
private:
Reference(MxBitset<N>& p_bitset, size_t p_offset) : m_bitset(&p_bitset), m_offset(p_offset) {}
MxBitset<N>* m_bitset; // 0x00
size_t m_offset; // 0x04
};
Reference operator[](size_t p_bit) { return (Reference(*this, p_bit)); }
MxBitset<N>& Flip(size_t p_bit)
{
if (N <= p_bit) {
Xran();
}
m_blocks[p_bit / e_bitsPerBlock] ^= 1 << p_bit % e_bitsPerBlock;
return (*this);
}
size_t Count()
{
// debug only, intentionally unimplemented
return 0;
}
bool Test(MxU32 p_bit)
{
if (p_bit >= N) {
Xran();
}
return (m_blocks[p_bit / e_bitsPerBlock] & (1 << p_bit % e_bitsPerBlock)) != 0;
}
MxU32 Size() const { return N; }
private:
void Tidy(MxU32 p_value = 0)
{
for (MxS32 i = e_blocksRequired; i >= 0; --i) {
m_blocks[i] = p_value;
}
// No need to trim if all bits were zeroed out
if (p_value != 0) {
Trim();
}
}
// Apply bit mask to most significant block
void Trim()
{
if (N % e_bitsPerBlock != 0) {
m_blocks[e_blocksRequired] &= ((1 << (N % e_bitsPerBlock)) - 1);
}
}
void Xran() { assert("invalid MxBitset<N> position" == NULL); }
// Not a real enum. This is how STL BITSET defines these constants.
enum {
e_bitsPerBlock = CHAR_BIT * sizeof(MxU32),
e_blocksRequired = N == 0 ? 0 : (N - 1) / e_bitsPerBlock
};
MxU32 m_blocks[e_blocksRequired + 1]; // 0x00
};
#endif // MXBITSET_H

View File

@@ -66,7 +66,7 @@ public:
inline MxU8* GetBuffer() { return m_pBuffer; }
inline MxU8** GetBufferRef() { return &m_pBuffer; }
inline undefined4 GetUnknown14() { return m_unk0x14; }
inline MxU16 GetRefCount() { return m_refcount; }
inline MxU16 GetRefCount() { return m_referenceCount; }
inline Type GetMode() { return m_mode; }
inline MxU32 GetWriteOffset() { return m_writeOffset; }
inline MxU32 GetBytesRemaining() { return m_bytesRemaining; }
@@ -85,7 +85,7 @@ private:
undefined4 m_unk0x14; // 0x14
undefined4 m_unk0x18; // 0x18
undefined4 m_unk0x1c; // 0x1c
MxU16 m_refcount; // 0x20
MxU16 m_referenceCount; // 0x20
Type m_mode; // 0x24
MxU32 m_writeOffset; // 0x28
MxU32 m_bytesRemaining; // 0x2c

View File

@@ -0,0 +1,86 @@
#ifndef MXMEMORYPOOL_H
#define MXMEMORYPOOL_H
#include "decomp.h"
#include "mxbitset.h"
#include "mxtypes.h"
#include <assert.h>
template <size_t BS, size_t NB>
class MxMemoryPool {
public:
MxMemoryPool() : m_pool(NULL), m_blockSize(BS) {}
~MxMemoryPool() { delete[] m_pool; }
MxResult Allocate();
MxU8* Get();
void Release(MxU8*);
MxU32 GetPoolSize() const { return m_blockRef.Size(); }
private:
MxU8* m_pool; // 0x00
MxU32 m_blockSize; // 0x04
MxBitset<NB> m_blockRef; // 0x08
};
template <size_t BS, size_t NB>
MxResult MxMemoryPool<BS, NB>::Allocate()
{
assert(m_pool == NULL);
assert(m_blockSize);
assert(m_blockRef.Size());
m_pool = new MxU8[GetPoolSize() * m_blockSize * 1024];
assert(m_pool);
return m_pool ? SUCCESS : FAILURE;
}
template <size_t BS, size_t NB>
MxU8* MxMemoryPool<BS, NB>::Get()
{
assert(m_pool != NULL);
assert(m_blockSize);
assert(m_blockRef.Size());
for (MxU32 i = 0; i < GetPoolSize(); i++) {
if (!m_blockRef[i]) {
m_blockRef[i].Flip();
#ifdef _DEBUG
// TODO: This is actually some debug print function, but
// we just need any func with variatic args to eliminate diff noise.
printf("Get> %d pool: busy %d blocks\n", m_blockSize, m_blockRef.Count());
#endif
return &m_pool[i * m_blockSize * 1024];
}
}
return NULL;
}
template <size_t BS, size_t NB>
void MxMemoryPool<BS, NB>::Release(MxU8* p_buf)
{
assert(m_pool != NULL);
assert(m_blockSize);
assert(m_blockRef.Size());
MxU32 i = (MxU32) (p_buf - m_pool) / (m_blockSize * 1024);
assert(i >= 0 && i < GetPoolSize());
assert(m_blockRef[i]);
if (m_blockRef[i]) {
m_blockRef[i].Flip();
}
#ifdef _DEBUG
printf("Release> %d pool: busy %d blocks\n", m_blockSize, m_blockRef.Count());
#endif
}
#endif // MXMEMORYPOOL_H

View File

@@ -4,50 +4,16 @@
#include "decomp.h"
#include "mxcore.h"
#include "mxdsobject.h"
#include "mxmemorypool.h"
#include "mxnotificationparam.h"
#include "mxstreamcontroller.h"
#include "mxtypes.h"
#include <assert.h>
#include <list>
// NOTE: This feels like some kind of templated class, maybe something from the
// STL. But I haven't figured out what yet (it's definitely not a vector).
class MxStreamerSubClass1 {
public:
inline MxStreamerSubClass1(undefined4 p_size)
{
m_buffer = NULL;
m_size = p_size;
undefined4* ptr = &m_unk0x08;
for (int i = 0; i >= 0; i--) {
ptr[i] = 0;
}
}
// FUNCTION: LEGO1 0x100b9110
~MxStreamerSubClass1() { delete[] m_buffer; }
undefined4 GetSize() const { return m_size; }
void SetBuffer(undefined* p_buf) { m_buffer = p_buf; }
inline undefined* GetBuffer() const { return m_buffer; }
inline undefined* GetUnk08Ref() const { return (undefined*) &m_unk0x08; }
private:
undefined* m_buffer;
undefined4 m_size;
undefined4 m_unk0x08;
};
class MxStreamerSubClass2 : public MxStreamerSubClass1 {
public:
inline MxStreamerSubClass2() : MxStreamerSubClass1(0x40) {}
};
class MxStreamerSubClass3 : public MxStreamerSubClass1 {
public:
inline MxStreamerSubClass3() : MxStreamerSubClass1(0x80) {}
};
typedef MxMemoryPool<64, 22> MxMemoryPool64;
typedef MxMemoryPool<128, 2> MxMemoryPool128;
// VTABLE: LEGO1 0x100dc760
class MxStreamerNotification : public MxNotificationParam {
@@ -105,13 +71,44 @@ public:
MxResult FUN_100b99b0(MxDSAction* p_action);
MxResult DeleteObject(MxDSAction* p_dsAction);
inline const MxStreamerSubClass2& GetSubclass1() { return m_subclass1; }
inline const MxStreamerSubClass3& GetSubclass2() { return m_subclass2; }
MxU8* GetMemoryBlock(MxU32 p_blockSize)
{
switch (p_blockSize) {
case 0x40:
return m_pool64.Get();
case 0x80:
return m_pool128.Get();
default:
assert("Invalid block size for memory pool" == NULL);
break;
}
return NULL;
}
void ReleaseMemoryBlock(MxU8* p_block, MxU32 p_blockSize)
{
switch (p_blockSize) {
case 0x40:
m_pool64.Release(p_block);
break;
case 0x80:
m_pool128.Release(p_block);
break;
default:
assert("Invalid block size for memory pool" == NULL);
break;
}
}
private:
list<MxStreamController*> m_openStreams; // 0x08
MxStreamerSubClass2 m_subclass1; // 0x14
MxStreamerSubClass3 m_subclass2; // 0x20
MxMemoryPool64 m_pool64; // 0x14
MxMemoryPool128 m_pool128; // 0x20
};
// clang-format off
@@ -119,6 +116,12 @@ private:
// list<MxStreamController *,allocator<MxStreamController *> >::~list<MxStreamController *,allocator<MxStreamController *> >
// clang-format on
// TEMPLATE: LEGO1 0x100b9100
// MxMemoryPool<64,22>::~MxMemoryPool<64,22>
// TEMPLATE: LEGO1 0x100b9110
// MxMemoryPool<128,2>::~MxMemoryPool<128,2>
// SYNTHETIC: LEGO1 0x100b9120
// MxStreamer::`scalar deleting destructor'