git-svn-id: svn://10.65.10.50/branches/R_10_00@23170 c028cbd2-c16b-5b4b-a496-9718f37d4682

This commit is contained in:
guy 2016-01-07 23:03:18 +00:00
parent 32ebb6b8f7
commit 28b6aab534
2 changed files with 215 additions and 170 deletions

View File

@ -1,26 +1,5 @@
#include <hashtable.h> #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 // Utilities
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
@ -42,106 +21,206 @@ static inline unsigned int ELFHash(const char* str)
return (hash & 0x7FFFFFFF); return (hash & 0x7FFFFFFF);
} }
static inline size_t calculateIndex(unsigned int bucketCount, unsigned int hash) static inline unsigned int JenkinsHash(const char *key)
{ return ((size_t)hash) & (bucketCount - 1); } {
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*2-1; !is_prime(p); p += 2);
return p;
}
///////////////////////////////////////////////////////////
// THash_bucket
///////////////////////////////////////////////////////////
struct THash_entry
{
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++];
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, 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;
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 // THash_table
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
void THash_table::expand_if_needed() void THash_table::expand()
{ {
// If the load factor exceeds 0.5... TArray* tmp = _buckets;
if (_size > (_bucketCount / 2)) 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--)
{ {
const size_t newBucketCount = _bucketCount * 2; THash_entry& e = bi[j];
THashEntry** newBuckets = (THashEntry**)calloc(newBucketCount, sizeof(THashEntry*)); const size_t buck = e.hash % bs;
if (newBuckets) bucket(buck).steal(e);
{
// 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;
}
} }
} bi._entries = 0;
}
void THash_table::Lock() delete tmp;
{
while (::InterlockedCompareExchange(&_lock, 1, 0))
::Sleep(0);
}
void THash_table::Unlock()
{
::InterlockedDecrement(&_lock);
} }
THash_table::~THash_table() THash_table::~THash_table()
{ {
Lock(); delete _buckets;
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_entry* THash_table::lookup_ex(const char* key, unsigned int& hash) const
THash_table::THashEntry::THashEntry(const char* str, unsigned int h, TObject* val)
{ {
CHECK(str && *str, "NULL hash key"); //hash = JenkinsHash(key);
key = _strdup(str); hash = ELFHash(key);
hash = h; const unsigned int buck = hash % _buckets->size();
value = val; THash_bucket* b = (THash_bucket*)_buckets->objptr(buck);
next = NULL; return b ? b->lookup(key, hash) : NULL;
} }
THash_table::THashEntry* THash_table::lookup_ex(const char* key, unsigned int& hash, size_t& index) const THash_entry* THash_table::lookup(const char* key) 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
{ {
TObject* obj = NULL;
unsigned int hash; unsigned int hash;
size_t index; return lookup_ex(key, hash);
return lookup_ex(key, hash, index); }
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) bool THash_table::add(const char* key, TObject* value, bool force)
{ {
Lock();
unsigned int hash; unsigned int hash;
size_t index; THash_entry* current = lookup_ex(key, hash);
THashEntry* current = lookup_ex(key, hash, index);
if (current) if (current)
{ {
if (force) if (force)
@ -153,22 +232,14 @@ bool THash_table::add(const char* key, TObject* value, bool force)
delete value; delete value;
} }
else else
{ add(value, key, hash);
THashEntry* new_entry = new THashEntry(key, hash, value);
new_entry->next = _buckets[index];
_buckets[index] = new_entry;
_size++;
}
Unlock();
return current != NULL; return current != NULL;
} }
bool THash_table::add(const char* key, const TObject& value, bool force) bool THash_table::add(const char* key, const TObject& value, bool force)
{ {
Lock();
unsigned int hash; unsigned int hash;
size_t index; THash_entry* current = lookup_ex(key, hash);
THashEntry* current = lookup_ex(key, hash, index);
if (current) if (current)
{ {
if (force) if (force)
@ -178,50 +249,28 @@ bool THash_table::add(const char* key, const TObject& value, bool force)
} }
} }
else else
{ add(value.dup(), key, hash);
THashEntry* new_entry = new THashEntry(key, hash, value.dup());
new_entry->next = _buckets[index];
_buckets[index] = new_entry;
_size++;
}
Unlock();
return current != NULL; return current != NULL;
} }
TObject* THash_table::get(const char* key) TObject* THash_table::get(const char* key)
{ {
THashEntry* entry = lookup(key); THash_entry* entry = lookup(key);
return entry ? entry->value : NULL; return entry ? entry->value : NULL;
} }
TObject* THash_table::remove(const char* key) TObject* THash_table::remove(const char* key)
{ {
Lock(); TObject* obj = NULL;
unsigned int hash; unsigned int hash;
size_t index; THash_entry* e = lookup_ex(key, hash);
THash_table::THashEntry* e = lookup_ex(key, hash, index);
if (e) if (e)
{ {
TObject* value = NULL; const size_t buck = hash % _buckets->size();
// Pointer to the current THashEntry. obj = bucket(buck).remove(e);
THashEntry** p = &(_buckets[index]); _items--;
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 obj;
return e ? e->value : NULL;
} }
bool THash_table::destroy(const char* key) bool THash_table::destroy(const char* key)
@ -233,42 +282,37 @@ bool THash_table::destroy(const char* key)
THash_table::THash_table(size_t initialCapacity) THash_table::THash_table(size_t initialCapacity)
{ {
_bucketCount = 8; size_t bucketCount = 7;
// Bucket count must be power of 2. if (bucketCount < initialCapacity)
while (_bucketCount <= initialCapacity) bucketCount = next_size(initialCapacity);
_bucketCount *= 2; _buckets = new TArray(bucketCount);
_buckets = (THashEntry**)calloc(_bucketCount, sizeof(THashEntry*)); _items = 0;
assert(_buckets != NULL);
_size = 0; _curr_bucket = _curr_item = 0;
_lock = 0;
_curr_bucket = 0;
_curr_entry = NULL;
} }
TObject* THash_table::succ_item() TObject* THash_table::succ_item()
{ {
for (;;) while (_curr_bucket >= 0 && _curr_bucket < _buckets->size())
{ {
while (_curr_entry) THash_bucket* b = (THash_bucket*)_buckets->objptr(_curr_bucket);
if (b)
{ {
TObject* obj = _curr_entry->value; if (_curr_item < b->items())
_curr_entry = _curr_entry->next; return (*b)[_curr_item++].value;
if (obj)
return obj;
} }
if (++_curr_bucket >= _bucketCount) else
break; break;
_curr_entry = _buckets[_curr_bucket]; _curr_bucket = _buckets->succ(_curr_bucket);
_curr_item = _curr_bucket >= 0 ? 0 : -1;
} }
return NULL; return NULL;
} }
TObject* THash_table::first_item() TObject* THash_table::first_item()
{ {
_curr_bucket = -1; _curr_bucket = _buckets->first();
_curr_entry = NULL; _curr_item = _curr_bucket >= 0 ? 0 : -1;
return succ_item(); return succ_item();
} }

View File

@ -6,39 +6,40 @@
#include <array.h> #include <array.h>
#endif #endif
struct THash_bucket;
struct THash_entry;
class THash_table : public TContainer class THash_table : public TContainer
{ {
struct THashEntry; TArray* _buckets;
THashEntry** _buckets; long _items;
size_t _bucketCount, _size;
volatile unsigned int _lock;
// container iterator // container iterator
size_t _curr_bucket; int _curr_bucket, _curr_item;
THashEntry* _curr_entry;
protected: protected:
THashEntry* lookup_ex(const char* key, unsigned int& hash, size_t& index) const; THash_bucket& bucket(size_t b);
THashEntry* lookup(const char* key) const; void add(TObject* value, const char* key, unsigned int hash);
void expand_if_needed(); THash_entry* lookup_ex(const char* key, unsigned int& hash) const;
void Lock(); THash_entry* lookup(const char* key) const;
void Unlock(); void expand();
public: // Container interface public: // Container interface
virtual TObject* first_item(); virtual TObject* first_item();
virtual TObject* last_item(); virtual TObject* last_item();
virtual TObject* succ_item(); virtual TObject* succ_item();
virtual TObject* pred_item(); virtual TObject* pred_item();
virtual long objects() const { return _size; } virtual long objects() const { return _items; }
public: public:
bool add(const char* key, TObject* value, bool force = false); bool add(const char* key, TObject* value, bool force = false);
bool add(const char* key, const TObject& value, bool force = false); bool add(const char* key, const TObject& value, bool force = false);
void steal(THash_entry& e);
bool is_key(const char* key) const { return lookup(key) != NULL; } bool is_key(const char* key) const { return lookup(key) != NULL; }
TObject* get(const char* key); TObject* get(const char* key);
TObject* remove(const char* key); TObject* remove(const char* key);
bool destroy(const char* key); bool destroy(const char* key);
size_t items() const { return _size; } size_t items() const { return _items; }
THash_table(size_t sz = 0); THash_table(size_t sz = 0);
~THash_table(); ~THash_table();