LCOV - code coverage report
Current view: top level - gfx/skia/skia/include/private - SkTHash.h (source / functions) Hit Total Coverage
Test: output.info Lines: 55 143 38.5 %
Date: 2017-07-14 16:53:18 Functions: 13 379 3.4 %
Legend: Lines: hit not hit

          Line data    Source code
       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          63 : class SkTHashTable : SkNoncopyable {
      26             : public:
      27          65 :     SkTHashTable() : fCount(0), fCapacity(0) {}
      28             : 
      29             :     // Clear the table.
      30           0 :     void reset() {
      31           0 :         this->~SkTHashTable();
      32           0 :         new (this) SkTHashTable;
      33           0 :     }
      34             : 
      35             :     // How many entries are in the table?
      36           0 :     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          60 :     T* set(T val) {
      54          60 :         if (4 * fCount >= 3 * fCapacity) {
      55           9 :             this->resize(fCapacity > 0 ? fCapacity * 2 : 4);
      56             :         }
      57          60 :         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         448 :     T* find(const K& key) const {
      62         448 :         uint32_t hash = Hash(key);
      63         448 :         int index = hash & (fCapacity-1);
      64         875 :         for (int n = 0; n < fCapacity; n++) {
      65         873 :             Slot& s = fSlots[index];
      66         873 :             if (s.empty()) {
      67          58 :                 return nullptr;
      68             :             }
      69         815 :             if (hash == s.hash && key == Traits::GetKey(s.val)) {
      70         388 :                 return &s.val;
      71             :             }
      72         427 :             index = this->next(index);
      73             :         }
      74           2 :         SkASSERT(fCapacity == 0);
      75           2 :         return nullptr;
      76             :     }
      77             : 
      78             :     // Remove the value with this key from the hash table.
      79           0 :     void remove(const K& key) {
      80           0 :         SkASSERT(this->find(key));
      81             : 
      82           0 :         uint32_t hash = Hash(key);
      83           0 :         int index = hash & (fCapacity-1);
      84           0 :         for (int n = 0; n < fCapacity; n++) {
      85           0 :             Slot& s = fSlots[index];
      86           0 :             SkASSERT(!s.empty());
      87           0 :             if (hash == s.hash && key == Traits::GetKey(s.val)) {
      88           0 :                 fCount--;
      89           0 :                 break;
      90             :             }
      91           0 :             index = this->next(index);
      92             :         }
      93             : 
      94             :         // Rearrange elements to restore the invariants for linear probing.
      95           0 :         for (;;) {
      96           0 :             Slot& emptySlot = fSlots[index];
      97           0 :             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           0 :             do {
     106           0 :                 index = this->next(index);
     107           0 :                 Slot& s = fSlots[index];
     108           0 :                 if (s.empty()) {
     109             :                     // We're done shuffling elements around.  Clear the last empty slot.
     110           0 :                     emptySlot = Slot();
     111           0 :                     return;
     112             :                 }
     113           0 :                 originalIndex = s.hash & (fCapacity - 1);
     114           0 :             } while ((index <= originalIndex && originalIndex < emptyIndex)
     115           0 :                      || (originalIndex < emptyIndex && emptyIndex < index)
     116           0 :                      || (emptyIndex < index && index <= originalIndex));
     117             :             // Move the element to the empty slot.
     118           0 :             Slot& moveFrom = fSlots[index];
     119           0 :             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           0 :     void foreach(Fn&& fn) {
     126           0 :         for (int i = 0; i < fCapacity; i++) {
     127           0 :             if (!fSlots[i].empty()) {
     128           0 :                 fn(&fSlots[i].val);
     129             :             }
     130             :         }
     131           0 :     }
     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           0 :     void foreach(Fn&& fn) const {
     136           0 :         for (int i = 0; i < fCapacity; i++) {
     137           0 :             if (!fSlots[i].empty()) {
     138           0 :                 fn(fSlots[i].val);
     139             :             }
     140             :         }
     141           0 :     }
     142             : 
     143             : private:
     144         126 :     T* uncheckedSet(T&& val) {
     145         126 :         const K& key = Traits::GetKey(val);
     146         126 :         uint32_t hash = Hash(key);
     147         126 :         int index = hash & (fCapacity-1);
     148         227 :         for (int n = 0; n < fCapacity; n++) {
     149         227 :             Slot& s = fSlots[index];
     150         227 :             if (s.empty()) {
     151             :                 // New entry.
     152         126 :                 s.val  = std::move(val);
     153         126 :                 s.hash = hash;
     154         126 :                 fCount++;
     155         126 :                 return &s.val;
     156             :             }
     157         101 :             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           0 :                 s.val = std::move(val);
     161           0 :                 return &s.val;
     162             :             }
     163             : 
     164         101 :             index = this->next(index);
     165             :         }
     166           0 :         SkASSERT(false);
     167           0 :         return nullptr;
     168             :     }
     169             : 
     170           9 :     void resize(int capacity) {
     171           9 :         int oldCapacity = fCapacity;
     172           9 :         SkDEBUGCODE(int oldCount = fCount);
     173             : 
     174           9 :         fCount = 0;
     175           9 :         fCapacity = capacity;
     176          18 :         SkAutoTArray<Slot> oldSlots(capacity);
     177           9 :         oldSlots.swap(fSlots);
     178             : 
     179          97 :         for (int i = 0; i < oldCapacity; i++) {
     180          88 :             Slot& s = oldSlots[i];
     181          88 :             if (!s.empty()) {
     182          66 :                 this->uncheckedSet(std::move(s.val));
     183             :             }
     184             :         }
     185           9 :         SkASSERT(fCount == oldCount);
     186           9 :     }
     187             : 
     188         528 :     int next(int index) const {
     189         528 :         index--;
     190         528 :         if (index < 0) { index += fCapacity; }
     191         528 :         return index;
     192             :     }
     193             : 
     194         574 :     static uint32_t Hash(const K& key) {
     195         574 :         uint32_t hash = Traits::Hash(key);
     196         574 :         return hash ? hash : 1;  // We reserve hash 0 to mark empty.
     197             :     }
     198             : 
     199           0 :     struct Slot {
     200         184 :         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           0 :         Slot& operator=(Slot&& o) {
     204           0 :             val  = std::move(o.val);
     205           0 :             hash = o.hash;
     206           0 :             return *this;
     207             :         }
     208             : 
     209        1188 :         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          63 : class SkTHashMap : SkNoncopyable {
     223             : public:
     224          63 :     SkTHashMap() {}
     225             : 
     226             :     // Clear the map.
     227           0 :     void reset() { fTable.reset(); }
     228             : 
     229             :     // How many key/value pairs are in the table?
     230           0 :     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           0 :     V* set(K key, V val) {
     240           0 :         Pair* out = fTable.set({std::move(key), std::move(val)});
     241           0 :         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           0 :     V* find(const K& key) const {
     247           0 :         if (Pair* p = fTable.find(key)) {
     248           0 :             return &p->val;
     249             :         }
     250           0 :         return nullptr;
     251             :     }
     252             : 
     253             :     // Remove the key/value entry in the table with this key.
     254           0 :     void remove(const K& key) {
     255           0 :         SkASSERT(this->find(key));
     256           0 :         fTable.remove(key);
     257           0 :     }
     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           0 :     void foreach(Fn&& fn) {
     262           0 :         fTable.foreach([&fn](Pair* p){ fn(p->key, &p->val); });
     263           0 :     }
     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           0 :     void foreach(Fn&& fn) const {
     268           0 :         fTable.foreach([&fn](const Pair& p){ fn(p.key, p.val); });
     269           0 :     }
     270             : 
     271             : private:
     272           0 :     struct Pair {
     273             :         K key;
     274             :         V val;
     275           0 :         static const K& GetKey(const Pair& p) { return p.key; }
     276           0 :         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 ordiary copyable C++ type.
     283             : template <typename T, typename HashT = SkGoodHash>
     284           0 : class SkTHashSet : SkNoncopyable {
     285             : public:
     286           0 :     SkTHashSet() {}
     287             : 
     288             :     // Clear the set.
     289           0 :     void reset() { fTable.reset(); }
     290             : 
     291             :     // How many items are in the set?
     292           0 :     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           0 :     void add(T item) { fTable.set(std::move(item)); }
     299             : 
     300             :     // Is this item in the set?
     301           0 :     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           0 :     const T* find(const T& item) const { return fTable.find(item); }
     306             : 
     307             :     // Remove the item in the set equal to this.
     308           0 :     void remove(const T& item) {
     309           0 :         SkASSERT(this->contains(item));
     310           0 :         fTable.remove(item);
     311           0 :     }
     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           0 :     void foreach (Fn&& fn) const {
     316           0 :         fTable.foreach(fn);
     317           0 :     }
     318             : 
     319             : private:
     320             :     struct Traits {
     321           0 :         static const T& GetKey(const T& item) { return item; }
     322           0 :         static uint32_t Hash(const T& item) { return HashT()(item); }
     323             :     };
     324             :     SkTHashTable<T, T, Traits> fTable;
     325             : };
     326             : 
     327             : #endif//SkTHash_DEFINED

Generated by: LCOV version 1.13