campo-sirio/include/hashtable.cpp

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 = &current->next;
delete current;
_size--;
break;
}
p = &current->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(); }