| Index: src/core/SkTDynamicHash.h
 | 
| diff --git a/src/core/SkTDynamicHash.h b/src/core/SkTDynamicHash.h
 | 
| index 665028abaa398cd25700237316122d47ca6b91e7..b4787fb89e361ec793ab13cf25181795e5978789 100644
 | 
| --- a/src/core/SkTDynamicHash.h
 | 
| +++ b/src/core/SkTDynamicHash.h
 | 
| @@ -14,19 +14,22 @@ template <typename T, typename KEY, const KEY& (KEY_FROM_T)(const T&),
 | 
|  uint32_t (HASH_FROM_KEY)(const Key&), bool (EQ_T_KEY)(const T&, const Key&)>
 | 
|  class SkTDynamicHash {
 | 
|  private:
 | 
| -    T* fStorage[1];
 | 
| +    T* fStorage[4];
 | 
|      T** fArray;
 | 
|      int fCapacity;
 | 
|      int fCountUsed;
 | 
|      int fCountDeleted;
 | 
|      
 | 
| -    unsigned hashMask() const { return fCapacity - 1; }
 | 
|      const T* deletedValue() const { return reinterpret_cast<const T*>(-1); }
 | 
| -    
 | 
| +    uint32_t hashMask() const { return fCapacity - 1; }
 | 
| +    int hashToIndex(uint32_t hash) const {
 | 
| +        return ((hash >> 16) ^ hash) & (fCapacity - 1);
 | 
| +    }
 | 
|  public:
 | 
|      SkTDynamicHash() {
 | 
| +        sk_bzero(fStorage, sizeof(fStorage));
 | 
|          fArray = fStorage;
 | 
| -        fCapacity = 1;
 | 
| +        fCapacity = SK_ARRAY_COUNT(fStorage);
 | 
|          fCountUsed = fCountDeleted = 0;
 | 
|      }
 | 
|      ~SkTDynamicHash() {
 | 
| @@ -38,8 +41,7 @@ public:
 | 
|      T* find(const KEY& key) {
 | 
|          const T* const deleted = this->deletedValue();
 | 
|          const unsigned mask = this->hashMask();
 | 
| -        int index = HASH_FROM_KEY(key) & mask;
 | 
| -        const int origIndex = index;
 | 
| +        int index = this->hashToIndex(HASH_FROM_KEY(key));
 | 
|          int delta = 1;
 | 
|          
 | 
|          do {
 | 
| @@ -52,7 +54,7 @@ public:
 | 
|              }
 | 
|              index = (index + delta) & mask;
 | 
|              delta <<= 1;
 | 
| -        } while (index != origIndex);
 | 
| +        } while (delta <= fCapacity);
 | 
|          return NULL;
 | 
|      }
 | 
|      
 | 
| @@ -60,8 +62,7 @@ public:
 | 
|          const T* const deleted = this->deletedValue();
 | 
|          for (;;) {
 | 
|              const unsigned mask = this->hashMask();
 | 
| -            int index = HASH_FROM_KEY(key) & mask;
 | 
| -            const int origIndex = index;
 | 
| +            int index = this->hashToIndex(HASH_FROM_KEY(key));
 | 
|              int delta = 1;
 | 
|              
 | 
|              do {
 | 
| @@ -77,7 +78,7 @@ public:
 | 
|                  }
 | 
|                  index = (index + delta) & mask;
 | 
|                  delta <<= 1;
 | 
| -            } while (index != origIndex);
 | 
| +            } while (delta <= fCapacity);
 | 
|              if (autoGrow) {
 | 
|                  this->grow();
 | 
|              } else {
 | 
| @@ -91,9 +92,7 @@ public:
 | 
|      void remove(const KEY& key) {
 | 
|          const T* const deleted = this->deletedValue();
 | 
|          const unsigned mask = this->hashMask();
 | 
| -        const uint32_t hash = HASH_FROM_KEY(key);
 | 
| -        int index = hash & mask;
 | 
| -        SkDEBUGCODE(const int origIndex = index;)
 | 
| +        int index = this->hashToIndex(HASH_FROM_KEY(key));
 | 
|          int delta = 1;
 | 
|          
 | 
|          for (;;) {
 | 
| @@ -101,20 +100,51 @@ public:
 | 
|              SkASSERT(candidate);
 | 
|              if (deleted != candidate && EQ_T_KEY(*candidate, key)) {
 | 
|                  fArray[index] = const_cast<T*>(deleted);
 | 
| +                fCountUsed -= 1;
 | 
|                  fCountDeleted += 1;
 | 
|                  break;
 | 
|              }
 | 
|              index = (index + delta) & mask;
 | 
|              delta <<= 1;
 | 
| -            SkASSERT(index != origIndex);
 | 
| +            SkASSERT(delta <= fCapacity);
 | 
|          }
 | 
|          this->checkStrink();
 | 
|      }
 | 
|      
 | 
|  private:
 | 
| +    int countCollisions(const Key& key) const {
 | 
| +        const T* const deleted = this->deletedValue();
 | 
| +        const unsigned mask = this->hashMask();
 | 
| +        int index = this->hashToIndex(HASH_FROM_KEY(key));
 | 
| +        int delta = 1;
 | 
| +        int collisionCount = 0;
 | 
| +
 | 
| +        for (;;) {
 | 
| +            const T* candidate = fArray[index];
 | 
| +            SkASSERT(candidate);
 | 
| +            if (deleted != candidate && EQ_T_KEY(*candidate, key)) {
 | 
| +                break;
 | 
| +            }
 | 
| +            index = (index + delta) & mask;
 | 
| +            delta <<= 1;
 | 
| +            collisionCount += 1;
 | 
| +            SkASSERT(delta <= fCapacity);
 | 
| +        }
 | 
| +        return collisionCount;
 | 
| +    }
 | 
| +
 | 
|      void grow() {
 | 
|          const T* const deleted = this->deletedValue();
 | 
| -        
 | 
| +#if 0
 | 
| +        SkDebugf("growing from %d: used=%d\n", fCapacity, fCountUsed);
 | 
| +        for (int i = 0; i < fCapacity; ++i) {
 | 
| +            T* elem = fArray[i];
 | 
| +            if (NULL == elem || deleted == elem) {
 | 
| +                continue;
 | 
| +            }
 | 
| +            SkDebugf("    entry[%d] had %d collisions\n", i, countCollisions(KEY_FROM_T(*elem)));
 | 
| +        }
 | 
| +#endif
 | 
|          int oldCapacity = fCapacity;
 | 
|          T** oldArray = fArray;
 | 
|          
 | 
| @@ -137,10 +167,16 @@ private:
 | 
|              SkASSERT(success);
 | 
|          }
 | 
|          SkASSERT(oldCountUsed == fCountUsed);
 | 
| -        sk_free(oldArray);
 | 
| +
 | 
| +        if (oldArray != fStorage) {
 | 
| +            sk_free(oldArray);
 | 
| +        }
 | 
|      }
 | 
|      
 | 
| -    void checkStrink() {}
 | 
| +    void checkStrink() {
 | 
| +        // todo: based on density and deadspace (fCountDeleted), consider
 | 
| +        // shrinking fArray and repopulating it.
 | 
| +    }
 | 
|  };
 | 
|  
 | 
|  #endif
 | 
| 
 |