OLD | NEW |
---|---|
(Empty) | |
1 #ifndef SkTHash_DEFINED | |
2 #define SkTHash_DEFINED | |
3 | |
4 #include "SkTypes.h" | |
5 #include "SkTemplates.h" | |
6 | |
7 // Before trying to use SkTHashTable, look below to see if SkTHashMap or SkTHash Set works for you. | |
8 // They're easier to use, usually perform the same, and have fewer sharp edges. | |
9 | |
10 // T and K are treated as ordinary copyable C++ types. | |
reed1
2015/02/12 20:42:56
btw -- this form of comment will not get scooped b
| |
11 // Traits must have: | |
12 // - static K GetKey(T) | |
13 // - static uint32_t Hash(K) | |
14 // If the key is large and stored inside T, you may want to make K a const&. | |
15 // Similarly, if T is large you might want it to be a pointer. | |
16 template <typename T, typename K, typename Traits = T> | |
17 class SkTHashTable : SkNoncopyable { | |
18 public: | |
19 SkTHashTable() : fCount(0), fCapacity(0) {} | |
20 | |
21 // How many entries are in the table? | |
22 int count() const { return fCount; } | |
23 | |
24 // !!!!!!!!!!!!!!!!! CAUTION !!!!!!!!!!!!! !!!! | |
25 // set(), find() and foreach() all allow mutable access to table entries. | |
26 // If you change an entry so that it no longer has the same key, all hell | |
27 // will break loose. Do not do that! | |
28 // | |
29 // Please prefer to use SkTHashMap or SkTHashSet, which do not have this dan ger. | |
30 | |
31 // The pointers returned by set() and find() are valid only until the next c all to set(). | |
32 // The pointers you receive in foreach() are only valid for its duration. | |
33 | |
34 // Copy val into the hash table, returning a pointer to the copy now in the table. | |
35 // If there already is an entry in the table with the same key, we overwrite it. | |
36 T* set(T val) { | |
37 if (4 * fCount >= 3 * fCapacity) { | |
38 this->resize(fCapacity > 0 ? fCapacity * 2 : 4); | |
39 } | |
40 return this->uncheckedSet(val); | |
41 } | |
42 | |
43 // If there is an entry in the table with this key, return a pointer to it. If not, NULL. | |
44 T* find(K key) const { | |
45 uint32_t hash = Hash(key); | |
46 int index = hash & (fCapacity-1); | |
47 for (int n = 0; n < fCapacity; n++) { | |
48 Slot& s = fSlots[index]; | |
49 if (s.empty()) { | |
50 return NULL; | |
51 } | |
52 if (hash == s.hash && key == Traits::GetKey(s.val)) { | |
53 return &s.val; | |
54 } | |
55 index = this->next(index, n); | |
56 } | |
57 SkASSERT(fCapacity == 0); | |
58 return NULL; | |
59 } | |
60 | |
61 // Call fn on every entry in the table. You may mutate the entries, but be very careful. | |
62 template <typename Arg> | |
63 void foreach(void(*fn)(T*, Arg), Arg arg) { | |
64 for (int i = 0; i < fCapacity; i++) { | |
65 Slot& s = fSlots[i]; | |
66 if (!s.empty()) { | |
67 fn(&s.val, arg); | |
68 } | |
69 } | |
70 } | |
71 | |
72 private: | |
73 T* uncheckedSet(T val) { | |
74 K key = Traits::GetKey(val); | |
75 uint32_t hash = Hash(key); | |
76 int index = hash & (fCapacity-1); | |
77 for (int n = 0; n < fCapacity; n++) { | |
78 Slot& s = fSlots[index]; | |
79 if (s.empty()) { | |
80 // New entry. | |
81 s.val = val; | |
82 s.hash = hash; | |
83 fCount++; | |
84 return &s.val; | |
85 } | |
86 if (hash == s.hash && key == Traits::GetKey(s.val)) { | |
87 // Overwrite previous entry. | |
88 s.val = val; | |
89 return &s.val; | |
90 } | |
91 index = this->next(index, n); | |
92 } | |
93 SkASSERT(false); | |
94 return NULL; | |
95 } | |
96 | |
97 void resize(int capacity) { | |
98 int oldCapacity = fCapacity; | |
99 SkDEBUGCODE(int oldCount = fCount); | |
100 | |
101 fCount = 0; | |
102 fCapacity = capacity; | |
103 SkAutoTArray<Slot> oldSlots(capacity); | |
104 oldSlots.swap(fSlots); | |
105 | |
106 for (int i = 0; i < oldCapacity; i++) { | |
107 const Slot& s = oldSlots[i]; | |
108 if (!s.empty()) { | |
109 this->uncheckedSet(s.val); | |
110 } | |
111 } | |
112 SkASSERT(fCount == oldCount); | |
113 } | |
114 | |
115 int next(int index, int n) const { | |
116 // A valid strategy explores all slots in [0, fCapacity) as n walks from 0 to fCapacity-1. | |
117 // Both of these strategies are valid: | |
118 //return (index + 0 + 1) & (fCapacity-1); // Linear probing. | |
119 return (index + n + 1) & (fCapacity-1); // Quadratic probing. | |
120 } | |
121 | |
122 static uint32_t Hash(K key) { | |
123 uint32_t hash = Traits::Hash(key); | |
124 return hash == 0 ? 1 : hash; // We reserve hash == 0 to mark empty slot s. | |
125 } | |
126 | |
127 struct Slot { | |
128 Slot() : hash(0) {} | |
129 bool empty() const { return hash == 0; } | |
130 | |
131 T val; | |
132 uint32_t hash; | |
133 }; | |
134 | |
135 int fCount, fCapacity; | |
136 SkAutoTArray<Slot> fSlots; | |
137 }; | |
138 | |
139 // Maps K->V. A more user-friendly wrapper around SkTHashTable, suitable for mo st use cases. | |
140 // K and V are treated as ordinary copyable C++ types, with no assumed relations hip between the two. | |
141 template <typename K, typename V, uint32_t(*HashK)(K)> | |
142 class SkTHashMap : SkNoncopyable { | |
143 public: | |
144 SkTHashMap() {} | |
145 | |
146 // How many key/value pairs are in the table? | |
147 int count() const { return fTable.count(); } | |
148 | |
149 // N.B. The pointers returned by set() and find() are valid only until the n ext call to set(). | |
150 | |
151 // Set key to val in the table, replacing any previous value with the same k ey. | |
152 // We copy both key and val, and return a pointer to the value copy now in t he table. | |
153 V* set(K key, V val) { | |
154 Pair in = { key, val }; | |
155 Pair* out = fTable.set(in); | |
156 return &out->val; | |
157 } | |
158 | |
159 // If there is key/value entry in the table with this key, return a pointer to the value. | |
160 // If not, return NULL. | |
161 V* find(K key) const { | |
162 if (Pair* p = fTable.find(key)) { | |
163 return &p->val; | |
164 } | |
165 return NULL; | |
166 } | |
167 | |
168 // Call fn on every key/value pair in the table. You may mutate the value b ut not the key. | |
169 void foreach(void(*fn)(K, V*)) { fTable.foreach(ForEach, fn); } | |
170 | |
171 private: | |
172 struct Pair { | |
173 K key; | |
174 V val; | |
175 static K GetKey(Pair p) { return p.key; } | |
176 static uint32_t Hash(K key) { return HashK(key); } | |
177 }; | |
178 static void ForEach(Pair* p, void (*fn)(K, V*)) { fn(p->key, &p->val); } | |
179 | |
180 SkTHashTable<Pair, K> fTable; | |
181 }; | |
182 | |
183 // A set of T. T is treated as an ordiary copyable C++ type. | |
184 template <typename T, uint32_t(*HashT)(T)> | |
185 class SkTHashSet : SkNoncopyable { | |
186 public: | |
187 SkTHashSet() {} | |
188 | |
189 // How many items are in the set? | |
190 int count() const { return fTable.count(); } | |
191 | |
192 // Copy an item into the set. | |
193 void add(T item) { fTable.set(item); } | |
194 | |
195 // Is this item in the set? | |
196 bool contains(T item) const { return fTable.find(item); } | |
197 | |
198 private: | |
199 struct Traits { | |
200 static T GetKey(T item) { return item; } | |
201 static uint32_t Hash(T item) { return HashT(item); } | |
202 }; | |
203 SkTHashTable<T, T, Traits> fTable; | |
204 }; | |
205 | |
206 #endif//SkTHash_DEFINED | |
OLD | NEW |