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

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial