280 lines
6.2 KiB
C++
280 lines
6.2 KiB
C++
#include <hashtable.h>
|
|
|
|
#define STRICT
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#define VC_EXTRALEAN
|
|
#include <windows.h>
|
|
#include <cassert>
|
|
|
|
struct THash_table::THashEntry
|
|
{
|
|
char* key;
|
|
unsigned int hash;
|
|
TObject* value;
|
|
THashEntry* next;
|
|
|
|
THashEntry(const char* key, unsigned int hash, TObject* value);
|
|
~THashEntry()
|
|
{
|
|
if (key) free(key);
|
|
if (value) delete value;
|
|
}
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// Utilities
|
|
///////////////////////////////////////////////////////////
|
|
|
|
static inline unsigned int ELFHash(const char* str)
|
|
{
|
|
unsigned int hash = 0;
|
|
unsigned int x = 0;
|
|
for(const char* c = str; *c; c++)
|
|
{
|
|
hash = (hash << 4) + *c;
|
|
x = hash & 0xF0000000L;
|
|
if (x != 0)
|
|
{
|
|
hash ^= (x >> 24);
|
|
hash &= ~x;
|
|
}
|
|
}
|
|
return (hash & 0x7FFFFFFF);
|
|
}
|
|
|
|
static inline size_t calculateIndex(unsigned int bucketCount, unsigned int hash)
|
|
{ return ((size_t)hash) & (bucketCount - 1); }
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// THash_table
|
|
///////////////////////////////////////////////////////////
|
|
|
|
void THash_table::expand_if_needed()
|
|
{
|
|
// If the load factor exceeds 0.5...
|
|
if (_size > (_bucketCount / 2))
|
|
{
|
|
const size_t newBucketCount = _bucketCount * 2;
|
|
THashEntry** newBuckets = (THashEntry**)calloc(newBucketCount, sizeof(THashEntry*));
|
|
if (newBuckets)
|
|
{
|
|
// Move over existing entries.
|
|
for (size_t i = 0; i < _bucketCount; i++)
|
|
{
|
|
THashEntry* entry = _buckets[i];
|
|
while (entry != NULL)
|
|
{
|
|
THash_table::THashEntry* next = entry->next;
|
|
const size_t index = ::calculateIndex(newBucketCount, entry->hash);
|
|
entry->next = newBuckets[index];
|
|
newBuckets[index] = entry;
|
|
entry = next;
|
|
}
|
|
}
|
|
// Copy over internals: _size remains untouched
|
|
free(_buckets);
|
|
_buckets = newBuckets;
|
|
_bucketCount = newBucketCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
void THash_table::Lock()
|
|
{
|
|
while (::InterlockedCompareExchange(&_lock, 1, 0))
|
|
::Sleep(0);
|
|
}
|
|
|
|
void THash_table::Unlock()
|
|
{
|
|
::InterlockedDecrement(&_lock);
|
|
}
|
|
|
|
THash_table::~THash_table()
|
|
{
|
|
Lock();
|
|
for (size_t i = 0; i < _bucketCount; i++)
|
|
{
|
|
THashEntry* entry = _buckets[i];
|
|
while (entry != NULL)
|
|
{
|
|
THash_table::THashEntry* next = entry->next;
|
|
delete entry;
|
|
entry = next;
|
|
}
|
|
}
|
|
::free(_buckets);
|
|
}
|
|
|
|
|
|
THash_table::THashEntry::THashEntry(const char* str, unsigned int h, TObject* val)
|
|
{
|
|
CHECK(str && *str, "NULL hash key");
|
|
key = _strdup(str);
|
|
hash = h;
|
|
value = val;
|
|
next = NULL;
|
|
}
|
|
|
|
THash_table::THashEntry* THash_table::lookup_ex(const char* key, unsigned int& hash, size_t& index) const
|
|
{
|
|
hash = ELFHash(key);
|
|
index = calculateIndex(_bucketCount, hash);
|
|
THashEntry* entry = _buckets[index];
|
|
for (; entry; entry = entry->next)
|
|
{
|
|
if (entry->hash == hash && strcmp(entry->key, key)==0)
|
|
break;
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
THash_table::THashEntry* THash_table::lookup(const char* key) const
|
|
{
|
|
unsigned int hash;
|
|
size_t index;
|
|
return lookup_ex(key, hash, index);
|
|
}
|
|
|
|
bool THash_table::add(const char* key, TObject* value, bool force)
|
|
{
|
|
Lock();
|
|
unsigned int hash;
|
|
size_t index;
|
|
THashEntry* current = lookup_ex(key, hash, index);
|
|
if (current)
|
|
{
|
|
if (force)
|
|
{
|
|
delete current->value;
|
|
current->value = value;
|
|
}
|
|
else
|
|
delete value;
|
|
}
|
|
else
|
|
{
|
|
THashEntry* new_entry = new THashEntry(key, hash, value);
|
|
new_entry->next = _buckets[index];
|
|
_buckets[index] = new_entry;
|
|
_size++;
|
|
}
|
|
Unlock();
|
|
return current != NULL;
|
|
}
|
|
|
|
bool THash_table::add(const char* key, const TObject& value, bool force)
|
|
{
|
|
Lock();
|
|
unsigned int hash;
|
|
size_t index;
|
|
THashEntry* current = lookup_ex(key, hash, index);
|
|
if (current)
|
|
{
|
|
if (force)
|
|
{
|
|
delete current->value;
|
|
current->value = value.dup();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
THashEntry* new_entry = new THashEntry(key, hash, value.dup());
|
|
new_entry->next = _buckets[index];
|
|
_buckets[index] = new_entry;
|
|
_size++;
|
|
}
|
|
Unlock();
|
|
return current != NULL;
|
|
}
|
|
|
|
TObject* THash_table::get(const char* key)
|
|
{
|
|
THashEntry* entry = lookup(key);
|
|
return entry ? entry->value : NULL;
|
|
}
|
|
|
|
TObject* THash_table::remove(const char* key)
|
|
{
|
|
Lock();
|
|
unsigned int hash;
|
|
size_t index;
|
|
THash_table::THashEntry* e = lookup_ex(key, hash, index);
|
|
if (e)
|
|
{
|
|
TObject* value = NULL;
|
|
// Pointer to the current THashEntry.
|
|
THashEntry** p = &(_buckets[index]);
|
|
THashEntry* current;
|
|
while ((current = *p) != NULL)
|
|
{
|
|
if (current == e)
|
|
{
|
|
value = current->value;
|
|
current->value = NULL;
|
|
p = ¤t->next;
|
|
delete current;
|
|
_size--;
|
|
break;
|
|
}
|
|
p = ¤t->next;
|
|
}
|
|
}
|
|
Unlock();
|
|
return e ? e->value : NULL;
|
|
}
|
|
|
|
bool THash_table::destroy(const char* key)
|
|
{
|
|
TObject* o = remove(key);
|
|
if (o) delete o;
|
|
return o != NULL;
|
|
}
|
|
|
|
THash_table::THash_table(size_t initialCapacity)
|
|
{
|
|
_bucketCount = 8;
|
|
// Bucket count must be power of 2.
|
|
while (_bucketCount <= initialCapacity)
|
|
_bucketCount *= 2;
|
|
_buckets = (THashEntry**)calloc(_bucketCount, sizeof(THashEntry*));
|
|
assert(_buckets != NULL);
|
|
|
|
_size = 0;
|
|
_lock = 0;
|
|
|
|
_curr_bucket = 0;
|
|
_curr_entry = NULL;
|
|
}
|
|
|
|
TObject* THash_table::succ_item()
|
|
{
|
|
for (;;)
|
|
{
|
|
while (_curr_entry)
|
|
{
|
|
TObject* obj = _curr_entry->value;
|
|
_curr_entry = _curr_entry->next;
|
|
if (obj)
|
|
return obj;
|
|
}
|
|
if (++_curr_bucket >= _bucketCount)
|
|
break;
|
|
_curr_entry = _buckets[_curr_bucket];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
TObject* THash_table::first_item()
|
|
{
|
|
_curr_bucket = -1;
|
|
_curr_entry = NULL;
|
|
return succ_item();
|
|
}
|
|
|
|
TObject* THash_table::last_item()
|
|
{ return first_item(); }
|
|
|
|
TObject* THash_table::pred_item()
|
|
{ return succ_item(); }
|