proxygen
F14SetTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <folly/container/F14Set.h>
18 
19 #include <unordered_map>
20 
21 #include <folly/Conv.h>
22 #include <folly/FBString.h>
25 
26 template <template <typename, typename, typename, typename> class TSet>
28  using std::swap;
29 
30  TSet<
31  int,
35  m0, m1;
37  swap(m0, m1);
38 
39  EXPECT_EQ(
41 }
42 
43 TEST(F14Set, customSwap) {
44  testCustomSwap<folly::F14ValueSet>();
45  testCustomSwap<folly::F14NodeSet>();
46  testCustomSwap<folly::F14VectorSet>();
47  testCustomSwap<folly::F14FastSet>();
48 }
49 
50 namespace {
51 template <
52  template <typename, typename, typename, typename> class TSet,
53  typename K>
54 void runAllocatedMemorySizeTest() {
55  using namespace folly::f14;
56  using namespace folly::f14::detail;
57  using A = SwapTrackingAlloc<K>;
58 
59  resetTracking();
60  {
61  TSet<K, DefaultHasher<K>, DefaultKeyEqual<K>, A> s;
62 
63  // if F14 intrinsics are not available then we fall back to using
64  // std::unordered_set underneath, but in that case the allocation
65  // info is only best effort
66  bool preciseAllocInfo = getF14IntrinsicsMode() != F14IntrinsicsMode::None;
67 
68  if (preciseAllocInfo) {
70  EXPECT_EQ(s.getAllocatedMemorySize(), 0);
71  }
72  auto emptySetAllocatedMemorySize = testAllocatedMemorySize;
73  auto emptySetAllocatedBlockCount = testAllocatedBlockCount;
74 
75  for (size_t i = 0; i < 1000; ++i) {
76  s.insert(folly::to<K>(i));
77  s.erase(folly::to<K>(i / 10 + 2));
78  if (preciseAllocInfo) {
79  EXPECT_EQ(testAllocatedMemorySize, s.getAllocatedMemorySize());
80  }
81  EXPECT_GE(s.getAllocatedMemorySize(), sizeof(K) * s.size());
82  std::size_t size = 0;
83  std::size_t count = 0;
84  s.visitAllocationClasses([&](std::size_t, std::size_t) mutable {});
85  s.visitAllocationClasses([&](std::size_t bytes, std::size_t n) {
86  size += bytes * n;
87  count += n;
88  });
89  if (preciseAllocInfo) {
92  }
93  }
94 
95  s = decltype(s){};
96  EXPECT_EQ(testAllocatedMemorySize, emptySetAllocatedMemorySize);
97  EXPECT_EQ(testAllocatedBlockCount, emptySetAllocatedBlockCount);
98 
99  s.reserve(5);
101  s = {};
103  }
106 }
107 
108 template <typename K>
109 void runAllocatedMemorySizeTests() {
110  runAllocatedMemorySizeTest<folly::F14ValueSet, K>();
111  runAllocatedMemorySizeTest<folly::F14NodeSet, K>();
112  runAllocatedMemorySizeTest<folly::F14VectorSet, K>();
113  runAllocatedMemorySizeTest<folly::F14FastSet, K>();
114 }
115 } // namespace
116 
117 TEST(F14Set, getAllocatedMemorySize) {
118  runAllocatedMemorySizeTests<bool>();
119  runAllocatedMemorySizeTests<int>();
120  runAllocatedMemorySizeTests<long>();
121  runAllocatedMemorySizeTests<double>();
122  runAllocatedMemorySizeTests<std::string>();
123  runAllocatedMemorySizeTests<folly::fbstring>();
124 }
125 
126 template <typename S>
128  S set;
129 
130  for (int i = 0; i < n; ++i) {
131  set.insert(i);
132  set.erase(i / 2);
133  }
134 
135  std::unordered_map<uintptr_t, bool> visited;
136  for (auto& entry : set) {
137  visited[reinterpret_cast<uintptr_t>(&entry)] = false;
138  }
139 
140  set.visitContiguousRanges([&](auto b, auto e) {
141  for (auto i = b; i != e; ++i) {
142  auto iter = visited.find(reinterpret_cast<uintptr_t>(i));
143  ASSERT_TRUE(iter != visited.end());
144  EXPECT_FALSE(iter->second);
145  iter->second = true;
146  }
147  });
148 
149  // ensure no entries were skipped
150  for (auto& e : visited) {
151  EXPECT_TRUE(e.second);
152  }
153 }
154 
155 template <typename S>
157  runVisitContiguousRangesTest<S>(0); // empty
158  runVisitContiguousRangesTest<S>(5); // single chunk
159  runVisitContiguousRangesTest<S>(1000); // many chunks
160 }
161 
162 TEST(F14ValueSet, visitContiguousRanges) {
163  runVisitContiguousRangesTest<folly::F14ValueSet<int>>();
164 }
165 
166 TEST(F14NodeSet, visitContiguousRanges) {
167  runVisitContiguousRangesTest<folly::F14NodeSet<int>>();
168 }
169 
170 TEST(F14VectorSet, visitContiguousRanges) {
171  runVisitContiguousRangesTest<folly::F14VectorSet<int>>();
172 }
173 
174 TEST(F14FastSet, visitContiguousRanges) {
175  runVisitContiguousRangesTest<folly::F14FastSet<int>>();
176 }
177 
179 #if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
180 
182 #include <chrono>
183 #include <random>
184 #include <string>
185 #include <unordered_set>
186 
187 #include <folly/Range.h>
188 
189 using namespace folly;
190 using namespace folly::f14;
191 using namespace folly::string_piece_literals;
192 
193 namespace {
194 std::string s(char const* p) {
195  return p;
196 }
197 } // namespace
198 
199 template <typename T>
200 void runSimple() {
201  T h;
202 
203  EXPECT_EQ(h.size(), 0);
204 
205  h.insert(s("abc"));
206  EXPECT_TRUE(h.find(s("def")) == h.end());
207  EXPECT_FALSE(h.find(s("abc")) == h.end());
208  h.insert(s("ghi"));
209  EXPECT_EQ(h.size(), 2);
210  h.erase(h.find(s("abc")));
211  EXPECT_EQ(h.size(), 1);
212 
213  T h2(std::move(h));
214  EXPECT_EQ(h.size(), 0);
215  EXPECT_TRUE(h.begin() == h.end());
216  EXPECT_EQ(h2.size(), 1);
217 
218  EXPECT_TRUE(h2.find(s("abc")) == h2.end());
219  EXPECT_EQ(*h2.begin(), s("ghi"));
220  {
221  auto i = h2.begin();
222  EXPECT_FALSE(i == h2.end());
223  ++i;
224  EXPECT_TRUE(i == h2.end());
225  }
226 
227  T h3;
228  h3.insert(s("xxx"));
229  h3.insert(s("yyy"));
230  h3 = std::move(h2);
231  EXPECT_EQ(h2.size(), 0);
232  EXPECT_EQ(h3.size(), 1);
233  EXPECT_TRUE(h3.find(s("xxx")) == h3.end());
234 
235  for (uint64_t i = 0; i < 1000; ++i) {
236  h.insert(std::move(to<std::string>(i * i * i)));
237  EXPECT_EQ(h.size(), i + 1);
238  }
239  {
240  using std::swap;
241  swap(h, h2);
242  }
243  for (uint64_t i = 0; i < 1000; ++i) {
244  EXPECT_TRUE(h2.find(to<std::string>(i * i * i)) != h2.end());
245  EXPECT_EQ(*h2.find(to<std::string>(i * i * i)), to<std::string>(i * i * i));
246  EXPECT_TRUE(h2.find(to<std::string>(i * i * i + 2)) == h2.end());
247  }
248 
249  T h4{h2};
250  EXPECT_EQ(h2.size(), 1000);
251  EXPECT_EQ(h4.size(), 1000);
252 
253  T h5{std::move(h2)};
254  T h6;
255  h6 = h4;
256  T h7 = h4;
257 
258  T h8({s("abc"), s("def")});
259  T h9({s("abd"), s("def")});
260  EXPECT_EQ(h8.size(), 2);
261  EXPECT_EQ(h8.count(s("abc")), 1);
262  EXPECT_EQ(h8.count(s("xyz")), 0);
263 
264  EXPECT_TRUE(h7 != h8);
265  EXPECT_TRUE(h8 != h9);
266 
267  h8 = std::move(h7);
268  // h2 and h7 are moved from, h4, h5, h6, and h8 should be identical
269 
270  EXPECT_TRUE(h4 == h8);
271 
272  EXPECT_TRUE(h2.empty());
273  EXPECT_TRUE(h7.empty());
274  for (uint64_t i = 0; i < 1000; ++i) {
275  auto k = to<std::string>(i * i * i);
276  EXPECT_EQ(h4.count(k), 1);
277  EXPECT_EQ(h5.count(k), 1);
278  EXPECT_EQ(h6.count(k), 1);
279  EXPECT_EQ(h8.count(k), 1);
280  }
281 
282  h8.clear();
283  h8.emplace(s("abc"));
284  EXPECT_GT(h8.bucket_count(), 1);
285  h8 = {};
286  EXPECT_GT(h8.bucket_count(), 1);
287  h9 = {s("abc"), s("def")};
288  EXPECT_TRUE(h8.empty());
289  EXPECT_EQ(h9.size(), 2);
290 
291  auto expectH8 = [&](T& ref) { EXPECT_EQ(&ref, &h8); };
292  expectH8((h8 = h2));
293  expectH8((h8 = std::move(h2)));
294  expectH8((h8 = {}));
295 
304 }
305 
306 template <typename T>
307 void runRehash() {
308  unsigned n = 10000;
309  T h;
310  for (unsigned i = 0; i < n; ++i) {
311  h.insert(to<std::string>(i));
312  }
313  EXPECT_EQ(h.size(), n);
315 }
316 
317 // T should be a set of uint64_t
318 template <typename T>
319 void runRandom() {
320  using R = std::unordered_set<uint64_t>;
321 
322  std::mt19937_64 gen(0);
323  std::uniform_int_distribution<> pctDist(0, 100);
324  std::uniform_int_distribution<uint64_t> bitsBitsDist(1, 6);
325  T t0;
326  T t1;
327  R r0;
328  R r1;
329 
330  for (std::size_t reps = 0; reps < 100000; ++reps) {
331  // discardBits will be from 0 to 62
332  auto discardBits = (uint64_t{1} << bitsBitsDist(gen)) - 2;
333  auto k = gen() >> discardBits;
334  auto pct = pctDist(gen);
335 
336  EXPECT_EQ(t0.size(), r0.size());
337  if (pct < 15) {
338  // insert
339  auto t = t0.insert(k);
340  auto r = r0.insert(k);
341  EXPECT_EQ(t.second, r.second);
342  EXPECT_EQ(*t.first, *r.first);
343  } else if (pct < 25) {
344  // emplace
345  auto t = t0.emplace(k);
346  auto r = r0.emplace(k);
347  EXPECT_EQ(t.second, r.second);
348  EXPECT_EQ(*t.first, *r.first);
349  } else if (pct < 30) {
350  // bulk insert
351  t0.insert(t1.begin(), t1.end());
352  r0.insert(r1.begin(), r1.end());
353  } else if (pct < 40) {
354  // erase by key
355  auto t = t0.erase(k);
356  auto r = r0.erase(k);
357  EXPECT_EQ(t, r);
358  } else if (pct < 47) {
359  // erase by iterator
360  if (t0.size() > 0) {
361  auto r = r0.find(k);
362  if (r == r0.end()) {
363  r = r0.begin();
364  }
365  k = *r;
366  auto t = t0.find(k);
367  t = t0.erase(t);
368  if (t != t0.end()) {
369  EXPECT_NE(*t, k);
370  }
371  r = r0.erase(r);
372  if (r != r0.end()) {
373  EXPECT_NE(*r, k);
374  }
375  }
376  } else if (pct < 50) {
377  // bulk erase
378  if (t0.size() > 0) {
379  auto r = r0.find(k);
380  if (r == r0.end()) {
381  r = r0.begin();
382  }
383  k = *r;
384  auto t = t0.find(k);
385  auto firstt = t;
386  auto lastt = ++t;
387  t = t0.erase(firstt, lastt);
388  if (t != t0.end()) {
389  EXPECT_NE(*t, k);
390  }
391  auto firstr = r;
392  auto lastr = ++r;
393  r = r0.erase(firstr, lastr);
394  if (r != r0.end()) {
395  EXPECT_NE(*r, k);
396  }
397  }
398  } else if (pct < 58) {
399  // find
400  auto t = t0.find(k);
401  auto r = r0.find(k);
402  EXPECT_EQ((t == t0.end()), (r == r0.end()));
403  if (t != t0.end() && r != r0.end()) {
404  EXPECT_EQ(*t, *r);
405  }
406  EXPECT_EQ(t0.count(k), r0.count(k));
407  } else if (pct < 60) {
408  // equal_range
409  auto t = t0.equal_range(k);
410  auto r = r0.equal_range(k);
411  EXPECT_EQ((t.first == t.second), (r.first == r.second));
412  if (t.first != t.second && r.first != r.second) {
413  EXPECT_EQ(*t.first, *r.first);
414  t.first++;
415  r.first++;
416  EXPECT_TRUE(t.first == t.second);
417  EXPECT_TRUE(r.first == r.second);
418  }
419  } else if (pct < 65) {
420  // iterate
421  uint64_t t = 0;
422  for (auto& e : t0) {
423  t += e + 1000;
424  }
425  uint64_t r = 0;
426  for (auto& e : r0) {
427  r += e + 1000;
428  }
429  EXPECT_EQ(t, r);
430  } else if (pct < 69) {
431  // swap
432  using std::swap;
433  swap(t0, t1);
434  swap(r0, r1);
435  } else if (pct < 70) {
436  // swap
437  t0.swap(t1);
438  r0.swap(r1);
439  } else if (pct < 72) {
440  // default construct
441  t0.~T();
442  new (&t0) T();
443  r0.~R();
444  new (&r0) R();
445  } else if (pct < 74) {
446  // default construct with capacity
447  std::size_t capacity = k & 0xffff;
448  t0.~T();
449  new (&t0) T(capacity);
450  r0.~R();
451  new (&r0) R(capacity);
452  } else if (pct < 80) {
453  // bulk iterator construct
454  t0.~T();
455  new (&t0) T(r1.begin(), r1.end());
456  r0.~R();
457  new (&r0) R(r1.begin(), r1.end());
458  } else if (pct < 82) {
459  // initializer list construct
460  auto k2 = gen() >> discardBits;
461  t0.~T();
462  new (&t0) T({k, k, k2});
463  r0.~R();
464  new (&r0) R({k, k, k2});
465  } else if (pct < 88) {
466  // copy construct
467  t0.~T();
468  new (&t0) T(t1);
469  r0.~R();
470  new (&r0) R(r1);
471  } else if (pct < 90) {
472  // move construct
473  t0.~T();
474  new (&t0) T(std::move(t1));
475  r0.~R();
476  new (&r0) R(std::move(r1));
477  } else if (pct < 94) {
478  // copy assign
479  t0 = t1;
480  r0 = r1;
481  } else if (pct < 96) {
482  // move assign
483  t0 = std::move(t1);
484  r0 = std::move(r1);
485  } else if (pct < 98) {
486  // operator==
487  EXPECT_EQ((t0 == t1), (r0 == r1));
488  } else if (pct < 99) {
489  // clear
490  t0.computeStats();
491  t0.clear();
492  r0.clear();
493  } else if (pct < 100) {
494  // reserve
495  auto scale = std::uniform_int_distribution<>(0, 8)(gen);
496  auto delta = std::uniform_int_distribution<>(-2, 2)(gen);
497  std::ptrdiff_t target = (t0.size() * scale) / 4 + delta;
498  if (target >= 0) {
499  t0.reserve(static_cast<std::size_t>(target));
500  r0.reserve(static_cast<std::size_t>(target));
501  }
502  }
503  }
504 }
505 
506 TEST(F14ValueSet, simple) {
507  runSimple<F14ValueSet<std::string>>();
508 }
509 
510 TEST(F14NodeSet, simple) {
511  runSimple<F14NodeSet<std::string>>();
512 }
513 
514 TEST(F14VectorSet, simple) {
515  runSimple<F14VectorSet<std::string>>();
516 }
517 
518 TEST(F14FastSet, simple) {
519  // F14FastSet inherits from a conditional typedef. Verify it compiles.
520  runRandom<F14FastSet<uint64_t>>();
521  runSimple<F14FastSet<std::string>>();
522 }
523 
524 TEST(F14Set, ContainerSize) {
525  {
527  set.insert(10);
528  EXPECT_EQ(sizeof(set), 4 * sizeof(void*));
529  if (alignof(folly::max_align_t) == 16) {
530  // chunks will be allocated as 2 max_align_t-s
531  EXPECT_EQ(set.getAllocatedMemorySize(), 32);
532  } else {
533  // chunks will be allocated using aligned_malloc with the true size
534  EXPECT_EQ(set.getAllocatedMemorySize(), 24);
535  }
536  }
537  {
539  set.insert(10);
540  EXPECT_EQ(sizeof(set), 4 * sizeof(void*));
541  if (alignof(folly::max_align_t) == 16) {
542  // chunks will be allocated as 2 max_align_t-s
543  EXPECT_EQ(set.getAllocatedMemorySize(), 36);
544  } else {
545  // chunks will be allocated using aligned_malloc with the true size
546  EXPECT_EQ(set.getAllocatedMemorySize(), 20 + 2 * sizeof(void*));
547  }
548  }
549  {
551  set.insert(10);
552  EXPECT_EQ(sizeof(set), 8 + 2 * sizeof(void*));
553  EXPECT_EQ(set.getAllocatedMemorySize(), 32);
554  }
555 }
556 
557 TEST(F14VectorMap, reverse_iterator) {
558  using TSet = F14VectorSet<uint64_t>;
559  auto populate = [](TSet& h, uint64_t lo, uint64_t hi) {
560  for (auto i = lo; i < hi; ++i) {
561  h.insert(i);
562  }
563  };
564  auto verify = [](TSet const& h, uint64_t lo, uint64_t hi) {
565  auto loIt = h.find(lo);
566  EXPECT_NE(h.end(), loIt);
567  uint64_t val = lo;
568  for (auto rit = h.riter(loIt); rit != h.rend(); ++rit) {
569  EXPECT_EQ(val, *rit);
570  TSet::const_iterator it = h.iter(rit);
571  EXPECT_EQ(val, *it);
572  val++;
573  }
574  EXPECT_EQ(hi, val);
575  };
576 
577  TSet h;
578  size_t prevSize = 0;
579  size_t newSize = 1;
580  // verify iteration order across rehashes, copies, and moves
581  while (newSize < 10'000) {
582  populate(h, prevSize, newSize);
583  verify(h, 0, newSize);
584  verify(h, newSize / 2, newSize);
585 
586  TSet h2{h};
587  verify(h2, 0, newSize);
588 
589  h = std::move(h2);
590  verify(h, 0, newSize);
591  prevSize = newSize;
592  newSize *= 10;
593  }
594 }
595 
596 TEST(F14ValueSet, rehash) {
597  runRehash<F14ValueSet<std::string>>();
598 }
599 
600 TEST(F14NodeSet, rehash) {
601  runRehash<F14NodeSet<std::string>>();
602 }
603 
604 TEST(F14VectorSet, rehash) {
605  runRehash<F14VectorSet<std::string>>();
606 }
607 
608 TEST(F14ValueSet, random) {
609  runRandom<F14ValueSet<uint64_t>>();
610 }
611 
612 TEST(F14NodeSet, random) {
613  runRandom<F14NodeSet<uint64_t>>();
614 }
615 
616 TEST(F14VectorSet, random) {
617  runRandom<F14VectorSet<uint64_t>>();
618 }
619 
620 TEST(F14ValueSet, grow_stats) {
621  F14ValueSet<uint64_t> h;
622  for (unsigned i = 1; i <= 3072; ++i) {
623  h.insert(i);
624  }
625  // F14ValueSet just before rehash
626  F14TableStats::compute(h);
627  h.insert(0);
628  // F14ValueSet just after rehash
629  F14TableStats::compute(h);
630 }
631 
632 TEST(F14ValueSet, steady_state_stats) {
633  // 10k keys, 14% probability of insert, 90% chance of erase, so the
634  // table should converge to 1400 size without triggering the rehash
635  // that would occur at 1536.
636  F14ValueSet<uint64_t> h;
637  std::mt19937 gen(0);
638  std::uniform_int_distribution<> dist(0, 10000);
639  for (std::size_t i = 0; i < 100000; ++i) {
640  auto key = dist(gen);
641  if (dist(gen) < 1400) {
642  h.insert(key);
643  } else {
644  h.erase(key);
645  }
646  if (((i + 1) % 10000) == 0) {
647  auto stats = F14TableStats::compute(h);
648  // Verify that average miss probe length is bounded despite continued
649  // erase + reuse. p99 of the average across 10M random steps is 4.69,
650  // average is 2.96.
651  EXPECT_LT(f14::expectedProbe(stats.missProbeLengthHisto), 10.0);
652  }
653  }
654  // F14ValueSet at steady state
655  F14TableStats::compute(h);
656 }
657 
658 // S should be a set of Tracked<0>. F should take a set
659 // and a key_type const& or key_type&& and cause it to be inserted
660 template <typename S, typename F>
661 void runInsertCases(std::string const& /* name */, F const& insertFunc) {
662  static_assert(std::is_same<typename S::value_type, Tracked<0>>::value, "");
663  {
664  typename S::value_type k{0};
665  S s;
666  resetTracking();
667  insertFunc(s, k);
668  // fresh key, value_type const& ->
669  // copy is expected
670  EXPECT_EQ(Tracked<0>::counts.dist(Counts{1, 0, 0, 0}), 0);
671  }
672  {
673  typename S::value_type k{0};
674  S s;
675  resetTracking();
676  insertFunc(s, std::move(k));
677  // fresh key, value_type&& ->
678  // move is expected
679  EXPECT_EQ(Tracked<0>::counts.dist(Counts{0, 1, 0, 0}), 0);
680  }
681 }
682 
683 struct DoInsert {
684  template <typename M, typename P>
685  void operator()(M& m, P&& p) const {
686  m.insert(std::forward<P>(p));
687  }
688 };
689 
690 struct DoEmplace1 {
691  template <typename M, typename P>
692  void operator()(M& m, P&& p) const {
693  m.emplace(std::forward<P>(p));
694  }
695 };
696 
697 template <typename S>
698 void runInsertAndEmplace() {
699  {
700  typename S::value_type k1{0};
701  typename S::value_type k2{0};
702  S s;
703  resetTracking();
704  EXPECT_TRUE(s.insert(k1).second);
705  // copy is expected on successful insert
706  EXPECT_EQ(Tracked<0>::counts.dist(Counts{1, 0, 0, 0}), 0);
707 
708  resetTracking();
709  EXPECT_FALSE(s.insert(k2).second);
710  // no copies or moves on failing insert
711  EXPECT_EQ(Tracked<0>::counts.dist(Counts{0, 0, 0, 0}), 0);
712  }
713  {
714  typename S::value_type k1{0};
715  typename S::value_type k2{0};
716  S s;
717  resetTracking();
718  EXPECT_TRUE(s.insert(std::move(k1)).second);
719  // move is expected on successful insert
720  EXPECT_EQ(Tracked<0>::counts.dist(Counts{0, 1, 0, 0}), 0);
721 
722  resetTracking();
723  EXPECT_FALSE(s.insert(std::move(k2)).second);
724  // no copies or moves on failing insert
725  EXPECT_EQ(Tracked<0>::counts.dist(Counts{0, 0, 0, 0}), 0);
726  }
727  {
728  typename S::value_type k1{0};
729  typename S::value_type k2{0};
730  uint64_t k3 = 0;
731  S s;
732  resetTracking();
733  EXPECT_TRUE(s.emplace(k1).second);
734  // copy is expected on successful emplace
735  EXPECT_EQ(Tracked<0>::counts.dist(Counts{1, 0, 0, 0}), 0);
736 
737  resetTracking();
738  EXPECT_FALSE(s.emplace(k2).second);
739  // no copies or moves on failing emplace with value_type
740  EXPECT_EQ(Tracked<0>::counts.dist(Counts{0, 0, 0, 0}), 0);
741 
742  resetTracking();
743  EXPECT_FALSE(s.emplace(k3).second);
744  // copy convert expected for failing emplace with wrong type
745  EXPECT_EQ(Tracked<0>::counts.dist(Counts{0, 0, 1, 0}), 0);
746 
747  s.clear();
748  resetTracking();
749  EXPECT_TRUE(s.emplace(k3).second);
750  // copy convert + move expected for successful emplace with wrong type
751  EXPECT_EQ(Tracked<0>::counts.dist(Counts{0, 1, 1, 0}), 0);
752  }
753  {
754  typename S::value_type k1{0};
755  typename S::value_type k2{0};
756  uint64_t k3 = 0;
757  S s;
758  resetTracking();
759  EXPECT_TRUE(s.emplace(std::move(k1)).second);
760  // move is expected on successful emplace
761  EXPECT_EQ(Tracked<0>::counts.dist(Counts{0, 1, 0, 0}), 0);
762 
763  resetTracking();
764  EXPECT_FALSE(s.emplace(std::move(k2)).second);
765  // no copies or moves on failing emplace with value_type
766  EXPECT_EQ(Tracked<0>::counts.dist(Counts{0, 0, 0, 0}), 0);
767 
768  resetTracking();
769  EXPECT_FALSE(s.emplace(std::move(k3)).second);
770  // move convert expected for failing emplace with wrong type
771  EXPECT_EQ(Tracked<0>::counts.dist(Counts{0, 0, 0, 1}), 0);
772 
773  s.clear();
774  resetTracking();
775  EXPECT_TRUE(s.emplace(std::move(k3)).second);
776  // move convert + move expected for successful emplace with wrong type
777  EXPECT_EQ(Tracked<0>::counts.dist(Counts{0, 1, 0, 1}), 0);
778  }
779 
780  // Calling the default pair constructor via emplace is valid, but not
781  // very useful in real life. Verify that it works.
782  S s;
783  typename S::value_type k;
784  EXPECT_EQ(s.count(k), 0);
785  s.emplace();
786  EXPECT_EQ(s.count(k), 1);
787  s.emplace();
788  EXPECT_EQ(s.count(k), 1);
789 }
790 
791 TEST(F14ValueSet, destructuring) {
792  runInsertAndEmplace<F14ValueSet<Tracked<0>>>();
793 }
794 
795 TEST(F14NodeSet, destructuring) {
796  runInsertAndEmplace<F14NodeSet<Tracked<0>>>();
797 }
798 
799 TEST(F14VectorSet, destructuring) {
800  runInsertAndEmplace<F14VectorSet<Tracked<0>>>();
801 }
802 
803 TEST(F14ValueSet, maxSize) {
804  F14ValueSet<int> s;
805  EXPECT_EQ(
806  s.max_size(), std::numeric_limits<std::size_t>::max() / sizeof(int));
807 }
808 
809 TEST(F14NodeSet, maxSize) {
810  F14NodeSet<int> s;
811  EXPECT_EQ(
812  s.max_size(), std::numeric_limits<std::size_t>::max() / sizeof(int));
813 }
814 
815 TEST(F14VectorSet, maxSize) {
816  F14VectorSet<int> s;
817  EXPECT_EQ(
818  s.max_size(),
819  std::min(
820  std::size_t{std::numeric_limits<uint32_t>::max()},
821  std::numeric_limits<std::size_t>::max() / sizeof(int)));
822 }
823 
824 template <typename S>
825 void runMoveOnlyTest() {
826  S t0;
827  t0.emplace(10);
828  t0.insert(20);
829  S t1{std::move(t0)};
830  EXPECT_TRUE(t0.empty());
831  S t2;
832  EXPECT_TRUE(t2.empty());
833  t2 = std::move(t1);
834  EXPECT_EQ(t2.size(), 2);
835 }
836 
837 TEST(F14ValueSet, moveOnly) {
838  runMoveOnlyTest<F14ValueSet<f14::MoveOnlyTestInt>>();
839 }
840 
841 TEST(F14NodeSet, moveOnly) {
842  runMoveOnlyTest<F14NodeSet<f14::MoveOnlyTestInt>>();
843 }
844 
845 TEST(F14VectorSet, moveOnly) {
846  runMoveOnlyTest<F14VectorSet<f14::MoveOnlyTestInt>>();
847 }
848 
849 TEST(F14FastSet, moveOnly) {
850  runMoveOnlyTest<F14FastSet<f14::MoveOnlyTestInt>>();
851 }
852 
853 template <typename S>
854 void runEraseIntoTest() {
855  S t0;
856  S t1;
857 
858  auto insertIntoT0 = [&t0](auto&& value) {
859  EXPECT_FALSE(value.destroyed);
860  t0.emplace(std::move(value));
861  };
862  auto insertIntoT0Mut = [&](typename S::value_type&& value) mutable {
863  insertIntoT0(std::move(value));
864  };
865 
866  t0.insert(10);
867  t1.insert(20);
868  t1.eraseInto(t1.begin(), insertIntoT0);
869  EXPECT_TRUE(t1.empty());
870  EXPECT_EQ(t0.size(), 2);
871  EXPECT_TRUE(t0.find(10) != t0.end());
872  EXPECT_TRUE(t0.find(20) != t0.end());
873 
874  t1.insert(20);
875  t1.insert(30);
876  t1.insert(40);
877  t1.eraseInto(t1.begin(), t1.end(), insertIntoT0Mut);
878  EXPECT_TRUE(t1.empty());
879  EXPECT_EQ(t0.size(), 4);
880  EXPECT_TRUE(t0.find(30) != t0.end());
881  EXPECT_TRUE(t0.find(40) != t0.end());
882 
883  t1.insert(50);
884  size_t erased = t1.eraseInto(*t1.find(50), insertIntoT0);
885  EXPECT_EQ(erased, 1);
886  EXPECT_TRUE(t1.empty());
887  EXPECT_EQ(t0.size(), 5);
888  EXPECT_TRUE(t0.find(50) != t0.end());
889 
890  typename S::value_type key{60};
891  erased = t1.eraseInto(key, insertIntoT0Mut);
892  EXPECT_EQ(erased, 0);
893  EXPECT_EQ(t0.size(), 5);
894 }
895 
896 TEST(F14ValueSet, eraseInto) {
897  runEraseIntoTest<F14ValueSet<f14::MoveOnlyTestInt>>();
898 }
899 
900 TEST(F14NodeSet, eraseInto) {
901  runEraseIntoTest<F14NodeSet<f14::MoveOnlyTestInt>>();
902 }
903 
904 TEST(F14VectorSet, eraseInto) {
905  runEraseIntoTest<F14VectorSet<f14::MoveOnlyTestInt>>();
906 }
907 
908 TEST(F14FastSet, eraseInto) {
909  runEraseIntoTest<F14FastSet<f14::MoveOnlyTestInt>>();
910 }
911 
912 TEST(F14ValueSet, heterogeneous) {
913  // note: std::string is implicitly convertible to but not from StringPiece
914  using Hasher = folly::transparent<folly::hasher<folly::StringPiece>>;
915  using KeyEqual = folly::transparent<std::equal_to<folly::StringPiece>>;
916 
917  constexpr auto hello = "hello"_sp;
918  constexpr auto buddy = "buddy"_sp;
919  constexpr auto world = "world"_sp;
920 
921  F14ValueSet<std::string, Hasher, KeyEqual> set;
922  set.emplace(hello);
923  set.emplace(world);
924 
925  auto checks = [hello, buddy](auto& ref) {
926  // count
927  EXPECT_EQ(0, ref.count(buddy));
928  EXPECT_EQ(1, ref.count(hello));
929 
930  // find
931  EXPECT_TRUE(ref.end() == ref.find(buddy));
932  EXPECT_EQ(hello, *ref.find(hello));
933 
934  // prehash + find
935  EXPECT_TRUE(ref.end() == ref.find(ref.prehash(buddy), buddy));
936  EXPECT_EQ(hello, *ref.find(ref.prehash(hello), hello));
937 
938  // equal_range
939  EXPECT_TRUE(std::make_pair(ref.end(), ref.end()) == ref.equal_range(buddy));
940  EXPECT_TRUE(
941  std::make_pair(ref.find(hello), ++ref.find(hello)) ==
942  ref.equal_range(hello));
943  };
944 
945  checks(set);
946  checks(folly::as_const(set));
947 }
948 
949 template <typename S>
950 void runStatefulFunctorTest() {
951  bool ranHasher = false;
952  bool ranEqual = false;
953  bool ranAlloc = false;
954  bool ranDealloc = false;
955 
956  auto hasher = [&](int x) {
957  ranHasher = true;
958  return x;
959  };
960  auto equal = [&](int x, int y) {
961  ranEqual = true;
962  return x == y;
963  };
964  auto alloc = [&](std::size_t n) {
965  ranAlloc = true;
966  return std::malloc(n);
967  };
968  auto dealloc = [&](void* p, std::size_t) {
969  ranDealloc = true;
970  std::free(p);
971  };
972 
973  {
974  S set(0, hasher, equal, {alloc, dealloc});
975  set.insert(10);
976  set.insert(10);
977  EXPECT_EQ(set.size(), 1);
978 
979  S set2(set);
980  S set3(std::move(set));
981  set = set2;
982  set2.clear();
983  set2 = std::move(set3);
984  }
985  EXPECT_TRUE(ranHasher);
986  EXPECT_TRUE(ranEqual);
987  EXPECT_TRUE(ranAlloc);
988  EXPECT_TRUE(ranDealloc);
989 }
990 
991 TEST(F14ValueSet, statefulFunctors) {
992  runStatefulFunctorTest<F14ValueSet<
993  int,
994  GenericHasher<int>,
995  GenericEqual<int>,
996  GenericAlloc<int>>>();
997 }
998 
999 TEST(F14NodeSet, statefulFunctors) {
1000  runStatefulFunctorTest<F14NodeSet<
1001  int,
1002  GenericHasher<int>,
1003  GenericEqual<int>,
1004  GenericAlloc<int>>>();
1005 }
1006 
1007 TEST(F14VectorSet, statefulFunctors) {
1008  runStatefulFunctorTest<F14VectorSet<
1009  int,
1010  GenericHasher<int>,
1011  GenericEqual<int>,
1012  GenericAlloc<int>>>();
1013 }
1014 
1015 TEST(F14FastSet, statefulFunctors) {
1016  runStatefulFunctorTest<F14FastSet<
1017  int,
1018  GenericHasher<int>,
1019  GenericEqual<int>,
1020  GenericAlloc<int>>>();
1021 }
1022 
1023 template <typename S>
1024 void runHeterogeneousInsertTest() {
1025  S set;
1026 
1027  resetTracking();
1028  EXPECT_EQ(set.count(10), 0);
1029  EXPECT_EQ(Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), 0)
1030  << Tracked<1>::counts;
1031 
1032  resetTracking();
1033  set.insert(10);
1034  EXPECT_EQ(Tracked<1>::counts.dist(Counts{0, 0, 0, 1}), 0)
1035  << Tracked<1>::counts;
1036 
1037  resetTracking();
1038  int k = 10;
1039  std::vector<int> v({10});
1040  set.insert(10);
1041  set.insert(k);
1042  set.insert(v.begin(), v.end());
1043  set.insert(
1044  std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));
1045  set.emplace(10);
1046  set.emplace(k);
1047  EXPECT_EQ(Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), 0)
1048  << Tracked<1>::counts;
1049 
1050  resetTracking();
1051  set.erase(20);
1052  EXPECT_EQ(set.size(), 1);
1053  EXPECT_EQ(Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), 0)
1054  << Tracked<1>::counts;
1055 
1056  resetTracking();
1057  set.erase(10);
1058  EXPECT_EQ(set.size(), 0);
1059  EXPECT_EQ(Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), 0)
1060  << Tracked<1>::counts;
1061 
1062  set.insert(10);
1063  resetTracking();
1064  set.eraseInto(10, [](auto&&) {});
1065  EXPECT_EQ(Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), 0)
1066  << Tracked<1>::counts;
1067 }
1068 
1069 template <typename S>
1070 void runHeterogeneousInsertStringTest() {
1071  S set;
1072  StringPiece k{"foo"};
1073  std::vector<StringPiece> v{k};
1074 
1075  set.insert(k);
1076  set.insert("foo");
1077  set.insert(StringPiece{"foo"});
1078  set.insert(v.begin(), v.end());
1079  set.insert(
1080  std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));
1081 
1082  set.emplace();
1083  set.emplace(k);
1084  set.emplace("foo");
1085  set.emplace(StringPiece("foo"));
1086 
1087  set.erase("");
1088  set.erase(k);
1089  set.erase(StringPiece{"foo"});
1090  EXPECT_TRUE(set.empty());
1091 }
1092 
1093 TEST(F14ValueSet, heterogeneousInsert) {
1094  runHeterogeneousInsertTest<F14ValueSet<
1095  Tracked<1>,
1096  TransparentTrackedHash<1>,
1097  TransparentTrackedEqual<1>>>();
1098  runHeterogeneousInsertStringTest<F14ValueSet<
1099  std::string,
1100  transparent<hasher<StringPiece>>,
1101  transparent<DefaultKeyEqual<StringPiece>>>>();
1102  runHeterogeneousInsertStringTest<F14ValueSet<std::string>>();
1103 }
1104 
1105 TEST(F14NodeSet, heterogeneousInsert) {
1106  runHeterogeneousInsertTest<F14NodeSet<
1107  Tracked<1>,
1108  TransparentTrackedHash<1>,
1109  TransparentTrackedEqual<1>>>();
1110  runHeterogeneousInsertStringTest<F14NodeSet<
1111  std::string,
1112  transparent<hasher<StringPiece>>,
1113  transparent<DefaultKeyEqual<StringPiece>>>>();
1114  runHeterogeneousInsertStringTest<F14NodeSet<std::string>>();
1115 }
1116 
1117 TEST(F14VectorSet, heterogeneousInsert) {
1118  runHeterogeneousInsertTest<F14VectorSet<
1119  Tracked<1>,
1120  TransparentTrackedHash<1>,
1121  TransparentTrackedEqual<1>>>();
1122  runHeterogeneousInsertStringTest<F14VectorSet<
1123  std::string,
1124  transparent<hasher<StringPiece>>,
1125  transparent<DefaultKeyEqual<StringPiece>>>>();
1126  runHeterogeneousInsertStringTest<F14VectorSet<std::string>>();
1127 }
1128 
1129 TEST(F14FastSet, heterogeneousInsert) {
1130  runHeterogeneousInsertTest<F14FastSet<
1131  Tracked<1>,
1132  TransparentTrackedHash<1>,
1133  TransparentTrackedEqual<1>>>();
1134  runHeterogeneousInsertStringTest<F14FastSet<
1135  std::string,
1136  transparent<hasher<StringPiece>>,
1137  transparent<DefaultKeyEqual<StringPiece>>>>();
1138  runHeterogeneousInsertStringTest<F14FastSet<std::string>>();
1139 }
1140 
1141 namespace {
1142 struct CharArrayHasher {
1143  template <std::size_t N>
1144  std::size_t operator()(std::array<char, N> const& value) const {
1145  return folly::Hash{}(
1146  StringPiece{value.data(), &value.data()[value.size()]});
1147  }
1148 };
1149 
1150 template <
1151  template <typename, typename, typename, typename> class S,
1152  std::size_t N>
1153 struct RunAllValueSizeTests {
1154  void operator()() const {
1155  using Key = std::array<char, N>;
1156  static_assert(sizeof(Key) == N, "");
1157  S<Key, CharArrayHasher, std::equal_to<Key>, std::allocator<Key>> set;
1158 
1159  for (int i = 0; i < 100; ++i) {
1160  Key key{{static_cast<char>(i)}};
1161  set.insert(key);
1162  }
1163  while (!set.empty()) {
1164  set.erase(set.begin());
1165  }
1166 
1167  RunAllValueSizeTests<S, N - 1>{}();
1168  }
1169 };
1170 
1171 template <template <typename, typename, typename, typename> class S>
1172 struct RunAllValueSizeTests<S, 0> {
1173  void operator()() const {}
1174 };
1175 } // namespace
1176 
1177 TEST(F14ValueSet, valueSize) {
1178  RunAllValueSizeTests<F14ValueSet, 32>{}();
1179 }
1180 
1182 #endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
void testCustomSwap()
Definition: F14SetTest.cpp:27
void populate(Vector &v, const pair< int, int > &ss)
*than *hazptr_holder h
Definition: Hazptr.h:116
void verify(int extras)
std::unique_ptr< int > A
char b
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
double val
Definition: String.cpp:273
static bool simple
folly::std T
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
#define EXPECT_GE(val1, val2)
Definition: gtest.h:1932
static F14TableStats compute(T const &m)
Definition: F14Table.h:108
thread_local size_t testAllocatedBlockCount
Definition: F14TestUtil.h:323
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
static constexpr F14IntrinsicsMode getF14IntrinsicsMode()
void resetTracking()
Definition: F14TestUtil.h:336
std::uniform_int_distribution< milliseconds::rep > dist
thread_local size_t testAllocatedMemorySize
Definition: F14TestUtil.h:322
int * count
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
const char * string
Definition: Conv.cpp:212
#define EXPECT_NE(val1, val2)
Definition: gtest.h:1926
static set< string > s
void runVisitContiguousRangesTest(int n)
Definition: F14SetTest.cpp:127
TEST(F14Set, customSwap)
Definition: F14SetTest.cpp:43
void swap(SwapTrackingAlloc< T > &, SwapTrackingAlloc< T > &)
Definition: F14TestUtil.h:414
#define EXPECT_FALSE(condition)
Definition: gtest.h:1862
#define ASSERT_TRUE(condition)
Definition: gtest.h:1865
KeyT k
TEST(SequencedExecutor, CPUThreadPoolExecutor)
#define EXPECT_GT(val1, val2)
Definition: gtest.h:1934