1 /*
2  * Copyright 2015 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  */
7 
8 #ifndef SkTHash_DEFINED
9 #define SkTHash_DEFINED
10 
11 #include "SkChecksum.h"
12 #include "SkTypes.h"
13 #include "SkTemplates.h"
14 
15 // Before trying to use SkTHashTable, look below to see if SkTHashMap or SkTHashSet works for you.
16 // They're easier to use, usually perform the same, and have fewer sharp edges.
17 
18 // T and K are treated as ordinary copyable C++ types.
19 // Traits must have:
20 //   - static K GetKey(T)
21 //   - static uint32_t Hash(K)
22 // If the key is large and stored inside T, you may want to make K a const&.
23 // Similarly, if T is large you might want it to be a pointer.
24 template <typename T, typename K, typename Traits = T>
25 class SkTHashTable : SkNoncopyable {
26 public:
27     SkTHashTable() : fCount(0), fRemoved(0), fCapacity(0) {}
28 
29     // Clear the table.
30     void reset() {
31         this->~SkTHashTable();
32         new (this) SkTHashTable;
33     }
34 
35     // How many entries are in the table?
36     int count() const { return fCount; }
37 
38     // Approximately how many bytes of memory do we use beyond sizeof(*this)?
39     size_t approxBytesUsed() const { return fCapacity * sizeof(Slot); }
40 
41     // !!!!!!!!!!!!!!!!!                 CAUTION                   !!!!!!!!!!!!!!!!!
42     // set(), find() and foreach() all allow mutable access to table entries.
43     // If you change an entry so that it no longer has the same key, all hell
44     // will break loose.  Do not do that!
45     //
46     // Please prefer to use SkTHashMap or SkTHashSet, which do not have this danger.
47 
48     // The pointers returned by set() and find() are valid only until the next call to set().
49     // The pointers you receive in foreach() are only valid for its duration.
50 
51     // Copy val into the hash table, returning a pointer to the copy now in the table.
52     // If there already is an entry in the table with the same key, we overwrite it.
53     T* set(const T& val) {
54         if (4 * (fCount+fRemoved) >= 3 * fCapacity) {
55             this->resize(fCapacity > 0 ? fCapacity * 2 : 4);
56         }
57         return this->uncheckedSet(val);
58     }
59 
60     // If there is an entry in the table with this key, return a pointer to it.  If not, NULL.
61     T* find(const K& key) const {
62         uint32_t hash = Hash(key);
63         int index = hash & (fCapacity-1);
64         for (int n = 0; n < fCapacity; n++) {
65             Slot& s = fSlots[index];
66             if (s.empty()) {
67                 return NULL;
68             }
69             if (!s.removed() && hash == s.hash && key == Traits::GetKey(s.val)) {
70                 return &s.val;
71             }
72             index = this->next(index, n);
73         }
74         SkASSERT(fCapacity == 0);
75         return NULL;
76     }
77 
78     // Remove the value with this key from the hash table.
79     void remove(const K& key) {
80         SkASSERT(this->find(key));
81 
82         uint32_t hash = Hash(key);
83         int index = hash & (fCapacity-1);
84         for (int n = 0; n < fCapacity; n++) {
85             Slot& s = fSlots[index];
86             SkASSERT(!s.empty());
87             if (!s.removed() && hash == s.hash && key == Traits::GetKey(s.val)) {
88                 fRemoved++;
89                 fCount--;
90                 s.markRemoved();
91                 return;
92             }
93             index = this->next(index, n);
94         }
95         SkASSERT(fCapacity == 0);
96     }
97 
98     // Call fn on every entry in the table.  You may mutate the entries, but be very careful.
99     template <typename Fn>  // f(T*)
100     void foreach(Fn&& fn) {
101         for (int i = 0; i < fCapacity; i++) {
102             if (!fSlots[i].empty() && !fSlots[i].removed()) {
103                 fn(&fSlots[i].val);
104             }
105         }
106     }
107 
108     // Call fn on every entry in the table.  You may not mutate anything.
109     template <typename Fn>  // f(T) or f(const T&)
110     void foreach(Fn&& fn) const {
111         for (int i = 0; i < fCapacity; i++) {
112             if (!fSlots[i].empty() && !fSlots[i].removed()) {
113                 fn(fSlots[i].val);
114             }
115         }
116     }
117 
118 private:
119     T* uncheckedSet(const T& val) {
120         const K& key = Traits::GetKey(val);
121         uint32_t hash = Hash(key);
122         int index = hash & (fCapacity-1);
123         for (int n = 0; n < fCapacity; n++) {
124             Slot& s = fSlots[index];
125             if (s.empty() || s.removed()) {
126                 // New entry.
127                 if (s.removed()) {
128                     fRemoved--;
129                 }
130                 s.val  = val;
131                 s.hash = hash;
132                 fCount++;
133                 return &s.val;
134             }
135             if (hash == s.hash && key == Traits::GetKey(s.val)) {
136                 // Overwrite previous entry.
137                 // Note: this triggers extra copies when adding the same value repeatedly.
138                 s.val = val;
139                 return &s.val;
140             }
141             index = this->next(index, n);
142         }
143         SkASSERT(false);
144         return NULL;
145     }
146 
147     void resize(int capacity) {
148         int oldCapacity = fCapacity;
149         SkDEBUGCODE(int oldCount = fCount);
150 
151         fCount = fRemoved = 0;
152         fCapacity = capacity;
153         SkAutoTArray<Slot> oldSlots(capacity);
154         oldSlots.swap(fSlots);
155 
156         for (int i = 0; i < oldCapacity; i++) {
157             const Slot& s = oldSlots[i];
158             if (!s.empty() && !s.removed()) {
159                 this->uncheckedSet(s.val);
160             }
161         }
162         SkASSERT(fCount == oldCount);
163     }
164 
165     int next(int index, int n) const {
166         // A valid strategy explores all slots in [0, fCapacity) as n walks from 0 to fCapacity-1.
167         // Both of these strategies are valid:
168         //return (index + 0 + 1) & (fCapacity-1);      // Linear probing.
169         return (index + n + 1) & (fCapacity-1);        // Quadratic probing.
170     }
171 
172     static uint32_t Hash(const K& key) {
173         uint32_t hash = Traits::Hash(key);
174         return hash < 2 ? hash+2 : hash;  // We reserve hash 0 and 1 to mark empty or removed slots.
175     }
176 
177     struct Slot {
178         Slot() : hash(0) {}
179         bool   empty() const { return this->hash == 0; }
180         bool removed() const { return this->hash == 1; }
181 
182         void markRemoved() { this->hash = 1; }
183 
184         T val;
185         uint32_t hash;
186     };
187 
188     int fCount, fRemoved, fCapacity;
189     SkAutoTArray<Slot> fSlots;
190 };
191 
192 // Maps K->V.  A more user-friendly wrapper around SkTHashTable, suitable for most use cases.
193 // K and V are treated as ordinary copyable C++ types, with no assumed relationship between the two.
194 template <typename K, typename V, typename HashK = SkGoodHash>
195 class SkTHashMap : SkNoncopyable {
196 public:
197     SkTHashMap() {}
198 
199     // Clear the map.
200     void reset() { fTable.reset(); }
201 
202     // How many key/value pairs are in the table?
203     int count() const { return fTable.count(); }
204 
205     // Approximately how many bytes of memory do we use beyond sizeof(*this)?
206     size_t approxBytesUsed() const { return fTable.approxBytesUsed(); }
207 
208     // N.B. The pointers returned by set() and find() are valid only until the next call to set().
209 
210     // Set key to val in the table, replacing any previous value with the same key.
211     // We copy both key and val, and return a pointer to the value copy now in the table.
212     V* set(const K& key, const V& val) {
213         Pair in = { key, val };
214         Pair* out = fTable.set(in);
215         return &out->val;
216     }
217 
218     // If there is key/value entry in the table with this key, return a pointer to the value.
219     // If not, return NULL.
220     V* find(const K& key) const {
221         if (Pair* p = fTable.find(key)) {
222             return &p->val;
223         }
224         return NULL;
225     }
226 
227     // Remove the key/value entry in the table with this key.
228     void remove(const K& key) {
229         SkASSERT(this->find(key));
230         fTable.remove(key);
231     }
232 
233     // Call fn on every key/value pair in the table.  You may mutate the value but not the key.
234     template <typename Fn>  // f(K, V*) or f(const K&, V*)
235     void foreach(Fn&& fn) {
236         fTable.foreach([&fn](Pair* p){ fn(p->key, &p->val); });
237     }
238 
239     // Call fn on every key/value pair in the table.  You may not mutate anything.
240     template <typename Fn>  // f(K, V), f(const K&, V), f(K, const V&) or f(const K&, const V&).
241     void foreach(Fn&& fn) const {
242         fTable.foreach([&fn](const Pair& p){ fn(p.key, p.val); });
243     }
244 
245 private:
246     struct Pair {
247         K key;
248         V val;
249         static const K& GetKey(const Pair& p) { return p.key; }
250         static uint32_t Hash(const K& key) { return HashK()(key); }
251     };
252 
253     SkTHashTable<Pair, K> fTable;
254 };
255 
256 // A set of T.  T is treated as an ordiary copyable C++ type.
257 template <typename T, typename HashT = SkGoodHash>
258 class SkTHashSet : SkNoncopyable {
259 public:
260     SkTHashSet() {}
261 
262     // Clear the set.
263     void reset() { fTable.reset(); }
264 
265     // How many items are in the set?
266     int count() const { return fTable.count(); }
267 
268     // Approximately how many bytes of memory do we use beyond sizeof(*this)?
269     size_t approxBytesUsed() const { return fTable.approxBytesUsed(); }
270 
271     // Copy an item into the set.
272     void add(const T& item) { fTable.set(item); }
273 
274     // Is this item in the set?
275     bool contains(const T& item) const { return SkToBool(this->find(item)); }
276 
277     // If an item equal to this is in the set, return a pointer to it, otherwise null.
278     // This pointer remains valid until the next call to add().
279     const T* find(const T& item) const { return fTable.find(item); }
280 
281     // Remove the item in the set equal to this.
282     void remove(const T& item) {
283         SkASSERT(this->contains(item));
284         fTable.remove(item);
285     }
286 
287     // Call fn on every item in the set.  You may not mutate anything.
288     template <typename Fn>  // f(T), f(const T&)
289     void foreach (Fn&& fn) const {
290         fTable.foreach(fn);
291     }
292 
293 private:
294     struct Traits {
295         static const T& GetKey(const T& item) { return item; }
296         static uint32_t Hash(const T& item) { return HashT()(item); }
297     };
298     SkTHashTable<T, T, Traits> fTable;
299 };
300 
301 #endif//SkTHash_DEFINED
302