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 // !!!!!!!!!!!!!!!!! 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 stragegies are valid: | |
|
f(malita)
2015/02/12 20:39:02
strategies
mtklein
2015/02/12 20:40:30
Done.
| |
| 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 |