#ifndef DMSDK_HASHTABLE_H
#define DMSDK_HASHTABLE_H
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
template <typename KEY, typename T>
class dmHashTable
{
enum STATE_FLAGS
{
STATE_DEFAULT = 0x0,
STATE_USER_ALLOCATED = 0x1
};
public:
struct Entry
{
KEY m_Key;
T m_Value;
uint32_t m_Next;
};
dmHashTable()
{
memset(this, 0, sizeof(*this));
m_FreeEntries = 0xffffffff;
}
dmHashTable(void *user_allocated, uint32_t table_size, uint32_t capacity)
{
assert(table_size < 0xffffffff);
assert(capacity < 0xffffffff);
memset(this, 0, sizeof(*this));
m_FreeEntries = 0xffffffff;
m_HashTableSize = table_size;
m_HashTable = (uint32_t*) user_allocated;
memset(m_HashTable, 0xff, sizeof(uint32_t) * table_size);
m_InitialEntries = (Entry*) (m_HashTable + table_size);
m_InitialEntriesNextFree = m_InitialEntries;
m_InitialEntriesEnd = m_InitialEntries + capacity;
m_State = STATE_USER_ALLOCATED;
}
void Clear()
{
memset(m_HashTable, 0xff, sizeof(uint32_t) * m_HashTableSize);
m_InitialEntriesNextFree = m_InitialEntries;
m_FreeEntries = 0xffffffff;
m_Count = 0;
}
~dmHashTable()
{
if(!(m_State & STATE_USER_ALLOCATED))
{
if (m_InitialEntries)
{
free(m_InitialEntries);
}
if (m_HashTable)
{
free(m_HashTable);
}
}
}
uint32_t Size() const
{
return m_Count;
}
uint32_t Capacity() const
{
return (uint32_t)(uintptr_t)(m_InitialEntriesEnd - m_InitialEntries);
}
void SetCapacity(uint32_t table_size, uint32_t capacity)
{
assert(table_size > 0);
assert(table_size < 0xffffffff);
assert(capacity < 0xffffffff);
assert(capacity >= Capacity());
if (m_InitialEntries == 0)
{
m_HashTableSize = table_size;
m_HashTable = (uint32_t*) malloc(sizeof(uint32_t) * table_size);
memset(m_HashTable, 0xff, sizeof(uint32_t) * table_size);
m_InitialEntries = (Entry*) malloc(sizeof(Entry) * capacity);
m_InitialEntriesNextFree = m_InitialEntries;
m_InitialEntriesEnd = m_InitialEntries + capacity;
}
else
{
dmHashTable<KEY, T> new_ht;
new_ht.SetCapacity(table_size, capacity);
this->Iterate<dmHashTable<KEY, T> >(&FillCallback<KEY, T>, &new_ht);
free(m_HashTable);
free(m_InitialEntries);
memcpy(this, &new_ht, sizeof(*this));
new_ht.m_HashTable = 0;
new_ht.m_InitialEntries = 0;
}
}
void Swap(dmHashTable<KEY, T>& other)
{
char buf[sizeof(*this)];
memcpy(buf, &other, sizeof(buf));
memcpy(&other, this, sizeof(buf));
memcpy(this, buf, sizeof(buf));
}
bool Full()
{
return m_Count == Capacity();
}
bool Empty()
{
return m_Count == 0;
}
void Put(KEY key, const T& value)
{
assert(!Full());
Entry* entry = FindEntry(key);
if (entry != 0)
{
entry->m_Value = value;
return;
}
else
{
entry = AllocateEntry();
entry->m_Key = key;
entry->m_Value = value;
entry->m_Next = 0xffffffff;
uint32_t bucket_index = (uint32_t) (key % m_HashTableSize);
uint32_t entry_ptr = m_HashTable[bucket_index];
if (entry_ptr == 0xffffffff)
{
m_HashTable[bucket_index] = (uint32_t)(uintptr_t)(entry - m_InitialEntries); }
else
{
Entry* prev_entry;
while (entry_ptr != 0xffffffff)
{
prev_entry = &m_InitialEntries[entry_ptr];
entry_ptr = prev_entry->m_Next;
}
assert(prev_entry->m_Next == 0xffffffff);
prev_entry->m_Next = (uint32_t)(uintptr_t)(entry - m_InitialEntries);
}
}
m_Count++;
}
T* Get(KEY key)
{
Entry* entry = FindEntry(key);
if (entry != 0)
{
return &entry->m_Value;
}
else
{
return 0;
}
}
const T* Get(KEY key) const
{
Entry* entry = FindEntry(key);
if (entry != 0)
{
return &entry->m_Value;
}
else
{
return 0;
}
}
void Erase(KEY key)
{
assert(m_HashTableSize != 0);
uint32_t bucket_index = key % m_HashTableSize;
uint32_t entry_ptr = m_HashTable[bucket_index];
assert(entry_ptr != 0xffffffff);
Entry* prev_e = 0;
while (entry_ptr != 0xffffffff)
{
Entry* e = &m_InitialEntries[entry_ptr];
if (e->m_Key == key)
{
--m_Count;
if (prev_e == 0)
{
m_HashTable[bucket_index] = e->m_Next;
}
else
{
prev_e->m_Next = e->m_Next;
}
FreeEntry(e);
return;
}
entry_ptr = e->m_Next;
prev_e = e;
}
assert(false && "Key not found (erase)");
}
template <typename CONTEXT>
void Iterate(void (*call_back)(CONTEXT *context, const KEY* key, T* value), CONTEXT* context) const
{
for (uint32_t i = 0; i < m_HashTableSize; ++i)
{
if (m_HashTable[i] != 0xffffffff)
{
uint32_t entry_ptr = m_HashTable[i];
while (entry_ptr != 0xffffffff)
{
Entry*e = &m_InitialEntries[entry_ptr];
call_back(context, &e->m_Key, &e->m_Value);
entry_ptr = e->m_Next;
}
}
}
}
void Verify()
{
uint32_t free_ptr = m_FreeEntries;
while (free_ptr != 0xffffffff)
{
Entry* e = &m_InitialEntries[free_ptr];
Entry* found = FindEntry(e->m_Key);
if (found && found == e )
{
printf("Key '%d' in table but also key '%d' in free list.\n", found->m_Key, e->m_Key);
}
assert( found != e );
free_ptr = e->m_Next;
}
uint32_t real_count = 0;
for (uint32_t i = 0; i < m_HashTableSize; ++i)
{
if (m_HashTable[i] != 0xffffffff)
{
uint32_t entry_ptr = m_HashTable[i];
while (entry_ptr != 0xffffffff)
{
real_count++;
Entry*e = &m_InitialEntries[entry_ptr];
entry_ptr = e->m_Next;
}
}
}
assert(real_count == m_Count);
}
private:
dmHashTable(const dmHashTable<KEY, T>&);
const dmHashTable<KEY, T>& operator=(const dmHashTable<KEY, T>&);
template <typename KEY2, typename T2>
static void FillCallback(dmHashTable<KEY2,T2> *ht, const KEY2* key, T2* value)
{
ht->Put(*key, *value);
}
Entry* FindEntry(KEY key) const
{
if (!m_HashTableSize)
return 0;
uint32_t bucket_index = (uint32_t) (key % m_HashTableSize);
uint32_t bucket = m_HashTable[bucket_index];
uint32_t entry_ptr = bucket;
while (entry_ptr != 0xffffffff)
{
Entry* e = &m_InitialEntries[entry_ptr];
if (e->m_Key == key)
{
return e;
}
entry_ptr = e->m_Next;
}
return 0;
}
Entry* AllocateEntry()
{
if (m_InitialEntriesNextFree != m_InitialEntriesEnd)
{
return m_InitialEntriesNextFree++;
}
else
{
assert(m_FreeEntries != 0xffffffff && "No free entries in hashtable");
Entry*ret = &m_InitialEntries[m_FreeEntries];
m_FreeEntries = ret->m_Next;
return ret;
}
}
void FreeEntry(Entry* e)
{
if (m_FreeEntries == 0xffffffff)
{
m_FreeEntries = (uint32_t)(uintptr_t)(e - m_InitialEntries);
e->m_Next = 0xffffffff;
}
else
{
e->m_Next = m_FreeEntries;
m_FreeEntries = (uint32_t)(uintptr_t)(e - m_InitialEntries);
}
}
uint32_t* m_HashTable;
uint32_t m_HashTableSize;
Entry* m_InitialEntries;
Entry* m_InitialEntriesNextFree;
Entry* m_InitialEntriesEnd;
uint32_t m_FreeEntries;
uint32_t m_Count;
uint16_t m_State : 1;
};
template <typename T>
class dmHashTable16 : public dmHashTable<uint16_t, T> {};
template <typename T>
class dmHashTable32 : public dmHashTable<uint32_t, T> {};
template <typename T>
class dmHashTable64 : public dmHashTable<uint64_t, T> {};
#endif