1 /* 2 * Copyright 2015 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SkTHash_DEFINED 9 #define SkTHash_DEFINED 10 11 #include "include/core/SkTypes.h" 12 #include "src/core/SkChecksum.h" 13 14 #include <initializer_list> 15 #include <memory> 16 #include <new> 17 #include <type_traits> 18 #include <utility> 19 20 namespace skia_private { 21 22 // Before trying to use THashTable, look below to see if THashMap or THashSet works for you. 23 // They're easier to use, usually perform the same, and have fewer sharp edges. 24 25 // T and K are treated as ordinary copyable C++ types. 26 // Traits must have: 27 // - static K GetKey(T) 28 // - static uint32_t Hash(K) 29 // If the key is large and stored inside T, you may want to make K a const&. 30 // Similarly, if T is large you might want it to be a pointer. 31 template <typename T, typename K, typename Traits = T> 32 class THashTable { 33 public: 34 THashTable() = default; 35 ~THashTable() = default; 36 THashTable(const THashTable & that)37 THashTable(const THashTable& that) { *this = that; } THashTable(THashTable && that)38 THashTable( THashTable&& that) { *this = std::move(that); } 39 40 THashTable& operator=(const THashTable& that) { 41 if (this != &that) { 42 fCount = that.fCount; 43 fCapacity = that.fCapacity; 44 fSlots.reset(new Slot[that.fCapacity]); 45 for (int i = 0; i < fCapacity; i++) { 46 fSlots[i] = that.fSlots[i]; 47 } 48 } 49 return *this; 50 } 51 52 THashTable& operator=(THashTable&& that) { 53 if (this != &that) { 54 fCount = that.fCount; 55 fCapacity = that.fCapacity; 56 fSlots = std::move(that.fSlots); 57 58 that.fCount = that.fCapacity = 0; 59 } 60 return *this; 61 } 62 63 // Clear the table. reset()64 void reset() { *this = THashTable(); } 65 66 // How many entries are in the table? count()67 int count() const { return fCount; } 68 69 // How many slots does the table contain? (Note that unlike an array, hash tables can grow 70 // before reaching 100% capacity.) capacity()71 int capacity() const { return fCapacity; } 72 73 // Approximately how many bytes of memory do we use beyond sizeof(*this)? approxBytesUsed()74 size_t approxBytesUsed() const { return fCapacity * sizeof(Slot); } 75 76 // Exchange two hash tables. swap(THashTable & that)77 void swap(THashTable& that) { 78 std::swap(fCount, that.fCount); 79 std::swap(fCapacity, that.fCapacity); 80 std::swap(fSlots, that.fSlots); 81 } 82 swap(THashTable && that)83 void swap(THashTable&& that) { 84 *this = std::move(that); 85 } 86 87 // !!!!!!!!!!!!!!!!! CAUTION !!!!!!!!!!!!!!!!! 88 // set(), find() and foreach() all allow mutable access to table entries. 89 // If you change an entry so that it no longer has the same key, all hell 90 // will break loose. Do not do that! 91 // 92 // Please prefer to use THashMap or THashSet, which do not have this danger. 93 94 // The pointers returned by set() and find() are valid only until the next call to set(). 95 // The pointers you receive in foreach() are only valid for its duration. 96 97 // Copy val into the hash table, returning a pointer to the copy now in the table. 98 // If there already is an entry in the table with the same key, we overwrite it. set(T val)99 T* set(T val) { 100 if (4 * fCount >= 3 * fCapacity) { 101 this->resize(fCapacity > 0 ? fCapacity * 2 : 4); 102 } 103 return this->uncheckedSet(std::move(val)); 104 } 105 106 // If there is an entry in the table with this key, return a pointer to it. If not, null. find(const K & key)107 T* find(const K& key) const { 108 uint32_t hash = Hash(key); 109 int index = hash & (fCapacity-1); 110 for (int n = 0; n < fCapacity; n++) { 111 Slot& s = fSlots[index]; 112 if (s.empty()) { 113 return nullptr; 114 } 115 if (hash == s.fHash && key == Traits::GetKey(*s)) { 116 return &*s; 117 } 118 index = this->next(index); 119 } 120 SkASSERT(fCapacity == fCount); 121 return nullptr; 122 } 123 124 // If there is an entry in the table with this key, return it. If not, null. 125 // This only works for pointer type T, and cannot be used to find an nullptr entry. findOrNull(const K & key)126 T findOrNull(const K& key) const { 127 if (T* p = this->find(key)) { 128 return *p; 129 } 130 return nullptr; 131 } 132 133 // If a value with this key exists in the hash table, removes it and returns true. 134 // Otherwise, returns false. removeIfExists(const K & key)135 bool removeIfExists(const K& key) { 136 uint32_t hash = Hash(key); 137 int index = hash & (fCapacity-1); 138 for (int n = 0; n < fCapacity; n++) { 139 Slot& s = fSlots[index]; 140 if (s.empty()) { 141 return false; 142 } 143 if (hash == s.fHash && key == Traits::GetKey(*s)) { 144 this->removeSlot(index); 145 if (4 * fCount <= fCapacity && fCapacity > 4) { 146 this->resize(fCapacity / 2); 147 } 148 return true; 149 } 150 index = this->next(index); 151 } 152 SkASSERT(fCapacity == fCount); 153 return false; 154 } 155 156 // Removes the value with this key from the hash table. Asserts if it is missing. remove(const K & key)157 void remove(const K& key) { 158 SkAssertResult(this->removeIfExists(key)); 159 } 160 161 // Hash tables will automatically resize themselves when set() and remove() are called, but 162 // resize() can be called to manually grow capacity before a bulk insertion. resize(int capacity)163 void resize(int capacity) { 164 SkASSERT(capacity >= fCount); 165 int oldCapacity = fCapacity; 166 SkDEBUGCODE(int oldCount = fCount); 167 168 fCount = 0; 169 fCapacity = capacity; 170 std::unique_ptr<Slot[]> oldSlots = std::move(fSlots); 171 fSlots.reset(new Slot[capacity]); 172 173 for (int i = 0; i < oldCapacity; i++) { 174 Slot& s = oldSlots[i]; 175 if (s.has_value()) { 176 this->uncheckedSet(*std::move(s)); 177 } 178 } 179 SkASSERT(fCount == oldCount); 180 } 181 182 // Call fn on every entry in the table. You may mutate the entries, but be very careful. 183 template <typename Fn> // f(T*) foreach(Fn && fn)184 void foreach(Fn&& fn) { 185 for (int i = 0; i < fCapacity; i++) { 186 if (fSlots[i].has_value()) { 187 fn(&*fSlots[i]); 188 } 189 } 190 } 191 192 // Call fn on every entry in the table. You may not mutate anything. 193 template <typename Fn> // f(T) or f(const T&) foreach(Fn && fn)194 void foreach(Fn&& fn) const { 195 for (int i = 0; i < fCapacity; i++) { 196 if (fSlots[i].has_value()) { 197 fn(*fSlots[i]); 198 } 199 } 200 } 201 202 // A basic iterator-like class which disallows mutation; sufficient for range-based for loops. 203 // Intended for use by THashMap and THashSet via begin() and end(). 204 // Adding or removing elements may invalidate all iterators. 205 template <typename SlotVal> 206 class Iter { 207 public: 208 using TTable = THashTable<T, K, Traits>; 209 Iter(const TTable * table,int slot)210 Iter(const TTable* table, int slot) : fTable(table), fSlot(slot) {} 211 MakeBegin(const TTable * table)212 static Iter MakeBegin(const TTable* table) { 213 return Iter{table, table->firstPopulatedSlot()}; 214 } 215 MakeEnd(const TTable * table)216 static Iter MakeEnd(const TTable* table) { 217 return Iter{table, table->capacity()}; 218 } 219 220 const SlotVal& operator*() const { 221 return *fTable->slot(fSlot); 222 } 223 224 const SlotVal* operator->() const { 225 return fTable->slot(fSlot); 226 } 227 228 bool operator==(const Iter& that) const { 229 // Iterators from different tables shouldn't be compared against each other. 230 SkASSERT(fTable == that.fTable); 231 return fSlot == that.fSlot; 232 } 233 234 bool operator!=(const Iter& that) const { 235 return !(*this == that); 236 } 237 238 Iter& operator++() { 239 fSlot = fTable->nextPopulatedSlot(fSlot); 240 return *this; 241 } 242 243 Iter operator++(int) { 244 Iter old = *this; 245 this->operator++(); 246 return old; 247 } 248 249 protected: 250 const TTable* fTable; 251 int fSlot; 252 }; 253 254 private: 255 // Finds the first non-empty slot for an iterator. firstPopulatedSlot()256 int firstPopulatedSlot() const { 257 for (int i = 0; i < fCapacity; i++) { 258 if (fSlots[i].has_value()) { 259 return i; 260 } 261 } 262 return fCapacity; 263 } 264 265 // Increments an iterator's slot. nextPopulatedSlot(int currentSlot)266 int nextPopulatedSlot(int currentSlot) const { 267 for (int i = currentSlot + 1; i < fCapacity; i++) { 268 if (fSlots[i].has_value()) { 269 return i; 270 } 271 } 272 return fCapacity; 273 } 274 275 // Reads from an iterator's slot. slot(int i)276 const T* slot(int i) const { 277 SkASSERT(fSlots[i].has_value()); 278 return &*fSlots[i]; 279 } 280 uncheckedSet(T && val)281 T* uncheckedSet(T&& val) { 282 const K& key = Traits::GetKey(val); 283 SkASSERT(key == key); 284 uint32_t hash = Hash(key); 285 int index = hash & (fCapacity-1); 286 for (int n = 0; n < fCapacity; n++) { 287 Slot& s = fSlots[index]; 288 if (s.empty()) { 289 // New entry. 290 s.emplace(std::move(val), hash); 291 fCount++; 292 return &*s; 293 } 294 if (hash == s.fHash && key == Traits::GetKey(*s)) { 295 // Overwrite previous entry. 296 // Note: this triggers extra copies when adding the same value repeatedly. 297 s.emplace(std::move(val), hash); 298 return &*s; 299 } 300 301 index = this->next(index); 302 } 303 SkASSERT(false); 304 return nullptr; 305 } 306 removeSlot(int index)307 void removeSlot(int index) { 308 fCount--; 309 310 // Rearrange elements to restore the invariants for linear probing. 311 for (;;) { 312 Slot& emptySlot = fSlots[index]; 313 int emptyIndex = index; 314 int originalIndex; 315 // Look for an element that can be moved into the empty slot. 316 // If the empty slot is in between where an element landed, and its native slot, then 317 // move it to the empty slot. Don't move it if its native slot is in between where 318 // the element landed and the empty slot. 319 // [native] <= [empty] < [candidate] == GOOD, can move candidate to empty slot 320 // [empty] < [native] < [candidate] == BAD, need to leave candidate where it is 321 do { 322 index = this->next(index); 323 Slot& s = fSlots[index]; 324 if (s.empty()) { 325 // We're done shuffling elements around. Clear the last empty slot. 326 emptySlot.reset(); 327 return; 328 } 329 originalIndex = s.fHash & (fCapacity - 1); 330 } while ((index <= originalIndex && originalIndex < emptyIndex) 331 || (originalIndex < emptyIndex && emptyIndex < index) 332 || (emptyIndex < index && index <= originalIndex)); 333 // Move the element to the empty slot. 334 Slot& moveFrom = fSlots[index]; 335 emptySlot = std::move(moveFrom); 336 } 337 } 338 next(int index)339 int next(int index) const { 340 index--; 341 if (index < 0) { index += fCapacity; } 342 return index; 343 } 344 Hash(const K & key)345 static uint32_t Hash(const K& key) { 346 uint32_t hash = Traits::Hash(key) & 0xffffffff; 347 return hash ? hash : 1; // We reserve hash 0 to mark empty. 348 } 349 350 class Slot { 351 public: 352 Slot() = default; ~Slot()353 ~Slot() { this->reset(); } 354 Slot(const Slot & that)355 Slot(const Slot& that) { *this = that; } 356 Slot& operator=(const Slot& that) { 357 if (this == &that) { 358 return *this; 359 } 360 if (fHash) { 361 if (that.fHash) { 362 fVal.fStorage = that.fVal.fStorage; 363 fHash = that.fHash; 364 } else { 365 this->reset(); 366 } 367 } else { 368 if (that.fHash) { 369 new (&fVal.fStorage) T(that.fVal.fStorage); 370 fHash = that.fHash; 371 } else { 372 // do nothing, no value on either side 373 } 374 } 375 return *this; 376 } 377 Slot(Slot && that)378 Slot(Slot&& that) { *this = std::move(that); } 379 Slot& operator=(Slot&& that) { 380 if (this == &that) { 381 return *this; 382 } 383 if (fHash) { 384 if (that.fHash) { 385 fVal.fStorage = std::move(that.fVal.fStorage); 386 fHash = that.fHash; 387 } else { 388 this->reset(); 389 } 390 } else { 391 if (that.fHash) { 392 new (&fVal.fStorage) T(std::move(that.fVal.fStorage)); 393 fHash = that.fHash; 394 } else { 395 // do nothing, no value on either side 396 } 397 } 398 return *this; 399 } 400 401 T& operator*() & { return fVal.fStorage; } 402 const T& operator*() const& { return fVal.fStorage; } 403 T&& operator*() && { return std::move(fVal.fStorage); } 404 const T&& operator*() const&& { return std::move(fVal.fStorage); } 405 emplace(T && v,uint32_t h)406 Slot& emplace(T&& v, uint32_t h) { 407 this->reset(); 408 new (&fVal.fStorage) T(std::move(v)); 409 fHash = h; 410 return *this; 411 } 412 has_value()413 bool has_value() const { return fHash != 0; } 414 explicit operator bool() const { return this->has_value(); } empty()415 bool empty() const { return !this->has_value(); } 416 reset()417 void reset() { 418 if (fHash) { 419 fVal.fStorage.~T(); 420 fHash = 0; 421 } 422 } 423 424 uint32_t fHash = 0; 425 426 private: 427 union Storage { 428 T fStorage; Storage()429 Storage() {} ~Storage()430 ~Storage() {} 431 } fVal; 432 }; 433 434 int fCount = 0, 435 fCapacity = 0; 436 std::unique_ptr<Slot[]> fSlots; 437 }; 438 439 // Maps K->V. A more user-friendly wrapper around THashTable, suitable for most use cases. 440 // K and V are treated as ordinary copyable C++ types, with no assumed relationship between the two. 441 template <typename K, typename V, typename HashK = SkGoodHash> 442 class THashMap { 443 public: 444 // Allow default construction and assignment. 445 THashMap() = default; 446 447 THashMap(THashMap<K, V, HashK>&& that) = default; 448 THashMap(const THashMap<K, V, HashK>& that) = default; 449 450 THashMap<K, V, HashK>& operator=(THashMap<K, V, HashK>&& that) = default; 451 THashMap<K, V, HashK>& operator=(const THashMap<K, V, HashK>& that) = default; 452 453 // Construct with an initializer list of key-value pairs. 454 struct Pair : public std::pair<K, V> { 455 using std::pair<K, V>::pair; GetKeyPair456 static const K& GetKey(const Pair& p) { return p.first; } HashPair457 static auto Hash(const K& key) { return HashK()(key); } 458 }; 459 THashMap(std::initializer_list<Pair> pairs)460 THashMap(std::initializer_list<Pair> pairs) { 461 fTable.resize(pairs.size() * 5 / 3); 462 for (const Pair& p : pairs) { 463 fTable.set(p); 464 } 465 } 466 467 // Clear the map. reset()468 void reset() { fTable.reset(); } 469 470 // How many key/value pairs are in the table? count()471 int count() const { return fTable.count(); } 472 473 // Is empty? empty()474 bool empty() const { return fTable.count() == 0; } 475 476 // Approximately how many bytes of memory do we use beyond sizeof(*this)? approxBytesUsed()477 size_t approxBytesUsed() const { return fTable.approxBytesUsed(); } 478 479 // Exchange two hash maps. swap(THashMap & that)480 void swap(THashMap& that) { fTable.swap(that.fTable); } swap(THashMap && that)481 void swap(THashMap&& that) { fTable.swap(std::move(that.fTable)); } 482 483 // N.B. The pointers returned by set() and find() are valid only until the next call to set(). 484 485 // Set key to val in the table, replacing any previous value with the same key. 486 // We copy both key and val, and return a pointer to the value copy now in the table. set(K key,V val)487 V* set(K key, V val) { 488 Pair* out = fTable.set({std::move(key), std::move(val)}); 489 return &out->second; 490 } 491 492 // If there is key/value entry in the table with this key, return a pointer to the value. 493 // If not, return null. find(const K & key)494 V* find(const K& key) const { 495 if (Pair* p = fTable.find(key)) { 496 return &p->second; 497 } 498 return nullptr; 499 } 500 501 V& operator[](const K& key) { 502 if (V* val = this->find(key)) { 503 return *val; 504 } 505 return *this->set(key, V{}); 506 } 507 508 // Removes the key/value entry in the table with this key. Asserts if the key is not present. remove(const K & key)509 void remove(const K& key) { 510 fTable.remove(key); 511 } 512 513 // If the key exists in the table, removes it and returns true. Otherwise, returns false. removeIfExists(const K & key)514 bool removeIfExists(const K& key) { 515 return fTable.removeIfExists(key); 516 } 517 518 // Call fn on every key/value pair in the table. You may mutate the value but not the key. 519 template <typename Fn, // f(K, V*) or f(const K&, V*) 520 std::enable_if_t<std::is_invocable_v<Fn, K, V*>>* = nullptr> foreach(Fn && fn)521 void foreach(Fn&& fn) { 522 fTable.foreach([&fn](Pair* p) { fn(p->first, &p->second); }); 523 } 524 525 // Call fn on every key/value pair in the table. You may not mutate anything. 526 template <typename Fn, // f(K, V), f(const K&, V), f(K, const V&) or f(const K&, const V&). 527 std::enable_if_t<std::is_invocable_v<Fn, K, V>>* = nullptr> foreach(Fn && fn)528 void foreach(Fn&& fn) const { 529 fTable.foreach([&fn](const Pair& p) { fn(p.first, p.second); }); 530 } 531 532 // Call fn on every key/value pair in the table. You may not mutate anything. 533 template <typename Fn, // f(Pair), or f(const Pair&) 534 std::enable_if_t<std::is_invocable_v<Fn, Pair>>* = nullptr> foreach(Fn && fn)535 void foreach(Fn&& fn) const { 536 fTable.foreach([&fn](const Pair& p) { fn(p); }); 537 } 538 539 // Dereferencing an iterator gives back a key-value pair, suitable for structured binding. 540 using Iter = typename THashTable<Pair, K>::template Iter<std::pair<K, V>>; 541 begin()542 Iter begin() const { 543 return Iter::MakeBegin(&fTable); 544 } 545 end()546 Iter end() const { 547 return Iter::MakeEnd(&fTable); 548 } 549 550 private: 551 THashTable<Pair, K> fTable; 552 }; 553 554 // A set of T. T is treated as an ordinary copyable C++ type. 555 template <typename T, typename HashT = SkGoodHash> 556 class THashSet { 557 public: 558 // Allow default construction and assignment. 559 THashSet() = default; 560 561 THashSet(THashSet<T, HashT>&& that) = default; 562 THashSet(const THashSet<T, HashT>& that) = default; 563 564 THashSet<T, HashT>& operator=(THashSet<T, HashT>&& that) = default; 565 THashSet<T, HashT>& operator=(const THashSet<T, HashT>& that) = default; 566 567 // Construct with an initializer list of Ts. THashSet(std::initializer_list<T> vals)568 THashSet(std::initializer_list<T> vals) { 569 fTable.resize(vals.size() * 5 / 3); 570 for (const T& val : vals) { 571 fTable.set(val); 572 } 573 } 574 575 // Clear the set. reset()576 void reset() { fTable.reset(); } 577 578 // How many items are in the set? count()579 int count() const { return fTable.count(); } 580 581 // Is empty? empty()582 bool empty() const { return fTable.count() == 0; } 583 584 // Approximately how many bytes of memory do we use beyond sizeof(*this)? approxBytesUsed()585 size_t approxBytesUsed() const { return fTable.approxBytesUsed(); } 586 587 // Exchange two hash sets. swap(THashSet & that)588 void swap(THashSet& that) { fTable.swap(that.fTable); } swap(THashSet && that)589 void swap(THashSet&& that) { fTable.swap(std::move(that.fTable)); } 590 591 // Copy an item into the set. add(T item)592 void add(T item) { fTable.set(std::move(item)); } 593 594 // Is this item in the set? contains(const T & item)595 bool contains(const T& item) const { return SkToBool(this->find(item)); } 596 597 // If an item equal to this is in the set, return a pointer to it, otherwise null. 598 // This pointer remains valid until the next call to add(). find(const T & item)599 const T* find(const T& item) const { return fTable.find(item); } 600 601 // Remove the item in the set equal to this. remove(const T & item)602 void remove(const T& item) { 603 SkASSERT(this->contains(item)); 604 fTable.remove(item); 605 } 606 607 // Call fn on every item in the set. You may not mutate anything. 608 template <typename Fn> // f(T), f(const T&) foreach(Fn && fn)609 void foreach (Fn&& fn) const { 610 fTable.foreach(fn); 611 } 612 613 private: 614 struct Traits { GetKeyTraits615 static const T& GetKey(const T& item) { return item; } HashTraits616 static auto Hash(const T& item) { return HashT()(item); } 617 }; 618 619 public: 620 using Iter = typename THashTable<T, T, Traits>::template Iter<T>; 621 begin()622 Iter begin() const { 623 return Iter::MakeBegin(&fTable); 624 } 625 end()626 Iter end() const { 627 return Iter::MakeEnd(&fTable); 628 } 629 630 private: 631 THashTable<T, T, Traits> fTable; 632 }; 633 634 } // namespace skia_private 635 636 #endif // SkTHash_DEFINED 637