//------------------------------------------------------------------------------------------------------- // 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 class HashBucket { public: TElement element; TData value; public: HashBucket() : element(NULL), value(NULL) {} static void Copy(HashBucket const& bucket, HashBucket& newBucket) { newBucket.element = bucket.element; newBucket.value = bucket.value; } }; class Key { public: static uint Get(Sym const *sym) { return static_cast(sym->m_id); } static uint Get(ExprHash hash) { return static_cast(hash); } }; #define FOREACH_GLOBHASHTABLE_ENTRY(bucket, hashTable) \ for (uint _iterHash = 0; _iterHash < (hashTable)->tableSize; _iterHash++) \ { \ FOREACH_SLISTBASE_ENTRY(GlobHashBucket, bucket, &(hashTable)->table[_iterHash]) \ { #define NEXT_GLOBHASHTABLE_ENTRY \ } \ NEXT_SLISTBASE_ENTRY; \ } template class ValueHashTable { private: typedef HashBucket HashBucket; public: JitArenaAllocator * alloc; uint tableSize; SListBase * table; public: static ValueHashTable * New(JitArenaAllocator *allocator, DECLSPEC_GUARD_OVERFLOW uint tableSize) { return AllocatorNewPlus(JitArenaAllocator, allocator, (tableSize*sizeof(SListBase)), ValueHashTable, allocator, tableSize); } void Delete() { AllocatorDeletePlus(JitArenaAllocator, alloc, (tableSize*sizeof(SListBase)), this); } ~ValueHashTable() { for (uint i = 0; i< tableSize; i++) { table[i].Clear(alloc); } } SListBase * SwapBucket(SListBase * newTable) { SListBase * retTable = table; table = newTable; return retTable; } TElement * FindOrInsertNew(TData value) { uint key = Key::Get(value); uint hash = this->Hash(key); #if PROFILE_DICTIONARY uint depth = 1; #endif // Keep sorted FOREACH_SLISTBASE_ENTRY_EDITING(HashBucket, bucket, &this->table[hash], iter) { if (Key::Get(bucket.value) <= key) { if (Key::Get(bucket.value) == key) { return &(bucket.element); } break; } #if PROFILE_DICTIONARY ++depth; #endif } NEXT_SLISTBASE_ENTRY_EDITING; HashBucket * newBucket = iter.InsertNodeBefore(this->alloc); newBucket->value = value; #if PROFILE_DICTIONARY if (stats) stats->Insert(depth); #endif return &newBucket->element; } TElement * FindOrInsertNewNoThrow(TData * value) { uint key = Key::Get(value); uint hash = this->Hash(key); #if PROFILE_DICTIONARY uint depth = 1; #endif // Keep sorted FOREACH_SLISTBASE_ENTRY_EDITING(HashBucket, bucket, &this->table[hash], iter) { if (Key::Get(bucket.value) <= key) { if (Key::Get(bucket.value) == key) { return &(bucket.element); } break; } #if PROFILE_DICTIONARY ++depth; #endif } NEXT_SLISTBASE_ENTRY_EDITING; HashBucket * 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; } TElement * FindOrInsert(TElement element, TData value) { uint key = Key::Get(value); uint hash = this->Hash(key); #if PROFILE_DICTIONARY uint depth = 1; #endif // Keep sorted FOREACH_SLISTBASE_ENTRY_EDITING(HashBucket, bucket, &this->table[hash], iter) { if (Key::Get(bucket.value) <= key) { if (Key::Get(bucket.value) == key) { return &(bucket.element); } break; } #if PROFILE_DICTIONARY ++depth; #endif } NEXT_SLISTBASE_ENTRY_EDITING; HashBucket * newBucket = iter.InsertNodeBefore(this->alloc); Assert(newBucket != nullptr); newBucket->value = value; newBucket->element = element; #if PROFILE_DICTIONARY if (stats) stats->Insert(depth); #endif return NULL; } TElement * Get(TData value) { uint key = Key::Get(value); return Get(key); } TElement * Get(uint key) { uint hash = this->Hash(key); // Assumes sorted lists FOREACH_SLISTBASE_ENTRY(HashBucket, bucket, &this->table[hash]) { if (Key::Get(bucket.value) <= key) { if (Key::Get(bucket.value) == key) { return &(bucket.element); } break; } } NEXT_SLISTBASE_ENTRY; return NULL; } HashBucket * GetBucket(uint key) { uint hash = this->Hash(key); // Assumes sorted lists FOREACH_SLISTBASE_ENTRY(HashBucket, bucket, &this->table[hash]) { if (Key::Get(bucket.value) <= key) { if (Key::Get(bucket.value) == key) { return &bucket; } break; } } NEXT_SLISTBASE_ENTRY; return nullptr; } TElement GetAndClear(TData * value) { uint key = Key::Get(value); uint hash = this->Hash(key); SListBase * list = &this->table[hash]; #if PROFILE_DICTIONARY bool first = true; #endif // Assumes sorted lists FOREACH_SLISTBASE_ENTRY_EDITING(HashBucket, bucket, list, iter) { if (Key::Get(bucket.value) <= key) { if (Key::Get(bucket.value) == key) { TElement 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 nullptr; } void Clear(uint key) { uint hash = this->Hash(key); SListBase * list = &this->table[hash]; // Assumes sorted lists #if PROFILE_DICTIONARY bool first = true; #endif FOREACH_SLISTBASE_ENTRY_EDITING(HashBucket, bucket, list, iter) { if (Key::Get(bucket.value) <= key) { if (Key::Get(bucket.value) == key) { 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(ValueHashTable *this2) { for (uint i = 0; i < this->tableSize; i++) { _TYPENAME SListBase::Iterator iter2(&this2->table[i]); iter2.Next(); FOREACH_SLISTBASE_ENTRY_EDITING(HashBucket, 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; } } template void Or(ValueHashTable * this2, Fn fn) { for (uint i = 0; i < this->tableSize; i++) { _TYPENAME SListBase::Iterator iter2(&this2->table[i]); iter2.Next(); FOREACH_SLISTBASE_ENTRY_EDITING((HashBucket), bucket, &this->table[i], iter) { while (iter2.IsValid() && bucket.value < iter2.Data().value) { HashBucket * newBucket = iter.InsertNodeBefore(this->alloc); newBucket->value = iter2.Data().value; newBucket->element = fn(null, 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()) { HashBucket * newBucket = iter.InsertNodeBefore(this->alloc); newBucket->value = iter2.Data().value; newBucket->element = fn(null, iter2.Data().element); iter2.Next(); } } } ValueHashTable *Copy() { ValueHashTable *newTable = ValueHashTable::New(this->alloc, this->tableSize); for (uint i = 0; i < this->tableSize; i++) { this->table[i].template CopyTo(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() { FOREACH_GLOBHASHTABLE_ENTRY(bucket, this) { Output::Print(_u("%4d => "), bucket.value); bucket.element->Dump(); Output::Print(_u("\n")); Output::Print(_u("\n")); } NEXT_GLOBHASHTABLE_ENTRY; } void Dump(void (*valueDump)(TData)) { Output::Print(_u("\n-------------------------------------------------------------------------------------------------\n")); FOREACH_GLOBHASHTABLE_ENTRY(bucket, this) { valueDump(bucket.value); Output::Print(_u(" => "), bucket.value); bucket.element->Dump(); Output::Print(_u("\n")); } NEXT_GLOBHASHTABLE_ENTRY; } #endif protected: ValueHashTable(JitArenaAllocator * allocator, uint tableSize) : alloc(allocator), tableSize(tableSize) { Init(); #if PROFILE_DICTIONARY stats = DictionaryStats::Create(typeid(this).name(), tableSize); #endif } void Init() { table = (SListBase *)(((char *)this) + sizeof(ValueHashTable)); for (uint i = 0; i < tableSize; i++) { // placement new ::new (&table[i]) SListBase(); } } private: uint Hash(uint key) { return (key % this->tableSize); } #if PROFILE_DICTIONARY DictionaryStats *stats; #endif };