| //===- 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 (m_NumOfEntries == 0); |
| } |
| |
| /// 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 (m_NumOfBuckets == 0) { |
| // 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 (firstTombstone != -1) { |
| m_Buckets[firstTombstone].FullHashValue = full_hash; |
| return firstTombstone; |
| } |
| |
| bucket.FullHashValue = full_hash; |
| return index; |
| } |
| |
| if (bucket_type::getTombstone() == bucket.Entry) { |
| if (firstTombstone == -1) { |
| 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 (m_NumOfBuckets == 0) |
| 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; |
| } |