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