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), fRemoved(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(const T& val) {
     54         if (4 * (fCount+fRemoved) >= 3 * fCapacity) {
     55             this->resize(fCapacity > 0 ? fCapacity * 2 : 4);
     56         }
     57         return this->uncheckedSet(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 NULL;
     68             }
     69             if (!s.removed() && hash == s.hash && key == Traits::GetKey(s.val)) {
     70                 return &s.val;
     71             }
     72             index = this->next(index, n);
     73         }
     74         SkASSERT(fCapacity == 0);
     75         return NULL;
     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 (!s.removed() && hash == s.hash && key == Traits::GetKey(s.val)) {
     88                 fRemoved++;
     89                 fCount--;
     90                 s.markRemoved();
     91                 return;
     92             }
     93             index = this->next(index, n);
     94         }
     95         SkASSERT(fCapacity == 0);
     96     }
     97 
     98     // Call fn on every entry in the table.  You may mutate the entries, but be very careful.
     99     template <typename Fn>  // f(T*)
    100     void foreach(Fn&& fn) {
    101         for (int i = 0; i < fCapacity; i++) {
    102             if (!fSlots[i].empty() && !fSlots[i].removed()) {
    103                 fn(&fSlots[i].val);
    104             }
    105         }
    106     }
    107 
    108     // Call fn on every entry in the table.  You may not mutate anything.
    109     template <typename Fn>  // f(T) or f(const T&)
    110     void foreach(Fn&& fn) const {
    111         for (int i = 0; i < fCapacity; i++) {
    112             if (!fSlots[i].empty() && !fSlots[i].removed()) {
    113                 fn(fSlots[i].val);
    114             }
    115         }
    116     }
    117 
    118 private:
    119     T* uncheckedSet(const T& val) {
    120         const K& key = Traits::GetKey(val);
    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             if (s.empty() || s.removed()) {
    126                 // New entry.
    127                 if (s.removed()) {
    128                     fRemoved--;
    129                 }
    130                 s.val  = val;
    131                 s.hash = hash;
    132                 fCount++;
    133                 return &s.val;
    134             }
    135             if (hash == s.hash && key == Traits::GetKey(s.val)) {
    136                 // Overwrite previous entry.
    137                 // Note: this triggers extra copies when adding the same value repeatedly.
    138                 s.val = val;
    139                 return &s.val;
    140             }
    141             index = this->next(index, n);
    142         }
    143         SkASSERT(false);
    144         return NULL;
    145     }
    146 
    147     void resize(int capacity) {
    148         int oldCapacity = fCapacity;
    149         SkDEBUGCODE(int oldCount = fCount);
    150 
    151         fCount = fRemoved = 0;
    152         fCapacity = capacity;
    153         SkAutoTArray<Slot> oldSlots(capacity);
    154         oldSlots.swap(fSlots);
    155 
    156         for (int i = 0; i < oldCapacity; i++) {
    157             const Slot& s = oldSlots[i];
    158             if (!s.empty() && !s.removed()) {
    159                 this->uncheckedSet(s.val);
    160             }
    161         }
    162         SkASSERT(fCount == oldCount);
    163     }
    164 
    165     int next(int index, int n) const {
    166         // A valid strategy explores all slots in [0, fCapacity) as n walks from 0 to fCapacity-1.
    167         // Both of these strategies are valid:
    168         //return (index + 0 + 1) & (fCapacity-1);      // Linear probing.
    169         return (index + n + 1) & (fCapacity-1);        // Quadratic probing.
    170     }
    171 
    172     static uint32_t Hash(const K& key) {
    173         uint32_t hash = Traits::Hash(key);
    174         return hash < 2 ? hash+2 : hash;  // We reserve hash 0 and 1 to mark empty or removed slots.
    175     }
    176 
    177     struct Slot {
    178         Slot() : hash(0) {}
    179         bool   empty() const { return this->hash == 0; }
    180         bool removed() const { return this->hash == 1; }
    181 
    182         void markRemoved() { this->hash = 1; }
    183 
    184         T val;
    185         uint32_t hash;
    186     };
    187 
    188     int fCount, fRemoved, fCapacity;
    189     SkAutoTArray<Slot> fSlots;
    190 };
    191 
    192 // Maps K->V.  A more user-friendly wrapper around SkTHashTable, suitable for most use cases.
    193 // K and V are treated as ordinary copyable C++ types, with no assumed relationship between the two.
    194 template <typename K, typename V, typename HashK = SkGoodHash>
    195 class SkTHashMap : SkNoncopyable {
    196 public:
    197     SkTHashMap() {}
    198 
    199     // Clear the map.
    200     void reset() { fTable.reset(); }
    201 
    202     // How many key/value pairs are in the table?
    203     int count() const { return fTable.count(); }
    204 
    205     // Approximately how many bytes of memory do we use beyond sizeof(*this)?
    206     size_t approxBytesUsed() const { return fTable.approxBytesUsed(); }
    207 
    208     // N.B. The pointers returned by set() and find() are valid only until the next call to set().
    209 
    210     // Set key to val in the table, replacing any previous value with the same key.
    211     // We copy both key and val, and return a pointer to the value copy now in the table.
    212     V* set(const K& key, const V& val) {
    213         Pair in = { key, val };
    214         Pair* out = fTable.set(in);
    215         return &out->val;
    216     }
    217 
    218     // If there is key/value entry in the table with this key, return a pointer to the value.
    219     // If not, return NULL.
    220     V* find(const K& key) const {
    221         if (Pair* p = fTable.find(key)) {
    222             return &p->val;
    223         }
    224         return NULL;
    225     }
    226 
    227     // Remove the key/value entry in the table with this key.
    228     void remove(const K& key) {
    229         SkASSERT(this->find(key));
    230         fTable.remove(key);
    231     }
    232 
    233     // Call fn on every key/value pair in the table.  You may mutate the value but not the key.
    234     template <typename Fn>  // f(K, V*) or f(const K&, V*)
    235     void foreach(Fn&& fn) {
    236         fTable.foreach([&fn](Pair* p){ fn(p->key, &p->val); });
    237     }
    238 
    239     // Call fn on every key/value pair in the table.  You may not mutate anything.
    240     template <typename Fn>  // f(K, V), f(const K&, V), f(K, const V&) or f(const K&, const V&).
    241     void foreach(Fn&& fn) const {
    242         fTable.foreach([&fn](const Pair& p){ fn(p.key, p.val); });
    243     }
    244 
    245 private:
    246     struct Pair {
    247         K key;
    248         V val;
    249         static const K& GetKey(const Pair& p) { return p.key; }
    250         static uint32_t Hash(const K& key) { return HashK()(key); }
    251     };
    252 
    253     SkTHashTable<Pair, K> fTable;
    254 };
    255 
    256 // A set of T.  T is treated as an ordiary copyable C++ type.
    257 template <typename T, typename HashT = SkGoodHash>
    258 class SkTHashSet : SkNoncopyable {
    259 public:
    260     SkTHashSet() {}
    261 
    262     // Clear the set.
    263     void reset() { fTable.reset(); }
    264 
    265     // How many items are in the set?
    266     int count() const { return fTable.count(); }
    267 
    268     // Approximately how many bytes of memory do we use beyond sizeof(*this)?
    269     size_t approxBytesUsed() const { return fTable.approxBytesUsed(); }
    270 
    271     // Copy an item into the set.
    272     void add(const T& item) { fTable.set(item); }
    273 
    274     // Is this item in the set?
    275     bool contains(const T& item) const { return SkToBool(this->find(item)); }
    276 
    277     // If an item equal to this is in the set, return a pointer to it, otherwise null.
    278     // This pointer remains valid until the next call to add().
    279     const T* find(const T& item) const { return fTable.find(item); }
    280 
    281     // Remove the item in the set equal to this.
    282     void remove(const T& item) {
    283         SkASSERT(this->contains(item));
    284         fTable.remove(item);
    285     }
    286 
    287     // Call fn on every item in the set.  You may not mutate anything.
    288     template <typename Fn>  // f(T), f(const T&)
    289     void foreach (Fn&& fn) const {
    290         fTable.foreach(fn);
    291     }
    292 
    293 private:
    294     struct Traits {
    295         static const T& GetKey(const T& item) { return item; }
    296         static uint32_t Hash(const T& item) { return HashT()(item); }
    297     };
    298     SkTHashTable<T, T, Traits> fTable;
    299 };
    300 
    301 #endif//SkTHash_DEFINED
    302