# hashtable.hpp

Go to the documentation of this file.

    00001 /* $Id$ */
    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 #include "../core/math_func.hpp"
    00016
    00017 template <class Titem_>
    00018 struct CHashTableSlotT
    00019 {
    00020   typedef typename Titem_::Key Key;          // make Titem_::Key a property of HashTable
    00021
    00022   Titem_ *m_pFirst;
    00023
    00024   FORCEINLINE CHashTableSlotT() : m_pFirst(NULL) {}
    00025
    00027   FORCEINLINE void Clear() {m_pFirst = NULL;}
    00028
    00030   FORCEINLINE const Titem_ *Find(const Key& key) const
    00031   {
    00032     for (const Titem_ *pItem = m_pFirst; pItem != NULL; pItem = pItem->GetHashNext()) {
    00033       if (pItem->GetKey() == key) {
    00034         /* we have found the item, return it */
    00035         return pItem;
    00036       }
    00037     }
    00038     return NULL;
    00039   }
    00040
    00042   FORCEINLINE Titem_ *Find(const Key& key)
    00043   {
    00044     for (Titem_ *pItem = m_pFirst; pItem != NULL; pItem = pItem->GetHashNext()) {
    00045       if (pItem->GetKey() == key) {
    00046         /* we have found the item, return it */
    00047         return pItem;
    00048       }
    00049     }
    00050     return NULL;
    00051   }
    00052
    00054   FORCEINLINE void Attach(Titem_& new_item)
    00055   {
    00056     assert(new_item.GetHashNext() == NULL);
    00057     new_item.SetHashNext(m_pFirst);
    00058     m_pFirst = &new_item;
    00059   }
    00060
    00062   FORCEINLINE bool Detach(Titem_& item_to_remove)
    00063   {
    00064     if (m_pFirst == &item_to_remove) {
    00065       m_pFirst = item_to_remove.GetHashNext();
    00066       item_to_remove.SetHashNext(NULL);
    00067       return true;
    00068     }
    00069     Titem_ *pItem = m_pFirst;
    00070     while (true) {
    00071       if (pItem == NULL) {
    00072         return false;
    00073       }
    00074       Titem_ *pNextItem = pItem->GetHashNext();
    00075       if (pNextItem == &item_to_remove) break;
    00076       pItem = pNextItem;
    00077     }
    00078     pItem->SetHashNext(item_to_remove.GetHashNext());
    00079     item_to_remove.SetHashNext(NULL);
    00080     return true;
    00081   }
    00082
    00084   FORCEINLINE Titem_ *Detach(const Key& key)
    00085   {
    00086     /* do we have any items? */
    00087     if (m_pFirst == NULL) {
    00088       return NULL;
    00089     }
    00090     /* is it our first item? */
    00091     if (m_pFirst->GetKey() == key) {
    00092       Titem_& ret_item = *m_pFirst;
    00093       m_pFirst = m_pFirst->GetHashNext();
    00094       ret_item.SetHashNext(NULL);
    00095       return &ret_item;
    00096     }
    00097     /* find it in the following items */
    00098     Titem_ *pPrev = m_pFirst;
    00099     for (Titem_ *pItem = m_pFirst->GetHashNext(); pItem != NULL; pPrev = pItem, pItem = pItem->GetHashNext()) {
    00100       if (pItem->GetKey() == key) {
    00101         /* we have found the item, unlink and return it */
    00102         pPrev->SetHashNext(pItem->GetHashNext());
    00103         pItem->SetHashNext(NULL);
    00104         return pItem;
    00105       }
    00106     }
    00107     return NULL;
    00108   }
    00109 };
    00110
    00133 template <class Titem_, int Thash_bits_>
    00134 class CHashTableT {
    00135 public:
    00136   typedef Titem_ Titem;                         // make Titem_ visible from outside of class
    00137   typedef typename Titem_::Key Tkey;            // make Titem_::Key a property of HashTable
    00138   static const int Thash_bits = Thash_bits_;    // publish num of hash bits
    00139   static const int Tcapacity = 1 << Thash_bits; // and num of slots 2^bits
    00140
    00141 protected:
    00146   typedef CHashTableSlotT<Titem_> Slot;
    00147
    00148   Slot *m_slots;     // here we store our data (array of blobs)
    00149   int   m_num_items; // item counter
    00150
    00151 public:
    00152   /* default constructor */
    00153   FORCEINLINE CHashTableT()
    00154   {
    00155     /* construct all slots */
    00156     m_slots = new Slot[Tcapacity];
    00157     m_num_items = 0;
    00158   }
    00159
    00160   ~CHashTableT() {delete [] m_slots; m_num_items = 0; m_slots = NULL;}
    00161
    00162 protected:
    00164   FORCEINLINE static int CalcHash(const Tkey& key)
    00165   {
    00166     int32 hash = key.CalcHash();
    00167     if ((8 * Thash_bits) < 32) hash ^= hash >> (min(8 * Thash_bits, 31));
    00168     if ((4 * Thash_bits) < 32) hash ^= hash >> (min(4 * Thash_bits, 31));
    00169     if ((2 * Thash_bits) < 32) hash ^= hash >> (min(2 * Thash_bits, 31));
    00170     if ((1 * Thash_bits) < 32) hash ^= hash >> (min(1 * Thash_bits, 31));
    00171     hash &= (1 << Thash_bits) - 1;
    00172     return hash;
    00173   }
    00174
    00176   FORCEINLINE static int CalcHash(const Titem_& item) {return CalcHash(item.GetKey());}
    00177
    00178 public:
    00180   FORCEINLINE int Count() const {return m_num_items;}
    00181
    00183   FORCEINLINE void Clear() const {for (int i = 0; i < Tcapacity; i++) m_slots[i].Clear();}
    00184
    00186   const Titem_ *Find(const Tkey& key) const
    00187   {
    00188     int hash = CalcHash(key);
    00189     const Slot& slot = m_slots[hash];
    00190     const Titem_ *item = slot.Find(key);
    00191     return item;
    00192   }
    00193
    00195   Titem_ *Find(const Tkey& key)
    00196   {
    00197     int hash = CalcHash(key);
    00198     Slot& slot = m_slots[hash];
    00199     Titem_ *item = slot.Find(key);
    00200     return item;
    00201   }
    00202
    00204   Titem_ *TryPop(const Tkey& key)
    00205   {
    00206     int hash = CalcHash(key);
    00207     Slot& slot = m_slots[hash];
    00208     Titem_ *item = slot.Detach(key);
    00209     if (item != NULL) {
    00210       m_num_items--;
    00211     }
    00212     return item;
    00213   }
    00214
    00216   Titem_& Pop(const Tkey& key)
    00217   {
    00218     Titem_ *item = TryPop(key);
    00219     assert(item != NULL);
    00220     return *item;
    00221   }
    00222
    00224   bool TryPop(Titem_& item)
    00225   {
    00226     const Tkey& key = item.GetKey();
    00227     int hash = CalcHash(key);
    00228     Slot& slot = m_slots[hash];
    00229     bool ret = slot.Detach(item);
    00230     if (ret) {
    00231       m_num_items--;
    00232     }
    00233     return ret;
    00234   }
    00235
    00237   void Pop(Titem_& item)
    00238   {
    00239     bool ret = TryPop(item);
    00240     assert(ret);
    00241   }
    00242
    00244   void Push(Titem_& new_item)
    00245   {
    00246     int hash = CalcHash(new_item);
    00247     Slot& slot = m_slots[hash];
    00248     assert(slot.Find(new_item.GetKey()) == NULL);
    00249     slot.Attach(new_item);
    00250     m_num_items++;
    00251   }
    00252 };
    00253
    00254 #endif /* HASHTABLE_HPP */