Home | History | Annotate | Download | only in private
      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 "SkChecksum.h"
     12 #include "SkTypes.h"
     13 #include "SkTemplates.h"
     14 
     15 // Before trying to use SkTHashTable, look below to see if SkTHashMap or SkTHashSet works for you.
     16 // They're easier to use, usually perform the same, and have fewer sharp edges.
     17 
     18 // T and K are treated as ordinary copyable C++ types.
     19 // Traits must have:
     20 //   - static K GetKey(T)
     21 //   - static uint32_t Hash(K)
     22 // If the key is large and stored inside T, you may want to make K a const&.
     23 // Similarly, if T is large you might want it to be a pointer.
     24 template <typename T, typename K, typename Traits = T>
     25 class SkTHashTable : SkNoncopyable {
     26 public:
     27     SkTHashTable() : fCount(0), fCapacity(0) {}
     28 
     29     // Clear the table.
     30     void reset() {
     31         this->~SkTHashTable();
     32         new (this) SkTHashTable;
     33     }
     34 
     35     // How many entries are in the table?
     36     int count() const { return fCount; }
     37 
     38     // Approximately how many bytes of memory do we use beyond sizeof(*this)?
     39     size_t approxBytesUsed() const { return fCapacity * sizeof(Slot); }
     40 
     41     // !!!!!!!!!!!!!!!!!                 CAUTION                   !!!!!!!!!!!!!!!!!
     42     // set(), find() and foreach() all allow mutable access to table entries.
     43     // If you change an entry so that it no longer has the same key, all hell
     44     // will break loose.  Do not do that!
     45     //
     46     // Please prefer to use SkTHashMap or SkTHashSet, which do not have this danger.
     47 
     48     // The pointers returned by set() and find() are valid only until the next call to set().
     49     // The pointers you receive in foreach() are only valid for its duration.
     50 
     51     // Copy val into the hash table, returning a pointer to the copy now in the table.
     52     // If there already is an entry in the table with the same key, we overwrite it.
     53     T* set(T val) {
     54         if (4 * fCount >= 3 * fCapacity) {
     55             this->resize(fCapacity > 0 ? fCapacity * 2 : 4);
     56         }
     57         return this->uncheckedSet(std::move(val));
     58     }
     59 
     60     // If there is an entry in the table with this key, return a pointer to it.  If not, null.
     61     T* find(const K& key) const {
     62         uint32_t hash = Hash(key);
     63         int index = hash & (fCapacity-1);
     64         for (int n = 0; n < fCapacity; n++) {
     65             Slot& s = fSlots[index];
     66             if (s.empty()) {
     67                 return nullptr;
     68             }
     69             if (hash == s.hash && key == Traits::GetKey(s.val)) {
     70                 return &s.val;
     71             }
     72             index = this->next(index);
     73         }
     74         SkASSERT(fCapacity == 0);
     75         return nullptr;
     76     }
     77 
     78     // Remove the value with this key from the hash table.
     79     void remove(const K& key) {
     80         SkASSERT(this->find(key));
     81 
     82         uint32_t hash = Hash(key);
     83         int index = hash & (fCapacity-1);
     84         for (int n = 0; n < fCapacity; n++) {
     85             Slot& s = fSlots[index];
     86             SkASSERT(!s.empty());
     87             if (hash == s.hash && key == Traits::GetKey(s.val)) {
     88                 fCount--;
     89                 break;
     90             }
     91             index = this->next(index);
     92         }
     93 
     94         // Rearrange elements to restore the invariants for linear probing.
     95         for (;;) {
     96             Slot& emptySlot = fSlots[index];
     97             int emptyIndex = index;
     98             int originalIndex;
     99             // Look for an element that can be moved into the empty slot.
    100             // If the empty slot is in between where an element landed, and its native slot, then
    101             // move it to the empty slot. Don't move it if its native slot is in between where
    102             // the element landed and the empty slot.
    103             // [native] <= [empty] < [candidate] == GOOD, can move candidate to empty slot
    104             // [empty] < [native] < [candidate] == BAD, need to leave candidate where it is
    105             do {
    106                 index = this->next(index);
    107                 Slot& s = fSlots[index];
    108                 if (s.empty()) {
    109                     // We're done shuffling elements around.  Clear the last empty slot.
    110                     emptySlot = Slot();
    111                     return;
    112                 }
    113                 originalIndex = s.hash & (fCapacity - 1);
    114             } while ((index <= originalIndex && originalIndex < emptyIndex)
    115                      || (originalIndex < emptyIndex && emptyIndex < index)
    116                      || (emptyIndex < index && index <= originalIndex));
    117             // Move the element to the empty slot.
    118             Slot& moveFrom = fSlots[index];
    119             emptySlot = std::move(moveFrom);
    120         }
    121     }
    122 
    123     // Call fn on every entry in the table.  You may mutate the entries, but be very careful.
    124     template <typename Fn>  // f(T*)
    125     void foreach(Fn&& fn) {
    126         for (int i = 0; i < fCapacity; i++) {
    127             if (!fSlots[i].empty()) {
    128                 fn(&fSlots[i].val);
    129             }
    130         }
    131     }
    132 
    133     // Call fn on every entry in the table.  You may not mutate anything.
    134     template <typename Fn>  // f(T) or f(const T&)
    135     void foreach(Fn&& fn) const {
    136         for (int i = 0; i < fCapacity; i++) {
    137             if (!fSlots[i].empty()) {
    138                 fn(fSlots[i].val);
    139             }
    140         }
    141     }
    142 
    143 private:
    144     T* uncheckedSet(T&& val) {
    145         const K& key = Traits::GetKey(val);
    146         uint32_t hash = Hash(key);
    147         int index = hash & (fCapacity-1);
    148         for (int n = 0; n < fCapacity; n++) {
    149             Slot& s = fSlots[index];
    150             if (s.empty()) {
    151                 // New entry.
    152                 s.val  = std::move(val);
    153                 s.hash = hash;
    154                 fCount++;
    155                 return &s.val;
    156             }
    157             if (hash == s.hash && key == Traits::GetKey(s.val)) {
    158                 // Overwrite previous entry.
    159                 // Note: this triggers extra copies when adding the same value repeatedly.
    160                 s.val = std::move(val);
    161                 return &s.val;
    162             }
    163 
    164             index = this->next(index);
    165         }
    166         SkASSERT(false);
    167         return nullptr;
    168     }
    169 
    170     void resize(int capacity) {
    171         int oldCapacity = fCapacity;
    172         SkDEBUGCODE(int oldCount = fCount);
    173 
    174         fCount = 0;
    175         fCapacity = capacity;
    176         SkAutoTArray<Slot> oldSlots(capacity);
    177         oldSlots.swap(fSlots);
    178 
    179         for (int i = 0; i < oldCapacity; i++) {
    180             Slot& s = oldSlots[i];
    181             if (!s.empty()) {
    182                 this->uncheckedSet(std::move(s.val));
    183             }
    184         }
    185         SkASSERT(fCount == oldCount);
    186     }
    187 
    188     int next(int index) const {
    189         index--;
    190         if (index < 0) { index += fCapacity; }
    191         return index;
    192     }
    193 
    194     static uint32_t Hash(const K& key) {
    195         uint32_t hash = Traits::Hash(key);
    196         return hash ? hash : 1;  // We reserve hash 0 to mark empty.
    197     }
    198 
    199     struct Slot {
    200         Slot() : hash(0) {}
    201         Slot(T&& v, uint32_t h) : val(std::move(v)), hash(h) {}
    202         Slot(Slot&& o) { *this = std::move(o); }
    203         Slot& operator=(Slot&& o) {
    204             val  = std::move(o.val);
    205             hash = o.hash;
    206             return *this;
    207         }
    208 
    209         bool empty() const { return this->hash == 0; }
    210 
    211         T        val;
    212         uint32_t hash;
    213     };
    214 
    215     int fCount, fCapacity;
    216     SkAutoTArray<Slot> fSlots;
    217 };
    218 
    219 // Maps K->V.  A more user-friendly wrapper around SkTHashTable, suitable for most use cases.
    220 // K and V are treated as ordinary copyable C++ types, with no assumed relationship between the two.
    221 template <typename K, typename V, typename HashK = SkGoodHash>
    222 class SkTHashMap : SkNoncopyable {
    223 public:
    224     SkTHashMap() {}
    225 
    226     // Clear the map.
    227     void reset() { fTable.reset(); }
    228 
    229     // How many key/value pairs are in the table?
    230     int count() const { return fTable.count(); }
    231 
    232     // Approximately how many bytes of memory do we use beyond sizeof(*this)?
    233     size_t approxBytesUsed() const { return fTable.approxBytesUsed(); }
    234 
    235     // N.B. The pointers returned by set() and find() are valid only until the next call to set().
    236 
    237     // Set key to val in the table, replacing any previous value with the same key.
    238     // We copy both key and val, and return a pointer to the value copy now in the table.
    239     V* set(K key, V val) {
    240         Pair* out = fTable.set({std::move(key), std::move(val)});
    241         return &out->val;
    242     }
    243 
    244     // If there is key/value entry in the table with this key, return a pointer to the value.
    245     // If not, return null.
    246     V* find(const K& key) const {
    247         if (Pair* p = fTable.find(key)) {
    248             return &p->val;
    249         }
    250         return nullptr;
    251     }
    252 
    253     // Remove the key/value entry in the table with this key.
    254     void remove(const K& key) {
    255         SkASSERT(this->find(key));
    256         fTable.remove(key);
    257     }
    258 
    259     // Call fn on every key/value pair in the table.  You may mutate the value but not the key.
    260     template <typename Fn>  // f(K, V*) or f(const K&, V*)
    261     void foreach(Fn&& fn) {
    262         fTable.foreach([&fn](Pair* p){ fn(p->key, &p->val); });
    263     }
    264 
    265     // Call fn on every key/value pair in the table.  You may not mutate anything.
    266     template <typename Fn>  // f(K, V), f(const K&, V), f(K, const V&) or f(const K&, const V&).
    267     void foreach(Fn&& fn) const {
    268         fTable.foreach([&fn](const Pair& p){ fn(p.key, p.val); });
    269     }
    270 
    271 private:
    272     struct Pair {
    273         K key;
    274         V val;
    275         static const K& GetKey(const Pair& p) { return p.key; }
    276         static uint32_t Hash(const K& key) { return HashK()(key); }
    277     };
    278 
    279     SkTHashTable<Pair, K> fTable;
    280 };
    281 
    282 // A set of T.  T is treated as an ordinary copyable C++ type.
    283 template <typename T, typename HashT = SkGoodHash>
    284 class SkTHashSet : SkNoncopyable {
    285 public:
    286     SkTHashSet() {}
    287 
    288     // Clear the set.
    289     void reset() { fTable.reset(); }
    290 
    291     // How many items are in the set?
    292     int count() const { return fTable.count(); }
    293 
    294     // Approximately how many bytes of memory do we use beyond sizeof(*this)?
    295     size_t approxBytesUsed() const { return fTable.approxBytesUsed(); }
    296 
    297     // Copy an item into the set.
    298     void add(T item) { fTable.set(std::move(item)); }
    299 
    300     // Is this item in the set?
    301     bool contains(const T& item) const { return SkToBool(this->find(item)); }
    302 
    303     // If an item equal to this is in the set, return a pointer to it, otherwise null.
    304     // This pointer remains valid until the next call to add().
    305     const T* find(const T& item) const { return fTable.find(item); }
    306 
    307     // Remove the item in the set equal to this.
    308     void remove(const T& item) {
    309         SkASSERT(this->contains(item));
    310         fTable.remove(item);
    311     }
    312 
    313     // Call fn on every item in the set.  You may not mutate anything.
    314     template <typename Fn>  // f(T), f(const T&)
    315     void foreach (Fn&& fn) const {
    316         fTable.foreach(fn);
    317     }
    318 
    319 private:
    320     struct Traits {
    321         static const T& GetKey(const T& item) { return item; }
    322         static uint32_t Hash(const T& item) { return HashT()(item); }
    323     };
    324     SkTHashTable<T, T, Traits> fTable;
    325 };
    326 
    327 #endif//SkTHash_DEFINED
    328