Flutter Engine
The Flutter Engine
HashTest.cpp
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
11#include "src/core/SkTHash.h"
12#include "tests/Test.h"
13
14#include <cstdint>
15#include <initializer_list>
16#include <string>
17#include <string_view>
18#include <utility>
19
20using namespace skia_private;
21
22// Tests use of const foreach(). map.count() is of course the better way to do this.
23static int count(const THashMap<int, double>& map) {
24 int n = 0;
25 map.foreach([&n](int, double) { n++; });
26 return n;
27}
28
29DEF_TEST(HashMap, r) {
31
32 map.set(3, 4.0);
33 REPORTER_ASSERT(r, map.count() == 1);
34
35 REPORTER_ASSERT(r, map.approxBytesUsed() > 0);
36
37 double* found = map.find(3);
38 REPORTER_ASSERT(r, found);
39 REPORTER_ASSERT(r, *found == 4.0);
40
41 map.foreach([](int key, double* d){ *d = -key; });
42 REPORTER_ASSERT(r, count(map) == 1);
43
44 found = map.find(3);
45 REPORTER_ASSERT(r, found);
46 REPORTER_ASSERT(r, *found == -3.0);
47
48 REPORTER_ASSERT(r, !map.find(2));
49
50 const int N = 20;
51
52 for (int i = 0; i < N; i++) {
53 map.set(i, 2.0*i);
54 }
55
56 // Test walking the map with foreach(const K&, V)
57 map.foreach([&](const int& key, double value) {
58 REPORTER_ASSERT(r, key * 2 == value);
59 });
60
61 // Test walking the map with foreach(const K&, V*)
62 map.foreach([&](const int& key, double* value) {
63 REPORTER_ASSERT(r, key * 2 == *value);
64 });
65
66 // Test walking the map with foreach(const Pair&)
67 map.foreach([&](const THashMap<int, double>::Pair& pair) {
68 REPORTER_ASSERT(r, pair.first * 2 == pair.second);
69 });
70
71 // Test walking the map with iterators, using preincrement (++iter).
72 for (THashMap<int, double>::Iter iter = map.begin(); iter != map.end(); ++iter) {
73 REPORTER_ASSERT(r, iter->first * 2 == (*iter).second);
74 }
75
76 // Test walking the map with range-based for.
77 for (auto& entry : map) {
78 REPORTER_ASSERT(r, entry.first * 2 == entry.second);
79 }
80
81 // Ensure that iteration works equally well on a const map, using postincrement (iter++).
82 const auto& cmap = map;
83 for (THashMap<int, double>::Iter iter = cmap.begin(); iter != cmap.end(); iter++) {
84 REPORTER_ASSERT(r, iter->first * 2 == (*iter).second);
85 }
86
87 // Ensure that range-based for works equally well on a const map.
88 for (const auto& entry : cmap) {
89 REPORTER_ASSERT(r, entry.first * 2 == entry.second);
90 }
91
92 // Ensure that structured bindings work.
93 for (const auto& [number, timesTwo] : cmap) {
94 REPORTER_ASSERT(r, number * 2 == timesTwo);
95 }
96
98
99 for (int i = 0; i < N; i++) {
100 found = map.find(i);
101 REPORTER_ASSERT(r, found);
102 REPORTER_ASSERT(r, *found == i*2.0);
103
104 found = clone.find(i);
105 REPORTER_ASSERT(r, found);
106 REPORTER_ASSERT(r, *found == i*2.0);
107 }
108 for (int i = N; i < 2*N; i++) {
109 REPORTER_ASSERT(r, !map.find(i));
110 REPORTER_ASSERT(r, !clone.find(i));
111 }
112
113 REPORTER_ASSERT(r, map.count() == N);
114 REPORTER_ASSERT(r, clone.count() == N);
115
116 for (int i = 0; i < N/2; i++) {
117 map.remove(i);
118 }
119 for (int i = 0; i < N; i++) {
120 found = map.find(i);
121 REPORTER_ASSERT(r, (found == nullptr) == (i < N/2));
122
123 found = clone.find(i);
124 REPORTER_ASSERT(r, *found == i*2.0);
125 }
126 REPORTER_ASSERT(r, map.count() == N/2);
127 REPORTER_ASSERT(r, clone.count() == N);
128
129 map.reset();
130 REPORTER_ASSERT(r, map.count() == 0);
131 REPORTER_ASSERT(r, clone.count() == N);
132
133 clone = map;
134 REPORTER_ASSERT(r, clone.count() == 0);
135
136 {
137 // Test that we don't leave dangling values in empty slots.
139 auto ref = sk_make_sp<SkRefCnt>();
140 REPORTER_ASSERT(r, ref->unique());
141
142 refMap.set(0, ref);
143 REPORTER_ASSERT(r, refMap.count() == 1);
144 REPORTER_ASSERT(r, !ref->unique());
145
146 refMap.remove(0);
147 REPORTER_ASSERT(r, refMap.count() == 0);
148 REPORTER_ASSERT(r, ref->unique());
149 }
150}
151
152DEF_TEST(HashMapCtor, r) {
153 THashMap<int, std::string_view> map{{1, "one"}, {2, "two"}, {3, "three"}, {4, "four"}};
154 REPORTER_ASSERT(r, map.count() == 4);
155 REPORTER_ASSERT(r, map.approxBytesUsed() >= 4 * (sizeof(int) + sizeof(std::string_view)));
156
157 std::string_view* found = map.find(1);
158 REPORTER_ASSERT(r, found);
159 REPORTER_ASSERT(r, *found == "one");
160
161 found = map.find(2);
162 REPORTER_ASSERT(r, found);
163 REPORTER_ASSERT(r, *found == "two");
164
165 found = map.find(3);
166 REPORTER_ASSERT(r, found);
167 REPORTER_ASSERT(r, *found == "three");
168
169 found = map.find(4);
170 REPORTER_ASSERT(r, found);
171 REPORTER_ASSERT(r, *found == "four");
172
173 found = map.find(5);
174 REPORTER_ASSERT(r, !found);
175
176 found = map.find(6);
177 REPORTER_ASSERT(r, !found);
178}
179
180DEF_TEST(HashMapCtorOneElem, r) {
181 // Start out with a single element. The initializer list constructor sets the capacity
182 // conservatively. Searching for elements beyond the capacity should succeed.
184 REPORTER_ASSERT(r, map.count() == 1);
185 REPORTER_ASSERT(r, map.approxBytesUsed() >= (sizeof(int) + sizeof(std::string_view)));
186
187 std::string_view* found = map.find(1);
188 REPORTER_ASSERT(r, found);
189 REPORTER_ASSERT(r, *found == "one");
190
191 found = map.find(2);
192 REPORTER_ASSERT(r, !found);
193
194 // Grow the collection by one element. Searching for non-existing elements should still succeed.
195 map.set(2, "two");
196 found = map.find(2);
197 REPORTER_ASSERT(r, found);
198 REPORTER_ASSERT(r, *found == "two");
199
200 found = map.find(3);
201 REPORTER_ASSERT(r, !found);
202}
203
204DEF_TEST(HashSetCtor, r) {
205 THashSet<std::string_view> set{"one", "two", "three", "four"};
206 REPORTER_ASSERT(r, set.count() == 4);
207 REPORTER_ASSERT(r, set.approxBytesUsed() >= 4 * sizeof(std::string_view));
208
209 REPORTER_ASSERT(r, set.contains("one"));
210 REPORTER_ASSERT(r, set.contains("two"));
211 REPORTER_ASSERT(r, set.contains("three"));
212 REPORTER_ASSERT(r, set.contains("four"));
213 REPORTER_ASSERT(r, !set.contains("five"));
214 REPORTER_ASSERT(r, !set.contains("six"));
215}
216
217DEF_TEST(HashSetCtorOneElem, r) {
218 // Start out with a single element. The initializer list constructor sets the capacity
219 // conservatively. Searching for elements beyond the capacity should succeed.
221 REPORTER_ASSERT(r, set.count() == 1);
222 REPORTER_ASSERT(r, set.approxBytesUsed() >= sizeof(std::string_view));
223
224 REPORTER_ASSERT(r, set.contains("one"));
225 REPORTER_ASSERT(r, !set.contains("two"));
226
227 // Grow the collection by one element. Searching for non-existing elements should still succeed.
228 set.add("two");
229 REPORTER_ASSERT(r, set.contains("one"));
230 REPORTER_ASSERT(r, set.contains("two"));
231 REPORTER_ASSERT(r, !set.contains("three"));
232}
233
234template <typename T>
237
238 set.add(T("Hello"));
239 set.add(T("World"));
240 REPORTER_ASSERT(r, set.count() == 2);
241 REPORTER_ASSERT(r, set.contains(T("Hello")));
242 REPORTER_ASSERT(r, set.contains(T("World")));
243 REPORTER_ASSERT(r, !set.contains(T("Goodbye")));
244 REPORTER_ASSERT(r, set.find(T("Hello")));
245 REPORTER_ASSERT(r, *set.find(T("Hello")) == T("Hello"));
246
247 // Test walking the set with iterators, using preincrement (++iter).
248 for (typename THashSet<T>::Iter iter = set.begin(); iter != set.end(); ++iter) {
249 REPORTER_ASSERT(r, *iter == T("Hello") || *iter == T("World"));
250 }
251
252 // Test walking the set with iterators, using postincrement (iter++).
253 for (typename THashSet<T>::Iter iter = set.begin(); iter != set.end(); iter++) {
254 REPORTER_ASSERT(r, *iter == T("Hello") || *iter == T("World"));
255 }
256
257 // Test walking the set with range-based for.
258 for (auto& entry : set) {
259 REPORTER_ASSERT(r, entry == T("Hello") || entry == T("World"));
260 }
261
262 // Ensure that iteration works equally well on a const set.
263 const auto& cset = set;
264 for (typename THashSet<T>::Iter iter = cset.begin(); iter != cset.end(); iter++) {
265 REPORTER_ASSERT(r, *iter == T("Hello") || *iter == T("World"));
266 }
267
268 // Ensure that range-based for works equally well on a const set.
269 for (auto& entry : cset) {
270 REPORTER_ASSERT(r, entry == T("Hello") || entry == T("World"));
271 }
272
273 THashSet<T> clone = set;
274 REPORTER_ASSERT(r, clone.count() == 2);
275 REPORTER_ASSERT(r, clone.contains(T("Hello")));
276 REPORTER_ASSERT(r, clone.contains(T("World")));
277 REPORTER_ASSERT(r, !clone.contains(T("Goodbye")));
278 REPORTER_ASSERT(r, clone.find(T("Hello")));
279 REPORTER_ASSERT(r, *clone.find(T("Hello")) == T("Hello"));
280
281 set.remove(T("Hello"));
282 REPORTER_ASSERT(r, !set.contains(T("Hello")));
283 REPORTER_ASSERT(r, set.count() == 1);
284 REPORTER_ASSERT(r, clone.contains(T("Hello")));
285 REPORTER_ASSERT(r, clone.count() == 2);
286
287 set.reset();
288 REPORTER_ASSERT(r, set.count() == 0);
289
290 clone = set;
291 REPORTER_ASSERT(r, clone.count() == 0);
292}
293
294DEF_TEST(HashSetWithSkString, r) {
295 test_hash_set<SkString>(r);
296}
297
298DEF_TEST(HashSetWithStdString, r) {
299 test_hash_set<std::string>(r);
300}
301
302DEF_TEST(HashSetWithStdStringView, r) {
303 test_hash_set<std::string_view>(r);
304}
305
306namespace {
307
308class CopyCounter {
309public:
310 CopyCounter() : fID(0), fCounter(nullptr) {}
311
312 CopyCounter(uint32_t id, uint32_t* counter) : fID(id), fCounter(counter) {}
313
314 CopyCounter(const CopyCounter& other)
315 : fID(other.fID)
316 , fCounter(other.fCounter) {
317 SkASSERT(fCounter);
318 *fCounter += 1;
319 }
320
321 void operator=(const CopyCounter& other) {
322 fID = other.fID;
323 fCounter = other.fCounter;
324 *fCounter += 1;
325 }
326
327 CopyCounter(CopyCounter&& other) { *this = std::move(other); }
328 void operator=(CopyCounter&& other) {
329 fID = other.fID;
330 fCounter = other.fCounter;
331 }
332
333
334 bool operator==(const CopyCounter& other) const {
335 return fID == other.fID;
336 }
337
338private:
339 uint32_t fID;
340 uint32_t* fCounter;
341};
342
343struct HashCopyCounter {
344 uint32_t operator()(const CopyCounter&) const {
345 return 0; // let them collide, what do we care?
346 }
347};
348
349} // namespace
350
351DEF_TEST(HashSetCopyCounter, r) {
353
354 uint32_t globalCounter = 0;
355 CopyCounter copyCounter1(1, &globalCounter);
356 CopyCounter copyCounter2(2, &globalCounter);
357 REPORTER_ASSERT(r, globalCounter == 0);
358
359 set.add(copyCounter1);
360 REPORTER_ASSERT(r, globalCounter == 1);
361 REPORTER_ASSERT(r, set.contains(copyCounter1));
362 REPORTER_ASSERT(r, globalCounter == 1);
363 set.add(copyCounter1);
364 // We allow copies for same-value adds for now.
365 REPORTER_ASSERT(r, globalCounter == 2);
366
367 set.add(copyCounter2);
368 REPORTER_ASSERT(r, globalCounter == 3);
369 REPORTER_ASSERT(r, set.contains(copyCounter1));
370 REPORTER_ASSERT(r, set.contains(copyCounter2));
371 REPORTER_ASSERT(r, globalCounter == 3);
372 set.add(copyCounter1);
373 set.add(copyCounter2);
374 // We allow copies for same-value adds for now.
375 REPORTER_ASSERT(r, globalCounter == 5);
376}
377
378DEF_TEST(HashFindOrNull, r) {
379 struct Entry {
380 int key = 0;
381 int val = 0;
382 };
383
384 struct HashTraits {
385 static int GetKey(const Entry* e) { return e->key; }
386 static uint32_t Hash(int key) { return key; }
387 };
388
390
391 REPORTER_ASSERT(r, nullptr == table.findOrNull(7));
392
393 Entry seven = { 7, 24 };
394 table.set(&seven);
395
396 REPORTER_ASSERT(r, &seven == table.findOrNull(7));
397}
398
399DEF_TEST(HashTableGrowsAndShrinks, r) {
401 auto check_count_cap = [&](int count, int cap) {
402 REPORTER_ASSERT(r, s.count() == count);
403 REPORTER_ASSERT(r, s.approxBytesUsed() == (sizeof(int) + sizeof(uint32_t)) * cap);
404 };
405
406 // Add and remove some elements to test basic growth and shrink patterns.
407 check_count_cap(0,0);
408 s.add(1); check_count_cap(1,4);
409 s.add(2); check_count_cap(2,4);
410 s.add(3); check_count_cap(3,4);
411 s.add(4); check_count_cap(4,8);
412
413 s.remove(4); check_count_cap(3,8);
414 s.remove(3); check_count_cap(2,4);
415 s.remove(2); check_count_cap(1,4);
416 s.remove(1); check_count_cap(0,4);
417
418 s.add(1); check_count_cap(1,4);
419 s.add(2); check_count_cap(2,4);
420 s.add(3); check_count_cap(3,4);
421 s.add(4); check_count_cap(4,8);
422
423 // Add and remove single elements repeatedly to test hysteresis
424 // avoids reallocating these small tables all the time.
425 for (int i = 0; i < 10; i++) {
426 s. add(5); check_count_cap(5,8);
427 s.remove(5); check_count_cap(4,8);
428 }
429
430 s.remove(4); check_count_cap(3,8);
431 for (int i = 0; i < 10; i++) {
432 s. add(4); check_count_cap(4,8);
433 s.remove(4); check_count_cap(3,8);
434 }
435
436 s.remove(3); check_count_cap(2,4);
437 for (int i = 0; i < 10; i++) {
438 s. add(4); check_count_cap(3,4);
439 s.remove(4); check_count_cap(2,4);
440 }
441
442 s.remove(2); check_count_cap(1,4);
443 for (int i = 0; i < 10; i++) {
444 s. add(2); check_count_cap(2,4);
445 s.remove(2); check_count_cap(1,4);
446 }
447}
448
449DEF_TEST(HashMapSwap, r) {
450 // Swap two maps.
451 THashMap<int, std::string_view> a{{1, "one"}, {2, "two"}, {3, "three"}, {4, "four"}};
452 THashMap<int, std::string_view> b{{1, "ichi"}, {2, "ni"}, {3, "san"}};
453
454 a.swap(b);
455 REPORTER_ASSERT(r, a.count() == 3);
456 REPORTER_ASSERT(r, a[1] == "ichi");
457 REPORTER_ASSERT(r, a[2] == "ni");
458 REPORTER_ASSERT(r, a[3] == "san");
459
460 REPORTER_ASSERT(r, b.count() == 4);
461 REPORTER_ASSERT(r, b[1] == "one");
462 REPORTER_ASSERT(r, b[2] == "two");
463 REPORTER_ASSERT(r, b[3] == "three");
464 REPORTER_ASSERT(r, b[4] == "four");
465
466 // Swap with an rvalue.
468 REPORTER_ASSERT(r, a.empty());
469}
470
471DEF_TEST(HashSetSwap, r) {
472 // Swap two maps.
473 THashSet<std::string_view> a{"one", "two", "three", "four"};
474 THashSet<std::string_view> b{"ichi", "ni", "san"};
475
476 a.swap(b);
477 REPORTER_ASSERT(r, a.count() == 3);
478 REPORTER_ASSERT(r, a.contains("ichi"));
479 REPORTER_ASSERT(r, a.contains("ni"));
480 REPORTER_ASSERT(r, a.contains("san"));
481
482 REPORTER_ASSERT(r, b.count() == 4);
483 REPORTER_ASSERT(r, b.contains("one"));
484 REPORTER_ASSERT(r, b.contains("two"));
485 REPORTER_ASSERT(r, b.contains("three"));
486 REPORTER_ASSERT(r, b.contains("four"));
487
488 // Swap with an rvalue.
490 REPORTER_ASSERT(r, a.empty());
491}
DEF_TEST(HashMap, r)
Definition: HashTest.cpp:29
static int count(const THashMap< int, double > &map)
Definition: HashTest.cpp:23
static void test_hash_set(skiatest::Reporter *r)
Definition: HashTest.cpp:235
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
SI F table(const skcms_Curve *curve, F v)
#define N
Definition: beziers.cpp:19
bool contains(double x, double y)
Definition: path.cc:287
int count() const
Definition: SkTHash.h:471
V * find(const K &key) const
Definition: SkTHash.h:494
V * set(K key, V val)
Definition: SkTHash.h:487
void remove(const K &key)
Definition: SkTHash.h:509
typename THashTable< Pair, K >::template Iter< std::pair< K, V > > Iter
Definition: SkTHash.h:540
int count() const
Definition: SkTHash.h:579
const T * find(const T &item) const
Definition: SkTHash.h:599
bool contains(const T &item) const
Definition: SkTHash.h:595
typename THashTable< T, T, Traits >::template Iter< T > Iter
Definition: SkTHash.h:620
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
static bool b
struct MyStruct s
struct MyStruct a[10]
uint8_t value
static uint32_t Hash(uint32_t key)
Definition: hashmap_test.cc:65
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not set
Definition: switches.h:76
bool operator==(C p1, const scoped_nsprotocol< C > &p2)
SI auto map(std::index_sequence< I... >, Fn &&fn, const Args &... args) -> skvx::Vec< sizeof...(I), decltype(fn(args[0]...))>
Definition: SkVx.h:680
#define T
Definition: precompiler.cc:65
const uintptr_t id