• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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