Chromium Code Reviews| 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 |