| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #ifndef SkTHash_DEFINED | 8 #ifndef SkTHash_DEFINED |
| 9 #define SkTHash_DEFINED | 9 #define SkTHash_DEFINED |
| 10 | 10 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 41 // !!!!!!!!!!!!!!!!! CAUTION !!!!!!!!!!!!!
!!!! | 41 // !!!!!!!!!!!!!!!!! CAUTION !!!!!!!!!!!!!
!!!! |
| 42 // set(), find() and foreach() all allow mutable access to table entries. | 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 | 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! | 44 // will break loose. Do not do that! |
| 45 // | 45 // |
| 46 // Please prefer to use SkTHashMap or SkTHashSet, which do not have this dan
ger. | 46 // Please prefer to use SkTHashMap or SkTHashSet, which do not have this dan
ger. |
| 47 | 47 |
| 48 // The pointers returned by set() and find() are valid only until the next c
all to set(). | 48 // The pointers returned by set() and find() are valid only until the next c
all to set(). |
| 49 // The pointers you receive in foreach() are only valid for its duration. | 49 // The pointers you receive in foreach() are only valid for its duration. |
| 50 | 50 |
| 51 // Copy val into the hash table, returning a pointer to the copy now in the
table. | 51 // Move 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. | 52 // If there already is an entry in the table with the same key, we overwrite
it. |
| 53 T* set(const T& val) { | 53 T* set(T val) { |
| 54 if (4 * (fCount+fRemoved) >= 3 * fCapacity) { | 54 if (4 * (fCount+fRemoved) >= 3 * fCapacity) { |
| 55 this->resize(fCapacity > 0 ? fCapacity * 2 : 4); | 55 this->resize(fCapacity > 0 ? fCapacity * 2 : 4); |
| 56 } | 56 } |
| 57 return this->uncheckedSet(val); | 57 return this->uncheckedSet(std::move(val)); |
| 58 } | 58 } |
| 59 | 59 |
| 60 |
| 60 // If there is an entry in the table with this key, return a pointer to it.
If not, NULL. | 61 // 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 T* find(const K& key) const { |
| 62 uint32_t hash = Hash(key); | 63 uint32_t hash = Hash(key); |
| 63 int index = hash & (fCapacity-1); | 64 int index = hash & (fCapacity-1); |
| 64 for (int n = 0; n < fCapacity; n++) { | 65 for (int n = 0; n < fCapacity; n++) { |
| 65 Slot& s = fSlots[index]; | 66 Slot& s = fSlots[index]; |
| 66 if (s.empty()) { | 67 if (s.empty()) { |
| 67 return NULL; | 68 return NULL; |
| 68 } | 69 } |
| 69 if (!s.removed() && hash == s.hash && key == Traits::GetKey(s.val))
{ | 70 if (!s.removed() && hash == s.hash && key == Traits::GetKey(s.val))
{ |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 109 template <typename Fn> // f(T) or f(const T&) | 110 template <typename Fn> // f(T) or f(const T&) |
| 110 void foreach(Fn&& fn) const { | 111 void foreach(Fn&& fn) const { |
| 111 for (int i = 0; i < fCapacity; i++) { | 112 for (int i = 0; i < fCapacity; i++) { |
| 112 if (!fSlots[i].empty() && !fSlots[i].removed()) { | 113 if (!fSlots[i].empty() && !fSlots[i].removed()) { |
| 113 fn(fSlots[i].val); | 114 fn(fSlots[i].val); |
| 114 } | 115 } |
| 115 } | 116 } |
| 116 } | 117 } |
| 117 | 118 |
| 118 private: | 119 private: |
| 119 T* uncheckedSet(const T& val) { | 120 T* uncheckedSet(T val) { |
| 120 const K& key = Traits::GetKey(val); | 121 const K& key = Traits::GetKey(val); |
| 121 uint32_t hash = Hash(key); | 122 uint32_t hash = Hash(key); |
| 122 int index = hash & (fCapacity-1); | 123 int index = hash & (fCapacity-1); |
| 123 for (int n = 0; n < fCapacity; n++) { | 124 for (int n = 0; n < fCapacity; n++) { |
| 124 Slot& s = fSlots[index]; | 125 Slot& s = fSlots[index]; |
| 125 if (s.empty() || s.removed()) { | 126 if (s.empty() || s.removed()) { |
| 126 // New entry. | 127 // New entry. |
| 127 if (s.removed()) { | 128 if (s.removed()) { |
| 128 fRemoved--; | 129 fRemoved--; |
| 129 } | 130 } |
| 130 s.val = val; | 131 s.val = std::move(val); |
| 131 s.hash = hash; | 132 s.hash = hash; |
| 132 fCount++; | 133 fCount++; |
| 133 return &s.val; | 134 return &s.val; |
| 134 } | 135 } |
| 135 if (hash == s.hash && key == Traits::GetKey(s.val)) { | 136 if (hash == s.hash && key == Traits::GetKey(s.val)) { |
| 136 // Overwrite previous entry. | 137 // Overwrite previous entry. |
| 137 // Note: this triggers extra copies when adding the same value r
epeatedly. | 138 // Note: this triggers extra copies when adding the same value r
epeatedly. |
| 138 s.val = val; | 139 s.val = std::move(val); |
| 139 return &s.val; | 140 return &s.val; |
| 140 } | 141 } |
| 141 index = this->next(index, n); | 142 index = this->next(index, n); |
| 142 } | 143 } |
| 143 SkASSERT(false); | 144 SkASSERT(false); |
| 144 return NULL; | 145 return NULL; |
| 145 } | 146 } |
| 146 | 147 |
| 147 void resize(int capacity) { | 148 void resize(int capacity) { |
| 148 int oldCapacity = fCapacity; | 149 int oldCapacity = fCapacity; |
| 149 SkDEBUGCODE(int oldCount = fCount); | 150 SkDEBUGCODE(int oldCount = fCount); |
| 150 | 151 |
| 151 fCount = fRemoved = 0; | 152 fCount = fRemoved = 0; |
| 152 fCapacity = capacity; | 153 fCapacity = capacity; |
| 153 SkAutoTArray<Slot> oldSlots(capacity); | 154 SkAutoTArray<Slot> oldSlots(capacity); |
| 154 oldSlots.swap(fSlots); | 155 oldSlots.swap(fSlots); |
| 155 | 156 |
| 156 for (int i = 0; i < oldCapacity; i++) { | 157 for (int i = 0; i < oldCapacity; i++) { |
| 157 const Slot& s = oldSlots[i]; | 158 const Slot& s = oldSlots[i]; |
| 158 if (!s.empty() && !s.removed()) { | 159 if (!s.empty() && !s.removed()) { |
| 159 this->uncheckedSet(s.val); | 160 this->uncheckedSet(std::move(s.val)); |
| 160 } | 161 } |
| 161 } | 162 } |
| 162 SkASSERT(fCount == oldCount); | 163 SkASSERT(fCount == oldCount); |
| 163 } | 164 } |
| 164 | 165 |
| 165 int next(int index, int n) const { | 166 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 // 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 // Both of these strategies are valid: |
| 168 //return (index + 0 + 1) & (fCapacity-1); // Linear probing. | 169 //return (index + 0 + 1) & (fCapacity-1); // Linear probing. |
| 169 return (index + n + 1) & (fCapacity-1); // Quadratic probing. | 170 return (index + n + 1) & (fCapacity-1); // Quadratic probing. |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 202 // How many key/value pairs are in the table? | 203 // How many key/value pairs are in the table? |
| 203 int count() const { return fTable.count(); } | 204 int count() const { return fTable.count(); } |
| 204 | 205 |
| 205 // Approximately how many bytes of memory do we use beyond sizeof(*this)? | 206 // Approximately how many bytes of memory do we use beyond sizeof(*this)? |
| 206 size_t approxBytesUsed() const { return fTable.approxBytesUsed(); } | 207 size_t approxBytesUsed() const { return fTable.approxBytesUsed(); } |
| 207 | 208 |
| 208 // N.B. The pointers returned by set() and find() are valid only until the n
ext call to set(). | 209 // N.B. The pointers returned by set() and find() are valid only until the n
ext call to set(). |
| 209 | 210 |
| 210 // Set key to val in the table, replacing any previous value with the same k
ey. | 211 // Set key to val in the table, replacing any previous value with the same k
ey. |
| 211 // We copy both key and val, and return a pointer to the value copy now in t
he table. | 212 // We copy both key and val, and return a pointer to the value copy now in t
he table. |
| 212 V* set(const K& key, const V& val) { | 213 V* set(const K& key, V val) { |
| 213 Pair in = { key, val }; | 214 Pair* out = fTable.set(Pair{ key, std::move(val) }); |
| 214 Pair* out = fTable.set(in); | |
| 215 return &out->val; | 215 return &out->val; |
| 216 } | 216 } |
| 217 | 217 |
| 218 // If there is key/value entry in the table with this key, return a pointer
to the value. | 218 // If there is key/value entry in the table with this key, return a pointer
to the value. |
| 219 // If not, return NULL. | 219 // If not, return NULL. |
| 220 V* find(const K& key) const { | 220 V* find(const K& key) const { |
| 221 if (Pair* p = fTable.find(key)) { | 221 if (Pair* p = fTable.find(key)) { |
| 222 return &p->val; | 222 return &p->val; |
| 223 } | 223 } |
| 224 return NULL; | 224 return NULL; |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 | 292 |
| 293 private: | 293 private: |
| 294 struct Traits { | 294 struct Traits { |
| 295 static const T& GetKey(const T& item) { return item; } | 295 static const T& GetKey(const T& item) { return item; } |
| 296 static uint32_t Hash(const T& item) { return HashT()(item); } | 296 static uint32_t Hash(const T& item) { return HashT()(item); } |
| 297 }; | 297 }; |
| 298 SkTHashTable<T, T, Traits> fTable; | 298 SkTHashTable<T, T, Traits> fTable; |
| 299 }; | 299 }; |
| 300 | 300 |
| 301 #endif//SkTHash_DEFINED | 301 #endif//SkTHash_DEFINED |
| OLD | NEW |