gfx/skia/trunk/src/core/SkTDynamicHash.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial