From 33be859acd5d0e5232b062e17130c6aa2179b645 Mon Sep 17 00:00:00 2001 From: fallenoak Date: Sun, 1 Jan 2023 22:50:45 -0600 Subject: [PATCH] feat(mempool): add MemPool --- common/CMakeLists.txt | 1 + common/MemPool.hpp | 6 ++++ common/mempool/MemChunk.cpp | 61 +++++++++++++++++++++++++++++++++ common/mempool/MemChunk.hpp | 22 ++++++++++++ common/mempool/MemPool.cpp | 67 +++++++++++++++++++++++++++++++++++++ common/mempool/MemPool.hpp | 25 ++++++++++++++ test/MemPool.cpp | 36 ++++++++++++++++++++ 7 files changed, 218 insertions(+) create mode 100644 common/MemPool.hpp create mode 100644 common/mempool/MemChunk.cpp create mode 100644 common/mempool/MemChunk.hpp create mode 100644 common/mempool/MemPool.cpp create mode 100644 common/mempool/MemPool.hpp create mode 100644 test/MemPool.cpp diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index bb67a65..61122f8 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,6 +1,7 @@ file(GLOB COMMON_SOURCES "*.cpp" "datastore/*.cpp" + "mempool/*.cpp" "objectalloc/*.cpp" "ref/*.cpp" "string/*.cpp" diff --git a/common/MemPool.hpp b/common/MemPool.hpp new file mode 100644 index 0000000..f2d1300 --- /dev/null +++ b/common/MemPool.hpp @@ -0,0 +1,6 @@ +#ifndef COMMON_MEM_POOL_HPP +#define COMMON_MEM_POOL_HPP + +#include "common/mempool/MemPool.hpp" + +#endif diff --git a/common/mempool/MemChunk.cpp b/common/mempool/MemChunk.cpp new file mode 100644 index 0000000..a63d721 --- /dev/null +++ b/common/mempool/MemChunk.cpp @@ -0,0 +1,61 @@ +#include "common/mempool/MemChunk.hpp" +#include + +struct MemBlock { + void* next; +}; + +MemChunk::MemChunk(size_t blockSize, size_t a2) { + this->m_blockSize = blockSize; + this->m_numBlocks = a2 / blockSize; + + size_t v4 = blockSize * (a2 / blockSize); + this->m_unk2 = v4; + + void* data = SMemAlloc(v4, __FILE__, __LINE__, 0x0); + this->m_memblocks = data; + this->m_free = data; + + auto bytes = static_cast(data); + auto last = &bytes[this->m_unk2 - this->m_blockSize]; + MemBlock* block; + + while ((uintptr_t)bytes < (uintptr_t)last) { + block = reinterpret_cast(bytes); + + // Drop a pointer to the next block + block->next = &bytes[this->m_blockSize]; + + // Move to the next block + bytes += this->m_blockSize; + } + + // Drop null pointer in last block + block = reinterpret_cast(bytes); + block->next = nullptr; +} + +bool MemChunk::Contains(void* ptr) { + return (uintptr_t)ptr >= (uintptr_t)this->m_memblocks && (uintptr_t)ptr < (uintptr_t)this->m_memblocks + this->m_unk2; +} + +void* MemChunk::MemAlloc() { + auto block = reinterpret_cast(this->m_free); + + if (block) { + this->m_free = block->next; + this->m_numBlocks--; + return static_cast(block); + } else { + return nullptr; + } +} + +void MemChunk::MemFree(void* ptr) { + auto block = reinterpret_cast(ptr); + + block->next = this->m_free; + this->m_free = block; + + this->m_numBlocks++; +} diff --git a/common/mempool/MemChunk.hpp b/common/mempool/MemChunk.hpp new file mode 100644 index 0000000..46a045f --- /dev/null +++ b/common/mempool/MemChunk.hpp @@ -0,0 +1,22 @@ +#ifndef COMMON_MEMPOOL_MEM_CHUNK_HPP +#define COMMON_MEMPOOL_MEM_CHUNK_HPP + +#include + +class MemChunk { + public: + // Member variables + void* m_memblocks; + void* m_free; + size_t m_unk2; + size_t m_blockSize; + size_t m_numBlocks; + + // Member functions + MemChunk(size_t blockSize, size_t a2); + bool Contains(void* ptr); + void* MemAlloc(); + void MemFree(void* ptr); +}; + +#endif diff --git a/common/mempool/MemPool.cpp b/common/mempool/MemPool.cpp new file mode 100644 index 0000000..c9fea3f --- /dev/null +++ b/common/mempool/MemPool.cpp @@ -0,0 +1,67 @@ +#include "common/mempool/MemPool.hpp" +#include "common/mempool/MemChunk.hpp" + +MemPool::MemPool() { + TSFixedArray(this->m_memchunks); +} + +void MemPool::Init(size_t blockSize, size_t a2) { + this->m_unk3 = blockSize * (a2 / blockSize); + this->m_blockSize = blockSize; + this->m_unk5 = a2 / blockSize; +} + +void* MemPool::MemAlloc() { + auto count = this->m_memchunks.Count(); + + // If chunks already exist, try allocate off of one of them + + for (int32_t i = 0; i < count; i++) { + auto chunk = this->m_memchunks[i]; + auto mem = chunk->MemAlloc(); + + if (mem) { + return mem; + } + } + + // No chunks exist, or all existing chunks are full + // Create a new chunk and allocate off of it + + this->m_memchunks.SetCount(count + 1); + + void* m = SMemAlloc(sizeof(MemChunk), __FILE__, __LINE__, 0x0); + MemChunk* chunk = new (m) MemChunk(this->m_blockSize, this->m_unk3); + + this->m_memchunks[count] = chunk; + + return chunk->MemAlloc(); +} + +int32_t MemPool::MemFree(void* ptr) { + auto count = this->m_memchunks.Count(); + + if (count == 0) { + return 0; + } + + int32_t v3 = 0; + MemChunk* v4; + + while (1) { + v4 = this->m_memchunks[v3]; + + if (v4->Contains(ptr)) { + break; + } + + if (++v3 >= count) { + return 0; + } + } + + auto relevantChunk = this->m_memchunks[v3]; + relevantChunk->MemFree(ptr); + + return 1; +} diff --git a/common/mempool/MemPool.hpp b/common/mempool/MemPool.hpp new file mode 100644 index 0000000..d666093 --- /dev/null +++ b/common/mempool/MemPool.hpp @@ -0,0 +1,25 @@ +#ifndef COMMON_MEMPOOL_MEM_POOL_HPP +#define COMMON_MEMPOOL_MEM_POOL_HPP + +#include +#include +#include + +class MemChunk; + +class MemPool { + public: + // Member variables + TSFixedArray m_memchunks; + size_t m_unk3; + size_t m_blockSize; + size_t m_unk5; + + // Member functions + MemPool(); + void Init(size_t blockSize, size_t a2); + void* MemAlloc(); + int32_t MemFree(void* ptr); +}; + +#endif diff --git a/test/MemPool.cpp b/test/MemPool.cpp new file mode 100644 index 0000000..e0f3c8a --- /dev/null +++ b/test/MemPool.cpp @@ -0,0 +1,36 @@ +#include "common/MemPool.hpp" +#include "test/Test.hpp" + +TEST_CASE("MemPool::MemPool", "[mempool]") { + SECTION("constructs new mem pool") { + MemPool pool; + SUCCEED(); + } +} + +TEST_CASE("MemPool::Init", "[mempool]") { + SECTION("inits mem pool") { + MemPool pool; + pool.Init(16, 16 << 10); + SUCCEED(); + } +} + +TEST_CASE("MemPool::MemAlloc", "[mempool]") { + SECTION("allocates pointer to new free memory") { + MemPool pool; + pool.Init(16, 16 << 10); + auto ptr = pool.MemAlloc(); + REQUIRE(ptr); + } +} + +TEST_CASE("MemPool::MemFree", "[mempool]") { + SECTION("frees allocated pointer") { + MemPool pool; + pool.Init(16, 16 << 10); + auto ptr = pool.MemAlloc(); + pool.MemFree(ptr); + SUCCEED(); + } +}