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