2015-12-21 11:22:35 +00:00
|
|
|
#include <hashtable.h>
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2016-01-07 23:03:18 +00:00
|
|
|
static inline unsigned int JenkinsHash(const char *key)
|
|
|
|
{
|
|
|
|
unsigned int hash = 0;
|
|
|
|
for(const char* k = key; *k; k++)
|
|
|
|
{
|
|
|
|
hash += *k;
|
|
|
|
hash += (hash << 10);
|
|
|
|
hash ^= (hash >> 6);
|
|
|
|
}
|
|
|
|
hash += (hash << 3);
|
|
|
|
hash ^= (hash >> 11);
|
|
|
|
hash += (hash << 15);
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool is_prime(size_t n)
|
|
|
|
{
|
|
|
|
if (n & 1)
|
|
|
|
{
|
|
|
|
for (size_t d = 3; d < n/2; d+=2)
|
|
|
|
if (n % d == 0)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t next_size(size_t p)
|
|
|
|
{
|
2016-02-25 11:34:01 +00:00
|
|
|
for (p = p*4-1; !is_prime(p); p += 2);
|
2016-01-07 23:03:18 +00:00
|
|
|
return p;
|
|
|
|
}
|
2015-12-21 11:22:35 +00:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
2016-01-07 23:03:18 +00:00
|
|
|
// THash_bucket
|
2015-12-21 11:22:35 +00:00
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
|
2016-01-07 23:03:18 +00:00
|
|
|
struct THash_entry
|
2015-12-21 11:22:35 +00:00
|
|
|
{
|
2016-02-25 11:34:01 +00:00
|
|
|
char buf[8];
|
2016-01-07 23:03:18 +00:00
|
|
|
char* key;
|
|
|
|
unsigned int hash;
|
|
|
|
TObject* value;
|
|
|
|
};
|
2015-12-21 11:22:35 +00:00
|
|
|
|
2016-01-07 23:03:18 +00:00
|
|
|
struct THash_bucket : public TObject
|
2015-12-21 11:22:35 +00:00
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
enum { nMaxEntries = 16 };
|
|
|
|
size_t _bloom;
|
|
|
|
THash_entry _entry[nMaxEntries];
|
|
|
|
int _entries;
|
|
|
|
|
|
|
|
private:
|
|
|
|
size_t bloom(unsigned int h) const { return 1 << (h%32); }
|
|
|
|
|
|
|
|
public:
|
|
|
|
int items() const { return _entries; }
|
|
|
|
int size() const { return nMaxEntries; }
|
|
|
|
|
|
|
|
bool add(const char* key, unsigned int hash, TObject* value);
|
|
|
|
void steal(THash_entry& e);
|
|
|
|
|
|
|
|
THash_entry* lookup(const char* key, unsigned int hash);
|
|
|
|
THash_entry& operator[](int i) { return _entry[i]; }
|
|
|
|
TObject* remove(int i);
|
|
|
|
TObject* remove(THash_entry* e);
|
|
|
|
THash_bucket() : _bloom(0), _entries(0) {}
|
|
|
|
~THash_bucket();
|
|
|
|
};
|
|
|
|
|
|
|
|
bool THash_bucket::add(const char* key, unsigned int hash, TObject* value)
|
|
|
|
{
|
|
|
|
CHECK(_entries < nMaxEntries, "bucket full");
|
|
|
|
_bloom |= bloom(hash);
|
|
|
|
THash_entry& e = _entry[_entries++];
|
2016-02-25 11:34:01 +00:00
|
|
|
if (strlen(key) < sizeof(e.buf))
|
|
|
|
{
|
|
|
|
strcpy_s(e.buf, sizeof(e.buf), key);
|
|
|
|
e.key = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*e.buf = '\0';
|
|
|
|
e.key = _strdup(key);
|
|
|
|
}
|
2016-01-07 23:03:18 +00:00
|
|
|
e.hash = hash;
|
|
|
|
e.value = value;
|
|
|
|
return _entries > 3*nMaxEntries/4; // Danger!
|
2015-12-21 11:22:35 +00:00
|
|
|
}
|
|
|
|
|
2016-01-07 23:03:18 +00:00
|
|
|
void THash_bucket::steal(THash_entry& i)
|
2015-12-21 11:22:35 +00:00
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
CHECK(_entries < nMaxEntries, "bucket full");
|
|
|
|
_bloom |= bloom(i.hash);
|
|
|
|
THash_entry& e = _entry[_entries++];
|
|
|
|
memcpy(&e, &i, sizeof(i));
|
2015-12-21 11:22:35 +00:00
|
|
|
}
|
|
|
|
|
2016-01-07 23:03:18 +00:00
|
|
|
THash_entry* THash_bucket::lookup(const char* key, unsigned int hash)
|
2015-12-21 11:22:35 +00:00
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
if (_bloom & bloom(hash))
|
2015-12-21 11:22:35 +00:00
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
for (int p = _entries-1; p >= 0; p--)
|
|
|
|
{
|
|
|
|
THash_entry& e = _entry[p];
|
2016-02-25 11:34:01 +00:00
|
|
|
if (e.hash == hash && strcmp(e.key ? e.key : e.buf, key) == 0)
|
2016-01-07 23:03:18 +00:00
|
|
|
return &e;
|
|
|
|
}
|
2015-12-21 11:22:35 +00:00
|
|
|
}
|
2016-01-07 23:03:18 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
TObject* THash_bucket::remove(int p)
|
|
|
|
{
|
|
|
|
CHECKD(p >= 0 && p < _entries, "Invalid item index ", p);
|
|
|
|
THash_entry& e = _entry[p];
|
|
|
|
TObject* obj = e.value;
|
2016-02-25 11:34:01 +00:00
|
|
|
if (e.key)
|
|
|
|
free(e.key);
|
2016-01-07 23:03:18 +00:00
|
|
|
if (p < _entries-1)
|
|
|
|
memcpy(&_entry[p], &_entry[_entries-1], sizeof(THash_entry));
|
|
|
|
_entries--;
|
|
|
|
if (_entries == 0) _bloom = 0;
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
TObject* THash_bucket::remove(THash_entry* e)
|
|
|
|
{
|
|
|
|
for (int p = _entries-1; p >= 0; p--) if (&_entry[p] == e)
|
|
|
|
return remove(p);
|
|
|
|
return NULL;
|
2015-12-21 11:22:35 +00:00
|
|
|
}
|
|
|
|
|
2016-01-07 23:03:18 +00:00
|
|
|
THash_bucket::~THash_bucket()
|
2015-12-21 11:22:35 +00:00
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
for (int p = _entries-1; p >= 0; p--)
|
|
|
|
delete remove(p);
|
2015-12-21 11:22:35 +00:00
|
|
|
}
|
|
|
|
|
2016-01-07 23:03:18 +00:00
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
// THash_table
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void THash_table::expand()
|
2015-12-21 11:22:35 +00:00
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
TArray* tmp = _buckets;
|
|
|
|
const size_t bs = next_size(_buckets->size());
|
|
|
|
_buckets = new TArray(bs);
|
|
|
|
|
|
|
|
for (int i = tmp->last(); i >= 0; i = tmp->pred(i))
|
|
|
|
{
|
|
|
|
THash_bucket& bi = *(THash_bucket*)tmp->objptr(i);
|
|
|
|
for (int j = bi.items()-1; j >= 0; j--)
|
2015-12-21 11:22:35 +00:00
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
THash_entry& e = bi[j];
|
|
|
|
const size_t buck = e.hash % bs;
|
|
|
|
bucket(buck).steal(e);
|
2015-12-21 11:22:35 +00:00
|
|
|
}
|
2016-01-07 23:03:18 +00:00
|
|
|
bi._entries = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
THash_table::~THash_table()
|
|
|
|
{
|
|
|
|
delete _buckets;
|
2015-12-21 11:22:35 +00:00
|
|
|
}
|
|
|
|
|
2016-01-07 23:03:18 +00:00
|
|
|
THash_entry* THash_table::lookup_ex(const char* key, unsigned int& hash) const
|
2015-12-21 11:22:35 +00:00
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
//hash = JenkinsHash(key);
|
|
|
|
hash = ELFHash(key);
|
|
|
|
const unsigned int buck = hash % _buckets->size();
|
|
|
|
THash_bucket* b = (THash_bucket*)_buckets->objptr(buck);
|
|
|
|
return b ? b->lookup(key, hash) : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
THash_entry* THash_table::lookup(const char* key) const
|
|
|
|
{
|
|
|
|
TObject* obj = NULL;
|
2015-12-21 11:22:35 +00:00
|
|
|
unsigned int hash;
|
2016-01-07 23:03:18 +00:00
|
|
|
return lookup_ex(key, hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
THash_bucket& THash_table::bucket(size_t n)
|
|
|
|
{
|
|
|
|
THash_bucket* b = (THash_bucket*)_buckets->objptr(n);
|
|
|
|
if (b == NULL)
|
|
|
|
{
|
|
|
|
b = new THash_bucket;
|
|
|
|
_buckets->add(b, n);
|
|
|
|
}
|
|
|
|
return *b;
|
|
|
|
}
|
|
|
|
|
|
|
|
void THash_table::add(TObject* value, const char* key, unsigned int hash)
|
|
|
|
{
|
|
|
|
const size_t buck = hash % _buckets->size();
|
|
|
|
THash_bucket& b = bucket(buck);
|
|
|
|
const bool expand_needed = b.add(key, hash, value);
|
|
|
|
_items++;
|
|
|
|
if (expand_needed)
|
|
|
|
expand();
|
2015-12-21 11:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool THash_table::add(const char* key, TObject* value, bool force)
|
|
|
|
{
|
|
|
|
unsigned int hash;
|
2016-01-07 23:03:18 +00:00
|
|
|
THash_entry* current = lookup_ex(key, hash);
|
2015-12-21 11:22:35 +00:00
|
|
|
if (current)
|
|
|
|
{
|
|
|
|
if (force)
|
|
|
|
{
|
|
|
|
delete current->value;
|
|
|
|
current->value = value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
delete value;
|
|
|
|
}
|
|
|
|
else
|
2016-01-07 23:03:18 +00:00
|
|
|
add(value, key, hash);
|
2015-12-21 11:22:35 +00:00
|
|
|
return current != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool THash_table::add(const char* key, const TObject& value, bool force)
|
|
|
|
{
|
|
|
|
unsigned int hash;
|
2016-01-07 23:03:18 +00:00
|
|
|
THash_entry* current = lookup_ex(key, hash);
|
2015-12-21 11:22:35 +00:00
|
|
|
if (current)
|
|
|
|
{
|
|
|
|
if (force)
|
|
|
|
{
|
|
|
|
delete current->value;
|
|
|
|
current->value = value.dup();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2016-01-07 23:03:18 +00:00
|
|
|
add(value.dup(), key, hash);
|
2015-12-21 11:22:35 +00:00
|
|
|
return current != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
TObject* THash_table::get(const char* key)
|
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
THash_entry* entry = lookup(key);
|
2015-12-21 11:22:35 +00:00
|
|
|
return entry ? entry->value : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
TObject* THash_table::remove(const char* key)
|
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
TObject* obj = NULL;
|
2015-12-21 11:22:35 +00:00
|
|
|
unsigned int hash;
|
2016-01-07 23:03:18 +00:00
|
|
|
THash_entry* e = lookup_ex(key, hash);
|
2015-12-21 11:22:35 +00:00
|
|
|
if (e)
|
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
const size_t buck = hash % _buckets->size();
|
|
|
|
obj = bucket(buck).remove(e);
|
|
|
|
_items--;
|
2015-12-21 11:22:35 +00:00
|
|
|
}
|
2016-01-07 23:03:18 +00:00
|
|
|
return obj;
|
2015-12-21 11:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
size_t bucketCount = 7;
|
|
|
|
if (bucketCount < initialCapacity)
|
|
|
|
bucketCount = next_size(initialCapacity);
|
|
|
|
_buckets = new TArray(bucketCount);
|
|
|
|
_items = 0;
|
|
|
|
|
|
|
|
_curr_bucket = _curr_item = 0;
|
2015-12-21 11:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TObject* THash_table::succ_item()
|
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
while (_curr_bucket >= 0 && _curr_bucket < _buckets->size())
|
2015-12-21 11:22:35 +00:00
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
THash_bucket* b = (THash_bucket*)_buckets->objptr(_curr_bucket);
|
|
|
|
if (b)
|
2015-12-21 11:22:35 +00:00
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
if (_curr_item < b->items())
|
|
|
|
return (*b)[_curr_item++].value;
|
2015-12-21 11:22:35 +00:00
|
|
|
}
|
2016-01-07 23:03:18 +00:00
|
|
|
else
|
2015-12-21 11:22:35 +00:00
|
|
|
break;
|
2016-01-07 23:03:18 +00:00
|
|
|
_curr_bucket = _buckets->succ(_curr_bucket);
|
|
|
|
_curr_item = _curr_bucket >= 0 ? 0 : -1;
|
2015-12-21 11:22:35 +00:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
TObject* THash_table::first_item()
|
|
|
|
{
|
2016-01-07 23:03:18 +00:00
|
|
|
_curr_bucket = _buckets->first();
|
|
|
|
_curr_item = _curr_bucket >= 0 ? 0 : -1;
|
2015-12-21 11:22:35 +00:00
|
|
|
return succ_item();
|
|
|
|
}
|
|
|
|
|
|
|
|
TObject* THash_table::last_item()
|
|
|
|
{ return first_item(); }
|
|
|
|
|
|
|
|
TObject* THash_table::pred_item()
|
|
|
|
{ return succ_item(); }
|