Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2013 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 SkTDynamicHash_DEFINED
      9 #define SkTDynamicHash_DEFINED
     10 
     11 #include "SkTypes.h"
     12 #include "SkMath.h"
     13 
     14 template <typename T,
     15           typename Key,
     16           const Key& (GetKey)(const T&),
     17           uint32_t (Hash)(const Key&),
     18           bool (Equal)(const T&, const Key&),
     19           int kGrowPercent   = 75,  // Larger -> more memory efficient, but slower.
     20           int kShrinkPercent = 25>
     21 class SkTDynamicHash {
     22     static const int kMinCapacity = 4;  // Smallest capacity we allow.
     23 public:
     24     SkTDynamicHash(int initialCapacity=64/sizeof(T*)) {
     25         this->reset(SkNextPow2(initialCapacity > kMinCapacity ? initialCapacity : kMinCapacity));
     26         SkASSERT(this->validate());
     27     }
     28 
     29     ~SkTDynamicHash() {
     30         sk_free(fArray);
     31     }
     32 
     33     int count() const { return fCount; }
     34 
     35     // Return the entry with this key if we have it, otherwise NULL.
     36     T* find(const Key& key) const {
     37         int index = this->firstIndex(key);
     38         for (int round = 0; round < fCapacity; round++) {
     39             T* candidate = fArray[index];
     40             if (Empty() == candidate) {
     41                 return NULL;
     42             }
     43             if (Deleted() != candidate && Equal(*candidate, key)) {
     44                 return candidate;
     45             }
     46             index = this->nextIndex(index, round);
     47         }
     48         SkASSERT(0); //  find: should be unreachable
     49         return NULL;
     50     }
     51 
     52     // Add an entry with this key.  We require that no entry with newEntry's key is already present.
     53     void add(T* newEntry) {
     54         SkASSERT(NULL == this->find(GetKey(*newEntry)));
     55         this->maybeGrow();
     56         SkASSERT(this->validate());
     57         this->innerAdd(newEntry);
     58         SkASSERT(this->validate());
     59     }
     60 
     61     // Remove the entry with this key.  We reqire that an entry with this key is present.
     62     void remove(const Key& key) {
     63         SkASSERT(NULL != this->find(key));
     64         this->innerRemove(key);
     65         SkASSERT(this->validate());
     66         this->maybeShrink();
     67         SkASSERT(this->validate());
     68     }
     69 
     70 protected:
     71     // These methods are used by tests only.
     72 
     73     int capacity() const { return fCapacity; }
     74 
     75     // How many collisions do we go through before finding where this entry should be inserted?
     76     int countCollisions(const Key& key) const {
     77         int index = this->firstIndex(key);
     78         for (int round = 0; round < fCapacity; round++) {
     79             const T* candidate = fArray[index];
     80             if (Empty() == candidate || Deleted() == candidate || Equal(*candidate, key)) {
     81                 return round;
     82             }
     83             index = this->nextIndex(index, round);
     84         }
     85         SkASSERT(0); // countCollisions: should be unreachable
     86         return -1;
     87     }
     88 
     89 private:
     90     // We have two special values to indicate an empty or deleted entry.
     91     static T* Empty()   { return reinterpret_cast<T*>(0); }  // i.e. NULL
     92     static T* Deleted() { return reinterpret_cast<T*>(1); }  // Also an invalid pointer.
     93 
     94     static T** AllocArray(int capacity) {
     95         return (T**)sk_calloc_throw(sizeof(T*) * capacity);  // All cells == Empty().
     96     }
     97 
     98     void reset(int capacity) {
     99         fCount = 0;
    100         fDeleted = 0;
    101         fCapacity = capacity;
    102         fArray = AllocArray(fCapacity);
    103     }
    104 
    105     bool validate() const {
    106         #define SKTDYNAMICHASH_CHECK(x) SkASSERT((x)); if (!(x)) return false
    107 
    108         // Is capacity sane?
    109         SKTDYNAMICHASH_CHECK(SkIsPow2(fCapacity));
    110         SKTDYNAMICHASH_CHECK(fCapacity >= kMinCapacity);
    111 
    112         // Is fCount correct?
    113         int count = 0;
    114         for (int i = 0; i < fCapacity; i++) {
    115             if (Empty() != fArray[i] && Deleted() != fArray[i]) {
    116                 count++;
    117             }
    118         }
    119         SKTDYNAMICHASH_CHECK(count == fCount);
    120 
    121         // Is fDeleted correct?
    122         int deleted = 0;
    123         for (int i = 0; i < fCapacity; i++) {
    124             if (Deleted() == fArray[i]) {
    125                 deleted++;
    126             }
    127         }
    128         SKTDYNAMICHASH_CHECK(deleted == fDeleted);
    129 
    130         // Are all entries findable?
    131         for (int i = 0; i < fCapacity; i++) {
    132             if (Empty() == fArray[i] || Deleted() == fArray[i]) {
    133                 continue;
    134             }
    135             SKTDYNAMICHASH_CHECK(NULL != this->find(GetKey(*fArray[i])));
    136         }
    137 
    138         // Are all entries unique?
    139         for (int i = 0; i < fCapacity; i++) {
    140             if (Empty() == fArray[i] || Deleted() == fArray[i]) {
    141                 continue;
    142             }
    143             for (int j = i+1; j < fCapacity; j++) {
    144                 if (Empty() == fArray[j] || Deleted() == fArray[j]) {
    145                     continue;
    146                 }
    147                 SKTDYNAMICHASH_CHECK(fArray[i] != fArray[j]);
    148                 SKTDYNAMICHASH_CHECK(!Equal(*fArray[i], GetKey(*fArray[j])));
    149                 SKTDYNAMICHASH_CHECK(!Equal(*fArray[j], GetKey(*fArray[i])));
    150             }
    151         }
    152         #undef SKTDYNAMICHASH_CHECK
    153         return true;
    154     }
    155 
    156     void innerAdd(T* newEntry) {
    157         const Key& key = GetKey(*newEntry);
    158         int index = this->firstIndex(key);
    159         for (int round = 0; round < fCapacity; round++) {
    160             const T* candidate = fArray[index];
    161             if (Empty() == candidate || Deleted() == candidate) {
    162                 if (Deleted() == candidate) {
    163                     fDeleted--;
    164                 }
    165                 fCount++;
    166                 fArray[index] = newEntry;
    167                 return;
    168             }
    169             index = this->nextIndex(index, round);
    170         }
    171         SkASSERT(0); // add: should be unreachable
    172     }
    173 
    174     void innerRemove(const Key& key) {
    175         const int firstIndex = this->firstIndex(key);
    176         int index = firstIndex;
    177         for (int round = 0; round < fCapacity; round++) {
    178             const T* candidate = fArray[index];
    179             if (Deleted() != candidate && Equal(*candidate, key)) {
    180                 fDeleted++;
    181                 fCount--;
    182                 fArray[index] = Deleted();
    183                 return;
    184             }
    185             index = this->nextIndex(index, round);
    186         }
    187         SkASSERT(0); // innerRemove: should be unreachable
    188     }
    189 
    190     void maybeGrow() {
    191         if (fCount + fDeleted + 1 > (fCapacity * kGrowPercent) / 100) {
    192             resize(fCapacity * 2);
    193         }
    194     }
    195 
    196     void maybeShrink() {
    197         if (fCount < (fCapacity * kShrinkPercent) / 100 && fCapacity / 2 > kMinCapacity) {
    198             resize(fCapacity / 2);
    199         }
    200     }
    201 
    202     void resize(int newCapacity) {
    203         SkDEBUGCODE(int oldCount = fCount;)
    204         int oldCapacity = fCapacity;
    205         T** oldArray = fArray;
    206 
    207         reset(newCapacity);
    208 
    209         for (int i = 0; i < oldCapacity; i++) {
    210             T* entry = oldArray[i];
    211             if (Empty() != entry && Deleted() != entry) {
    212                 this->add(entry);
    213             }
    214         }
    215         SkASSERT(oldCount == fCount);
    216 
    217         sk_free(oldArray);
    218     }
    219 
    220     // fCapacity is always a power of 2, so this masks the correct low bits to index into our hash.
    221     uint32_t hashMask() const { return fCapacity - 1; }
    222 
    223     int firstIndex(const Key& key) const {
    224         return Hash(key) & this->hashMask();
    225     }
    226 
    227     // Given index at round N, what is the index to check at N+1?  round should start at 0.
    228     int nextIndex(int index, int round) const {
    229         // This will search a power-of-two array fully without repeating an index.
    230         return (index + round + 1) & this->hashMask();
    231     }
    232 
    233     int fCount;     // Number of non Empty(), non Deleted() entries in fArray.
    234     int fDeleted;   // Number of Deleted() entries in fArray.
    235     int fCapacity;  // Number of entries in fArray.  Always a power of 2.
    236     T** fArray;
    237 };
    238 
    239 #endif
    240