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