#pragma once #include "../Common.h" #include "Array.h" NS_BF_BEGIN; struct MultiHashSetFuncs { void* Allocate(intptr size, intptr align) { return malloc(size); } void* AllocateZero(intptr size, intptr align) { void* mem = malloc(size); memset(mem, 0, size); return mem; } void Deallocate(void* ptr) { free(ptr); } bool DeallocateAll() { return false; } }; template > class MultiHashSet : public TFuncs { public: struct Entry { T mValue; Entry* mNext; int mHash; }; struct Iterator { public: MultiHashSet* mSet; Entry* mCurEntry; int mCurBucket; public: Iterator(MultiHashSet* set) { this->mSet = set; this->mCurBucket = 0; this->mCurEntry = NULL; } Iterator& operator++() { if (this->mCurEntry != NULL) { this->mCurEntry = this->mCurEntry->mNext; if (this->mCurEntry != NULL) return *this; this->mCurBucket++; } if (mSet->mHashHeads == NULL) { this->mCurBucket = this->mSet->mHashSize; return *this; // At end } while (this->mCurBucket < mSet->mHashSize) { this->mCurEntry = this->mSet->mHashHeads[mCurBucket]; if (this->mCurEntry != NULL) return *this; this->mCurBucket++; } return *this; // At end } bool operator!=(const Iterator& itr) const { return ((itr.mCurEntry != this->mCurEntry) || (itr.mCurBucket != this->mCurBucket)); } T operator*() { return this->mCurEntry->mValue; } operator bool() const { return this->mCurEntry != NULL; } void MoveToNextHashMatch() { int wantHash = this->mCurEntry->mHash; do { this->mCurEntry = this->mCurEntry->mNext; } while ((this->mCurEntry != NULL) && (this->mCurEntry->mHash != wantHash)); } }; public: Entry** mHashHeads; const int cDefaultHashSize = 17; int mHashSize; int mCount; MultiHashSet() { this->mHashHeads = NULL; this->mHashSize = cDefaultHashSize; this->mCount = 0; } ~MultiHashSet() { this->Clear(); } void Add(T value) { if (this->mHashHeads == NULL) this->mHashHeads = (Entry**)TFuncs::AllocateZero(sizeof(Entry*) * mHashSize, alignof(Entry*)); int hash = TFuncs::GetHash(value); int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; Entry* headEntry = this->mHashHeads[hashIdx]; Entry* newEntry = (Entry*)TFuncs::Allocate(sizeof(Entry), alignof(Entry)); newEntry->mValue = value; newEntry->mNext = headEntry; newEntry->mHash = hash; mCount++; mHashHeads[hashIdx] = newEntry; } void AddAfter(T value, Entry* afterEntry) { int hash = TFuncs::GetHash(value); int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; BF_ASSERT(hash == afterEntry->mHash); Entry* newEntry = (Entry*)TFuncs::Allocate(sizeof(Entry), alignof(Entry)); newEntry->mValue = value; newEntry->mNext = afterEntry->mNext; newEntry->mHash = hash; mCount++; afterEntry->mNext = newEntry; } void Rehash(int newHashSize) { auto newHashHeads = (Entry**)TFuncs::AllocateZero(sizeof(Entry*) * newHashSize, alignof(Entry*)); for (int hashIdx = 0; hashIdx < mHashSize; hashIdx++) { Entry* checkEntry = mHashHeads[hashIdx]; while (checkEntry != NULL) { auto nextEntry = checkEntry->mNext; int newHashIdx = (checkEntry->mHash & 0x7FFFFFFF) % newHashSize; checkEntry->mNext = newHashHeads[newHashIdx]; newHashHeads[newHashIdx] = checkEntry; checkEntry = nextEntry; } } TFuncs::Deallocate(mHashHeads); mHashHeads = newHashHeads; mHashSize = newHashSize; } void CheckRehash() { // Make the lookup load reasonable if (mHashSize < mCount) { this->Rehash(BF_MAX(mCount, (int)(mHashSize * 1.5f)) | 1); } } template bool TryGet(const TKey& key, T* val) { if (mHashHeads == NULL) return false; this->CheckRehash(); int hash = TFuncs::GetHash(key); int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; Entry* checkEntry = this->mHashHeads[hashIdx]; while (checkEntry != NULL) { if ((checkEntry->mHash == hash) && (TFuncs::Matches(key, checkEntry->mValue))) { if (val != NULL) *val = checkEntry->mValue; return true; } checkEntry = checkEntry->mNext; } return false; } template Iterator TryGet(const TKey& key) { if (mHashHeads == NULL) return end(); this->CheckRehash(); int hash = TFuncs::GetHash(key); int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; Entry* checkEntry = this->mHashHeads[hashIdx]; while (checkEntry != NULL) { if ((checkEntry->mHash == hash) && (TFuncs::Matches(key, checkEntry->mValue))) { Iterator itr(this); itr.mCurEntry = checkEntry; itr.mCurBucket = hashIdx; return itr; } checkEntry = checkEntry->mNext; } return end(); } template bool Remove(const TKey& key) { if (mHashHeads == NULL) return false; this->CheckRehash(); int hash = TFuncs::GetHash(key); int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; Entry** srcCheckEntryPtr = &this->mHashHeads[hashIdx]; Entry* checkEntry = *srcCheckEntryPtr; while (checkEntry != NULL) { if ((checkEntry->mHash == hash) && (TFuncs::Matches(key, checkEntry->mValue))) { this->mCount--; *srcCheckEntryPtr = checkEntry->mNext; TFuncs::Deallocate(checkEntry); return true; } srcCheckEntryPtr = &checkEntry->mNext; checkEntry = checkEntry->mNext; } return false; } Iterator Erase(const Iterator& itr) { Iterator next = itr; ++next; bool found = false; auto entry = itr.mCurEntry; int hashIdx = (entry->mHash & 0x7FFFFFFF) % this->mHashSize; Entry** srcCheckEntryPtr = &this->mHashHeads[hashIdx]; Entry* checkEntry = *srcCheckEntryPtr; while (checkEntry != NULL) { if (checkEntry == itr.mCurEntry) { this->mCount--; *srcCheckEntryPtr = checkEntry->mNext; found = true; } srcCheckEntryPtr = &checkEntry->mNext; checkEntry = checkEntry->mNext; } BF_ASSERT(found); TFuncs::Deallocate(entry); return next; } void Clear() { if (!TFuncs::DeallocateAll()) { auto itr = begin(); auto endItr = end(); while (itr != endItr) { auto entry = itr.mCurEntry; ++itr; TFuncs::Deallocate(entry); } TFuncs::Deallocate(this->mHashHeads); } this->mHashSize = cDefaultHashSize; this->mHashHeads = NULL; this->mCount = 0; } Iterator begin() { return ++Iterator(this); } Iterator end() { Iterator itr(this); itr.mCurBucket = this->mHashSize; return itr; } }; NS_BF_END;