Home | History | Annotate | Download | only in ADT
      1 //===- HashBase.tcc -------------------------------------------------------===//
      2 //
      3 //                     The MCLinker Project
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 //===----------------------------------------------------------------------===//
     11 // internal non-member functions
     12 //===----------------------------------------------------------------------===//
     13 inline static unsigned int compute_bucket_count(unsigned int pNumOfBuckets) {
     14   static const unsigned int bucket_size[] = {
     15       1,     3,     17,    37,    67,    97,     197,
     16       419,   977,   2593,  4099,  8209,  12289,  16411,
     17       20483, 32771, 49157, 65537, 98317, 131101, 196613};
     18 
     19   const unsigned int buckets_count =
     20       sizeof(bucket_size) / sizeof(bucket_size[0]);
     21   unsigned int idx = 0;
     22   do {
     23     if (pNumOfBuckets < bucket_size[idx]) {
     24       return bucket_size[idx];
     25     }
     26     ++idx;
     27   } while (idx < buckets_count);
     28 
     29   return (pNumOfBuckets + 131101);  // rare case. increase constantly
     30 }
     31 
     32 //===----------------------------------------------------------------------===//
     33 // template implementation of HashBucket
     34 //===----------------------------------------------------------------------===//
     35 template <typename DataType>
     36 typename HashBucket<DataType>::entry_type*
     37 HashBucket<DataType>::getEmptyBucket() {
     38   static entry_type* empty_bucket = reinterpret_cast<entry_type*>(0x0);
     39   return empty_bucket;
     40 }
     41 
     42 template <typename DataType>
     43 typename HashBucket<DataType>::entry_type*
     44 HashBucket<DataType>::getTombstone() {
     45   static entry_type* tombstone = reinterpret_cast<entry_type*>(0x1);
     46   return tombstone;
     47 }
     48 
     49 //===----------------------------------------------------------------------===//
     50 // template implementation of HashTableImpl
     51 //===----------------------------------------------------------------------===//
     52 template <typename HashEntryTy, typename HashFunctionTy>
     53 HashTableImpl<HashEntryTy, HashFunctionTy>::HashTableImpl()
     54     : m_Buckets(0),
     55       m_NumOfBuckets(0),
     56       m_NumOfEntries(0),
     57       m_NumOfTombstones(0),
     58       m_Hasher() {
     59 }
     60 
     61 template <typename HashEntryTy, typename HashFunctionTy>
     62 HashTableImpl<HashEntryTy, HashFunctionTy>::HashTableImpl(
     63     unsigned int pInitSize)
     64     : m_Hasher() {
     65   if (pInitSize) {
     66     init(pInitSize);
     67     return;
     68   }
     69 
     70   m_Buckets = 0;
     71   m_NumOfBuckets = 0;
     72   m_NumOfEntries = 0;
     73   m_NumOfTombstones = 0;
     74 }
     75 
     76 template <typename HashEntryTy, typename HashFunctionTy>
     77 HashTableImpl<HashEntryTy, HashFunctionTy>::~HashTableImpl() {
     78   clear();
     79 }
     80 
     81 /// empty - check if the hash table is empty
     82 template <typename HashEntryTy, typename HashFunctionTy>
     83 bool HashTableImpl<HashEntryTy, HashFunctionTy>::empty() const {
     84   return (m_NumOfEntries == 0);
     85 }
     86 
     87 /// init - initialize the hash table.
     88 template <typename HashEntryTy, typename HashFunctionTy>
     89 void HashTableImpl<HashEntryTy, HashFunctionTy>::init(unsigned int pInitSize) {
     90   m_NumOfBuckets =
     91       pInitSize ? compute_bucket_count(pInitSize) : NumOfInitBuckets;
     92 
     93   m_NumOfEntries = 0;
     94   m_NumOfTombstones = 0;
     95 
     96   /** calloc also set bucket.Item = bucket_type::getEmptyStone() **/
     97   m_Buckets = (bucket_type*)calloc(m_NumOfBuckets, sizeof(bucket_type));
     98 }
     99 
    100 /// clear - clear the hash table.
    101 template <typename HashEntryTy, typename HashFunctionTy>
    102 void HashTableImpl<HashEntryTy, HashFunctionTy>::clear() {
    103   free(m_Buckets);
    104 
    105   m_Buckets = 0;
    106   m_NumOfBuckets = 0;
    107   m_NumOfEntries = 0;
    108   m_NumOfTombstones = 0;
    109 }
    110 
    111 /// lookUpBucketFor - look up the bucket whose key is pKey
    112 template <typename HashEntryTy, typename HashFunctionTy>
    113 unsigned int HashTableImpl<HashEntryTy, HashFunctionTy>::lookUpBucketFor(
    114     const typename HashTableImpl<HashEntryTy, HashFunctionTy>::key_type& pKey) {
    115   if (m_NumOfBuckets == 0) {
    116     // NumOfBuckets is changed after init(pInitSize)
    117     init(NumOfInitBuckets);
    118   }
    119 
    120   unsigned int full_hash = m_Hasher(pKey);
    121   unsigned int index = full_hash % m_NumOfBuckets;
    122 
    123   const unsigned int probe = 1;
    124   int firstTombstone = -1;
    125 
    126   // linear probing
    127   while (true) {
    128     bucket_type& bucket = m_Buckets[index];
    129     // If we found an empty bucket, this key isn't in the table yet, return it.
    130     if (bucket_type::getEmptyBucket() == bucket.Entry) {
    131       if (firstTombstone != -1) {
    132         m_Buckets[firstTombstone].FullHashValue = full_hash;
    133         return firstTombstone;
    134       }
    135 
    136       bucket.FullHashValue = full_hash;
    137       return index;
    138     }
    139 
    140     if (bucket_type::getTombstone() == bucket.Entry) {
    141       if (firstTombstone == -1) {
    142         firstTombstone = index;
    143       }
    144     } else if (bucket.FullHashValue == full_hash) {
    145       if (bucket.Entry->compare(pKey)) {
    146         return index;
    147       }
    148     }
    149 
    150     index += probe;
    151     if (index == m_NumOfBuckets)
    152       index = 0;
    153   }
    154 }
    155 
    156 template <typename HashEntryTy, typename HashFunctionTy>
    157 int HashTableImpl<HashEntryTy, HashFunctionTy>::findKey(
    158     const typename HashTableImpl<HashEntryTy, HashFunctionTy>::key_type& pKey)
    159     const {
    160   if (m_NumOfBuckets == 0)
    161     return -1;
    162 
    163   unsigned int full_hash = m_Hasher(pKey);
    164   unsigned int index = full_hash % m_NumOfBuckets;
    165 
    166   const unsigned int probe = 1;
    167   // linear probing
    168   while (true) {
    169     bucket_type& bucket = m_Buckets[index];
    170 
    171     if (bucket_type::getEmptyBucket() == bucket.Entry)
    172       return -1;
    173 
    174     if (bucket_type::getTombstone() == bucket.Entry) {
    175       // Ignore tombstones.
    176     } else if (full_hash == bucket.FullHashValue) {
    177       // get string, compare, if match, return index
    178       if (bucket.Entry->compare(pKey))
    179         return index;
    180     }
    181     index += probe;
    182     if (index == m_NumOfBuckets)
    183       index = 0;
    184   }
    185 }
    186 
    187 template <typename HashEntryTy, typename HashFunctionTy>
    188 void HashTableImpl<HashEntryTy, HashFunctionTy>::mayRehash() {
    189   unsigned int new_size;
    190   // If the hash table is now more than 3/4 full, or if fewer than 1/8 of
    191   // the buckets are empty (meaning that many are filled with tombstones),
    192   // grow/rehash the table.
    193   if ((m_NumOfEntries << 2) > m_NumOfBuckets * 3)
    194     new_size = compute_bucket_count(m_NumOfBuckets);
    195   else if (((m_NumOfBuckets - (m_NumOfEntries + m_NumOfTombstones)) << 3) <
    196            m_NumOfBuckets)
    197     new_size = m_NumOfBuckets;
    198   else
    199     return;
    200 
    201   doRehash(new_size);
    202 }
    203 
    204 template <typename HashEntryTy, typename HashFunctionTy>
    205 void HashTableImpl<HashEntryTy, HashFunctionTy>::doRehash(
    206     unsigned int pNewSize) {
    207   bucket_type* new_table = (bucket_type*)calloc(pNewSize, sizeof(bucket_type));
    208 
    209   // Rehash all the items into their new buckets.  Luckily :) we already have
    210   // the hash values available, so we don't have to recall hash function again.
    211   for (bucket_type* IB = m_Buckets, * E = m_Buckets + m_NumOfBuckets; IB != E;
    212        ++IB) {
    213     if (IB->Entry != bucket_type::getEmptyBucket() &&
    214         IB->Entry != bucket_type::getTombstone()) {
    215       // Fast case, bucket available.
    216       unsigned full_hash = IB->FullHashValue;
    217       unsigned new_bucket = full_hash % pNewSize;
    218       if (bucket_type::getEmptyBucket() == new_table[new_bucket].Entry) {
    219         new_table[new_bucket].Entry = IB->Entry;
    220         new_table[new_bucket].FullHashValue = full_hash;
    221         continue;
    222       }
    223 
    224       // Otherwise probe for a spot.
    225       const unsigned int probe = 1;
    226       do {
    227         new_bucket += probe;
    228         if (new_bucket == pNewSize)
    229           new_bucket = 0;
    230       } while (new_table[new_bucket].Entry != bucket_type::getEmptyBucket());
    231 
    232       // Finally found a slot.  Fill it in.
    233       new_table[new_bucket].Entry = IB->Entry;
    234       new_table[new_bucket].FullHashValue = full_hash;
    235     }
    236   }
    237 
    238   free(m_Buckets);
    239 
    240   m_Buckets = new_table;
    241   m_NumOfBuckets = pNewSize;
    242   m_NumOfTombstones = 0;
    243 }
    244