Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
97 THashMap<int, double> clone = map;
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.
183 THashMap<int, std::string_view> map{{1, "one"}};
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>
236 THashSet<T> set;
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
378
379DEF_TEST(HashFindOrNull, r) {
380 struct Entry {
381 int key = 0;
382 int val = 0;
383 };
384
385 struct HashTraits {
386 static int GetKey(const Entry* e) { return e->key; }
387 static uint32_t Hash(int key) { return key; }
388 };
389
391
392 REPORTER_ASSERT(r, nullptr == table.findOrNull(7));
393
394 Entry seven = { 7, 24 };
395 table.set(&seven);
396
397 REPORTER_ASSERT(r, &seven == table.findOrNull(7));
398}
399
400DEF_TEST(HashTableGrowsAndShrinks, r) {
402 auto check_count_cap = [&](int count, int cap) {
403 REPORTER_ASSERT(r, s.count() == count);
404 REPORTER_ASSERT(r, s.approxBytesUsed() == (sizeof(int) + sizeof(uint32_t)) * cap);
405 };
406
407 // Add and remove some elements to test basic growth and shrink patterns.
408 check_count_cap(0,0);
409 s.add(1); check_count_cap(1,4);
410 s.add(2); check_count_cap(2,4);
411 s.add(3); check_count_cap(3,4);
412 s.add(4); check_count_cap(4,8);
413
414 s.remove(4); check_count_cap(3,8);
415 s.remove(3); check_count_cap(2,4);
416 s.remove(2); check_count_cap(1,4);
417 s.remove(1); check_count_cap(0,4);
418
419 s.add(1); check_count_cap(1,4);
420 s.add(2); check_count_cap(2,4);
421 s.add(3); check_count_cap(3,4);
422 s.add(4); check_count_cap(4,8);
423
424 // Add and remove single elements repeatedly to test hysteresis
425 // avoids reallocating these small tables all the time.
426 for (int i = 0; i < 10; i++) {
427 s. add(5); check_count_cap(5,8);
428 s.remove(5); check_count_cap(4,8);
429 }
430
431 s.remove(4); check_count_cap(3,8);
432 for (int i = 0; i < 10; i++) {
433 s. add(4); check_count_cap(4,8);
434 s.remove(4); check_count_cap(3,8);
435 }
436
437 s.remove(3); check_count_cap(2,4);
438 for (int i = 0; i < 10; i++) {
439 s. add(4); check_count_cap(3,4);
440 s.remove(4); check_count_cap(2,4);
441 }
442
443 s.remove(2); check_count_cap(1,4);
444 for (int i = 0; i < 10; i++) {
445 s. add(2); check_count_cap(2,4);
446 s.remove(2); check_count_cap(1,4);
447 }
448}
int count
static void test_hash_set(skiatest::Reporter *r)
Definition HashTest.cpp:235
#define SkASSERT(cond)
Definition SkAssert.h:116
#define DEF_TEST(name, reporter)
Definition Test.h:312
#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:460
V * find(const K &key) const
Definition SkTHash.h:479
V * set(K key, V val)
Definition SkTHash.h:472
void remove(const K &key)
Definition SkTHash.h:494
typename THashTable< Pair, K >::template Iter< std::pair< K, V > > Iter
Definition SkTHash.h:525
int count() const
Definition SkTHash.h:564
const T * find(const T &item) const
Definition SkTHash.h:580
bool contains(const T &item) const
Definition SkTHash.h:576
typename THashTable< T, T, Traits >::template Iter< T > Iter
Definition SkTHash.h:601
bool operator==(const FlutterPoint &a, const FlutterPoint &b)
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
struct MyStruct s
uint8_t value
#define T
const uintptr_t id