#include /////////////////////////////////////////////////////////// // 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(); }