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