From ac1be572c4777afe3ad89b3b517687f65b9c6364 Mon Sep 17 00:00:00 2001 From: fallenoak Date: Sun, 15 Nov 2020 13:20:10 -0600 Subject: [PATCH] feat(hash): add hash templates --- storm/CMakeLists.txt | 1 + storm/Hash.hpp | 8 ++ storm/hash/Hashkey.cpp | 42 ++++++ storm/hash/Hashkey.hpp | 37 ++++++ storm/hash/TSHashObject.hpp | 17 +++ storm/hash/TSHashTable.hpp | 256 ++++++++++++++++++++++++++++++++++++ test/Hash.cpp | 13 ++ 7 files changed, 374 insertions(+) create mode 100644 storm/Hash.hpp create mode 100644 storm/hash/Hashkey.cpp create mode 100644 storm/hash/Hashkey.hpp create mode 100644 storm/hash/TSHashObject.hpp create mode 100644 storm/hash/TSHashTable.hpp create mode 100644 test/Hash.cpp diff --git a/storm/CMakeLists.txt b/storm/CMakeLists.txt index cef64e0..68d3401 100644 --- a/storm/CMakeLists.txt +++ b/storm/CMakeLists.txt @@ -1,5 +1,6 @@ file(GLOB STORM_SOURCES "*.cpp" + "hash/*.cpp" "string/*.cpp" "thread/*.cpp" ) diff --git a/storm/Hash.hpp b/storm/Hash.hpp new file mode 100644 index 0000000..9895c13 --- /dev/null +++ b/storm/Hash.hpp @@ -0,0 +1,8 @@ +#ifndef STORM_HASH_HPP +#define STORM_HASH_HPP + +#include "storm/hash/Hashkey.hpp" +#include "storm/hash/TSHashObject.hpp" +#include "storm/hash/TSHashTable.hpp" + +#endif diff --git a/storm/hash/Hashkey.cpp b/storm/hash/Hashkey.cpp new file mode 100644 index 0000000..d715370 --- /dev/null +++ b/storm/hash/Hashkey.cpp @@ -0,0 +1,42 @@ +#include "storm/hash/Hashkey.hpp" +#include "storm/Memory.hpp" +#include "storm/String.hpp" + +bool HASHKEY_NONE::operator==(const HASHKEY_NONE& key) { + return true; +} + +bool HASHKEY_PTR::operator==(const HASHKEY_PTR& key) { + return this->m_key == key.m_key; +} + +HASHKEY_STR::~HASHKEY_STR() { + if (this->m_str) { + SMemFree(this->m_str, __FILE__, __LINE__, 0x0); + } +} + +HASHKEY_STR& HASHKEY_STR::operator=(const char* str) { + if (this->m_str != str) { + if (this->m_str) { + SMemFree(this->m_str, __FILE__, __LINE__, 0x0); + } + + this->m_str = SStrDupA(str, __FILE__, __LINE__); + } + + return *this; +} + +bool HASHKEY_STR::operator==(const char* str) { + return SStrCmp(this->m_str, str, STORM_MAX_STR) == 0; +} + +HASHKEY_STRI& HASHKEY_STRI::operator=(const char* str) { + static_cast(*this) = str; + return *this; +} + +bool HASHKEY_STRI::operator==(const char* str) { + return SStrCmpI(this->m_str, str, STORM_MAX_STR) == 0; +} diff --git a/storm/hash/Hashkey.hpp b/storm/hash/Hashkey.hpp new file mode 100644 index 0000000..a6f96a5 --- /dev/null +++ b/storm/hash/Hashkey.hpp @@ -0,0 +1,37 @@ +#ifndef STORM_HASH_HASHKEY_HPP +#define STORM_HASH_HASHKEY_HPP + +class HASHKEY_PTR { + public: + // Member variables + void* m_key; + + // Member functions + bool operator==(const HASHKEY_PTR&); +}; + +class HASHKEY_STR { + public: + // Member variables + char* m_str; + + // Member functions + ~HASHKEY_STR(); + HASHKEY_STR& operator=(const char*); + bool operator==(const char*); +}; + +class HASHKEY_STRI : public HASHKEY_STR { + public: + // Member functions + HASHKEY_STRI& operator=(const char*); + bool operator==(const char*); +}; + +class HASHKEY_NONE { + public: + // Member functions + bool operator==(const HASHKEY_NONE&); +}; + +#endif diff --git a/storm/hash/TSHashObject.hpp b/storm/hash/TSHashObject.hpp new file mode 100644 index 0000000..4d38763 --- /dev/null +++ b/storm/hash/TSHashObject.hpp @@ -0,0 +1,17 @@ +#ifndef STORM_HASH_TS_HASH_OBJECT_HPP +#define STORM_HASH_TS_HASH_OBJECT_HPP + +#include "storm/List.hpp" +#include + +template +class TSHashObject { + public: + // Member variables + uint32_t m_hashval; + TSLink m_linktoslot; + TSLink m_linktofull; + TKey m_key; +}; + +#endif diff --git a/storm/hash/TSHashTable.hpp b/storm/hash/TSHashTable.hpp new file mode 100644 index 0000000..556d9e7 --- /dev/null +++ b/storm/hash/TSHashTable.hpp @@ -0,0 +1,256 @@ +#ifndef STORM_HASH_TS_HASH_TABLE_HPP +#define STORM_HASH_TS_HASH_TABLE_HPP + +#include "storm/Array.hpp" +#include "storm/List.hpp" +#include "storm/String.hpp" + +template +class TSHashTable { + public: + // Member variables + STORM_EXPLICIT_LIST(T, m_linktofull) + m_fulllist; + uint32_t m_fullnessIndicator = 0; + TSGrowableArray m_slotlistarray; + uint32_t m_slotmask = -1; + + // Member functions + ~TSHashTable(); + void Clear(void); + uint32_t ComputeSlot(uint32_t); + void Destroy(void); + int32_t GetLinkOffset(void); + T* Head(void); + void Initialize(void); + bool Initialized(void); + void Insert(T*, uint32_t, const TKey&); + void InternalClear(int32_t); + void InternalDelete(T*); + void InternalLinkNode(T*, uint32_t); + T* InternalNew(STORM_EXPLICIT_LIST(T, m_linktoslot)*, size_t, size_t); + T* InternalNewNode(uint32_t, size_t, size_t); + int32_t MonitorFullness(uint32_t); + T* New(const char*, size_t, size_t); + T* New(uint32_t, const char*, size_t, uint32_t); + T* New(uint32_t, const TKey&, size_t, uint32_t); + T* Next(const T*); + T* Ptr(const char*); + T* Ptr(uint32_t, const TKey&); + void Unlink(T*); +}; + +template +TSHashTable::~TSHashTable() { + this->InternalClear(1); +} + +template +void TSHashTable::Clear() { + this->InternalClear(0); +} + +template +uint32_t TSHashTable::ComputeSlot(uint32_t hashval) { + return hashval & this->m_slotmask; +} + +template +int32_t TSHashTable::GetLinkOffset() { + return offsetof(T, m_linktoslot); +} + +template +T* TSHashTable::Head() { + return this->m_fulllist.Head(); +} + +template +void TSHashTable::Initialize() { + this->m_slotmask = 3; + this->m_slotlistarray.SetCount(4); + + int32_t linkOfs = this->GetLinkOffset(); + int32_t v3 = 0; + STORM_EXPLICIT_LIST(T, m_linktoslot)* v4; + + do { + v4 = &this->m_slotlistarray[v3]; + v4->ChangeLinkOffset(linkOfs); + ++v3; + } while (v3 < this->m_slotmask); +} + +template +bool TSHashTable::Initialized() { + return this->m_slotmask != -1; +} + +template +void TSHashTable::Insert(T* ptr, uint32_t hashval, const TKey& key) { + this->InternalLinkNode(ptr, hashval); + ptr->m_hashval = hashval; + ptr->m_key = key; +} + +template +void TSHashTable::InternalClear(int32_t a2) { + this->m_fulllist.UnlinkAll(); + + uint32_t slotlistCount = this->m_slotlistarray.Count(); + + if (slotlistCount) { + // TODO + } +} + +template +void TSHashTable::InternalLinkNode(T* ptr, uint32_t hashval) { + if (!this->Initialized()) { + this->Initialize(); + } + + uint32_t slot = this->ComputeSlot(hashval); + + if (this->MonitorFullness(slot)) { + slot = this->ComputeSlot(hashval); + } + + auto& slotList = this->m_slotlistarray[slot]; + slotList.LinkToTail(ptr); + this->m_fulllist.LinkToTail(ptr); +} + +template +T* TSHashTable::InternalNew(STORM_EXPLICIT_LIST(T, m_linktoslot)* listptr, size_t extrabytes, size_t flags) { + return listptr->NewNode(1, extrabytes, flags); +} + +template +T* TSHashTable::InternalNewNode(uint32_t hashval, size_t extrabytes, size_t flags) { + if (!this->Initialized()) { + this->Initialize(); + } + + uint32_t slot = this->ComputeSlot(hashval); + + if (this->MonitorFullness(slot)) { + slot = this->ComputeSlot(hashval); + } + + STORM_EXPLICIT_LIST(T, m_linktoslot)* v7 = &this->m_slotlistarray[slot]; + T* v8 = this->InternalNew(v7, extrabytes, flags); + this->m_fulllist.LinkNode(v8, 2, 0); + + return v8; +} + +template +int32_t TSHashTable::MonitorFullness(uint32_t slot) { + // TODO + + return 0; +} + +template +T* TSHashTable::New(const char* str, size_t a2, size_t a3) { + uint32_t hashval = SStrHashHT(str); + return this->New(hashval, str, a2, a3); +} + +template +T* TSHashTable::New(uint32_t hashval, const char* str, size_t extrabytes, uint32_t flags) { + T* ptr = this->InternalNewNode(hashval, extrabytes, flags); + + ptr->m_hashval = hashval; + ptr->m_key = str; + + return ptr; +} + +template +T* TSHashTable::New(uint32_t hashval, const TKey& key, size_t extrabytes, uint32_t flags) { + T* ptr = this->InternalNewNode(hashval, extrabytes, flags); + + ptr->m_hashval = hashval; + ptr->m_key = key; + + return ptr; +} + +template +T* TSHashTable::Next(const T* ptr) { + return this->m_fulllist.Next(ptr); +} + +template +T* TSHashTable::Ptr(const char* str) { + if (!this->Initialized()) { + return nullptr; + } + + uint32_t hashval = SStrHashHT(str); + + uint32_t slot = this->ComputeSlot(hashval); + auto slotlist = &this->m_slotlistarray[slot]; + + T* v7; + + v7 = slotlist->Head(); + + if (!v7) { + return nullptr; + } + + while (v7->m_hashval != hashval) { + uint32_t v8 = this->ComputeSlot(hashval); + auto v9 = &this->m_slotlistarray[v8]; + v7 = v9->RawNext(v7); + + if (reinterpret_cast(v7) <= 0) { + return nullptr; + } + } + + if (!(v7->m_key == str)) { + // TODO Handle collisions + } + + return v7; +} + +template +T* TSHashTable::Ptr(uint32_t hashval, const TKey& key) { + if (!this->Initialized()) { + return nullptr; + } + + uint32_t slot = this->ComputeSlot(hashval); + auto slotlist = &this->m_slotlistarray[slot]; + + T* ptr = slotlist->Head(); + + if (!ptr) { + return nullptr; + } + + while (ptr->m_hashval != hashval || !(ptr->m_key == key)) { + ptr = slotlist->RawNext(ptr); + + if (reinterpret_cast(ptr) <= 0) { + return nullptr; + } + } + + return ptr; +} + +template +void TSHashTable::Unlink(T* ptr) { + if (ptr->m_linktoslot.IsLinked()) { + ptr->m_linktoslot.Unlink(); + ptr->m_linktofull.Unlink(); + } +} + +#endif diff --git a/test/Hash.cpp b/test/Hash.cpp new file mode 100644 index 0000000..fb176cf --- /dev/null +++ b/test/Hash.cpp @@ -0,0 +1,13 @@ +#include "storm/Hash.hpp" +#include "test/Test.hpp" + +struct TestHashObject: TSHashObject { + uint32_t index = 0; +}; + +TEST_CASE("TSHashTable", "[hash]") { + SECTION("constructs correctly") { + TSHashTable hashTable; + REQUIRE(hashTable.Head() == nullptr); + } +}