#include #define STRICT #define WIN32_LEAN_AND_MEAN #define VC_EXTRALEAN #include #include 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 /////////////////////////////////////////////////////////// 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 size_t calculateIndex(unsigned int bucketCount, unsigned int hash) { return ((size_t)hash) & (bucketCount - 1); } /////////////////////////////////////////////////////////// // THash_table /////////////////////////////////////////////////////////// void THash_table::expand_if_needed() { // If the load factor exceeds 0.5... if (_size > (_bucketCount / 2)) { 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; } } } void THash_table::Lock() { while (::InterlockedCompareExchange(&_lock, 1, 0)) ::Sleep(0); } void THash_table::Unlock() { ::InterlockedDecrement(&_lock); } 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); } THash_table::THashEntry::THashEntry(const char* str, unsigned int h, TObject* val) { CHECK(str && *str, "NULL hash key"); key = _strdup(str); hash = h; value = val; next = 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 { unsigned int hash; size_t index; return lookup_ex(key, hash, index); } 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); if (current) { if (force) { delete current->value; current->value = value; } else delete value; } else { THashEntry* new_entry = new THashEntry(key, hash, value); new_entry->next = _buckets[index]; _buckets[index] = new_entry; _size++; } Unlock(); 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); if (current) { if (force) { delete current->value; current->value = value.dup(); } } else { THashEntry* new_entry = new THashEntry(key, hash, value.dup()); new_entry->next = _buckets[index]; _buckets[index] = new_entry; _size++; } Unlock(); return current != NULL; } TObject* THash_table::get(const char* key) { THashEntry* entry = lookup(key); return entry ? entry->value : NULL; } TObject* THash_table::remove(const char* key) { Lock(); unsigned int hash; size_t index; THash_table::THashEntry* e = lookup_ex(key, hash, index); 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; } } Unlock(); return e ? e->value : NULL; } 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) { _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; _curr_bucket = 0; _curr_entry = NULL; } TObject* THash_table::succ_item() { for (;;) { while (_curr_entry) { TObject* obj = _curr_entry->value; _curr_entry = _curr_entry->next; if (obj) return obj; } if (++_curr_bucket >= _bucketCount) break; _curr_entry = _buckets[_curr_bucket]; } return NULL; } TObject* THash_table::first_item() { _curr_bucket = -1; _curr_entry = NULL; return succ_item(); } TObject* THash_table::last_item() { return first_item(); } TObject* THash_table::pred_item() { return succ_item(); }