# hashtable.hpp

Go to the documentation of this file.

    00001 /* $Id: hashtable.hpp 17248 2009-08-21 20:21:05Z rubidium $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #ifndef  HASHTABLE_HPP
    00013 #define  HASHTABLE_HPP
    00014
    00015 template <class Titem_>
    00016 struct CHashTableSlotT
    00017 {
    00018   typedef typename Titem_::Key Key;          // make Titem_::Key a property of HashTable
    00019
    00020   Titem_ *m_pFirst;
    00021
    00022   FORCEINLINE CHashTableSlotT() : m_pFirst(NULL) {}
    00023
    00025   FORCEINLINE void Clear() {m_pFirst = NULL;}
    00026
    00028   FORCEINLINE const Titem_ *Find(const Key& key) const
    00029   {
    00030     for (const Titem_ *pItem = m_pFirst; pItem != NULL; pItem = pItem->GetHashNext()) {
    00031       if (pItem->GetKey() == key) {
    00032         /* we have found the item, return it */
    00033         return pItem;
    00034       }
    00035     }
    00036     return NULL;
    00037   }
    00038
    00040   FORCEINLINE Titem_ *Find(const Key& key)
    00041   {
    00042     for (Titem_ *pItem = m_pFirst; pItem != NULL; pItem = pItem->GetHashNext()) {
    00043       if (pItem->GetKey() == key) {
    00044         /* we have found the item, return it */
    00045         return pItem;
    00046       }
    00047     }
    00048     return NULL;
    00049   }
    00050
    00052   FORCEINLINE void Attach(Titem_& new_item)
    00053   {
    00054     assert(new_item.GetHashNext() == NULL);
    00055     new_item.SetHashNext(m_pFirst);
    00056     m_pFirst = &new_item;
    00057   }
    00058
    00060   FORCEINLINE bool Detach(Titem_& item_to_remove)
    00061   {
    00062     if (m_pFirst == &item_to_remove) {
    00063       m_pFirst = item_to_remove.GetHashNext();
    00064       item_to_remove.SetHashNext(NULL);
    00065       return true;
    00066     }
    00067     Titem_ *pItem = m_pFirst;
    00068     while (true) {
    00069       if (pItem == NULL) {
    00070         return false;
    00071       }
    00072       Titem_ *pNextItem = pItem->GetHashNext();
    00073       if (pNextItem == &item_to_remove) break;
    00074       pItem = pNextItem;
    00075     }
    00076     pItem->SetHashNext(item_to_remove.GetHashNext());
    00077     item_to_remove.SetHashNext(NULL);
    00078     return true;
    00079   }
    00080
    00082   FORCEINLINE Titem_ *Detach(const Key& key)
    00083   {
    00084     /* do we have any items? */
    00085     if (m_pFirst == NULL) {
    00086       return NULL;
    00087     }
    00088     /* is it our first item? */
    00089     if (m_pFirst->GetKey() == key) {
    00090       Titem_& ret_item = *m_pFirst;
    00091       m_pFirst = m_pFirst->GetHashNext();
    00092       ret_item.SetHashNext(NULL);
    00093       return &ret_item;
    00094     }
    00095     /* find it in the following items */
    00096     Titem_ *pPrev = m_pFirst;
    00097     for (Titem_ *pItem = m_pFirst->GetHashNext(); pItem != NULL; pPrev = pItem, pItem = pItem->GetHashNext()) {
    00098       if (pItem->GetKey() == key) {
    00099         /* we have found the item, unlink and return it */
    00100         pPrev->SetHashNext(pItem->GetHashNext());
    00101         pItem->SetHashNext(NULL);
    00102         return pItem;
    00103       }
    00104     }
    00105     return NULL;
    00106   }
    00107 };
    00108
    00130 template <class Titem_, int Thash_bits_>
    00131 class CHashTableT {
    00132 public:
    00133   typedef Titem_ Titem;                         // make Titem_ visible from outside of class
    00134   typedef typename Titem_::Key Tkey;            // make Titem_::Key a property of HashTable
    00135   static const int Thash_bits = Thash_bits_;    // publish num of hash bits
    00136   static const int Tcapacity = 1 << Thash_bits; // and num of slots 2^bits
    00137
    00138 protected:
    00141   typedef CHashTableSlotT<Titem_> Slot;
    00142
    00143   Slot *m_slots;     // here we store our data (array of blobs)
    00144   int   m_num_items; // item counter
    00145
    00146 public:
    00147   /* default constructor */
    00148   FORCEINLINE CHashTableT()
    00149   {
    00150     /* construct all slots */
    00151     m_slots = new Slot[Tcapacity];
    00152     m_num_items = 0;
    00153   }
    00154
    00155   ~CHashTableT() {delete [] m_slots; m_num_items = 0; m_slots = NULL;}
    00156
    00157 protected:
    00159   FORCEINLINE static int CalcHash(const Tkey& key)
    00160   {
    00161     int32 hash = key.CalcHash();
    00162     if ((8 * Thash_bits) < 32) hash ^= hash >> (min(8 * Thash_bits, 31));
    00163     if ((4 * Thash_bits) < 32) hash ^= hash >> (min(4 * Thash_bits, 31));
    00164     if ((2 * Thash_bits) < 32) hash ^= hash >> (min(2 * Thash_bits, 31));
    00165     if ((1 * Thash_bits) < 32) hash ^= hash >> (min(1 * Thash_bits, 31));
    00166     hash &= (1 << Thash_bits) - 1;
    00167     return hash;
    00168   }
    00169
    00171   FORCEINLINE static int CalcHash(const Titem_& item) {return CalcHash(item.GetKey());}
    00172
    00173 public:
    00175   FORCEINLINE int Count() const {return m_num_items;}
    00176
    00178   FORCEINLINE void Clear() const {for (int i = 0; i < Tcapacity; i++) m_slots[i].Clear();}
    00179
    00181   const Titem_ *Find(const Tkey& key) const
    00182   {
    00183     int hash = CalcHash(key);
    00184     const Slot& slot = m_slots[hash];
    00185     const Titem_ *item = slot.Find(key);
    00186     return item;
    00187   }
    00188
    00190   Titem_ *Find(const Tkey& key)
    00191   {
    00192     int hash = CalcHash(key);
    00193     Slot& slot = m_slots[hash];
    00194     Titem_ *item = slot.Find(key);
    00195     return item;
    00196   }
    00197
    00199   Titem_ *TryPop(const Tkey& key)
    00200   {
    00201     int hash = CalcHash(key);
    00202     Slot& slot = m_slots[hash];
    00203     Titem_ *item = slot.Detach(key);
    00204     if (item != NULL) {
    00205       m_num_items--;
    00206     }
    00207     return item;
    00208   }
    00209
    00211   Titem_& Pop(const Tkey& key)
    00212   {
    00213     Titem_ *item = TryPop(key);
    00214     assert(item != NULL);
    00215     return *item;
    00216   }
    00217
    00219   bool TryPop(Titem_& item)
    00220   {
    00221     const Tkey& key = item.GetKey();
    00222     int hash = CalcHash(key);
    00223     Slot& slot = m_slots[hash];
    00224     bool ret = slot.Detach(item);
    00225     if (ret) {
    00226       m_num_items--;
    00227     }
    00228     return ret;
    00229   }
    00230
    00232   void Pop(Titem_& item)
    00233   {
    00234     bool ret = TryPop(item);
    00235     assert(ret);
    00236   }
    00237
    00239   void Push(Titem_& new_item)
    00240   {
    00241     int hash = CalcHash(new_item);
    00242     Slot& slot = m_slots[hash];
    00243     assert(slot.Find(new_item.GetKey()) == NULL);
    00244     slot.Attach(new_item);
    00245     m_num_items++;
    00246   }
    00247 };
    00248
    00249 #endif /* HASHTABLE_HPP */