Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkTHash.h
Go to the documentation of this file.
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
12#include "src/core/SkChecksum.h"
13
14#include <initializer_list>
15#include <memory>
16#include <new>
17#include <type_traits>
18#include <utility>
19
20namespace skia_private {
21
22// Before trying to use THashTable, look below to see if THashMap or THashSet works for you.
23// They're easier to use, usually perform the same, and have fewer sharp edges.
24
25// T and K are treated as ordinary copyable C++ types.
26// Traits must have:
27// - static K GetKey(T)
28// - static uint32_t Hash(K)
29// If the key is large and stored inside T, you may want to make K a const&.
30// Similarly, if T is large you might want it to be a pointer.
31template <typename T, typename K, typename Traits = T>
33public:
34 THashTable() = default;
35 ~THashTable() = default;
36
37 THashTable(const THashTable& that) { *this = that; }
38 THashTable( THashTable&& that) { *this = std::move(that); }
39
41 if (this != &that) {
42 fCount = that.fCount;
43 fCapacity = that.fCapacity;
44 fSlots.reset(new Slot[that.fCapacity]);
45 for (int i = 0; i < fCapacity; i++) {
46 fSlots[i] = that.fSlots[i];
47 }
48 }
49 return *this;
50 }
51
53 if (this != &that) {
54 fCount = that.fCount;
55 fCapacity = that.fCapacity;
56 fSlots = std::move(that.fSlots);
57
58 that.fCount = that.fCapacity = 0;
59 }
60 return *this;
61 }
62
63 // Clear the table.
64 void reset() { *this = THashTable(); }
65
66 // How many entries are in the table?
67 int count() const { return fCount; }
68
69 // How many slots does the table contain? (Note that unlike an array, hash tables can grow
70 // before reaching 100% capacity.)
71 int capacity() const { return fCapacity; }
72
73 // Approximately how many bytes of memory do we use beyond sizeof(*this)?
74 size_t approxBytesUsed() const { return fCapacity * sizeof(Slot); }
75
76 // !!!!!!!!!!!!!!!!! CAUTION !!!!!!!!!!!!!!!!!
77 // set(), find() and foreach() all allow mutable access to table entries.
78 // If you change an entry so that it no longer has the same key, all hell
79 // will break loose. Do not do that!
80 //
81 // Please prefer to use THashMap or THashSet, which do not have this danger.
82
83 // The pointers returned by set() and find() are valid only until the next call to set().
84 // The pointers you receive in foreach() are only valid for its duration.
85
86 // Copy val into the hash table, returning a pointer to the copy now in the table.
87 // If there already is an entry in the table with the same key, we overwrite it.
88 T* set(T val) {
89 if (4 * fCount >= 3 * fCapacity) {
90 this->resize(fCapacity > 0 ? fCapacity * 2 : 4);
91 }
92 return this->uncheckedSet(std::move(val));
93 }
94
95 // If there is an entry in the table with this key, return a pointer to it. If not, null.
96 T* find(const K& key) const {
97 uint32_t hash = Hash(key);
98 int index = hash & (fCapacity-1);
99 for (int n = 0; n < fCapacity; n++) {
100 Slot& s = fSlots[index];
101 if (s.empty()) {
102 return nullptr;
103 }
104 if (hash == s.fHash && key == Traits::GetKey(*s)) {
105 return &*s;
106 }
107 index = this->next(index);
108 }
109 SkASSERT(fCapacity == fCount);
110 return nullptr;
111 }
112
113 // If there is an entry in the table with this key, return it. If not, null.
114 // This only works for pointer type T, and cannot be used to find an nullptr entry.
115 T findOrNull(const K& key) const {
116 if (T* p = this->find(key)) {
117 return *p;
118 }
119 return nullptr;
120 }
121
122 // If a value with this key exists in the hash table, removes it and returns true.
123 // Otherwise, returns false.
124 bool removeIfExists(const K& key) {
125 uint32_t hash = Hash(key);
126 int index = hash & (fCapacity-1);
127 for (int n = 0; n < fCapacity; n++) {
128 Slot& s = fSlots[index];
129 if (s.empty()) {
130 return false;
131 }
132 if (hash == s.fHash && key == Traits::GetKey(*s)) {
133 this->removeSlot(index);
134 if (4 * fCount <= fCapacity && fCapacity > 4) {
135 this->resize(fCapacity / 2);
136 }
137 return true;
138 }
139 index = this->next(index);
140 }
141 SkASSERT(fCapacity == fCount);
142 return false;
143 }
144
145 // Removes the value with this key from the hash table. Asserts if it is missing.
146 void remove(const K& key) {
147 SkAssertResult(this->removeIfExists(key));
148 }
149
150 // Hash tables will automatically resize themselves when set() and remove() are called, but
151 // resize() can be called to manually grow capacity before a bulk insertion.
152 void resize(int capacity) {
153 SkASSERT(capacity >= fCount);
154 int oldCapacity = fCapacity;
155 SkDEBUGCODE(int oldCount = fCount);
156
157 fCount = 0;
158 fCapacity = capacity;
159 std::unique_ptr<Slot[]> oldSlots = std::move(fSlots);
160 fSlots.reset(new Slot[capacity]);
161
162 for (int i = 0; i < oldCapacity; i++) {
163 Slot& s = oldSlots[i];
164 if (s.has_value()) {
165 this->uncheckedSet(*std::move(s));
166 }
167 }
168 SkASSERT(fCount == oldCount);
169 }
170
171 // Call fn on every entry in the table. You may mutate the entries, but be very careful.
172 template <typename Fn> // f(T*)
173 void foreach(Fn&& fn) {
174 for (int i = 0; i < fCapacity; i++) {
175 if (fSlots[i].has_value()) {
176 fn(&*fSlots[i]);
177 }
178 }
179 }
180
181 // Call fn on every entry in the table. You may not mutate anything.
182 template <typename Fn> // f(T) or f(const T&)
183 void foreach(Fn&& fn) const {
184 for (int i = 0; i < fCapacity; i++) {
185 if (fSlots[i].has_value()) {
186 fn(*fSlots[i]);
187 }
188 }
189 }
190
191 // A basic iterator-like class which disallows mutation; sufficient for range-based for loops.
192 // Intended for use by THashMap and THashSet via begin() and end().
193 // Adding or removing elements may invalidate all iterators.
194 template <typename SlotVal>
195 class Iter {
196 public:
198
199 Iter(const TTable* table, int slot) : fTable(table), fSlot(slot) {}
200
201 static Iter MakeBegin(const TTable* table) {
202 return Iter{table, table->firstPopulatedSlot()};
203 }
204
205 static Iter MakeEnd(const TTable* table) {
206 return Iter{table, table->capacity()};
207 }
208
209 const SlotVal& operator*() const {
210 return *fTable->slot(fSlot);
211 }
212
213 const SlotVal* operator->() const {
214 return fTable->slot(fSlot);
215 }
216
217 bool operator==(const Iter& that) const {
218 // Iterators from different tables shouldn't be compared against each other.
219 SkASSERT(fTable == that.fTable);
220 return fSlot == that.fSlot;
221 }
222
223 bool operator!=(const Iter& that) const {
224 return !(*this == that);
225 }
226
228 fSlot = fTable->nextPopulatedSlot(fSlot);
229 return *this;
230 }
231
233 Iter old = *this;
234 this->operator++();
235 return old;
236 }
237
238 protected:
240 int fSlot;
241 };
242
243private:
244 // Finds the first non-empty slot for an iterator.
245 int firstPopulatedSlot() const {
246 for (int i = 0; i < fCapacity; i++) {
247 if (fSlots[i].has_value()) {
248 return i;
249 }
250 }
251 return fCapacity;
252 }
253
254 // Increments an iterator's slot.
255 int nextPopulatedSlot(int currentSlot) const {
256 for (int i = currentSlot + 1; i < fCapacity; i++) {
257 if (fSlots[i].has_value()) {
258 return i;
259 }
260 }
261 return fCapacity;
262 }
263
264 // Reads from an iterator's slot.
265 const T* slot(int i) const {
266 SkASSERT(fSlots[i].has_value());
267 return &*fSlots[i];
268 }
269
270 T* uncheckedSet(T&& val) {
271 const K& key = Traits::GetKey(val);
272 SkASSERT(key == key);
273 uint32_t hash = Hash(key);
274 int index = hash & (fCapacity-1);
275 for (int n = 0; n < fCapacity; n++) {
276 Slot& s = fSlots[index];
277 if (s.empty()) {
278 // New entry.
279 s.emplace(std::move(val), hash);
280 fCount++;
281 return &*s;
282 }
283 if (hash == s.fHash && key == Traits::GetKey(*s)) {
284 // Overwrite previous entry.
285 // Note: this triggers extra copies when adding the same value repeatedly.
286 s.emplace(std::move(val), hash);
287 return &*s;
288 }
289
290 index = this->next(index);
291 }
292 SkASSERT(false);
293 return nullptr;
294 }
295
296 void removeSlot(int index) {
297 fCount--;
298
299 // Rearrange elements to restore the invariants for linear probing.
300 for (;;) {
301 Slot& emptySlot = fSlots[index];
302 int emptyIndex = index;
303 int originalIndex;
304 // Look for an element that can be moved into the empty slot.
305 // If the empty slot is in between where an element landed, and its native slot, then
306 // move it to the empty slot. Don't move it if its native slot is in between where
307 // the element landed and the empty slot.
308 // [native] <= [empty] < [candidate] == GOOD, can move candidate to empty slot
309 // [empty] < [native] < [candidate] == BAD, need to leave candidate where it is
310 do {
311 index = this->next(index);
312 Slot& s = fSlots[index];
313 if (s.empty()) {
314 // We're done shuffling elements around. Clear the last empty slot.
315 emptySlot.reset();
316 return;
317 }
318 originalIndex = s.fHash & (fCapacity - 1);
319 } while ((index <= originalIndex && originalIndex < emptyIndex)
320 || (originalIndex < emptyIndex && emptyIndex < index)
321 || (emptyIndex < index && index <= originalIndex));
322 // Move the element to the empty slot.
323 Slot& moveFrom = fSlots[index];
324 emptySlot = std::move(moveFrom);
325 }
326 }
327
328 int next(int index) const {
329 index--;
330 if (index < 0) { index += fCapacity; }
331 return index;
332 }
333
334 static uint32_t Hash(const K& key) {
335 uint32_t hash = Traits::Hash(key) & 0xffffffff;
336 return hash ? hash : 1; // We reserve hash 0 to mark empty.
337 }
338
339 class Slot {
340 public:
341 Slot() = default;
342 ~Slot() { this->reset(); }
343
344 Slot(const Slot& that) { *this = that; }
345 Slot& operator=(const Slot& that) {
346 if (this == &that) {
347 return *this;
348 }
349 if (fHash) {
350 if (that.fHash) {
351 fVal.fStorage = that.fVal.fStorage;
352 fHash = that.fHash;
353 } else {
354 this->reset();
355 }
356 } else {
357 if (that.fHash) {
358 new (&fVal.fStorage) T(that.fVal.fStorage);
359 fHash = that.fHash;
360 } else {
361 // do nothing, no value on either side
362 }
363 }
364 return *this;
365 }
366
367 Slot(Slot&& that) { *this = std::move(that); }
368 Slot& operator=(Slot&& that) {
369 if (this == &that) {
370 return *this;
371 }
372 if (fHash) {
373 if (that.fHash) {
374 fVal.fStorage = std::move(that.fVal.fStorage);
375 fHash = that.fHash;
376 } else {
377 this->reset();
378 }
379 } else {
380 if (that.fHash) {
381 new (&fVal.fStorage) T(std::move(that.fVal.fStorage));
382 fHash = that.fHash;
383 } else {
384 // do nothing, no value on either side
385 }
386 }
387 return *this;
388 }
389
390 T& operator*() & { return fVal.fStorage; }
391 const T& operator*() const& { return fVal.fStorage; }
392 T&& operator*() && { return std::move(fVal.fStorage); }
393 const T&& operator*() const&& { return std::move(fVal.fStorage); }
394
395 Slot& emplace(T&& v, uint32_t h) {
396 this->reset();
397 new (&fVal.fStorage) T(std::move(v));
398 fHash = h;
399 return *this;
400 }
401
402 bool has_value() const { return fHash != 0; }
403 explicit operator bool() const { return this->has_value(); }
404 bool empty() const { return !this->has_value(); }
405
406 void reset() {
407 if (fHash) {
408 fVal.fStorage.~T();
409 fHash = 0;
410 }
411 }
412
413 uint32_t fHash = 0;
414
415 private:
416 union Storage {
417 T fStorage;
418 Storage() {}
419 ~Storage() {}
420 } fVal;
421 };
422
423 int fCount = 0,
424 fCapacity = 0;
425 std::unique_ptr<Slot[]> fSlots;
426};
427
428// Maps K->V. A more user-friendly wrapper around THashTable, suitable for most use cases.
429// K and V are treated as ordinary copyable C++ types, with no assumed relationship between the two.
430template <typename K, typename V, typename HashK = SkGoodHash>
431class THashMap {
432public:
433 // Allow default construction and assignment.
434 THashMap() = default;
435
437 THashMap(const THashMap<K, V, HashK>& that) = default;
438
441
442 // Construct with an initializer list of key-value pairs.
443 struct Pair : public std::pair<K, V> {
444 using std::pair<K, V>::pair;
445 static const K& GetKey(const Pair& p) { return p.first; }
446 static auto Hash(const K& key) { return HashK()(key); }
447 };
448
449 THashMap(std::initializer_list<Pair> pairs) {
450 fTable.resize(pairs.size() * 5 / 3);
451 for (const Pair& p : pairs) {
452 fTable.set(p);
453 }
454 }
455
456 // Clear the map.
457 void reset() { fTable.reset(); }
458
459 // How many key/value pairs are in the table?
460 int count() const { return fTable.count(); }
461
462 // Is empty?
463 bool empty() const { return fTable.count() == 0; }
464
465 // Approximately how many bytes of memory do we use beyond sizeof(*this)?
466 size_t approxBytesUsed() const { return fTable.approxBytesUsed(); }
467
468 // N.B. The pointers returned by set() and find() are valid only until the next call to set().
469
470 // Set key to val in the table, replacing any previous value with the same key.
471 // We copy both key and val, and return a pointer to the value copy now in the table.
472 V* set(K key, V val) {
473 Pair* out = fTable.set({std::move(key), std::move(val)});
474 return &out->second;
475 }
476
477 // If there is key/value entry in the table with this key, return a pointer to the value.
478 // If not, return null.
479 V* find(const K& key) const {
480 if (Pair* p = fTable.find(key)) {
481 return &p->second;
482 }
483 return nullptr;
484 }
485
486 V& operator[](const K& key) {
487 if (V* val = this->find(key)) {
488 return *val;
489 }
490 return *this->set(key, V{});
491 }
492
493 // Removes the key/value entry in the table with this key. Asserts if the key is not present.
494 void remove(const K& key) {
495 fTable.remove(key);
496 }
497
498 // If the key exists in the table, removes it and returns true. Otherwise, returns false.
499 bool removeIfExists(const K& key) {
500 return fTable.removeIfExists(key);
501 }
502
503 // Call fn on every key/value pair in the table. You may mutate the value but not the key.
504 template <typename Fn, // f(K, V*) or f(const K&, V*)
505 std::enable_if_t<std::is_invocable_v<Fn, K, V*>>* = nullptr>
506 void foreach(Fn&& fn) {
507 fTable.foreach([&fn](Pair* p) { fn(p->first, &p->second); });
508 }
509
510 // Call fn on every key/value pair in the table. You may not mutate anything.
511 template <typename Fn, // f(K, V), f(const K&, V), f(K, const V&) or f(const K&, const V&).
512 std::enable_if_t<std::is_invocable_v<Fn, K, V>>* = nullptr>
513 void foreach(Fn&& fn) const {
514 fTable.foreach([&fn](const Pair& p) { fn(p.first, p.second); });
515 }
516
517 // Call fn on every key/value pair in the table. You may not mutate anything.
518 template <typename Fn, // f(Pair), or f(const Pair&)
519 std::enable_if_t<std::is_invocable_v<Fn, Pair>>* = nullptr>
520 void foreach(Fn&& fn) const {
521 fTable.foreach([&fn](const Pair& p) { fn(p); });
522 }
523
524 // Dereferencing an iterator gives back a key-value pair, suitable for structured binding.
526
527 Iter begin() const {
528 return Iter::MakeBegin(&fTable);
529 }
530
531 Iter end() const {
532 return Iter::MakeEnd(&fTable);
533 }
534
535private:
536 THashTable<Pair, K> fTable;
537};
538
539// A set of T. T is treated as an ordinary copyable C++ type.
540template <typename T, typename HashT = SkGoodHash>
541class THashSet {
542public:
543 // Allow default construction and assignment.
544 THashSet() = default;
545
546 THashSet(THashSet<T, HashT>&& that) = default;
547 THashSet(const THashSet<T, HashT>& that) = default;
548
551
552 // Construct with an initializer list of Ts.
553 THashSet(std::initializer_list<T> vals) {
554 fTable.resize(vals.size() * 5 / 3);
555 for (const T& val : vals) {
556 fTable.set(val);
557 }
558 }
559
560 // Clear the set.
561 void reset() { fTable.reset(); }
562
563 // How many items are in the set?
564 int count() const { return fTable.count(); }
565
566 // Is empty?
567 bool empty() const { return fTable.count() == 0; }
568
569 // Approximately how many bytes of memory do we use beyond sizeof(*this)?
570 size_t approxBytesUsed() const { return fTable.approxBytesUsed(); }
571
572 // Copy an item into the set.
573 void add(T item) { fTable.set(std::move(item)); }
574
575 // Is this item in the set?
576 bool contains(const T& item) const { return SkToBool(this->find(item)); }
577
578 // If an item equal to this is in the set, return a pointer to it, otherwise null.
579 // This pointer remains valid until the next call to add().
580 const T* find(const T& item) const { return fTable.find(item); }
581
582 // Remove the item in the set equal to this.
583 void remove(const T& item) {
584 SkASSERT(this->contains(item));
585 fTable.remove(item);
586 }
587
588 // Call fn on every item in the set. You may not mutate anything.
589 template <typename Fn> // f(T), f(const T&)
590 void foreach (Fn&& fn) const {
591 fTable.foreach(fn);
592 }
593
594private:
595 struct Traits {
596 static const T& GetKey(const T& item) { return item; }
597 static auto Hash(const T& item) { return HashT()(item); }
598 };
599
600public:
602
603 Iter begin() const {
604 return Iter::MakeBegin(&fTable);
605 }
606
607 Iter end() const {
608 return Iter::MakeEnd(&fTable);
609 }
610
611private:
613};
614
615} // namespace skia_private
616
617#endif // SkTHash_DEFINED
m reset()
#define SkAssertResult(cond)
Definition SkAssert.h:123
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
static uint32_t hash(const SkShaderBase::GradientInfo &v)
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
SI F table(const skcms_Curve *curve, F v)
THashMap< K, V, HashK > & operator=(const THashMap< K, V, HashK > &that)=default
Iter begin() const
Definition SkTHash.h:527
Iter end() const
Definition SkTHash.h:531
V & operator[](const K &key)
Definition SkTHash.h:486
THashMap< K, V, HashK > & operator=(THashMap< K, V, HashK > &&that)=default
size_t approxBytesUsed() const
Definition SkTHash.h:466
int count() const
Definition SkTHash.h:460
V * find(const K &key) const
Definition SkTHash.h:479
THashMap(const THashMap< K, V, HashK > &that)=default
THashMap(THashMap< K, V, HashK > &&that)=default
V * set(K key, V val)
Definition SkTHash.h:472
bool empty() const
Definition SkTHash.h:463
bool removeIfExists(const K &key)
Definition SkTHash.h:499
void remove(const K &key)
Definition SkTHash.h:494
THashMap(std::initializer_list< Pair > pairs)
Definition SkTHash.h:449
typename THashTable< Pair, K >::template Iter< std::pair< K, V > > Iter
Definition SkTHash.h:525
THashSet(THashSet< T, HashT > &&that)=default
THashSet< T, HashT > & operator=(THashSet< T, HashT > &&that)=default
int count() const
Definition SkTHash.h:564
const T * find(const T &item) const
Definition SkTHash.h:580
THashSet(std::initializer_list< T > vals)
Definition SkTHash.h:553
Iter begin() const
Definition SkTHash.h:603
size_t approxBytesUsed() const
Definition SkTHash.h:570
void add(T item)
Definition SkTHash.h:573
bool empty() const
Definition SkTHash.h:567
Iter end() const
Definition SkTHash.h:607
bool contains(const T &item) const
Definition SkTHash.h:576
THashSet< T, HashT > & operator=(const THashSet< T, HashT > &that)=default
typename THashTable< T, T, Traits >::template Iter< T > Iter
Definition SkTHash.h:601
void remove(const T &item)
Definition SkTHash.h:583
THashSet(const THashSet< T, HashT > &that)=default
bool operator==(const Iter &that) const
Definition SkTHash.h:217
const SlotVal * operator->() const
Definition SkTHash.h:213
bool operator!=(const Iter &that) const
Definition SkTHash.h:223
static Iter MakeEnd(const TTable *table)
Definition SkTHash.h:205
const SlotVal & operator*() const
Definition SkTHash.h:209
Iter(const TTable *table, int slot)
Definition SkTHash.h:199
static Iter MakeBegin(const TTable *table)
Definition SkTHash.h:201
void remove(const K &key)
Definition SkTHash.h:146
THashTable & operator=(THashTable &&that)
Definition SkTHash.h:52
void foreach(Fn &&fn)
Definition SkTHash.h:173
void resize(int capacity)
Definition SkTHash.h:152
THashTable(const THashTable &that)
Definition SkTHash.h:37
THashTable(THashTable &&that)
Definition SkTHash.h:38
int capacity() const
Definition SkTHash.h:71
THashTable & operator=(const THashTable &that)
Definition SkTHash.h:40
T * find(const K &key) const
Definition SkTHash.h:96
T findOrNull(const K &key) const
Definition SkTHash.h:115
size_t approxBytesUsed() const
Definition SkTHash.h:74
bool removeIfExists(const K &key)
Definition SkTHash.h:124
static const int K
Definition daa.cpp:21
struct MyStruct s
EMSCRIPTEN_KEEPALIVE void empty()
T __attribute__((ext_vector_type(N))) V
constexpr Color operator*(T value, const Color &c)
Definition color.h:901
SkScalar h
#define T
static auto Hash(const K &key)
Definition SkTHash.h:446
static const K & GetKey(const Pair &p)
Definition SkTHash.h:445