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>
#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
///////////////////////////////////////////////////////////
@ -42,106 +21,206 @@ static inline unsigned int ELFHash(const char* str)
return (hash & 0x7FFFFFFF);
}
static inline size_t calculateIndex(unsigned int bucketCount, unsigned int hash)
{ return ((size_t)hash) & (bucketCount - 1); }
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*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
///////////////////////////////////////////////////////////
void THash_table::expand_if_needed()
void THash_table::expand()
{
// If the load factor exceeds 0.5...
if (_size > (_bucketCount / 2))
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--)
{
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;
}
THash_entry& e = bi[j];
const size_t buck = e.hash % bs;
bucket(buck).steal(e);
}
}
bi._entries = 0;
}
void THash_table::Lock()
{
while (::InterlockedCompareExchange(&_lock, 1, 0))
::Sleep(0);
}
void THash_table::Unlock()
{
::InterlockedDecrement(&_lock);
delete tmp;
}
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);
delete _buckets;
}
THash_table::THashEntry::THashEntry(const char* str, unsigned int h, TObject* val)
THash_entry* THash_table::lookup_ex(const char* key, unsigned int& hash) const
{
CHECK(str && *str, "NULL hash key");
key = _strdup(str);
hash = h;
value = val;
next = NULL;
//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_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
THash_entry* THash_table::lookup(const char* key) const
{
TObject* obj = NULL;
unsigned int hash;
size_t index;
return lookup_ex(key, hash, index);
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)
{
Lock();
unsigned int hash;
size_t index;
THashEntry* current = lookup_ex(key, hash, index);
THash_entry* current = lookup_ex(key, hash);
if (current)
{
if (force)
@ -153,22 +232,14 @@ bool THash_table::add(const char* key, TObject* value, bool force)
delete value;
}
else
{
THashEntry* new_entry = new THashEntry(key, hash, value);
new_entry->next = _buckets[index];
_buckets[index] = new_entry;
_size++;
}
Unlock();
add(value, key, hash);
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);
THash_entry* current = lookup_ex(key, hash);
if (current)
{
if (force)
@ -178,50 +249,28 @@ bool THash_table::add(const char* key, const TObject& value, bool force)
}
}
else
{
THashEntry* new_entry = new THashEntry(key, hash, value.dup());
new_entry->next = _buckets[index];
_buckets[index] = new_entry;
_size++;
}
Unlock();
add(value.dup(), key, hash);
return current != NULL;
}
TObject* THash_table::get(const char* key)
{
THashEntry* entry = lookup(key);
THash_entry* entry = lookup(key);
return entry ? entry->value : NULL;
}
TObject* THash_table::remove(const char* key)
{
Lock();
TObject* obj = NULL;
unsigned int hash;
size_t index;
THash_table::THashEntry* e = lookup_ex(key, hash, index);
THash_entry* e = lookup_ex(key, hash);
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;
}
const size_t buck = hash % _buckets->size();
obj = bucket(buck).remove(e);
_items--;
}
Unlock();
return e ? e->value : NULL;
return obj;
}
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)
{
_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;
size_t bucketCount = 7;
if (bucketCount < initialCapacity)
bucketCount = next_size(initialCapacity);
_buckets = new TArray(bucketCount);
_items = 0;
_curr_bucket = 0;
_curr_entry = NULL;
_curr_bucket = _curr_item = 0;
}
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;
_curr_entry = _curr_entry->next;
if (obj)
return obj;
if (_curr_item < b->items())
return (*b)[_curr_item++].value;
}
if (++_curr_bucket >= _bucketCount)
else
break;
_curr_entry = _buckets[_curr_bucket];
_curr_bucket = _buckets->succ(_curr_bucket);
_curr_item = _curr_bucket >= 0 ? 0 : -1;
}
return NULL;
}
TObject* THash_table::first_item()
{
_curr_bucket = -1;
_curr_entry = NULL;
_curr_bucket = _buckets->first();
_curr_item = _curr_bucket >= 0 ? 0 : -1;
return succ_item();
}

View File

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