git-svn-id: svn://10.65.10.50/branches/R_10_00@23170 c028cbd2-c16b-5b4b-a496-9718f37d4682
This commit is contained in:
parent
32ebb6b8f7
commit
28b6aab534
@ -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 = ¤t->next;
|
||||
delete current;
|
||||
_size--;
|
||||
break;
|
||||
}
|
||||
p = ¤t->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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user