campo-sirio/include/hashtable.cpp
guy e9a34d143f Migliorata gestione sheet ricerca
git-svn-id: svn://10.65.10.50/branches/R_10_00@23179 c028cbd2-c16b-5b4b-a496-9718f37d4682
2016-02-25 11:34:01 +00:00

334 lines
7.2 KiB
C++

#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);
}
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)
{
for (p = p*4-1; !is_prime(p); p += 2);
return p;
}
///////////////////////////////////////////////////////////
// THash_bucket
///////////////////////////////////////////////////////////
struct THash_entry
{
char buf[8];
char* key;
unsigned int hash;
TObject* value;
};
struct THash_bucket : public TObject
{
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++];
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);
}
e.hash = hash;
e.value = value;
return _entries > 3*nMaxEntries/4; // Danger!
}
void THash_bucket::steal(THash_entry& i)
{
CHECK(_entries < nMaxEntries, "bucket full");
_bloom |= bloom(i.hash);
THash_entry& e = _entry[_entries++];
memcpy(&e, &i, sizeof(i));
}
THash_entry* THash_bucket::lookup(const char* key, unsigned int hash)
{
if (_bloom & bloom(hash))
{
for (int p = _entries-1; p >= 0; p--)
{
THash_entry& e = _entry[p];
if (e.hash == hash && strcmp(e.key ? e.key : e.buf, key) == 0)
return &e;
}
}
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;
if (e.key)
free(e.key);
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;
}
THash_bucket::~THash_bucket()
{
for (int p = _entries-1; p >= 0; p--)
delete remove(p);
}
///////////////////////////////////////////////////////////
// THash_table
///////////////////////////////////////////////////////////
void THash_table::expand()
{
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--)
{
THash_entry& e = bi[j];
const size_t buck = e.hash % bs;
bucket(buck).steal(e);
}
bi._entries = 0;
}
delete tmp;
}
THash_table::~THash_table()
{
delete _buckets;
}
THash_entry* THash_table::lookup_ex(const char* key, unsigned int& hash) const
{
//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;
unsigned int hash;
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();
}
bool THash_table::add(const char* key, TObject* value, bool force)
{
unsigned int hash;
THash_entry* current = lookup_ex(key, hash);
if (current)
{
if (force)
{
delete current->value;
current->value = value;
}
else
delete value;
}
else
add(value, key, hash);
return current != NULL;
}
bool THash_table::add(const char* key, const TObject& value, bool force)
{
unsigned int hash;
THash_entry* current = lookup_ex(key, hash);
if (current)
{
if (force)
{
delete current->value;
current->value = value.dup();
}
}
else
add(value.dup(), key, hash);
return current != NULL;
}
TObject* THash_table::get(const char* key)
{
THash_entry* entry = lookup(key);
return entry ? entry->value : NULL;
}
TObject* THash_table::remove(const char* key)
{
TObject* obj = NULL;
unsigned int hash;
THash_entry* e = lookup_ex(key, hash);
if (e)
{
const size_t buck = hash % _buckets->size();
obj = bucket(buck).remove(e);
_items--;
}
return obj;
}
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)
{
size_t bucketCount = 7;
if (bucketCount < initialCapacity)
bucketCount = next_size(initialCapacity);
_buckets = new TArray(bucketCount);
_items = 0;
_curr_bucket = _curr_item = 0;
}
TObject* THash_table::succ_item()
{
while (_curr_bucket >= 0 && _curr_bucket < _buckets->size())
{
THash_bucket* b = (THash_bucket*)_buckets->objptr(_curr_bucket);
if (b)
{
if (_curr_item < b->items())
return (*b)[_curr_item++].value;
}
else
break;
_curr_bucket = _buckets->succ(_curr_bucket);
_curr_item = _curr_bucket >= 0 ? 0 : -1;
}
return NULL;
}
TObject* THash_table::first_item()
{
_curr_bucket = _buckets->first();
_curr_item = _curr_bucket >= 0 ? 0 : -1;
return succ_item();
}
TObject* THash_table::last_item()
{ return first_item(); }
TObject* THash_table::pred_item()
{ return succ_item(); }