| //===- HashBase.tcc -------------------------------------------------------===// |
| // |
| // The MCLinker Project |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| //===--------------------------------------------------------------------===// |
| // internal non-member functions |
| inline static unsigned int compute_bucket_count(unsigned int pNumOfBuckets) |
| { |
| static const unsigned int bucket_size[] = |
| { |
| 1, 3, 17, 37, 67, 97, 197, 419, 977, 2593, 4099, 8209, 12289, |
| 16411, 20483, 32771, 49157, 65537, 98317, 131101, 196613 |
| }; |
| |
| const unsigned int buckets_count = |
| sizeof(bucket_size) / sizeof(bucket_size[0]); |
| unsigned int idx = 0; |
| do { |
| if (pNumOfBuckets < bucket_size[idx]) { |
| return bucket_size[idx]; |
| } |
| ++idx; |
| } while(idx < buckets_count); |
| |
| return (pNumOfBuckets+131101); // rare case. increase constantly |
| } |
| |
| //===--------------------------------------------------------------------===// |
| // template implementation of HashBucket |
| template<typename DataType> |
| typename HashBucket<DataType>::entry_type* |
| HashBucket<DataType>::getEmptyBucket() |
| { |
| static entry_type* empty_bucket = reinterpret_cast<entry_type*>(0x0); |
| return empty_bucket; |
| } |
| |
| template<typename DataType> |
| typename HashBucket<DataType>::entry_type* |
| HashBucket<DataType>::getTombstone() |
| { |
| static entry_type* tombstone = reinterpret_cast<entry_type*>(0x1); |
| return tombstone; |
| } |
| |
| //===--------------------------------------------------------------------===// |
| // template implementation of HashTableImpl |
| template<typename HashEntryTy, |
| typename HashFunctionTy> |
| HashTableImpl<HashEntryTy, HashFunctionTy>::HashTableImpl() |
| : m_Buckets(0), |
| m_NumOfBuckets(0), |
| m_NumOfEntries(0), |
| m_NumOfTombstones(0), |
| m_Hasher() { |
| } |
| |
| template<typename HashEntryTy, |
| typename HashFunctionTy> |
| HashTableImpl<HashEntryTy, HashFunctionTy>::HashTableImpl( |
| unsigned int pInitSize) |
| : m_Hasher() { |
| if (pInitSize) { |
| init(pInitSize); |
| return; |
| } |
| |
| m_Buckets = 0; |
| m_NumOfBuckets = 0; |
| m_NumOfEntries = 0; |
| m_NumOfTombstones = 0; |
| } |
| |
| template<typename HashEntryTy, |
| typename HashFunctionTy> |
| HashTableImpl<HashEntryTy, HashFunctionTy>::~HashTableImpl() |
| { |
| clear(); |
| } |
| |
| /// empty - check if the hash table is empty |
| template<typename HashEntryTy, |
| typename HashFunctionTy> |
| bool HashTableImpl<HashEntryTy, HashFunctionTy>::empty() const |
| { |
| return (0 == m_NumOfEntries); |
| } |
| |
| /// init - initialize the hash table. |
| template<typename HashEntryTy, |
| typename HashFunctionTy> |
| void HashTableImpl<HashEntryTy, HashFunctionTy>::init(unsigned int pInitSize) |
| { |
| m_NumOfBuckets = pInitSize? compute_bucket_count(pInitSize): NumOfInitBuckets; |
| |
| m_NumOfEntries = 0; |
| m_NumOfTombstones = 0; |
| |
| /** calloc also set bucket.Item = bucket_type::getEmptyStone() **/ |
| m_Buckets = (bucket_type*)calloc(m_NumOfBuckets, sizeof(bucket_type)); |
| } |
| |
| /// clear - clear the hash table. |
| template<typename HashEntryTy, |
| typename HashFunctionTy> |
| void HashTableImpl<HashEntryTy, HashFunctionTy>::clear() |
| { |
| free(m_Buckets); |
| |
| m_Buckets = 0; |
| m_NumOfBuckets = 0; |
| m_NumOfEntries = 0; |
| m_NumOfTombstones = 0; |
| } |
| |
| /// lookUpBucketFor - look up the bucket whose key is pKey |
| template<typename HashEntryTy, |
| typename HashFunctionTy> |
| unsigned int |
| HashTableImpl<HashEntryTy, HashFunctionTy>::lookUpBucketFor( |
| const typename HashTableImpl<HashEntryTy, HashFunctionTy>::key_type& pKey) |
| { |
| if (0 == m_NumOfBuckets) { |
| // NumOfBuckets is changed after init(pInitSize) |
| init(NumOfInitBuckets); |
| } |
| |
| unsigned int full_hash = m_Hasher(pKey); |
| unsigned int index = full_hash % m_NumOfBuckets; |
| |
| const unsigned int probe = 1; |
| int firstTombstone = -1; |
| |
| // linear probing |
| while(true) { |
| bucket_type& bucket = m_Buckets[index]; |
| // If we found an empty bucket, this key isn't in the table yet, return it. |
| if (bucket_type::getEmptyBucket() == bucket.Entry) { |
| if (-1 != firstTombstone) { |
| m_Buckets[firstTombstone].FullHashValue = full_hash; |
| return firstTombstone; |
| } |
| |
| bucket.FullHashValue = full_hash; |
| return index; |
| } |
| |
| if (bucket_type::getTombstone() == bucket.Entry) { |
| if (-1 == firstTombstone) { |
| firstTombstone = index; |
| } |
| } |
| else if (bucket.FullHashValue == full_hash) { |
| if (bucket.Entry->compare(pKey)) { |
| return index; |
| } |
| } |
| |
| index += probe; |
| if (index == m_NumOfBuckets) |
| index = 0; |
| } |
| } |
| |
| template<typename HashEntryTy, |
| typename HashFunctionTy> |
| int |
| HashTableImpl<HashEntryTy, HashFunctionTy>::findKey( |
| const typename HashTableImpl<HashEntryTy, HashFunctionTy>::key_type& pKey) const |
| { |
| if (0 == m_NumOfBuckets) |
| return -1; |
| |
| unsigned int full_hash = m_Hasher(pKey); |
| unsigned int index = full_hash % m_NumOfBuckets; |
| |
| const unsigned int probe = 1; |
| // linear probing |
| while (true) { |
| bucket_type &bucket = m_Buckets[index]; |
| |
| if (bucket_type::getEmptyBucket() == bucket.Entry) |
| return -1; |
| |
| if (bucket_type::getTombstone() == bucket.Entry) { |
| // Ignore tombstones. |
| } |
| else if (full_hash == bucket.FullHashValue) { |
| // get string, compare, if match, return index |
| if (bucket.Entry->compare(pKey)) |
| return index; |
| } |
| index += probe; |
| if (index == m_NumOfBuckets) |
| index = 0; |
| } |
| } |
| |
| template<typename HashEntryTy, |
| typename HashFunctionTy> |
| void HashTableImpl<HashEntryTy, HashFunctionTy>::mayRehash() |
| { |
| |
| unsigned int new_size; |
| // If the hash table is now more than 3/4 full, or if fewer than 1/8 of |
| // the buckets are empty (meaning that many are filled with tombstones), |
| // grow/rehash the table. |
| if ((m_NumOfEntries<<2) > m_NumOfBuckets*3) |
| new_size = compute_bucket_count(m_NumOfBuckets); |
| else if (((m_NumOfBuckets-(m_NumOfEntries+m_NumOfTombstones))<<3) < m_NumOfBuckets) |
| new_size = m_NumOfBuckets; |
| else |
| return; |
| |
| doRehash(new_size); |
| } |
| |
| template<typename HashEntryTy, |
| typename HashFunctionTy> |
| void HashTableImpl<HashEntryTy, HashFunctionTy>::doRehash(unsigned int pNewSize) |
| { |
| bucket_type* new_table = (bucket_type*)calloc(pNewSize, sizeof(bucket_type)); |
| |
| // Rehash all the items into their new buckets. Luckily :) we already have |
| // the hash values available, so we don't have to recall hash function again. |
| for (bucket_type *IB = m_Buckets, *E = m_Buckets+m_NumOfBuckets; IB != E; ++IB) { |
| if (IB->Entry != bucket_type::getEmptyBucket() && |
| IB->Entry != bucket_type::getTombstone()) { |
| // Fast case, bucket available. |
| unsigned full_hash = IB->FullHashValue; |
| unsigned new_bucket = full_hash % pNewSize; |
| if (bucket_type::getEmptyBucket() == new_table[new_bucket].Entry) { |
| new_table[new_bucket].Entry = IB->Entry; |
| new_table[new_bucket].FullHashValue = full_hash; |
| continue; |
| } |
| |
| // Otherwise probe for a spot. |
| const unsigned int probe = 1; |
| do { |
| new_bucket += probe; |
| if (new_bucket == pNewSize) |
| new_bucket = 0; |
| } while (new_table[new_bucket].Entry != bucket_type::getEmptyBucket()); |
| |
| // Finally found a slot. Fill it in. |
| new_table[new_bucket].Entry = IB->Entry; |
| new_table[new_bucket].FullHashValue = full_hash; |
| } |
| } |
| |
| free(m_Buckets); |
| |
| m_Buckets = new_table; |
| m_NumOfBuckets = pNewSize; |
| m_NumOfTombstones = 0; |
| } |
| |