/* $Id$ */ #ifndef HASHTABLE_HPP #define HASHTABLE_HPP template <class Titem_> struct CHashTableSlotT { typedef typename Titem_::Key Key; // make Titem_::Key a property of HashTable Titem_* m_pFirst; CHashTableSlotT() : m_pFirst(NULL) {} /** hash table slot helper - linear search for item with given key through the given blob - const version */ FORCEINLINE const Titem_& Find(const Key& key) const { for (const Titem_* pItem = m_pFirst; pItem != NULL; pItem = pItem->GetHashNext()) { if (pItem->GetKey() == key) { // we have found the item, return it return *pItem; } } return *(Titem_*)NULL; } /** hash table slot helper - linear search for item with given key through the given blob - non-const version */ FORCEINLINE Titem_& Find(const Key& key) { for (Titem_* pItem = m_pFirst; pItem != NULL; pItem = pItem->GetHashNext()) { if (pItem->GetKey() == key) { // we have found the item, return it return *pItem; } } return *(Titem_*)NULL; } /** hash table slot helper - add new item to the slot */ FORCEINLINE void Attach(Titem_& new_item) { assert(new_item.GetHashNext() == NULL); new_item.SetHashNext(m_pFirst); m_pFirst = &new_item; } /** hash table slot helper - remove item from a slot */ FORCEINLINE bool Detach(Titem_& item_to_remove) { if (m_pFirst == &item_to_remove) { m_pFirst = item_to_remove.GetHashNext(); item_to_remove.SetHashNext(NULL); return true; } Titem_* pItem = m_pFirst; while (true) { if (pItem == NULL) { return false; } Titem_* pNextItem = pItem->GetHashNext(); if (pNextItem == &item_to_remove) break; pItem = pNextItem; } pItem->SetHashNext(item_to_remove.GetHashNext()); item_to_remove.SetHashNext(NULL); return true; } /** hash table slot helper - remove and return item from a slot */ FORCEINLINE Titem_& Detach(const Key& key) { // do we have any items? if (m_pFirst == NULL) { return *(Titem_*)NULL; } // is it our first item? if (m_pFirst->GetKey() == key) { Titem_& ret_item = *m_pFirst; m_pFirst = m_pFirst->GetHashNext(); ret_item.SetHashNext(NULL); return ret_item; } // find it in the following items Titem_* pPrev = m_pFirst; for (Titem_* pItem = m_pFirst->GetHashNext(); pItem != NULL; pPrev = pItem, pItem = pItem->GetHashNext()) { if (pItem->GetKey() == key) { // we have found the item, unlink and return it pPrev->SetHashNext(pItem->GetHashNext()); pItem->SetHashNext(NULL); return *pItem; } } return *(Titem_*)NULL; } }; /** @class CHashTableT<Titem, Thash_bits> - simple hash table of pointers allocated elsewhere. Supports: Add/Find/Remove of Titems. Your Titem must meet some extra requirements to be CHashTableT compliant: - its constructor/destructor (if any) must be public - if the copying of item requires an extra resource management, you must define also copy constructor - must support nested type (struct, class or typedef) Titem::Key that defines the type of key class for that item - must support public method: const Key& GetKey() const; // return the item's key object In addition, the Titem::Key class must support: - public method that calculates key's hash: int CalcHash() const; - public 'equality' operator to compare the key with another one bool operator == (const Key& other) const; */ template <class Titem_, int Thash_bits_> class CHashTableT { public: typedef Titem_ Titem; // make Titem_ visible from outside of class typedef typename Titem_::Key Tkey; // make Titem_::Key a property of HashTable ST_CONST(int, Thash_bits = Thash_bits_); // publish num of hash bits ST_CONST(int, Tcapacity = 1 << Thash_bits); // and num of slots 2^bits protected: /** each slot contains pointer to the first item in the list, Titem contains pointer to the next item - GetHashNext(), SetHashNext() */ typedef CHashTableSlotT<Titem_> Slot; Slot* m_slots; // here we store our data (array of blobs) int m_num_items; // item counter public: // default constructor FORCEINLINE CHashTableT() { // construct all slots m_slots = new Slot[Tcapacity]; m_num_items = 0; } ~CHashTableT() {delete [] m_slots; m_num_items = 0; m_slots = NULL;} protected: /** static helper - return hash for the given key modulo number of slots */ FORCEINLINE static int CalcHash(const Tkey& key) { int32 hash = key.CalcHash(); if ((8 * Thash_bits) < 32) hash ^= hash >> (min(8 * Thash_bits, 31)); if ((4 * Thash_bits) < 32) hash ^= hash >> (min(4 * Thash_bits, 31)); if ((2 * Thash_bits) < 32) hash ^= hash >> (min(2 * Thash_bits, 31)); if ((1 * Thash_bits) < 32) hash ^= hash >> (min(1 * Thash_bits, 31)); hash &= (1 << Thash_bits) - 1; return hash; } /** static helper - return hash for the given item modulo number of slots */ FORCEINLINE static int CalcHash(const Titem_& item) {return CalcHash(item.GetKey());} public: /** item count */ FORCEINLINE int Count() const {return m_num_items;} /** const item search */ const Titem_& Find(const Tkey& key) const { int hash = CalcHash(key); const Slot& slot = m_slots[hash]; const Titem_& item = slot.Find(key); return item; } /** non-const item search */ Titem_& Find(const Tkey& key) { int hash = CalcHash(key); Slot& slot = m_slots[hash]; Titem_& item = slot.Find(key); return item; } /** non-const item search & optional removal (if found) */ Titem_& TryPop(const Tkey& key) { int hash = CalcHash(key); Slot& slot = m_slots[hash]; Titem_& item = slot.Detach(key); if (&item != NULL) { m_num_items--; } return item; } /** non-const item search & removal */ Titem_& Pop(const Tkey& key) { Titem_& item = TryPop(key); assert(&item != NULL); return item; } /** non-const item search & optional removal (if found) */ bool TryPop(Titem_& item) { const Tkey& key = item.GetKey(); int hash = CalcHash(key); Slot& slot = m_slots[hash]; bool ret = slot.Detach(item); if (ret) { m_num_items--; } return ret; } /** non-const item search & removal */ void Pop(Titem_& item) { bool ret = TryPop(item); assert(ret); } /** add one item - copy it from the given item */ void Push(Titem_& new_item) { int hash = CalcHash(new_item); Slot& slot = m_slots[hash]; assert(&slot.Find(new_item.GetKey()) == NULL); slot.Attach(new_item); m_num_items++; } }; #endif /* HASHTABLE_HPP */