| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #pragma once
- #if PROFILE_DICTIONARY
- #include "DictionaryStats.h"
- #endif
- template<typename T>
- class Bucket
- {
- public:
- T element;
- int value;
- public:
- Bucket() : element(), value(0) {}
- static void Copy(Bucket<T> const& bucket, Bucket<T>& newBucket)
- {
- bucket.element.Copy(&(newBucket.element));
- newBucket.value = bucket.value;
- }
- };
- template<typename T, typename TAllocator = ArenaAllocator>
- class HashTable
- {
- public:
- TAllocator * alloc;
- uint tableSize;
- SListBase<Bucket<T>> * table;
- public:
- static HashTable<T, TAllocator> * New(TAllocator *allocator, DECLSPEC_GUARD_OVERFLOW uint tableSize)
- {
- return AllocatorNewPlus(TAllocator, allocator, (tableSize*sizeof(SListBase<Bucket<T>>)), HashTable, allocator, tableSize);
- }
- void Delete()
- {
- AllocatorDeletePlus(TAllocator, alloc, (tableSize*sizeof(SListBase<Bucket<T>>)), this);
- }
- ~HashTable()
- {
- for (uint i = 0; i< tableSize; i++)
- {
- table[i].Clear(alloc);
- }
- }
- SListBase<Bucket<T>> * SwapBucket(SListBase<Bucket<T>> * newTable)
- {
- SListBase<Bucket<T>> * retTable = table;
- table = newTable;
- return retTable;
- }
- T * FindOrInsertNew(int value)
- {
- uint hash = this->Hash(value);
- #if PROFILE_DICTIONARY
- uint depth = 1;
- #endif
- // Keep sorted
- FOREACH_SLISTBASE_ENTRY_EDITING(Bucket<T>, bucket, &this->table[hash], iter)
- {
- if (bucket.value <= value)
- {
- if (bucket.value == value)
- {
- return &(bucket.element);
- }
- break;
- }
- #if PROFILE_DICTIONARY
- ++depth;
- #endif
- } NEXT_SLISTBASE_ENTRY_EDITING;
- Bucket<T> * newBucket = iter.InsertNodeBefore(this->alloc);
- newBucket->value = value;
- #if PROFILE_DICTIONARY
- if (stats)
- stats->Insert(depth);
- #endif
- return &newBucket->element;
- }
- T * FindOrInsertNewNoThrow(int value)
- {
- uint hash = this->Hash(value);
- #if PROFILE_DICTIONARY
- uint depth = 1;
- #endif
- // Keep sorted
- FOREACH_SLISTBASE_ENTRY_EDITING(Bucket<T>, bucket, &this->table[hash], iter)
- {
- if (bucket.value <= value)
- {
- if (bucket.value == value)
- {
- return &(bucket.element);
- }
- break;
- }
- #if PROFILE_DICTIONARY
- ++depth;
- #endif
- } NEXT_SLISTBASE_ENTRY_EDITING;
- Bucket<T> * newBucket = iter.InsertNodeBeforeNoThrow(this->alloc);
- if (newBucket == nullptr)
- {
- return nullptr;
- }
- newBucket->value = value;
- #if PROFILE_DICTIONARY
- if (stats)
- stats->Insert(depth);
- #endif
- return &newBucket->element;
- }
- T * FindOrInsert(T element, int value)
- {
- uint hash = this->Hash(value);
- #if PROFILE_DICTIONARY
- uint depth = 1;
- #endif
- // Keep sorted
- FOREACH_SLISTBASE_ENTRY_EDITING(Bucket<T>, bucket, &this->table[hash], iter)
- {
- if (bucket.value <= value)
- {
- if (bucket.value == value)
- {
- return &(bucket.element);
- }
- break;
- }
- #if PROFILE_DICTIONARY
- ++depth;
- #endif
- } NEXT_SLISTBASE_ENTRY_EDITING;
- Bucket<T> * newBucket = iter.InsertNodeBefore(this->alloc);
- Assert(newBucket != nullptr);
- newBucket->value = value;
- newBucket->element = element;
- #if PROFILE_DICTIONARY
- if (stats)
- stats->Insert(depth);
- #endif
- return nullptr;
- }
- T * Get(int value)
- {
- // Assumes sorted lists
- FOREACH_SLISTBASE_ENTRY(Bucket<T>, bucket, &this->table[this->Hash(value)])
- {
- if (bucket.value <= value)
- {
- if (bucket.value == value)
- {
- return &(bucket.element);
- }
- break;
- }
- } NEXT_SLISTBASE_ENTRY;
- return nullptr;
- }
- T GetAndClear(int value)
- {
- SListBase<Bucket<T>> * list = &this->table[this->Hash(value)];
- #if PROFILE_DICTIONARY
- bool first = true;
- #endif
- // Assumes sorted lists
- FOREACH_SLISTBASE_ENTRY_EDITING(Bucket<T>, bucket, list, iter)
- {
- if (bucket.value <= value)
- {
- if (bucket.value == value)
- {
- T retVal = bucket.element;
- iter.RemoveCurrent(this->alloc);
- #if PROFILE_DICTIONARY
- if (stats)
- stats->Remove(first && !(iter.Next()));
- #endif
- return retVal;
- }
- break;
- }
- #if PROFILE_DICTIONARY
- first = false;
- #endif
- } NEXT_SLISTBASE_ENTRY_EDITING;
- return T();
- }
- void Clear(int value)
- {
- SListBase<Bucket<T>> * list = &this->table[this->Hash(value)];
- // Assumes sorted lists
- #if PROFILE_DICTIONARY
- bool first = true;
- #endif
- FOREACH_SLISTBASE_ENTRY_EDITING(Bucket<T>, bucket, list, iter)
- {
- if (bucket.value <= value)
- {
- if (bucket.value == value)
- {
- iter.RemoveCurrent(this->alloc);
- #if PROFILE_DICTIONARY
- if (stats)
- stats->Remove(first && !(iter.Next()));
- #endif
- }
- return;
- }
- #if PROFILE_DICTIONARY
- first = false;
- #endif
- } NEXT_SLISTBASE_ENTRY_EDITING;
- }
- void And(HashTable<T> *this2)
- {
- for (uint i = 0; i < this->tableSize; i++)
- {
- _TYPENAME SListBase<Bucket<T>>::Iterator iter2(&this2->table[i]);
- iter2.Next();
- FOREACH_SLISTBASE_ENTRY_EDITING(Bucket<T>, bucket, &this->table[i], iter)
- {
- while (iter2.IsValid() && bucket.value < iter2.Data().value)
- {
- iter2.Next();
- }
- if (!iter2.IsValid() || bucket.value != iter2.Data().value || bucket.element != iter2.Data().element)
- {
- iter.RemoveCurrent(this->alloc);
- #if PROFILE_DICTIONARY
- if (stats)
- stats->Remove(false);
- #endif
- continue;
- }
- else
- {
- AssertMsg(bucket.value == iter2.Data().value && bucket.element == iter2.Data().element, "Huh??");
- }
- iter2.Next();
- } NEXT_SLISTBASE_ENTRY_EDITING;
- }
- }
- // "And" with fixup actions to take when data don't make it to the result.
- template <class FnFrom, class FnTo>
- void AndWithFixup(HashTable<T> *this2, FnFrom fnFixupFrom, FnTo fnFixupTo)
- {
- for (uint i = 0; i < this->tableSize; i++)
- {
- _TYPENAME SListBase<Bucket<T>>::Iterator iter2(&this2->table[i]);
- iter2.Next();
- FOREACH_SLISTBASE_ENTRY_EDITING(Bucket<T>, bucket, &this->table[i], iter)
- {
- while (iter2.IsValid() && bucket.value < iter2.Data().value)
- {
- // Skipping a this2 value.
- fnFixupTo(iter2.Data());
- iter2.Next();
- }
- if (!iter2.IsValid() || bucket.value != iter2.Data().value || bucket.element != iter2.Data().element)
- {
- // Skipping a this value.
- fnFixupFrom(bucket);
- iter.RemoveCurrent(this->alloc);
- #if PROFILE_DICTIONARY
- if (stats)
- stats->Remove(false);
- #endif
- continue;
- }
- else
- {
- AssertMsg(bucket.value == iter2.Data().value && bucket.element == iter2.Data().element, "Huh??");
- }
- iter2.Next();
- } NEXT_SLISTBASE_ENTRY_EDITING;
- while (iter2.IsValid())
- {
- // Skipping a this2 value.
- fnFixupTo(iter2.Data());
- iter2.Next();
- }
- }
- }
- template <class Fn>
- void Or(HashTable<T> * this2, Fn fn)
- {
- for (uint i = 0; i < this->tableSize; i++)
- {
- _TYPENAME SListBase<Bucket<T>>::Iterator iter2(&this2->table[i]);
- iter2.Next();
- FOREACH_SLISTBASE_ENTRY_EDITING(Bucket<T>, bucket, &this->table[i], iter)
- {
- while (iter2.IsValid() && bucket.value < iter2.Data().value)
- {
- Bucket<T> * newBucket = iter.InsertNodeBefore(this->alloc);
- newBucket->value = iter2.Data().value;
- newBucket->element = fn(nullptr, iter2.Data().element);
- iter2.Next();
- }
- if (!iter2.IsValid())
- {
- break;
- }
- if (bucket.value == iter2.Data().value)
- {
- bucket.element = fn(bucket.element, iter2.Data().element);
- iter2.Next();
- }
- } NEXT_SLISTBASE_ENTRY_EDITING;
- while (iter2.IsValid())
- {
- Bucket<T> * newBucket = iter.InsertNodeBefore(this->alloc);
- newBucket->value = iter2.Data().value;
- newBucket->element = fn(nullptr, iter2.Data().element);
- iter2.Next();
- }
- }
- }
- HashTable<T> *Copy()
- {
- HashTable<T> *newTable = HashTable<T>::New(this->alloc, this->tableSize);
- for (uint i = 0; i < this->tableSize; i++)
- {
- this->table[i].template CopyTo<Bucket<T>::Copy>(this->alloc, newTable->table[i]);
- }
- #if PROFILE_DICTIONARY
- if (stats)
- newTable->stats = stats->Clone();
- #endif
- return newTable;
- }
- void ClearAll()
- {
- for (uint i = 0; i < this->tableSize; i++)
- {
- this->table[i].Clear(this->alloc);
- }
- #if PROFILE_DICTIONARY
- // To not lose previously collected data, we will treat cleared dictionary as a separate instance for stats tracking purpose
- stats = DictionaryStats::Create(typeid(this).name(), tableSize);
- #endif
- }
- #if DBG_DUMP
- void Dump(uint newLinePerEntry = 2);
- void Dump(void (*valueDump)(int));
- #endif
- protected:
- HashTable(TAllocator * allocator, DECLSPEC_GUARD_OVERFLOW uint tableSize) : alloc(allocator), tableSize(tableSize)
- {
- Init();
- #if PROFILE_DICTIONARY
- stats = DictionaryStats::Create(typeid(this).name(), tableSize);
- #endif
- }
- void Init()
- {
- table = (SListBase<Bucket<T>> *)(((char *)this) + sizeof(HashTable<T>));
- for (uint i = 0; i < tableSize; i++)
- {
- // placement new
- ::new (&table[i]) SListBase<Bucket<T>>();
- }
- }
- private:
- uint Hash(int value) { return (value % this->tableSize); }
- #if PROFILE_DICTIONARY
- DictionaryStats *stats;
- #endif
- };
- template <typename T, uint size, typename TAllocator = ArenaAllocator>
- class HashTableS : public HashTable<T, TAllocator>
- {
- public:
- HashTableS(TAllocator * allocator) : HashTable(allocator, size) {}
- void Reset()
- {
- __super::Init();
- }
- private:
- char tableSpace[size * sizeof(SListBase<Bucket<T>>)];
- };
- #define FOREACH_HASHTABLE_ENTRY(T, bucket, hashTable) \
- for (uint _iterHash = 0; _iterHash < (hashTable)->tableSize; _iterHash++) \
- { \
- FOREACH_SLISTBASE_ENTRY(Bucket<T>, bucket, &(hashTable)->table[_iterHash]) \
- {
- #define NEXT_HASHTABLE_ENTRY \
- } \
- NEXT_SLISTBASE_ENTRY; \
- }
- #if DBG_DUMP
- template <typename T, typename TAllocator>
- inline void
- HashTable<T, TAllocator>::Dump(uint newLinePerEntry)
- {
- FOREACH_HASHTABLE_ENTRY(T, bucket, this)
- {
- Output::Print(_u("%4d => "), bucket.value);
- ::Dump<T>(bucket.element);
- for (uint i = 0; i < newLinePerEntry; i++)
- {
- Output::Print(_u("\n"));
- }
- }
- NEXT_HASHTABLE_ENTRY;
- }
- template <typename T, typename TAllocator>
- inline void
- HashTable<T, TAllocator>::Dump(void (*valueDump)(int))
- {
- FOREACH_HASHTABLE_ENTRY(T, bucket, this)
- {
- valueDump(bucket.value);
- Output::Print(_u(" => "), bucket.value);
- ::Dump<T>(bucket.element);
- Output::Print(_u("\n"));
- }
- NEXT_HASHTABLE_ENTRY;
- }
- template <typename T>
- inline void Dump(T const& t)
- {
- t.Dump();
- }
- #endif
|