blob: 6c043473da374ff12d20fcd3c02333c8922cea80 [file]
//-------------------------------------------------------------------------------------------------------
// 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 TData, typename TElement>
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<uint>(sym->m_id); }
static uint Get(ExprHash hash) { return static_cast<uint>(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<typename TData, typename TElement>
class ValueHashTable
{
private:
typedef HashBucket<TData, TElement> HashBucket;
public:
JitArenaAllocator * alloc;
uint tableSize;
SListBase<HashBucket> * table;
public:
static ValueHashTable * New(JitArenaAllocator *allocator, DECLSPEC_GUARD_OVERFLOW uint tableSize)
{
return AllocatorNewPlus(JitArenaAllocator, allocator, (tableSize*sizeof(SListBase<HashBucket>)), ValueHashTable, allocator, tableSize);
}
void Delete()
{
AllocatorDeletePlus(JitArenaAllocator, alloc, (tableSize*sizeof(SListBase<HashBucket>)), this);
}
~ValueHashTable()
{
for (uint i = 0; i< tableSize; i++)
{
table[i].Clear(alloc);
}
}
SListBase<HashBucket> * SwapBucket(SListBase<HashBucket> * newTable)
{
SListBase<HashBucket> * 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<HashBucket> * 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<HashBucket> * 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<HashBucket>::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 <class Fn>
void Or(ValueHashTable * this2, Fn fn)
{
for (uint i = 0; i < this->tableSize; i++)
{
_TYPENAME SListBase<HashBucket>::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<HashBucket::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()
{
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<HashBucket> *)(((char *)this) + sizeof(ValueHashTable));
for (uint i = 0; i < tableSize; i++)
{
// placement new
::new (&table[i]) SListBase<HashBucket>();
}
}
private:
uint Hash(uint key) { return (key % this->tableSize); }
#if PROFILE_DICTIONARY
DictionaryStats *stats;
#endif
};