Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(152)

Side by Side Diff: src/core/SkTDynamicHash.h

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

Powered by Google App Engine
This is Rietveld 408576698