From 8a232d34f236111cb9b989ae7310443d37bb84a4 Mon Sep 17 00:00:00 2001 From: fallenoak Date: Sun, 26 Mar 2023 15:49:41 -0500 Subject: [PATCH] feat(hash): add memory recycling hash table --- storm/Hash.hpp | 1 + storm/hash/TSHashObjectChunk.hpp | 15 ++++++ storm/hash/TSHashTable.hpp | 18 +++++-- storm/hash/TSHashTableReuse.hpp | 86 ++++++++++++++++++++++++++++++++ test/Hash.cpp | 43 +++++++++++++++- 5 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 storm/hash/TSHashObjectChunk.hpp create mode 100644 storm/hash/TSHashTableReuse.hpp diff --git a/storm/Hash.hpp b/storm/Hash.hpp index 9895c13..0323aff 100644 --- a/storm/Hash.hpp +++ b/storm/Hash.hpp @@ -4,5 +4,6 @@ #include "storm/hash/Hashkey.hpp" #include "storm/hash/TSHashObject.hpp" #include "storm/hash/TSHashTable.hpp" +#include "storm/hash/TSHashTableReuse.hpp" #endif diff --git a/storm/hash/TSHashObjectChunk.hpp b/storm/hash/TSHashObjectChunk.hpp new file mode 100644 index 0000000..099ff1b --- /dev/null +++ b/storm/hash/TSHashObjectChunk.hpp @@ -0,0 +1,15 @@ +#ifndef STORM_HASH_TS_OBJECT_CHUNK_HPP +#define STORM_HASH_TS_OBJECT_CHUNK_HPP + +#include "storm/Array.hpp" +#include "storm/List.hpp" + +template +class TSHashObjectChunk { + public: + // Member variables + TSGrowableArray m_array; + TSLink> m_link; +}; + +#endif diff --git a/storm/hash/TSHashTable.hpp b/storm/hash/TSHashTable.hpp index eadc784..ab6cd2d 100644 --- a/storm/hash/TSHashTable.hpp +++ b/storm/hash/TSHashTable.hpp @@ -14,20 +14,22 @@ class TSHashTable { TSGrowableArray m_slotlistarray; uint32_t m_slotmask = -1; + // Virtual member functions + virtual void InternalDelete(T* ptr); + virtual T* InternalNew(STORM_EXPLICIT_LIST(T, m_linktoslot)* listptr, size_t extrabytes, uint32_t flags); + virtual ~TSHashTable(); + virtual void Destroy(); + // Member functions - ~TSHashTable(); void Clear(); uint32_t ComputeSlot(uint32_t hashval); - void Destroy(); int32_t GetLinkOffset(); T* Head(); void Initialize(); bool Initialized(); void Insert(T* ptr, uint32_t hashval, const TKey& key); void InternalClear(int32_t warn); - void InternalDelete(T* ptr); void InternalLinkNode(T* ptr, uint32_t hashval); - T* InternalNew(STORM_EXPLICIT_LIST(T, m_linktoslot)* listptr, size_t extrabytes, uint32_t flags); T* InternalNewNode(uint32_t, size_t extrabytes, uint32_t flags); int32_t MonitorFullness(uint32_t slot); T* New(const char* str, size_t extrabytes, uint32_t flags); @@ -49,6 +51,14 @@ void TSHashTable::Clear() { this->InternalClear(0); } +template +void TSHashTable::Destroy() { + this->InternalClear(1); + this->m_fullnessIndicator = 0; + this->m_slotmask = -1; + this->m_slotlistarray.Clear(); +} + template uint32_t TSHashTable::ComputeSlot(uint32_t hashval) { return hashval & this->m_slotmask; diff --git a/storm/hash/TSHashTableReuse.hpp b/storm/hash/TSHashTableReuse.hpp new file mode 100644 index 0000000..aba114b --- /dev/null +++ b/storm/hash/TSHashTableReuse.hpp @@ -0,0 +1,86 @@ +#ifndef STORM_HASH_TS_HASH_TABLE_REUSE_HPP +#define STORM_HASH_TS_HASH_TABLE_REUSE_HPP + +#include "storm/hash/TSHashObjectChunk.hpp" +#include "storm/hash/TSHashTable.hpp" +#include "storm/Error.hpp" +#include "storm/List.hpp" +#include +#include + +template +class TSHashTableReuse : public TSHashTable { + public: + // Virtual member functions + virtual ~TSHashTableReuse(); + virtual void Destroy(); + + private: + using object_chunk_t = TSHashObjectChunk; + + // Member variables + STORM_EXPLICIT_LIST(T, m_linktoslot) m_reuseList; + uint32_t m_chunkSize = 16; + STORM_EXPLICIT_LIST(object_chunk_t, m_link) m_chunkList; + + // Virtual member functions + virtual void InternalDelete(T* ptr); + virtual T* InternalNew(STORM_EXPLICIT_LIST(T, m_linktoslot)* listptr, size_t extrabytes, uint32_t flags); + + // Member functions + void Destructor(); +}; + +template +TSHashTableReuse::~TSHashTableReuse() { + this->Destructor(); +} + +template +void TSHashTableReuse::Destroy() { + this->Clear(); + this->Destructor(); +} + +template +void TSHashTableReuse::Destructor() { + this->m_chunkList.Clear(); + this->m_reuseList.Clear(); + this->m_chunkSize = 16; +} + +template +void TSHashTableReuse::InternalDelete(T* ptr) { + this->m_fulllist.UnlinkNode(ptr); + this->m_reuseList.LinkNode(ptr, 1, nullptr); +} + +template +T* TSHashTableReuse::InternalNew(STORM_EXPLICIT_LIST(T, m_linktoslot)* listptr, size_t extrabytes, uint32_t flags) { + STORM_ASSERT(!extrabytes); + + auto node = this->m_reuseList.Head(); + + if (!node) { + object_chunk_t* chunk; + + while (true) { + chunk = this->m_chunkList.Head(); + + if (chunk && chunk->m_array.Reserved()) { + break; + } + + chunk = this->m_chunkList.NewNode(1, 0, 0x0); + chunk->m_array.Reserve(this->m_chunkSize, 0); + } + + node = chunk->m_array.New(); + } + + listptr->LinkNode(node, 1, nullptr); + + return node; +} + +#endif diff --git a/test/Hash.cpp b/test/Hash.cpp index 9831e6a..b773d9a 100644 --- a/test/Hash.cpp +++ b/test/Hash.cpp @@ -2,7 +2,7 @@ #include "test/Test.hpp" struct TestHashObject : TSHashObject { - uint32_t index = 0; + uint32_t index = 255; }; TEST_CASE("TSHashTable", "[hash]") { @@ -34,3 +34,44 @@ TEST_CASE("TSHashTable::Clear", "[hash]") { REQUIRE(hashTable.Head() == nullptr); } } + +TEST_CASE("TSHashTableReuse", "[hash]") { + SECTION("constructs correctly") { + TSHashTableReuse hashTable; + REQUIRE(hashTable.Head() == nullptr); + } +} + +TEST_CASE("TSHashTableReuse::New", "[hash]") { + SECTION("allocates new object correctly") { + TSHashTableReuse hashTable; + auto object = hashTable.New("testKey1", 0, 0x0); + + REQUIRE(object != nullptr); + REQUIRE(object->index == 255); + } + + SECTION("recycles memory correctly") { + TSHashTableReuse hashTable; + + auto object1 = hashTable.New("testKey1", 0, 0x0); + auto object1Ptr = reinterpret_cast(object1); + + hashTable.Clear(); + + auto object2 = hashTable.New("testKey2", 0, 0x0); + auto object2Ptr = reinterpret_cast(object2); + + hashTable.Clear(); + + auto object3 = hashTable.New("testKey3", 0, 0x0); + auto object3Ptr = reinterpret_cast(object3); + + REQUIRE(object1Ptr == object2Ptr); + REQUIRE(object1Ptr == object3Ptr); + REQUIRE(hashTable.Ptr("testKey1") == nullptr); + REQUIRE(hashTable.Ptr("testKey2") == nullptr); + REQUIRE(hashTable.Ptr("testKey3") != nullptr); + REQUIRE(hashTable.Ptr("testKey3")->index == 255); + } +}