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