Added RootScopes for AST roots.
1 // vim: set ts=4 sw=4 tw=99 et:
4 // (C) Copyright 2010-2011 AlliedModders LLC
6 // Initial Author: David Anderson
11 #ifndef _INCLUDE_KEIMA_HASHTABLE_H_
12 #define _INCLUDE_KEIMA_HASHTABLE_H_
18 #include "Environment.h"
29 static const uint32 kFreeHash = 0;
30 static const uint32 kRemovedHash = 1;
33 void setHash(uint32 hash) {
49 bool removed() const {
50 return hash_ == kRemovedHash;
53 return hash_ == kFreeHash;
56 return hash_ > kRemovedHash;
61 bool sameHash(uint32 hash) const {
66 template <typename HashPolicy, typename AllocPolicy>
67 class HashTable : public AllocPolicy
69 typedef typename HashPolicy::Payload Payload;
70 typedef HashTableEntry<Payload> Entry;
78 static const uint32 kMinCapacity = 16;
79 static const uint32 kMaxCapacity = INT_MAX / sizeof(Entry);
81 template <typename Key>
82 uint32 computeHash(const Key &key) {
83 // Multiply by golden ratio.
84 uint32 hash = HashPolicy::hash(key) * 0x9E3779B9;
85 if (hash == Entry::kFreeHash || hash == Entry::kRemovedHash)
90 Entry *createTable(uint32 capacity) {
91 KE_ASSERT(capacity <= kMaxCapacity);
93 Entry *table = (Entry *)this->malloc(capacity * sizeof(Entry));
97 for (size_t i = 0; i < capacity; i++)
106 friend class HashTable;
119 Payload * operator ->() {
120 return entry_->payload();
122 Payload & operator *() {
123 return entry_->payload();
127 return entry_->isLive();
131 class Insert : public Result
136 Insert(Entry *entry, uint32 hash)
142 uint32 hash() const {
153 Probulator(uint32 hash, uint32 capacity)
157 KE_ASSERT(IsPowerOfTwo(capacity_));
160 uint32 entry() const {
161 return hash_ & (capacity_ - 1);
169 bool underloaded() const {
170 // Check if the table is underloaded: < 25% entries used.
171 return (capacity_ > kMinCapacity) && (nelements_ + ndeleted_ < capacity_ / 4);
173 bool overloaded() const {
174 // Grow if the table is overloaded: > 75% entries used.
175 return (nelements_ + ndeleted_) > ((capacity_ / 2) + (capacity_ / 4));
179 return changeCapacity(capacity_ >> 1);
183 KE_ASSERT(IsPowerOfTwo(kMaxCapacity));
184 if (capacity_ >= kMaxCapacity) {
185 this->reportAllocationOverflow();
188 return changeCapacity(capacity_ << 1);
191 bool checkDensity() {
199 bool changeCapacity(uint32 newCapacity) {
200 KE_ASSERT(newCapacity <= kMaxCapacity);
202 Entry *newTable = createTable(newCapacity);
206 Entry *oldTable = table_;
207 uint32 oldCapacity = capacity_;
210 capacity_ = newCapacity;
213 for (uint32 i = 0; i < oldCapacity; i++) {
214 Entry &oldEntry = oldTable[i];
215 if (oldEntry.isLive()) {
216 Insert p = insertUnique(oldEntry.hash());
217 p.entry().setHash(p.hash());
218 p.entry().payload() = oldEntry.payload();
227 // For use when the key is known to be unique.
228 Insert insertUnique(uint32 hash) {
229 Probulator probulator(hash, capacity_);
231 Entry *e = &table_[probulator.entry()];
233 if (e->free() || e->removed())
235 e = &table_[probulator.next()];
238 return Insert(e, hash);
241 template <typename Key>
242 Result lookup(const Key &key) {
243 uint32 hash = computeHash(key);
244 Probulator probulator(hash, capacity_);
246 Entry *e = &table_[probulator.entry()];
252 HashPolicy::matches(key, e->payload()))
256 e = &table_[probulator.next()];
262 template <typename Key>
263 Insert lookupForAdd(const Key &key) {
264 uint32 hash = computeHash(key);
265 Probulator probulator(hash, capacity_);
267 Entry *e = &table_[probulator.entry()];
271 if (e->sameHash(hash) && HashPolicy::matches(key, e->payload()))
273 e = &table_[probulator.next()];
276 return Insert(e, hash);
279 bool add(Insert &i) {
280 KE_ASSERT(!i.found());
282 // If the entry is deleted, just re-use the slot.
283 if (i.entry().removed()) {
288 // Otherwise, see if we're at max capacity.
289 if (nelements_ == kMaxCapacity) {
290 this->reportAllocationOverflow();
294 // Check if the table is over or underloaded. The table is always at
295 // least 25% free, so this check is enough to guarantee one free slot.
296 // (Without one free slot, insertion search could infinite loop.)
297 uint32 oldCapacity = capacity_;
301 // If the table changed size, we need to find a new insertion point.
302 // Note that a removed entry is impossible: either we caught it above,
303 // or we just resized and no entries are removed.
304 if (capacity_ != oldCapacity)
305 i = insertUnique(i.hash());
307 i.entry().setHash(i.hash());
314 HashTable(AllocPolicy ap)
325 for (uint32 i = 0; i < capacity_; i++)
326 table_[i].destruct();
330 bool init(uint32 capacity = 0) {
331 if (capacity < kMinCapacity)
332 capacity = kMinCapacity;
333 else if (capacity > kMaxCapacity)
334 capacity = kMaxCapacity;
336 KE_ASSERT(IsPowerOfTwo(capacity));
337 capacity_ = capacity;
339 table_ = createTable(capacity_);
346 template <typename Key>
347 Result find(const Key &key) {
351 template <typename Key>
352 Insert findForAdd(const Key &key) {
353 return lookupForAdd(key);
356 bool add(Insert &i, const Payload &payload) {
366 #endif // _INCLUDE_KEIMA_HASHTABLE_H_