123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 |
- #pragma once
- #include "../Common.h"
- #include "Array.h"
- NS_BF_BEGIN;
- struct MultiDictionaryFuncs : AllocatorCLib
- {
- template <typename T>
- size_t GetHash(const T& value)
- {
- return BeefHash<T>()(value);
- }
- template <typename T>
- bool Matches(const T& lhs, const T& rhs)
- {
- return lhs == rhs;
- }
- };
- template <typename TKey, typename TValue, typename TFuncs = MultiDictionaryFuncs>
- class MultiDictionary : public TFuncs
- {
- public:
- struct Entry
- {
- TKey mKey;
- TValue mValue;
- int mNext;
- int mHashCode;
- };
- struct EntryRef
- {
- public:
- MultiDictionary* mSet;
- int mIndex;
- public:
- EntryRef()
- {
- mSet = NULL;
- mIndex = -1;
- }
- EntryRef(MultiDictionary* set, int index)
- {
- mSet = set;
- mIndex = index;
- }
- Entry* operator*()
- {
- return &this->mSet->mEntries[this->mIndex];
- }
- Entry* operator->()
- {
- return &this->mSet->mEntries[this->mIndex];
- }
- operator bool() const
- {
- return this->mIndex != -1;
- }
- };
- struct Iterator
- {
- public:
- MultiDictionary* mDict;
- int mCurEntry;
- int mCurBucket;
- protected:
- Iterator()
- {
- this->mDict = NULL;
- this->mCurBucket = 0;
- this->mCurEntry = -1;
- }
- public:
- Iterator(MultiDictionary* dict)
- {
- this->mDict = dict;
- this->mCurBucket = 0;
- this->mCurEntry = -1;
- }
- Iterator& operator++()
- {
- if (this->mCurEntry != -1)
- {
- this->mCurEntry = this->mDict->mEntries[this->mCurEntry].mNext;
- if (this->mCurEntry != -1)
- return *this;
- this->mCurBucket++;
- }
- if (mDict->mHashHeads == NULL)
- {
- this->mCurBucket = this->mDict->mHashSize;
- return *this; // At end
- }
- while (this->mCurBucket < mDict->mHashSize)
- {
- this->mCurEntry = this->mDict->mHashHeads[mCurBucket];
- if (this->mCurEntry != -1)
- return *this;
- this->mCurBucket++;
- }
- return *this; // At end
- }
- TKey& GetKey()
- {
- return this->mDict->mEntries[this->mCurEntry].mKey;
- }
- TValue& GetValue()
- {
- return this->mDict->mEntries[this->mCurEntry].mValue;
- }
-
- bool operator!=(const Iterator& itr) const
- {
- return ((itr.mCurEntry != this->mCurEntry) || (itr.mCurBucket != this->mCurBucket));
- }
- operator bool() const
- {
- return this->mCurEntry != -1;
- }
- void MoveToNextHashMatch()
- {
- int wantHash = this->mDict->mEntries[this->mCurEntry].mHashCode;
- do
- {
- this->mCurEntry = this->mDict->mEntries[this->mCurEntry].mNext;
- } while ((this->mCurEntry != -1) && (this->mDict->mEntries[this->mCurEntry].mHashCode != wantHash));
- if (this->mCurEntry == -1)
- this->mCurBucket = mDict->mHashSize;
- }
- };
- struct MatchIterator : public Iterator
- {
- public:
- TKey mKey;
- public:
- MatchIterator(const Iterator& iterator)
- {
- this->mDict = iterator.mDict;
- this->mCurBucket = iterator.mCurBucket;
- this->mCurEntry = iterator.mCurEntry;
- }
- MatchIterator(MultiDictionary* dict, const TKey& key) : Iterator(dict)
- {
- mKey = key;
- }
- MatchIterator& operator++()
- {
- while (true)
- {
- MoveToNextHashMatch();
- if (*this == mDict->end())
- break;
- if (mKey == GetKey())
- break;
- }
- return *this;
- }
- };
- protected:
- int GetPrimeish(int min)
- {
- // This is a minimal effort to help address-aligned dataa
- return (min | 1);
- }
- int ExpandSize(int oldSize)
- {
- int newSize = 2 * oldSize;
- // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.
- // Note that this check works even when mAllocSize overflowed thanks to the (uint) cast
- /*if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
- {
- Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
- return MaxPrimeArrayLength;
- }*/
- return GetPrimeish(newSize);
- }
- void ResizeEntries()
- {
- ResizeEntries(ExpandSize(mCount));
- }
- void ResizeEntries(int newSize)
- {
- BF_ASSERT(newSize >= mAllocSize);
- Entry* newEntries = TFuncs::allocate<Entry>(newSize);
- for (int i = 0; i < mCount; i++)
- {
- auto& newEntry = newEntries[i];
- auto& oldEntry = mEntries[i];
- newEntry.mHashCode = oldEntry.mHashCode;
- newEntry.mNext = oldEntry.mNext;
- new (&newEntry.mKey) TKey(std::move(*(TKey*)&oldEntry.mKey));
- new (&newEntry.mValue) TValue(std::move(*(TValue*)&oldEntry.mValue));
- }
- for (int i = mCount; i < newSize; i++)
- {
- newEntries[i].mHashCode = -1;
- }
- TFuncs::deallocate(mEntries);
- mEntries = newEntries;
- mAllocSize = (int)newSize;
- }
- void FreeIdx(int entryIdx)
- {
- this->mEntries[entryIdx].mNext = this->mFreeList;
- this->mFreeList = entryIdx;
- this->mFreeCount++;
- }
- int AllocEntry()
- {
- int index;
- if (this->mFreeCount > 0)
- {
- index = this->mFreeList;
- this->mFreeList = this->mEntries[index].mNext;
- this->mFreeCount--;
- }
- else
- {
- if (this->mCount == this->mAllocSize)
- ResizeEntries();
- index = mCount;
- this->mCount++;
- }
- return index;
- }
- public:
- int* mHashHeads;
- int mAllocSize;
- Entry* mEntries;
- int mFreeList;
- int mFreeCount;
- static const int cDefaultHashSize = 17;
- int mHashSize;
- int mCount;
- MultiDictionary()
- {
- this->mHashHeads = NULL;
- this->mHashSize = cDefaultHashSize;
- this->mEntries = NULL;
- this->mAllocSize = 0;
- this->mCount = 0;
- this->mFreeList = -1;
- this->mFreeCount = 0;
- }
- ~MultiDictionary()
- {
- this->Clear();
- }
- void EnsureFreeCount(int wantFreeCount)
- {
- int freeCount = mFreeCount + (mAllocSize - mCount);
- if (freeCount >= wantFreeCount)
- return;
- ResizeEntries(BF_MAX(ExpandSize(mCount), mAllocSize + wantFreeCount - freeCount));
- }
- int GetCount() const
- {
- return mCount - mFreeCount;
- }
- int size() const
- {
- return mCount - mFreeCount;
- }
- EntryRef AddRaw(int hash)
- {
- if (this->mHashHeads == NULL)
- {
- this->mHashHeads = TFuncs::allocate<int>(mHashSize);
- memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
- }
- int index = AllocEntry();
- int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
- int headEntry = this->mHashHeads[hashIdx];
- Entry* newEntry = &mEntries[index];
- newEntry->mValue = T();
- newEntry->mNext = headEntry;
- newEntry->mHashCode = hash;
- mHashHeads[hashIdx] = index;
- return EntryRef(this, index);
- }
- void Add(TKey key, TValue value)
- {
- if (this->mHashHeads == NULL)
- {
- this->mHashHeads = TFuncs::allocate<int>(mHashSize);
- memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
- }
- int index = AllocEntry();
- int hash = TFuncs::GetHash(key);
- int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
- int headEntry = this->mHashHeads[hashIdx];
- Entry* newEntry = &mEntries[index];
- newEntry->mKey = key;
- newEntry->mValue = value;
- newEntry->mNext = headEntry;
- newEntry->mHashCode = hash;
- mHashHeads[hashIdx] = index;
- }
- void AddAfter(TKey key, TValue value, Entry* afterEntry)
- {
- int hash = TFuncs::GetHash(key);
- int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
- BF_ASSERT(hash == afterEntry->mHashCode);
- int index = AllocEntry();
- Entry* newEntry = &mEntries[index];
- newEntry->mKey = key;
- newEntry->mValue = value;
- newEntry->mNext = afterEntry->mNext;
- newEntry->mHashCode = hash;
- afterEntry->mNext = index;
- }
- void Rehash(int newHashSize)
- {
- auto newHashHeads = TFuncs::allocate<int>(newHashSize);
- memset(newHashHeads, -1, sizeof(int) * newHashSize);
- if (mHashHeads != NULL)
- {
- SizedArray<int, 1024> entryList;
- for (int hashIdx = 0; hashIdx < mHashSize; hashIdx++)
- {
- int checkEntryIdx = mHashHeads[hashIdx];
- if (checkEntryIdx != -1)
- {
- // We want to keep elements with equal hashes in their insert order so we need to
- // iterate through the linked list in reverse
- entryList.Clear();
- while (checkEntryIdx != -1)
- {
- entryList.Add(checkEntryIdx);
- checkEntryIdx = mEntries[checkEntryIdx].mNext;
- }
- for (int i = (int)entryList.mSize - 1; i >= 0; i--)
- {
- int checkEntryIdx = entryList[i];
- auto checkEntry = &mEntries[checkEntryIdx];
- int newHashIdx = (checkEntry->mHashCode & 0x7FFFFFFF) % newHashSize;
- checkEntry->mNext = newHashHeads[newHashIdx];
- newHashHeads[newHashIdx] = checkEntryIdx;
- }
- }
- }
- 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 <typename TKey>
- bool TryGet(const TKey& key, TKey* outKey, TValue* outValue)
- {
- if (mHashHeads == NULL)
- return false;
- this->CheckRehash();
- int hash = TFuncs::GetHash(key);
- int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
- int checkEntryIdx = this->mHashHeads[hashIdx];
- while (checkEntryIdx != -1)
- {
- Entry* checkEntry = &mEntries[checkEntryIdx];
- if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
- {
- if (outKey != NULL)
- *outKey = checkEntry->mKey;
- if (outValue != NULL)
- *outValue = checkEntry->mValue;
- return true;
- }
- checkEntryIdx = checkEntry->mNext;
- }
- return false;
- }
- template <typename TKey>
- MatchIterator TryGet(const TKey& key)
- {
- if (mHashHeads == NULL)
- return end();
- this->CheckRehash();
- int hash = TFuncs::GetHash(key);
- int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
- int checkEntryIdx = this->mHashHeads[hashIdx];
- while (checkEntryIdx != -1)
- {
- auto checkEntry = &this->mEntries[checkEntryIdx];
- if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
- {
- MatchIterator itr(this, key);
- itr.mCurEntry = checkEntryIdx;
- itr.mCurBucket = hashIdx;
- return itr;
- }
- checkEntryIdx = checkEntry->mNext;
- }
- return end();
- }
- template <typename TKey>
- bool Contains(const TKey& key)
- {
- return TryGet(key) != end();
- }
- template <typename TKey>
- int GetCount(const TKey& key)
- {
- int count = 0;
- for (auto itr = TryGet(key); itr != end(); ++itr)
- count++;
- return count;
- }
- template <typename TKey>
- bool Remove(const TKey& key)
- {
- if (mHashHeads == NULL)
- return false;
- this->CheckRehash();
- int hash = TFuncs::GetHash(key);
- int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
- int* srcCheckEntryPtr = &this->mHashHeads[hashIdx];
- int checkEntryIdx = *srcCheckEntryPtr;
- while (checkEntryIdx != -1)
- {
- auto checkEntry = &mEntries[checkEntryIdx];
- if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
- {
- *srcCheckEntryPtr = checkEntry->mNext;
- FreeIdx(checkEntryIdx);
- return true;
- }
- srcCheckEntryPtr = &checkEntry->mNext;
- checkEntryIdx = checkEntry->mNext;
- }
- return false;
- }
- Iterator Erase(const Iterator& itr)
- {
- Iterator next = itr;
- ++next;
- bool found = false;
- auto entryIdx = itr.mCurEntry;
- auto entry = &mEntries[entryIdx];
- int hashIdx = (entry->mHashCode & 0x7FFFFFFF) % this->mHashSize;
- int* srcCheckEntryPtr = &this->mHashHeads[hashIdx];
- int checkEntryIdx = *srcCheckEntryPtr;
- while (checkEntryIdx != -1)
- {
- auto checkEntry = &mEntries[checkEntryIdx];
- if (checkEntryIdx == itr.mCurEntry)
- {
- *srcCheckEntryPtr = checkEntry->mNext;
- found = true;
- }
- srcCheckEntryPtr = &checkEntry->mNext;
- checkEntryIdx = checkEntry->mNext;
- }
- BF_ASSERT(found);
- FreeIdx(entryIdx);
- return next;
- }
- void Clear()
- {
- if (!TFuncs::deallocateAll())
- {
- auto itr = begin();
- auto endItr = end();
- while (itr != endItr)
- {
- auto entry = itr.mCurEntry;
- ++itr;
- FreeIdx(entry);
- }
- TFuncs::deallocate(this->mHashHeads);
- TFuncs::deallocate(this->mEntries);
- }
- this->mHashSize = cDefaultHashSize;
- this->mHashHeads = NULL;
- this->mEntries = NULL;
- this->mCount = 0;
- }
- Iterator begin()
- {
- return ++Iterator(this);
- }
- Iterator end()
- {
- Iterator itr(this);
- itr.mCurBucket = this->mHashSize;
- return itr;
- }
- };
- NS_BF_END;
|