1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // The reason we write our own hash map instead of using unordered_map in STL, 6 // is that STL containers use a mutex pool on debug build, which will lead to 7 // deadlock when we are using async signal handler. 8 9 #ifndef V8_BASE_HASHMAP_H_ 10 #define V8_BASE_HASHMAP_H_ 11 12 #include <stdlib.h> 13 14 #include "src/base/bits.h" 15 #include "src/base/hashmap-entry.h" 16 #include "src/base/logging.h" 17 18 namespace v8 { 19 namespace base { 20 21 class DefaultAllocationPolicy { 22 public: 23 V8_INLINE void* New(size_t size) { return malloc(size); } 24 V8_INLINE static void Delete(void* p) { free(p); } 25 }; 26 27 template <typename Key, typename Value, class MatchFun, class AllocationPolicy> 28 class TemplateHashMapImpl { 29 public: 30 typedef TemplateHashMapEntry<Key, Value> Entry; 31 32 // The default capacity. This is used by the call sites which want 33 // to pass in a non-default AllocationPolicy but want to use the 34 // default value of capacity specified by the implementation. 35 static const uint32_t kDefaultHashMapCapacity = 8; 36 37 // initial_capacity is the size of the initial hash map; 38 // it must be a power of 2 (and thus must not be 0). 39 TemplateHashMapImpl(uint32_t capacity = kDefaultHashMapCapacity, 40 MatchFun match = MatchFun(), 41 AllocationPolicy allocator = AllocationPolicy()); 42 43 // Clones the given hashmap and creates a copy with the same entries. 44 TemplateHashMapImpl(const TemplateHashMapImpl<Key, Value, MatchFun, 45 AllocationPolicy>* original, 46 AllocationPolicy allocator = AllocationPolicy()); 47 48 ~TemplateHashMapImpl(); 49 50 // If an entry with matching key is found, returns that entry. 51 // Otherwise, nullptr is returned. 52 Entry* Lookup(const Key& key, uint32_t hash) const; 53 54 // If an entry with matching key is found, returns that entry. 55 // If no matching entry is found, a new entry is inserted with 56 // corresponding key, key hash, and default initialized value. 57 Entry* LookupOrInsert(const Key& key, uint32_t hash, 58 AllocationPolicy allocator = AllocationPolicy()); 59 60 // If an entry with matching key is found, returns that entry. 61 // If no matching entry is found, a new entry is inserted with 62 // corresponding key, key hash, and value created by func. 63 template <typename Func> 64 Entry* LookupOrInsert(const Key& key, uint32_t hash, const Func& value_func, 65 AllocationPolicy allocator = AllocationPolicy()); 66 67 Entry* InsertNew(const Key& key, uint32_t hash, 68 AllocationPolicy allocator = AllocationPolicy()); 69 70 // Removes the entry with matching key. 71 // It returns the value of the deleted entry 72 // or null if there is no value for such key. 73 Value Remove(const Key& key, uint32_t hash); 74 75 // Empties the hash map (occupancy() == 0). 76 void Clear(); 77 78 // Empties the map and makes it unusable for allocation. 79 void Invalidate() { 80 AllocationPolicy::Delete(map_); 81 map_ = nullptr; 82 occupancy_ = 0; 83 capacity_ = 0; 84 } 85 86 // The number of (non-empty) entries in the table. 87 uint32_t occupancy() const { return occupancy_; } 88 89 // The capacity of the table. The implementation 90 // makes sure that occupancy is at most 80% of 91 // the table capacity. 92 uint32_t capacity() const { return capacity_; } 93 94 // Iteration 95 // 96 // for (Entry* p = map.Start(); p != nullptr; p = map.Next(p)) { 97 // ... 98 // } 99 // 100 // If entries are inserted during iteration, the effect of 101 // calling Next() is undefined. 102 Entry* Start() const; 103 Entry* Next(Entry* entry) const; 104 105 void Reset(AllocationPolicy allocator) { 106 Initialize(capacity_, allocator); 107 occupancy_ = 0; 108 } 109 110 protected: 111 void Initialize(uint32_t capacity, AllocationPolicy allocator); 112 113 private: 114 Entry* map_; 115 uint32_t capacity_; 116 uint32_t occupancy_; 117 // TODO(leszeks): This takes up space even if it has no state, maybe replace 118 // with something that does the empty base optimisation e.g. std::tuple 119 MatchFun match_; 120 121 Entry* map_end() const { return map_ + capacity_; } 122 Entry* Probe(const Key& key, uint32_t hash) const; 123 Entry* FillEmptyEntry(Entry* entry, const Key& key, const Value& value, 124 uint32_t hash, 125 AllocationPolicy allocator = AllocationPolicy()); 126 void Resize(AllocationPolicy allocator); 127 128 DISALLOW_COPY_AND_ASSIGN(TemplateHashMapImpl); 129 }; 130 template <typename Key, typename Value, typename MatchFun, 131 class AllocationPolicy> 132 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>:: 133 TemplateHashMapImpl(uint32_t initial_capacity, MatchFun match, 134 AllocationPolicy allocator) 135 : match_(match) { 136 Initialize(initial_capacity, allocator); 137 } 138 139 template <typename Key, typename Value, typename MatchFun, 140 class AllocationPolicy> 141 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>:: 142 TemplateHashMapImpl(const TemplateHashMapImpl<Key, Value, MatchFun, 143 AllocationPolicy>* original, 144 AllocationPolicy allocator) 145 : capacity_(original->capacity_), 146 occupancy_(original->occupancy_), 147 match_(original->match_) { 148 map_ = reinterpret_cast<Entry*>(allocator.New(capacity_ * sizeof(Entry))); 149 memcpy(map_, original->map_, capacity_ * sizeof(Entry)); 150 } 151 152 template <typename Key, typename Value, typename MatchFun, 153 class AllocationPolicy> 154 TemplateHashMapImpl<Key, Value, MatchFun, 155 AllocationPolicy>::~TemplateHashMapImpl() { 156 AllocationPolicy::Delete(map_); 157 } 158 159 template <typename Key, typename Value, typename MatchFun, 160 class AllocationPolicy> 161 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry* 162 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Lookup( 163 const Key& key, uint32_t hash) const { 164 Entry* entry = Probe(key, hash); 165 return entry->exists() ? entry : nullptr; 166 } 167 168 template <typename Key, typename Value, typename MatchFun, 169 class AllocationPolicy> 170 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry* 171 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::LookupOrInsert( 172 const Key& key, uint32_t hash, AllocationPolicy allocator) { 173 return LookupOrInsert(key, hash, []() { return Value(); }, allocator); 174 } 175 176 template <typename Key, typename Value, typename MatchFun, 177 class AllocationPolicy> 178 template <typename Func> 179 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry* 180 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::LookupOrInsert( 181 const Key& key, uint32_t hash, const Func& value_func, 182 AllocationPolicy allocator) { 183 // Find a matching entry. 184 Entry* entry = Probe(key, hash); 185 if (entry->exists()) { 186 return entry; 187 } 188 189 return FillEmptyEntry(entry, key, value_func(), hash, allocator); 190 } 191 192 template <typename Key, typename Value, typename MatchFun, 193 class AllocationPolicy> 194 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry* 195 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::InsertNew( 196 const Key& key, uint32_t hash, AllocationPolicy allocator) { 197 Entry* entry = Probe(key, hash); 198 return FillEmptyEntry(entry, key, Value(), hash, allocator); 199 } 200 201 template <typename Key, typename Value, typename MatchFun, 202 class AllocationPolicy> 203 Value TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Remove( 204 const Key& key, uint32_t hash) { 205 // Lookup the entry for the key to remove. 206 Entry* p = Probe(key, hash); 207 if (!p->exists()) { 208 // Key not found nothing to remove. 209 return nullptr; 210 } 211 212 Value value = p->value; 213 // To remove an entry we need to ensure that it does not create an empty 214 // entry that will cause the search for another entry to stop too soon. If all 215 // the entries between the entry to remove and the next empty slot have their 216 // initial position inside this interval, clearing the entry to remove will 217 // not break the search. If, while searching for the next empty entry, an 218 // entry is encountered which does not have its initial position between the 219 // entry to remove and the position looked at, then this entry can be moved to 220 // the place of the entry to remove without breaking the search for it. The 221 // entry made vacant by this move is now the entry to remove and the process 222 // starts over. 223 // Algorithm from http://en.wikipedia.org/wiki/Open_addressing. 224 225 // This guarantees loop termination as there is at least one empty entry so 226 // eventually the removed entry will have an empty entry after it. 227 DCHECK(occupancy_ < capacity_); 228 229 // p is the candidate entry to clear. q is used to scan forwards. 230 Entry* q = p; // Start at the entry to remove. 231 while (true) { 232 // Move q to the next entry. 233 q = q + 1; 234 if (q == map_end()) { 235 q = map_; 236 } 237 238 // All entries between p and q have their initial position between p and q 239 // and the entry p can be cleared without breaking the search for these 240 // entries. 241 if (!q->exists()) { 242 break; 243 } 244 245 // Find the initial position for the entry at position q. 246 Entry* r = map_ + (q->hash & (capacity_ - 1)); 247 248 // If the entry at position q has its initial position outside the range 249 // between p and q it can be moved forward to position p and will still be 250 // found. There is now a new candidate entry for clearing. 251 if ((q > p && (r <= p || r > q)) || (q < p && (r <= p && r > q))) { 252 *p = *q; 253 p = q; 254 } 255 } 256 257 // Clear the entry which is allowed to en emptied. 258 p->clear(); 259 occupancy_--; 260 return value; 261 } 262 263 template <typename Key, typename Value, typename MatchFun, 264 class AllocationPolicy> 265 void TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Clear() { 266 // Mark all entries as empty. 267 for (size_t i = 0; i < capacity_; ++i) { 268 map_[i].clear(); 269 } 270 occupancy_ = 0; 271 } 272 273 template <typename Key, typename Value, typename MatchFun, 274 class AllocationPolicy> 275 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry* 276 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Start() const { 277 return Next(map_ - 1); 278 } 279 280 template <typename Key, typename Value, typename MatchFun, 281 class AllocationPolicy> 282 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry* 283 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Next( 284 Entry* entry) const { 285 const Entry* end = map_end(); 286 DCHECK(map_ - 1 <= entry && entry < end); 287 for (entry++; entry < end; entry++) { 288 if (entry->exists()) { 289 return entry; 290 } 291 } 292 return nullptr; 293 } 294 295 template <typename Key, typename Value, typename MatchFun, 296 class AllocationPolicy> 297 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry* 298 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Probe( 299 const Key& key, uint32_t hash) const { 300 DCHECK(base::bits::IsPowerOfTwo32(capacity_)); 301 size_t i = hash & (capacity_ - 1); 302 DCHECK(i < capacity_); 303 304 DCHECK(occupancy_ < capacity_); // Guarantees loop termination. 305 while (map_[i].exists() && !match_(hash, map_[i].hash, key, map_[i].key)) { 306 i = (i + 1) & (capacity_ - 1); 307 } 308 309 return &map_[i]; 310 } 311 312 template <typename Key, typename Value, typename MatchFun, 313 class AllocationPolicy> 314 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry* 315 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::FillEmptyEntry( 316 Entry* entry, const Key& key, const Value& value, uint32_t hash, 317 AllocationPolicy allocator) { 318 DCHECK(!entry->exists()); 319 320 new (entry) Entry(key, value, hash); 321 occupancy_++; 322 323 // Grow the map if we reached >= 80% occupancy. 324 if (occupancy_ + occupancy_ / 4 >= capacity_) { 325 Resize(allocator); 326 entry = Probe(key, hash); 327 } 328 329 return entry; 330 } 331 332 template <typename Key, typename Value, typename MatchFun, 333 class AllocationPolicy> 334 void TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Initialize( 335 uint32_t capacity, AllocationPolicy allocator) { 336 DCHECK(base::bits::IsPowerOfTwo32(capacity)); 337 map_ = reinterpret_cast<Entry*>(allocator.New(capacity * sizeof(Entry))); 338 if (map_ == nullptr) { 339 FATAL("Out of memory: HashMap::Initialize"); 340 return; 341 } 342 capacity_ = capacity; 343 Clear(); 344 } 345 346 template <typename Key, typename Value, typename MatchFun, 347 class AllocationPolicy> 348 void TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Resize( 349 AllocationPolicy allocator) { 350 Entry* map = map_; 351 uint32_t n = occupancy_; 352 353 // Allocate larger map. 354 Initialize(capacity_ * 2, allocator); 355 356 // Rehash all current entries. 357 for (Entry* entry = map; n > 0; entry++) { 358 if (entry->exists()) { 359 Entry* new_entry = Probe(entry->key, entry->hash); 360 new_entry = FillEmptyEntry(new_entry, entry->key, entry->value, 361 entry->hash, allocator); 362 n--; 363 } 364 } 365 366 // Delete old map. 367 AllocationPolicy::Delete(map); 368 } 369 370 // Match function which compares hashes before executing a (potentially 371 // expensive) key comparison. 372 template <typename Key, typename MatchFun> 373 struct HashEqualityThenKeyMatcher { 374 explicit HashEqualityThenKeyMatcher(MatchFun match) : match_(match) {} 375 376 bool operator()(uint32_t hash1, uint32_t hash2, const Key& key1, 377 const Key& key2) const { 378 return hash1 == hash2 && match_(key1, key2); 379 } 380 381 private: 382 MatchFun match_; 383 }; 384 385 // Hashmap<void*, void*> which takes a custom key comparison function pointer. 386 template <typename AllocationPolicy> 387 class CustomMatcherTemplateHashMapImpl 388 : public TemplateHashMapImpl< 389 void*, void*, 390 HashEqualityThenKeyMatcher<void*, bool (*)(void*, void*)>, 391 AllocationPolicy> { 392 typedef TemplateHashMapImpl< 393 void*, void*, HashEqualityThenKeyMatcher<void*, bool (*)(void*, void*)>, 394 AllocationPolicy> 395 Base; 396 397 public: 398 typedef bool (*MatchFun)(void*, void*); 399 400 CustomMatcherTemplateHashMapImpl( 401 MatchFun match, uint32_t capacity = Base::kDefaultHashMapCapacity, 402 AllocationPolicy allocator = AllocationPolicy()) 403 : Base(capacity, HashEqualityThenKeyMatcher<void*, MatchFun>(match), 404 allocator) {} 405 406 CustomMatcherTemplateHashMapImpl( 407 const CustomMatcherTemplateHashMapImpl<AllocationPolicy>* original, 408 AllocationPolicy allocator = AllocationPolicy()) 409 : Base(original, allocator) {} 410 411 private: 412 DISALLOW_COPY_AND_ASSIGN(CustomMatcherTemplateHashMapImpl); 413 }; 414 415 typedef CustomMatcherTemplateHashMapImpl<DefaultAllocationPolicy> 416 CustomMatcherHashMap; 417 418 // Match function which compares keys directly by equality. 419 template <typename Key> 420 struct KeyEqualityMatcher { 421 bool operator()(uint32_t hash1, uint32_t hash2, const Key& key1, 422 const Key& key2) const { 423 return key1 == key2; 424 } 425 }; 426 427 // Hashmap<void*, void*> which compares the key pointers directly. 428 template <typename AllocationPolicy> 429 class PointerTemplateHashMapImpl 430 : public TemplateHashMapImpl<void*, void*, KeyEqualityMatcher<void*>, 431 AllocationPolicy> { 432 typedef TemplateHashMapImpl<void*, void*, KeyEqualityMatcher<void*>, 433 AllocationPolicy> 434 Base; 435 436 public: 437 PointerTemplateHashMapImpl(uint32_t capacity = Base::kDefaultHashMapCapacity, 438 AllocationPolicy allocator = AllocationPolicy()) 439 : Base(capacity, KeyEqualityMatcher<void*>(), allocator) {} 440 }; 441 442 typedef PointerTemplateHashMapImpl<DefaultAllocationPolicy> HashMap; 443 444 // A hash map for pointer keys and values with an STL-like interface. 445 template <class Key, class Value, class MatchFun, class AllocationPolicy> 446 class TemplateHashMap 447 : private TemplateHashMapImpl<void*, void*, 448 HashEqualityThenKeyMatcher<void*, MatchFun>, 449 AllocationPolicy> { 450 typedef TemplateHashMapImpl<void*, void*, 451 HashEqualityThenKeyMatcher<void*, MatchFun>, 452 AllocationPolicy> 453 Base; 454 455 public: 456 STATIC_ASSERT(sizeof(Key*) == sizeof(void*)); // NOLINT 457 STATIC_ASSERT(sizeof(Value*) == sizeof(void*)); // NOLINT 458 struct value_type { 459 Key* first; 460 Value* second; 461 }; 462 463 class Iterator { 464 public: 465 Iterator& operator++() { 466 entry_ = map_->Next(entry_); 467 return *this; 468 } 469 470 value_type* operator->() { return reinterpret_cast<value_type*>(entry_); } 471 bool operator!=(const Iterator& other) { return entry_ != other.entry_; } 472 473 private: 474 Iterator(const Base* map, typename Base::Entry* entry) 475 : map_(map), entry_(entry) {} 476 477 const Base* map_; 478 typename Base::Entry* entry_; 479 480 friend class TemplateHashMap; 481 }; 482 483 TemplateHashMap(MatchFun match, 484 AllocationPolicy allocator = AllocationPolicy()) 485 : Base(Base::kDefaultHashMapCapacity, 486 HashEqualityThenKeyMatcher<void*, MatchFun>(match), allocator) {} 487 488 Iterator begin() const { return Iterator(this, this->Start()); } 489 Iterator end() const { return Iterator(this, nullptr); } 490 Iterator find(Key* key, bool insert = false, 491 AllocationPolicy allocator = AllocationPolicy()) { 492 if (insert) { 493 return Iterator(this, this->LookupOrInsert(key, key->Hash(), allocator)); 494 } 495 return Iterator(this, this->Lookup(key, key->Hash())); 496 } 497 }; 498 499 } // namespace base 500 } // namespace v8 501 502 #endif // V8_BASE_HASHMAP_H_ 503