proxygen
F14MapTest.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/F14Map.h>
18 
19 #include <algorithm>
20 #include <unordered_map>
21 
22 #include <folly/Conv.h>
23 #include <folly/FBString.h>
26 
27 template <template <typename, typename, typename, typename, typename>
28  class TMap>
30  using std::swap;
31 
32  TMap<
33  int,
34  int,
38  m0, m1;
40  swap(m0, m1);
41 
42  EXPECT_EQ(
44 }
45 
46 TEST(F14Map, customSwap) {
47  testCustomSwap<folly::F14ValueMap>();
48  testCustomSwap<folly::F14NodeMap>();
49  testCustomSwap<folly::F14VectorMap>();
50  testCustomSwap<folly::F14FastMap>();
51 }
52 
53 namespace {
54 template <
55  template <typename, typename, typename, typename, typename> class TMap,
56  typename K,
57  typename V>
58 void runAllocatedMemorySizeTest() {
59  using namespace folly::f14;
60  using namespace folly::f14::detail;
62 
63  resetTracking();
64  {
65  TMap<K, V, DefaultHasher<K>, DefaultKeyEqual<K>, A> m;
66 
67  // if F14 intrinsics are not available then we fall back to using
68  // std::unordered_map underneath, but in that case the allocation
69  // info is only best effort
70  bool preciseAllocInfo = getF14IntrinsicsMode() != F14IntrinsicsMode::None;
71 
72  if (preciseAllocInfo) {
74  EXPECT_EQ(m.getAllocatedMemorySize(), 0);
75  }
76  auto emptyMapAllocatedMemorySize = testAllocatedMemorySize;
77  auto emptyMapAllocatedBlockCount = testAllocatedBlockCount;
78 
79  for (size_t i = 0; i < 1000; ++i) {
80  m.insert(std::make_pair(folly::to<K>(i), V{}));
81  m.erase(folly::to<K>(i / 10 + 2));
82  if (preciseAllocInfo) {
83  EXPECT_EQ(testAllocatedMemorySize, m.getAllocatedMemorySize());
84  }
85  EXPECT_GE(m.getAllocatedMemorySize(), sizeof(std::pair<K, V>) * m.size());
86  std::size_t size = 0;
87  std::size_t count = 0;
88  m.visitAllocationClasses([&](std::size_t, std::size_t) mutable {});
89  m.visitAllocationClasses([&](std::size_t bytes, std::size_t n) {
90  size += bytes * n;
91  count += n;
92  });
93  if (preciseAllocInfo) {
96  }
97  }
98 
99  m = decltype(m){};
100  EXPECT_EQ(testAllocatedMemorySize, emptyMapAllocatedMemorySize);
101  EXPECT_EQ(testAllocatedBlockCount, emptyMapAllocatedBlockCount);
102 
103  m.reserve(5);
105  m = {};
107  }
110 }
111 
112 template <typename K, typename V>
113 void runAllocatedMemorySizeTests() {
114  runAllocatedMemorySizeTest<folly::F14ValueMap, K, V>();
115  runAllocatedMemorySizeTest<folly::F14NodeMap, K, V>();
116  runAllocatedMemorySizeTest<folly::F14VectorMap, K, V>();
117  runAllocatedMemorySizeTest<folly::F14FastMap, K, V>();
118 }
119 } // namespace
120 
121 TEST(F14Map, getAllocatedMemorySize) {
122  runAllocatedMemorySizeTests<bool, bool>();
123  runAllocatedMemorySizeTests<int, int>();
124  runAllocatedMemorySizeTests<bool, std::string>();
125  runAllocatedMemorySizeTests<double, std::string>();
126  runAllocatedMemorySizeTests<std::string, int>();
127  runAllocatedMemorySizeTests<std::string, std::string>();
128  runAllocatedMemorySizeTests<folly::fbstring, long>();
129 }
130 
131 template <typename M>
133  M map;
134 
135  for (int i = 0; i < n; ++i) {
136  map[i] = i;
137  map.erase(i / 2);
138  }
139 
140  std::unordered_map<uintptr_t, bool> visited;
141  for (auto& entry : map) {
142  visited[reinterpret_cast<uintptr_t>(&entry)] = false;
143  }
144 
145  map.visitContiguousRanges([&](auto b, auto e) {
146  for (auto i = b; i != e; ++i) {
147  auto iter = visited.find(reinterpret_cast<uintptr_t>(i));
148  ASSERT_TRUE(iter != visited.end());
149  EXPECT_FALSE(iter->second);
150  iter->second = true;
151  }
152  });
153 
154  // ensure no entries were skipped
155  for (auto& e : visited) {
156  EXPECT_TRUE(e.second);
157  }
158 }
159 
160 template <typename M>
162  runVisitContiguousRangesTest<M>(0); // empty
163  runVisitContiguousRangesTest<M>(5); // single chunk
164  runVisitContiguousRangesTest<M>(1000); // many chunks
165 }
166 
167 TEST(F14ValueMap, visitContiguousRanges) {
168  runVisitContiguousRangesTest<folly::F14ValueMap<int, int>>();
169 }
170 
171 TEST(F14NodeMap, visitContiguousRanges) {
172  runVisitContiguousRangesTest<folly::F14NodeMap<int, int>>();
173 }
174 
175 TEST(F14VectorMap, visitContiguousRanges) {
176  runVisitContiguousRangesTest<folly::F14VectorMap<int, int>>();
177 }
178 
179 TEST(F14FastMap, visitContiguousRanges) {
180  runVisitContiguousRangesTest<folly::F14FastMap<int, int>>();
181 }
182 
184 #if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
185 
187 #include <chrono>
188 #include <random>
189 #include <string>
190 #include <typeinfo>
191 #include <unordered_map>
192 
193 #include <folly/Range.h>
194 #include <folly/hash/Hash.h>
195 
196 using namespace folly;
197 using namespace folly::f14;
198 using namespace folly::string_piece_literals;
199 
200 namespace {
201 std::string s(char const* p) {
202  return p;
203 }
204 } // namespace
205 
206 template <typename T>
207 void runSimple() {
208  T h;
209 
210  EXPECT_EQ(h.size(), 0);
211 
212  h.insert(std::make_pair(s("abc"), s("ABC")));
213  EXPECT_TRUE(h.find(s("def")) == h.end());
214  EXPECT_FALSE(h.find(s("abc")) == h.end());
215  EXPECT_EQ(h[s("abc")], s("ABC"));
216  h[s("ghi")] = s("GHI");
217  EXPECT_EQ(h.size(), 2);
218  h.erase(h.find(s("abc")));
219  EXPECT_EQ(h.size(), 1);
220 
221  T h2(std::move(h));
222  EXPECT_EQ(h.size(), 0);
223  EXPECT_TRUE(h.begin() == h.end());
224  EXPECT_EQ(h2.size(), 1);
225 
226  EXPECT_TRUE(h2.find(s("abc")) == h2.end());
227  EXPECT_EQ(h2.begin()->first, s("ghi"));
228  {
229  auto i = h2.begin();
230  EXPECT_FALSE(i == h2.end());
231  ++i;
232  EXPECT_TRUE(i == h2.end());
233  }
234 
235  T h3;
236  h3.try_emplace(s("xxx"));
237  h3.insert_or_assign(s("yyy"), s("YYY"));
238  h3 = std::move(h2);
239  EXPECT_EQ(h2.size(), 0);
240  EXPECT_EQ(h3.size(), 1);
241  EXPECT_TRUE(h3.find(s("xxx")) == h3.end());
242 
243  for (uint64_t i = 0; i < 1000; ++i) {
244  h[to<std::string>(i * i * i)] = s("x");
245  EXPECT_EQ(h.size(), i + 1);
246  }
247  {
248  using std::swap;
249  swap(h, h2);
250  }
251  for (uint64_t i = 0; i < 1000; ++i) {
252  EXPECT_TRUE(h2.find(to<std::string>(i * i * i)) != h2.end());
253  EXPECT_EQ(
254  h2.find(to<std::string>(i * i * i))->first, to<std::string>(i * i * i));
255  EXPECT_TRUE(h2.find(to<std::string>(i * i * i + 2)) == h2.end());
256  }
257 
258  T h4{h2};
259  EXPECT_EQ(h2.size(), 1000);
260  EXPECT_EQ(h4.size(), 1000);
261 
262  T h5{std::move(h2)};
263  T h6;
264  h6 = h4;
265  T h7 = h4;
266  T h8({{s("abc"), s("ABC")}, {s("def"), s("DEF")}});
267  T h9({{s("abc"), s("ABD")}, {s("def"), s("DEF")}});
268  EXPECT_EQ(h8.size(), 2);
269  EXPECT_EQ(h8.count(s("abc")), 1);
270  EXPECT_EQ(h8.count(s("xyz")), 0);
271 
272  EXPECT_TRUE(h7 != h8);
273  EXPECT_TRUE(h8 != h9);
274 
275  h8 = std::move(h7);
276  // h2 and h7 are moved from, h4, h5, h6, and h8 should be identical
277 
278  EXPECT_TRUE(h4 == h8);
279 
280  EXPECT_TRUE(h2.empty());
281  EXPECT_TRUE(h7.empty());
282  for (uint64_t i = 0; i < 1000; ++i) {
283  auto k = to<std::string>(i * i * i);
284  EXPECT_EQ(h4.count(k), 1);
285  EXPECT_EQ(h5.count(k), 1);
286  EXPECT_EQ(h6.count(k), 1);
287  EXPECT_EQ(h8.count(k), 1);
288  }
289 
290  EXPECT_TRUE(h2 == h7);
291  EXPECT_TRUE(h4 != h7);
292 
293  EXPECT_EQ(h3.at(s("ghi")), s("GHI"));
294  EXPECT_THROW(h3.at(s("abc")), std::out_of_range);
295 
296  h8.clear();
297  h8.emplace(s("abc"), s("ABC"));
298  EXPECT_GE(h8.bucket_count(), 1);
299  h8 = {};
300  EXPECT_GE(h8.bucket_count(), 1);
301  h9 = {{s("abc"), s("ABD")}, {s("def"), s("DEF")}};
302  EXPECT_TRUE(h8.empty());
303  EXPECT_EQ(h9.size(), 2);
304 
305  auto expectH8 = [&](T& ref) { EXPECT_EQ(&ref, &h8); };
306  expectH8((h8 = h2));
307  expectH8((h8 = std::move(h2)));
308  expectH8((h8 = {}));
309 
319 }
320 
321 template <typename T>
322 void runRehash() {
323  unsigned n = 10000;
324  T h;
325  auto b = h.bucket_count();
326  for (unsigned i = 0; i < n; ++i) {
327  h.insert(std::make_pair(to<std::string>(i), s("")));
328  if (b != h.bucket_count()) {
330  b = h.bucket_count();
331  }
332  }
333  EXPECT_EQ(h.size(), n);
335 }
336 
337 // T should be a map from uint64_t to Tracked<1> that uses SwapTrackingAlloc
338 template <typename T>
339 void runRandom() {
340  using R = std::unordered_map<uint64_t, Tracked<2>>;
341 
342  resetTracking();
343 
344  std::mt19937_64 gen(0);
345  std::uniform_int_distribution<> pctDist(0, 100);
346  std::uniform_int_distribution<uint64_t> bitsBitsDist(1, 6);
347  {
348  T t0;
349  T t1;
350  R r0;
351  R r1;
352  std::size_t rollbacks = 0;
353  std::size_t resizingSmallRollbacks = 0;
354  std::size_t resizingLargeRollbacks = 0;
355 
356  for (std::size_t reps = 0; reps < 100000 || rollbacks < 10 ||
357  resizingSmallRollbacks < 1 || resizingLargeRollbacks < 1;
358  ++reps) {
359  if (pctDist(gen) < 20) {
360  // 10% chance allocator will fail after 0 to 3 more allocations
361  limitTestAllocations(gen() & 3);
362  } else {
364  }
365  bool leakCheckOnly = false;
366 
367  // discardBits will be from 0 to 62
368  auto discardBits = (uint64_t{1} << bitsBitsDist(gen)) - 2;
369  auto k = gen() >> discardBits;
370  auto v = gen();
371  auto pct = pctDist(gen);
372 
373  try {
374  EXPECT_EQ(t0.empty(), r0.empty());
375  EXPECT_EQ(t0.size(), r0.size());
376  EXPECT_EQ(2, Tracked<0>::counts.liveCount());
377  EXPECT_EQ(t0.size() + t1.size(), Tracked<1>::counts.liveCount());
378  EXPECT_EQ(r0.size() + r1.size(), Tracked<2>::counts.liveCount());
379  if (pct < 15) {
380  // insert
381  auto t = t0.insert(std::make_pair(k, v));
382  auto r = r0.insert(std::make_pair(k, v));
383  EXPECT_EQ(t.first->first, r.first->first);
384  EXPECT_EQ(t.first->second.val_, r.first->second.val_);
385  EXPECT_EQ(t.second, r.second);
386  } else if (pct < 25) {
387  // emplace
388  auto t = t0.emplace(k, v);
389  auto r = r0.emplace(k, v);
390  EXPECT_EQ(t.first->first, r.first->first);
391  EXPECT_EQ(t.first->second.val_, r.first->second.val_);
392  EXPECT_EQ(t.second, r.second);
393  } else if (pct < 30) {
394  // bulk insert
395  leakCheckOnly = true;
396  t0.insert(t1.begin(), t1.end());
397  r0.insert(r1.begin(), r1.end());
398  } else if (pct < 40) {
399  // erase by key
400  auto t = t0.erase(k);
401  auto r = r0.erase(k);
402  EXPECT_EQ(t, r);
403  } else if (pct < 47) {
404  // erase by iterator
405  if (t0.size() > 0) {
406  auto r = r0.find(k);
407  if (r == r0.end()) {
408  r = r0.begin();
409  }
410  k = r->first;
411  auto t = t0.find(k);
412  t = t0.erase(t);
413  if (t != t0.end()) {
414  EXPECT_NE(t->first, k);
415  }
416  r = r0.erase(r);
417  if (r != r0.end()) {
418  EXPECT_NE(r->first, k);
419  }
420  }
421  } else if (pct < 50) {
422  // bulk erase
423  if (t0.size() > 0) {
424  auto r = r0.find(k);
425  if (r == r0.end()) {
426  r = r0.begin();
427  }
428  k = r->first;
429  auto t = t0.find(k);
430  auto firstt = t;
431  auto lastt = ++t;
432  t = t0.erase(firstt, lastt);
433  if (t != t0.end()) {
434  EXPECT_NE(t->first, k);
435  }
436  auto firstr = r;
437  auto lastr = ++r;
438  r = r0.erase(firstr, lastr);
439  if (r != r0.end()) {
440  EXPECT_NE(r->first, k);
441  }
442  }
443  } else if (pct < 58) {
444  // find
445  auto t = t0.find(k);
446  auto r = r0.find(k);
447  EXPECT_EQ((t == t0.end()), (r == r0.end()));
448  if (t != t0.end() && r != r0.end()) {
449  EXPECT_EQ(t->first, r->first);
450  EXPECT_EQ(t->second.val_, r->second.val_);
451  }
452  EXPECT_EQ(t0.count(k), r0.count(k));
453  } else if (pct < 60) {
454  // equal_range
455  auto t = t0.equal_range(k);
456  auto r = r0.equal_range(k);
457  EXPECT_EQ((t.first == t.second), (r.first == r.second));
458  if (t.first != t.second && r.first != r.second) {
459  EXPECT_EQ(t.first->first, r.first->first);
460  EXPECT_EQ(t.first->second.val_, r.first->second.val_);
461  t.first++;
462  r.first++;
463  EXPECT_TRUE(t.first == t.second);
464  EXPECT_TRUE(r.first == r.second);
465  }
466  } else if (pct < 65) {
467  // iterate
468  uint64_t t = 0;
469  for (auto& e : t0) {
470  t += e.first * 37 + e.second.val_ + 1000;
471  }
472  uint64_t r = 0;
473  for (auto& e : r0) {
474  r += e.first * 37 + e.second.val_ + 1000;
475  }
476  EXPECT_EQ(t, r);
477  } else if (pct < 69) {
478  // swap
479  using std::swap;
480  swap(t0, t1);
481  swap(r0, r1);
482  } else if (pct < 70) {
483  // swap
484  t0.swap(t1);
485  r0.swap(r1);
486  } else if (pct < 72) {
487  // default construct
488  t0.~T();
489  new (&t0) T();
490  r0.~R();
491  new (&r0) R();
492  } else if (pct < 74) {
493  // default construct with capacity
494  std::size_t capacity = k & 0xffff;
495  T t(capacity);
496  t0 = std::move(t);
497  R r(capacity);
498  r0 = std::move(r);
499  } else if (pct < 80) {
500  // bulk iterator construct
501  t0 = T{t1.begin(), t1.end()};
502  r0 = R{r1.begin(), r1.end()};
503  } else if (pct < 82) {
504  // initializer list construct
505  auto k2 = gen() >> discardBits;
506  auto v2 = gen();
507  T t({{k, v}, {k2, v}, {k2, v2}});
508  t0 = std::move(t);
509  R r({{k, v}, {k2, v}, {k2, v2}});
510  r0 = std::move(r);
511  } else if (pct < 85) {
512  // copy construct
513  T t(t1);
514  t0 = std::move(t);
515  R r(r1);
516  r0 = std::move(r);
517  } else if (pct < 88) {
518  // copy construct
519  T t(t1, t1.get_allocator());
520  t0 = std::move(t);
521  R r(r1, r1.get_allocator());
522  r0 = std::move(r);
523  } else if (pct < 89) {
524  // move construct
525  t0.~T();
526  new (&t0) T(std::move(t1));
527  r0.~R();
528  new (&r0) R(std::move(r1));
529  } else if (pct < 90) {
530  // move construct
531  t0.~T();
532  auto ta = t1.get_allocator();
533  new (&t0) T(std::move(t1), ta);
534  r0.~R();
535  auto ra = r1.get_allocator();
536  new (&r0) R(std::move(r1), ra);
537  } else if (pct < 94) {
538  // copy assign
539  leakCheckOnly = true;
540  t0 = t1;
541  r0 = r1;
542  } else if (pct < 96) {
543  // move assign
544  t0 = std::move(t1);
545  r0 = std::move(r1);
546  } else if (pct < 98) {
547  // operator==
548  EXPECT_EQ((t0 == t1), (r0 == r1));
549  } else if (pct < 99) {
550  // clear
552  t0.clear();
553  r0.clear();
554  } else if (pct < 100) {
555  // reserve
556  auto scale = std::uniform_int_distribution<>(0, 8)(gen);
557  auto delta = std::uniform_int_distribution<>(-2, 2)(gen);
558  std::ptrdiff_t target = (t0.size() * scale) / 4 + delta;
559  if (target >= 0) {
560  t0.reserve(static_cast<std::size_t>(target));
561  r0.reserve(static_cast<std::size_t>(target));
562  }
563  }
564  } catch (std::bad_alloc const&) {
565  ++rollbacks;
566 
568 
569  if (leakCheckOnly) {
571  t0.clear();
572  for (auto&& kv : r0) {
573  t0[kv.first] = kv.second.val_;
574  }
575  }
576 
577  if (t0.bucket_count() == t0.size() && t0.size() > 0) {
578  if (t0.size() < 10) {
579  ++resizingSmallRollbacks;
580  } else {
581  ++resizingLargeRollbacks;
582  }
583  }
584 
585  assert(t0.size() == r0.size());
586  for (auto&& kv : r0) {
587  auto t = t0.find(kv.first);
588  EXPECT_TRUE(
589  t != t0.end() && t->first == kv.first &&
590  t->second.val_ == kv.second.val_);
591  }
592  }
593  }
594  }
595 
597 }
598 
599 template <typename T>
600 void runPrehash() {
601  T h;
602 
603  EXPECT_EQ(h.size(), 0);
604 
605  h.insert(std::make_pair(s("abc"), s("ABC")));
606  EXPECT_TRUE(h.find(s("def")) == h.end());
607  EXPECT_FALSE(h.find(s("abc")) == h.end());
608 
609  auto t1 = h.prehash(s("def"));
610  F14HashToken t2;
611  t2 = h.prehash(s("abc"));
612  EXPECT_TRUE(h.find(t1, s("def")) == h.end());
613  EXPECT_FALSE(h.find(t2, s("abc")) == h.end());
614 }
615 
616 TEST(F14ValueMap, simple) {
617  runSimple<F14ValueMap<std::string, std::string>>();
618 }
619 
620 TEST(F14NodeMap, simple) {
621  runSimple<F14NodeMap<std::string, std::string>>();
622 }
623 
624 TEST(F14VectorMap, simple) {
625  runSimple<F14VectorMap<std::string, std::string>>();
626 }
627 
628 TEST(F14FastMap, simple) {
629  runSimple<F14FastMap<std::string, std::string>>();
630 }
631 
632 TEST(F14VectorMap, reverse_iterator) {
633  using TMap = F14VectorMap<uint64_t, uint64_t>;
634  auto populate = [](TMap& h, uint64_t lo, uint64_t hi) {
635  for (auto i = lo; i < hi; ++i) {
636  h.emplace(i, i);
637  }
638  };
639  auto verify = [](TMap const& h, uint64_t lo, uint64_t hi) {
640  auto loIt = h.find(lo);
641  EXPECT_NE(h.end(), loIt);
642  uint64_t val = lo;
643  for (auto rit = h.riter(loIt); rit != h.rend(); ++rit) {
644  EXPECT_EQ(val, rit->first);
645  EXPECT_EQ(val, rit->second);
646  TMap::const_iterator it = h.iter(rit);
647  EXPECT_EQ(val, it->first);
648  EXPECT_EQ(val, it->second);
649  val++;
650  }
651  EXPECT_EQ(hi, val);
652  };
653  TMap h;
654  size_t prevSize = 0;
655  size_t newSize = 1;
656  // verify iteration order across rehashes, copies, and moves
657  while (newSize < 10'000) {
658  populate(h, prevSize, newSize);
659  verify(h, 0, newSize);
660  verify(h, newSize / 2, newSize);
661 
662  TMap h2{h};
663  verify(h2, 0, newSize);
664 
665  h = std::move(h2);
666  verify(h, 0, newSize);
667  prevSize = newSize;
668  newSize *= 10;
669  }
670 }
671 
672 TEST(F14ValueMap, rehash) {
673  runRehash<F14ValueMap<std::string, std::string>>();
674 }
675 
676 TEST(F14NodeMap, rehash) {
677  runRehash<F14NodeMap<std::string, std::string>>();
678 }
679 
680 TEST(F14VectorMap, rehash) {
681  runRehash<F14VectorMap<std::string, std::string>>();
682 }
683 
684 TEST(F14ValueMap, prehash) {
685  runPrehash<F14ValueMap<std::string, std::string>>();
686 }
687 
688 TEST(F14NodeMap, prehash) {
689  runPrehash<F14NodeMap<std::string, std::string>>();
690 }
691 
692 TEST(F14ValueMap, random) {
693  runRandom<F14ValueMap<
694  uint64_t,
695  Tracked<1>,
696  std::hash<uint64_t>,
697  std::equal_to<uint64_t>,
698  SwapTrackingAlloc<std::pair<uint64_t const, Tracked<1>>>>>();
699 }
700 
701 TEST(F14NodeMap, random) {
702  runRandom<F14NodeMap<
703  uint64_t,
704  Tracked<1>,
705  std::hash<uint64_t>,
706  std::equal_to<uint64_t>,
707  SwapTrackingAlloc<std::pair<uint64_t const, Tracked<1>>>>>();
708 }
709 
710 TEST(F14VectorMap, random) {
711  runRandom<F14VectorMap<
712  uint64_t,
713  Tracked<1>,
714  std::hash<uint64_t>,
715  std::equal_to<uint64_t>,
716  SwapTrackingAlloc<std::pair<uint64_t const, Tracked<1>>>>>();
717 }
718 
719 TEST(F14FastMap, random) {
720  runRandom<F14FastMap<
721  uint64_t,
722  Tracked<1>,
723  std::hash<uint64_t>,
724  std::equal_to<uint64_t>,
725  SwapTrackingAlloc<std::pair<uint64_t const, Tracked<1>>>>>();
726 }
727 
728 TEST(F14ValueMap, grow_stats) {
729  F14ValueMap<uint64_t, uint64_t> h;
730  for (unsigned i = 1; i <= 3072; ++i) {
731  h[i]++;
732  }
733  // F14ValueMap just before rehash
734  F14TableStats::compute(h);
735  h[0]++;
736  // F14ValueMap just after rehash
737  F14TableStats::compute(h);
738 }
739 
740 TEST(F14ValueMap, steady_state_stats) {
741  // 10k keys, 14% probability of insert, 90% chance of erase, so the
742  // table should converge to 1400 size without triggering the rehash
743  // that would occur at 1536.
744  F14ValueMap<uint64_t, uint64_t> h;
745  std::mt19937_64 gen(0);
746  std::uniform_int_distribution<> dist(0, 10000);
747  for (std::size_t i = 0; i < 100000; ++i) {
748  auto key = dist(gen);
749  if (dist(gen) < 1400) {
750  h.insert_or_assign(key, i);
751  } else {
752  h.erase(key);
753  }
754  if (((i + 1) % 10000) == 0) {
755  auto stats = F14TableStats::compute(h);
756  // Verify that average miss probe length is bounded despite continued
757  // erase + reuse. p99 of the average across 10M random steps is 4.69,
758  // average is 2.96.
759  EXPECT_LT(f14::expectedProbe(stats.missProbeLengthHisto), 10.0);
760  }
761  }
762  // F14ValueMap at steady state
763  F14TableStats::compute(h);
764 }
765 
766 TEST(F14VectorMap, steady_state_stats) {
767  // 10k keys, 14% probability of insert, 90% chance of erase, so the
768  // table should converge to 1400 size without triggering the rehash
769  // that would occur at 1536.
770  F14VectorMap<std::string, uint64_t> h;
771  std::mt19937_64 gen(0);
772  std::uniform_int_distribution<> dist(0, 10000);
773  for (std::size_t i = 0; i < 100000; ++i) {
774  auto key = "0123456789ABCDEFGHIJKLMNOPQ" + to<std::string>(dist(gen));
775  if (dist(gen) < 1400) {
776  h.insert_or_assign(key, i);
777  } else {
778  h.erase(key);
779  }
780  if (((i + 1) % 10000) == 0) {
781  auto stats = F14TableStats::compute(h);
782  // Verify that average miss probe length is bounded despite continued
783  // erase + reuse. p99 of the average across 10M random steps is 4.69,
784  // average is 2.96.
785  EXPECT_LT(f14::expectedProbe(stats.missProbeLengthHisto), 10.0);
786  }
787  }
788  // F14ValueMap at steady state
789  F14TableStats::compute(h);
790 }
791 
792 TEST(Tracked, baseline) {
793  Tracked<0> a0;
794 
795  {
796  resetTracking();
797  Tracked<0> b0{a0};
798  EXPECT_EQ(a0.val_, b0.val_);
799  EXPECT_EQ(sumCounts, (Counts{1, 0, 0, 0}));
800  EXPECT_EQ(Tracked<0>::counts, (Counts{1, 0, 0, 0}));
801  }
802  {
803  resetTracking();
804  Tracked<0> b0{std::move(a0)};
805  EXPECT_EQ(a0.val_, b0.val_);
806  EXPECT_EQ(sumCounts, (Counts{0, 1, 0, 0}));
807  EXPECT_EQ(Tracked<0>::counts, (Counts{0, 1, 0, 0}));
808  }
809  {
810  resetTracking();
811  Tracked<1> b1{a0};
812  EXPECT_EQ(a0.val_, b1.val_);
813  EXPECT_EQ(sumCounts, (Counts{0, 0, 1, 0}));
814  EXPECT_EQ(Tracked<1>::counts, (Counts{0, 0, 1, 0}));
815  }
816  {
817  resetTracking();
818  Tracked<1> b1{std::move(a0)};
819  EXPECT_EQ(a0.val_, b1.val_);
820  EXPECT_EQ(sumCounts, (Counts{0, 0, 0, 1}));
821  EXPECT_EQ(Tracked<1>::counts, (Counts{0, 0, 0, 1}));
822  }
823  {
824  Tracked<0> b0;
825  resetTracking();
826  b0 = a0;
827  EXPECT_EQ(a0.val_, b0.val_);
828  EXPECT_EQ(sumCounts, (Counts{0, 0, 0, 0, 1, 0}));
829  EXPECT_EQ(Tracked<0>::counts, (Counts{0, 0, 0, 0, 1, 0}));
830  }
831  {
832  Tracked<0> b0;
833  resetTracking();
834  b0 = std::move(a0);
835  EXPECT_EQ(a0.val_, b0.val_);
836  EXPECT_EQ(sumCounts, (Counts{0, 0, 0, 0, 0, 1}));
837  EXPECT_EQ(Tracked<0>::counts, (Counts{0, 0, 0, 0, 0, 1}));
838  }
839  {
840  Tracked<1> b1;
841  resetTracking();
842  b1 = a0;
843  EXPECT_EQ(a0.val_, b1.val_);
844  EXPECT_EQ(sumCounts, (Counts{0, 0, 1, 0, 0, 1, 0, 1}));
845  EXPECT_EQ(Tracked<1>::counts, (Counts{0, 0, 1, 0, 0, 1, 0, 1}));
846  }
847  {
848  Tracked<1> b1;
849  resetTracking();
850  b1 = std::move(a0);
851  EXPECT_EQ(a0.val_, b1.val_);
852  EXPECT_EQ(sumCounts, (Counts{0, 0, 0, 1, 0, 1, 0, 1}));
853  EXPECT_EQ(Tracked<1>::counts, (Counts{0, 0, 0, 1, 0, 1, 0, 1}));
854  }
855 }
856 
857 // M should be a map from Tracked<0> to Tracked<1>. F should take a map
858 // and a pair const& or pair&& and cause it to be inserted
859 template <typename M, typename F>
860 void runInsertCases(
861  std::string const& name,
862  F const& insertFunc,
863  uint64_t expectedDist = 0) {
864  static_assert(std::is_same<typename M::key_type, Tracked<0>>::value, "");
865  static_assert(std::is_same<typename M::mapped_type, Tracked<1>>::value, "");
866  {
867  typename M::value_type p{0, 0};
868  M m;
869  resetTracking();
870  insertFunc(m, p);
871  // fresh key, value_type const& ->
872  // copy is expected
873  EXPECT_EQ(
874  Tracked<0>::counts.dist(Counts{1, 0, 0, 0}) +
875  Tracked<1>::counts.dist(Counts{1, 0, 0, 0}),
876  expectedDist)
877  << name << "\n0 -> " << Tracked<0>::counts << "\n1 -> "
878  << Tracked<1>::counts;
879  }
880  {
881  typename M::value_type p{0, 0};
882  M m;
883  resetTracking();
884  insertFunc(m, std::move(p));
885  // fresh key, value_type&& ->
886  // key copy is unfortunate but required
887  EXPECT_EQ(
888  Tracked<0>::counts.dist(Counts{1, 0, 0, 0}) +
889  Tracked<1>::counts.dist(Counts{0, 1, 0, 0}),
890  expectedDist)
891  << name << "\n0 -> " << Tracked<0>::counts << "\n1 -> "
892  << Tracked<1>::counts;
893  }
894  {
895  std::pair<Tracked<0>, Tracked<1>> p{0, 0};
896  M m;
897  resetTracking();
898  insertFunc(m, p);
899  // fresh key, pair<key_type,mapped_type> const& ->
900  // 1 copy is required
901  EXPECT_EQ(
902  Tracked<0>::counts.dist(Counts{1, 0, 0, 0}) +
903  Tracked<1>::counts.dist(Counts{1, 0, 0, 0}),
904  expectedDist)
905  << name << "\n0 -> " << Tracked<0>::counts << "\n1 -> "
906  << Tracked<1>::counts;
907  }
908  {
909  std::pair<Tracked<0>, Tracked<1>> p{0, 0};
910  M m;
911  resetTracking();
912  insertFunc(m, std::move(p));
913  // fresh key, pair<key_type,mapped_type>&& ->
914  // this is the happy path for insert(make_pair(.., ..))
915  EXPECT_EQ(
916  Tracked<0>::counts.dist(Counts{0, 1, 0, 0}) +
917  Tracked<1>::counts.dist(Counts{0, 1, 0, 0}),
918  expectedDist)
919  << name << "\n0 -> " << Tracked<0>::counts << "\n1 -> "
920  << Tracked<1>::counts;
921  }
922  {
923  std::pair<Tracked<2>, Tracked<3>> p{0, 0};
924  M m;
925  resetTracking();
926  insertFunc(m, p);
927  // fresh key, convertible const& ->
928  // key_type ops: Tracked<0>::counts
929  // mapped_type ops: Tracked<1>::counts
930  // key_src ops: Tracked<2>::counts
931  // mapped_src ops: Tracked<3>::counts;
932 
933  // There are three strategies that could be optimal for particular
934  // ratios of cost:
935  //
936  // - convert key and value in place to final position, destroy if
937  // insert fails. This is the strategy used by std::unordered_map
938  // and FBHashMap
939  //
940  // - convert key and default value in place to final position,
941  // convert value only if insert succeeds. Nobody uses this strategy
942  //
943  // - convert key to a temporary, move key and convert value if
944  // insert succeeds. This is the strategy used by F14 and what is
945  // EXPECT_EQ here.
946 
947  // The expectedDist * 3 is just a hack for the emplace-pieces-by-value
948  // test, whose test harness copies the original pair and then uses
949  // move conversion instead of copy conversion.
950  EXPECT_EQ(
951  Tracked<0>::counts.dist(Counts{0, 1, 1, 0}) +
952  Tracked<1>::counts.dist(Counts{0, 0, 1, 0}) +
953  Tracked<2>::counts.dist(Counts{0, 0, 0, 0}) +
954  Tracked<3>::counts.dist(Counts{0, 0, 0, 0}),
955  expectedDist * 3);
956  }
957  {
958  std::pair<Tracked<2>, Tracked<3>> p{0, 0};
959  M m;
960  resetTracking();
961  insertFunc(m, std::move(p));
962  // fresh key, convertible&& ->
963  // key_type ops: Tracked<0>::counts
964  // mapped_type ops: Tracked<1>::counts
965  // key_src ops: Tracked<2>::counts
966  // mapped_src ops: Tracked<3>::counts;
967  EXPECT_EQ(
968  Tracked<0>::counts.dist(Counts{0, 1, 0, 1}) +
969  Tracked<1>::counts.dist(Counts{0, 0, 0, 1}) +
970  Tracked<2>::counts.dist(Counts{0, 0, 0, 0}) +
971  Tracked<3>::counts.dist(Counts{0, 0, 0, 0}),
972  expectedDist);
973  }
974  {
975  typename M::value_type p{0, 0};
976  M m;
977  m[0] = 0;
978  resetTracking();
979  insertFunc(m, p);
980  // duplicate key, value_type const&
981  EXPECT_EQ(
982  Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) +
983  Tracked<1>::counts.dist(Counts{0, 0, 0, 0}),
984  expectedDist);
985  }
986  {
987  typename M::value_type p{0, 0};
988  M m;
989  m[0] = 0;
990  resetTracking();
991  insertFunc(m, std::move(p));
992  // duplicate key, value_type&&
993  EXPECT_EQ(
994  Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) +
995  Tracked<1>::counts.dist(Counts{0, 0, 0, 0}),
996  expectedDist);
997  }
998  {
999  std::pair<Tracked<0>, Tracked<1>> p{0, 0};
1000  M m;
1001  m[0] = 0;
1002  resetTracking();
1003  insertFunc(m, p);
1004  // duplicate key, pair<key_type,mapped_type> const&
1005  EXPECT_EQ(
1006  Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) +
1007  Tracked<1>::counts.dist(Counts{0, 0, 0, 0}),
1008  expectedDist);
1009  }
1010  {
1011  std::pair<Tracked<0>, Tracked<1>> p{0, 0};
1012  M m;
1013  m[0] = 0;
1014  resetTracking();
1015  insertFunc(m, std::move(p));
1016  // duplicate key, pair<key_type,mapped_type>&&
1017  EXPECT_EQ(
1018  Tracked<0>::counts.dist(Counts{0, 0, 0, 0}) +
1019  Tracked<1>::counts.dist(Counts{0, 0, 0, 0}),
1020  expectedDist);
1021  }
1022  {
1023  std::pair<Tracked<2>, Tracked<3>> p{0, 0};
1024  M m;
1025  m[0] = 0;
1026  resetTracking();
1027  insertFunc(m, p);
1028  // duplicate key, convertible const& ->
1029  // key_type ops: Tracked<0>::counts
1030  // mapped_type ops: Tracked<1>::counts
1031  // key_src ops: Tracked<2>::counts
1032  // mapped_src ops: Tracked<3>::counts;
1033  EXPECT_EQ(
1034  Tracked<0>::counts.dist(Counts{0, 0, 1, 0}) +
1035  Tracked<1>::counts.dist(Counts{0, 0, 0, 0}) +
1036  Tracked<2>::counts.dist(Counts{0, 0, 0, 0}) +
1037  Tracked<3>::counts.dist(Counts{0, 0, 0, 0}),
1038  expectedDist * 2);
1039  }
1040  {
1041  std::pair<Tracked<2>, Tracked<3>> p{0, 0};
1042  M m;
1043  m[0] = 0;
1044  resetTracking();
1045  insertFunc(m, std::move(p));
1046  // duplicate key, convertible&& ->
1047  // key_type ops: Tracked<0>::counts
1048  // mapped_type ops: Tracked<1>::counts
1049  // key_src ops: Tracked<2>::counts
1050  // mapped_src ops: Tracked<3>::counts;
1051  EXPECT_EQ(
1052  Tracked<0>::counts.dist(Counts{0, 0, 0, 1}) +
1053  Tracked<1>::counts.dist(Counts{0, 0, 0, 0}) +
1054  Tracked<2>::counts.dist(Counts{0, 0, 0, 0}) +
1055  Tracked<3>::counts.dist(Counts{0, 0, 0, 0}),
1056  expectedDist);
1057  }
1058 }
1059 
1060 struct DoInsert {
1061  template <typename M, typename P>
1062  void operator()(M& m, P&& p) const {
1063  m.insert(std::forward<P>(p));
1064  }
1065 };
1066 
1067 struct DoEmplace1 {
1068  template <typename M, typename P>
1069  void operator()(M& m, P&& p) const {
1070  m.emplace(std::forward<P>(p));
1071  }
1072 };
1073 
1074 struct DoEmplace2 {
1075  template <typename M, typename U1, typename U2>
1076  void operator()(M& m, std::pair<U1, U2> const& p) const {
1077  m.emplace(p.first, p.second);
1078  }
1079 
1080  template <typename M, typename U1, typename U2>
1081  void operator()(M& m, std::pair<U1, U2>&& p) const {
1082  m.emplace(std::move(p.first), std::move(p.second));
1083  }
1084 };
1085 
1086 struct DoEmplace3 {
1087  template <typename M, typename U1, typename U2>
1088  void operator()(M& m, std::pair<U1, U2> const& p) const {
1089  m.emplace(
1090  std::piecewise_construct,
1091  std::forward_as_tuple(p.first),
1092  std::forward_as_tuple(p.second));
1093  }
1094 
1095  template <typename M, typename U1, typename U2>
1096  void operator()(M& m, std::pair<U1, U2>&& p) const {
1097  m.emplace(
1098  std::piecewise_construct,
1099  std::forward_as_tuple(std::move(p.first)),
1100  std::forward_as_tuple(std::move(p.second)));
1101  }
1102 };
1103 
1104 // Simulates use of piecewise_construct without proper use of
1105 // forward_as_tuple. This code doesn't yield the normal pattern, but
1106 // it should have exactly 1 additional move or copy of the key and 1
1107 // additional move or copy of the mapped value.
1108 struct DoEmplace3Value {
1109  template <typename M, typename U1, typename U2>
1110  void operator()(M& m, std::pair<U1, U2> const& p) const {
1111  m.emplace(
1112  std::piecewise_construct,
1113  std::tuple<U1>{p.first},
1114  std::tuple<U2>{p.second});
1115  }
1116 
1117  template <typename M, typename U1, typename U2>
1118  void operator()(M& m, std::pair<U1, U2>&& p) const {
1119  m.emplace(
1120  std::piecewise_construct,
1121  std::tuple<U1>{std::move(p.first)},
1122  std::tuple<U2>{std::move(p.second)});
1123  }
1124 };
1125 
1126 template <typename M>
1127 void runInsertAndEmplace(std::string const& name) {
1128  runInsertCases<M>(name + " insert", DoInsert{});
1129  runInsertCases<M>(name + " emplace pair", DoEmplace1{});
1130  runInsertCases<M>(name + " emplace k,v", DoEmplace2{});
1131  runInsertCases<M>(name + " emplace pieces", DoEmplace3{});
1132  runInsertCases<M>(name + " emplace pieces by value", DoEmplace3Value{}, 2);
1133 
1134  // Calling the default pair constructor via emplace is valid, but not
1135  // very useful in real life. Verify that it works.
1136  M m;
1137  typename M::key_type k;
1138  EXPECT_EQ(m.count(k), 0);
1139  m.emplace();
1140  EXPECT_EQ(m.count(k), 1);
1141  m.emplace();
1142  EXPECT_EQ(m.count(k), 1);
1143 }
1144 
1145 TEST(F14ValueMap, destructuring) {
1146  runInsertAndEmplace<F14ValueMap<Tracked<0>, Tracked<1>>>("f14value");
1147 }
1148 
1149 TEST(F14NodeMap, destructuring) {
1150  runInsertAndEmplace<F14NodeMap<Tracked<0>, Tracked<1>>>("f14node");
1151 }
1152 
1153 TEST(F14VectorMap, destructuring) {
1154  runInsertAndEmplace<F14VectorMap<Tracked<0>, Tracked<1>>>("f14vector");
1155 }
1156 
1157 TEST(F14VectorMap, destructuringErase) {
1158  using M = F14VectorMap<Tracked<0>, Tracked<1>>;
1159  typename M::value_type p1{0, 0};
1160  typename M::value_type p2{2, 2};
1161  M m;
1162  m.insert(p1);
1163  m.insert(p2);
1164 
1165  resetTracking();
1166  m.erase(p1.first);
1167  LOG(INFO) << "erase -> "
1168  << "key_type ops " << Tracked<0>::counts << ", mapped_type ops "
1169  << Tracked<1>::counts;
1170  // deleting p1 will cause p2 to be moved to the front of the values array
1171  EXPECT_EQ(
1172  Tracked<0>::counts.dist(Counts{0, 1, 0, 0}) +
1173  Tracked<1>::counts.dist(Counts{0, 1, 0, 0}),
1174  0);
1175 }
1176 
1177 TEST(F14ValueMap, maxSize) {
1178  F14ValueMap<int, int> m;
1179  EXPECT_EQ(
1180  m.max_size(),
1181  std::numeric_limits<std::size_t>::max() / sizeof(std::pair<int, int>));
1182 }
1183 
1184 TEST(F14NodeMap, maxSize) {
1185  F14NodeMap<int, int> m;
1186  EXPECT_EQ(
1187  m.max_size(),
1188  std::numeric_limits<std::size_t>::max() / sizeof(std::pair<int, int>));
1189 }
1190 
1191 TEST(F14VectorMap, vectorMaxSize) {
1192  F14VectorMap<int, int> m;
1193  EXPECT_EQ(
1194  m.max_size(),
1195  std::min(
1196  std::size_t{std::numeric_limits<uint32_t>::max()},
1198  sizeof(std::pair<int, int>)));
1199 }
1200 
1201 template <typename M>
1202 void runMoveOnlyTest() {
1203  M t0;
1204  t0[10] = 20;
1205  t0.emplace(30, 40);
1206  t0.insert(std::make_pair(50, 60));
1207  M t1{std::move(t0)};
1208  EXPECT_TRUE(t0.empty());
1209  M t2;
1210  EXPECT_TRUE(t2.empty());
1211  t2 = std::move(t1);
1212  EXPECT_EQ(t2.size(), 3);
1213 }
1214 
1215 TEST(F14ValueMap, moveOnly) {
1216  runMoveOnlyTest<F14ValueMap<f14::MoveOnlyTestInt, int>>();
1217  runMoveOnlyTest<F14ValueMap<int, f14::MoveOnlyTestInt>>();
1218  runMoveOnlyTest<F14ValueMap<f14::MoveOnlyTestInt, f14::MoveOnlyTestInt>>();
1219 }
1220 
1221 TEST(F14NodeMap, moveOnly) {
1222  runMoveOnlyTest<F14NodeMap<f14::MoveOnlyTestInt, int>>();
1223  runMoveOnlyTest<F14NodeMap<int, f14::MoveOnlyTestInt>>();
1224  runMoveOnlyTest<F14NodeMap<f14::MoveOnlyTestInt, f14::MoveOnlyTestInt>>();
1225 }
1226 
1227 TEST(F14VectorMap, moveOnly) {
1228  runMoveOnlyTest<F14VectorMap<f14::MoveOnlyTestInt, int>>();
1229  runMoveOnlyTest<F14VectorMap<int, f14::MoveOnlyTestInt>>();
1230  runMoveOnlyTest<F14VectorMap<f14::MoveOnlyTestInt, f14::MoveOnlyTestInt>>();
1231 }
1232 
1233 TEST(F14FastMap, moveOnly) {
1234  runMoveOnlyTest<F14FastMap<f14::MoveOnlyTestInt, int>>();
1235  runMoveOnlyTest<F14FastMap<int, f14::MoveOnlyTestInt>>();
1236  runMoveOnlyTest<F14FastMap<f14::MoveOnlyTestInt, f14::MoveOnlyTestInt>>();
1237 }
1238 
1239 TEST(F14ValueMap, heterogeneousLookup) {
1242 
1243  constexpr auto hello = "hello"_sp;
1244  constexpr auto buddy = "buddy"_sp;
1245  constexpr auto world = "world"_sp;
1246 
1247  F14ValueMap<std::string, bool, Hasher, KeyEqual> map;
1248  map.emplace(hello, true);
1249  map.emplace(world, false);
1250 
1251  auto checks = [hello, buddy](auto& ref) {
1252  // count
1253  EXPECT_EQ(0, ref.count(buddy));
1254  EXPECT_EQ(1, ref.count(hello));
1255 
1256  // find
1257  EXPECT_TRUE(ref.end() == ref.find(buddy));
1258  EXPECT_EQ(hello, ref.find(hello)->first);
1259 
1260  // prehash + find
1261  EXPECT_TRUE(ref.end() == ref.find(ref.prehash(buddy), buddy));
1262  EXPECT_EQ(hello, ref.find(ref.prehash(hello), hello)->first);
1263 
1264  // equal_range
1265  EXPECT_TRUE(std::make_pair(ref.end(), ref.end()) == ref.equal_range(buddy));
1266  EXPECT_TRUE(
1267  std::make_pair(ref.find(hello), ++ref.find(hello)) ==
1268  ref.equal_range(hello));
1269  };
1270 
1271  checks(map);
1272  checks(folly::as_const(map));
1273 }
1274 
1275 template <typename M>
1276 void runStatefulFunctorTest() {
1277  bool ranHasher = false;
1278  bool ranEqual = false;
1279  bool ranAlloc = false;
1280  bool ranDealloc = false;
1281 
1282  auto hasher = [&](int x) {
1283  ranHasher = true;
1284  return x;
1285  };
1286  auto equal = [&](int x, int y) {
1287  ranEqual = true;
1288  return x == y;
1289  };
1290  auto alloc = [&](std::size_t n) {
1291  ranAlloc = true;
1292  return std::malloc(n);
1293  };
1294  auto dealloc = [&](void* p, std::size_t) {
1295  ranDealloc = true;
1296  std::free(p);
1297  };
1298 
1299  {
1300  M map(0, hasher, equal, {alloc, dealloc});
1301  map[10]++;
1302  map[10]++;
1303  EXPECT_EQ(map[10], 2);
1304 
1305  M map2(map);
1306  M map3(std::move(map));
1307  map = map2;
1308  map2.clear();
1309  map2 = std::move(map3);
1310  }
1311  EXPECT_TRUE(ranHasher);
1312  EXPECT_TRUE(ranEqual);
1313  EXPECT_TRUE(ranAlloc);
1314  EXPECT_TRUE(ranDealloc);
1315 }
1316 
1317 TEST(F14ValueMap, statefulFunctors) {
1318  runStatefulFunctorTest<F14ValueMap<
1319  int,
1320  int,
1324 }
1325 
1326 TEST(F14NodeMap, statefulFunctors) {
1327  runStatefulFunctorTest<F14NodeMap<
1328  int,
1329  int,
1333 }
1334 
1335 TEST(F14VectorMap, statefulFunctors) {
1336  runStatefulFunctorTest<F14VectorMap<
1337  int,
1338  int,
1342 }
1343 
1344 TEST(F14FastMap, statefulFunctors) {
1345  runStatefulFunctorTest<F14FastMap<
1346  int,
1347  int,
1351 }
1352 
1353 template <typename M>
1354 void runHeterogeneousInsertTest() {
1355  M map;
1356 
1357  resetTracking();
1358  EXPECT_EQ(map.count(10), 0);
1359  EXPECT_EQ(Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), 0)
1360  << Tracked<1>::counts;
1361 
1362  resetTracking();
1363  map[10] = 20;
1364  EXPECT_EQ(Tracked<1>::counts.dist(Counts{0, 0, 0, 1}), 0)
1365  << Tracked<1>::counts;
1366 
1367  resetTracking();
1368  std::pair<int, int> p(10, 30);
1369  std::vector<std::pair<int, int>> v({p});
1370  map[10] = 30;
1371  map.insert(std::pair<int, int>(10, 30));
1372  map.insert(std::pair<int const, int>(10, 30));
1373  map.insert(p);
1374  map.insert(v.begin(), v.end());
1375  map.insert(
1376  std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));
1377  map.insert_or_assign(10, 40);
1378  EXPECT_EQ(Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), 0)
1379  << Tracked<1>::counts;
1380 
1381  resetTracking();
1382  map.emplace(10, 30);
1383  map.emplace(
1384  std::piecewise_construct,
1385  std::forward_as_tuple(10),
1386  std::forward_as_tuple(30));
1387  map.emplace(p);
1388  map.try_emplace(10, 30);
1389  map.try_emplace(10);
1390  EXPECT_EQ(Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), 0)
1391  << Tracked<1>::counts;
1392 
1393  resetTracking();
1394  map.erase(10);
1395  EXPECT_EQ(map.size(), 0);
1396  EXPECT_EQ(Tracked<1>::counts.dist(Counts{0, 0, 0, 0}), 0)
1397  << Tracked<1>::counts;
1398 }
1399 
1400 template <typename M>
1401 void runHeterogeneousInsertStringTest() {
1402  using P = std::pair<StringPiece, std::string>;
1403  using CP = std::pair<const StringPiece, std::string>;
1404 
1405  M map;
1406  P p{"foo", "hello"};
1407  std::vector<P> v{p};
1408  StringPiece foo{"foo"};
1409 
1410  map.insert(P("foo", "hello"));
1411  // TODO(T31574848): the list-initialization below does not work on libstdc++
1412  // versions (e.g., GCC < 6) with no implementation of N4387 ("perfect
1413  // initialization" for pairs and tuples).
1414  // StringPiece sp{"foo"};
1415  // map.insert({sp, "hello"});
1416  map.insert({"foo", "hello"});
1417  map.insert(CP("foo", "hello"));
1418  map.insert(p);
1419  map.insert(v.begin(), v.end());
1420  map.insert(
1421  std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));
1422  map.insert_or_assign("foo", "hello");
1423  map.insert_or_assign(StringPiece{"foo"}, "hello");
1424  EXPECT_EQ(map["foo"], "hello");
1425 
1426  map.emplace(StringPiece{"foo"}, "hello");
1427  map.emplace("foo", "hello");
1428  map.emplace(p);
1429  map.emplace();
1430  map.emplace(
1431  std::piecewise_construct,
1432  std::forward_as_tuple(StringPiece{"foo"}),
1433  std::forward_as_tuple(/* count */ 20, 'x'));
1434  map.try_emplace(StringPiece{"foo"}, "hello");
1435  map.try_emplace(foo, "hello");
1436  map.try_emplace(foo);
1437  map.try_emplace("foo");
1438  map.try_emplace("foo", "hello");
1439  map.try_emplace("foo", /* count */ 20, 'x');
1440 
1441  map.erase(StringPiece{"foo"});
1442  map.erase(foo);
1443  map.erase("");
1444  EXPECT_TRUE(map.empty());
1445 }
1446 
1447 TEST(F14ValueMap, heterogeneousInsert) {
1448  runHeterogeneousInsertTest<F14ValueMap<
1449  Tracked<1>,
1450  int,
1453  runHeterogeneousInsertStringTest<F14ValueMap<
1454  std::string,
1455  std::string,
1458  runHeterogeneousInsertStringTest<F14ValueMap<std::string, std::string>>();
1459 }
1460 
1461 TEST(F14NodeMap, heterogeneousInsert) {
1462  runHeterogeneousInsertTest<F14NodeMap<
1463  Tracked<1>,
1464  int,
1467  runHeterogeneousInsertStringTest<F14NodeMap<
1468  std::string,
1469  std::string,
1472  runHeterogeneousInsertStringTest<F14NodeMap<std::string, std::string>>();
1473 }
1474 
1475 TEST(F14VectorMap, heterogeneousInsert) {
1476  runHeterogeneousInsertTest<F14VectorMap<
1477  Tracked<1>,
1478  int,
1481  runHeterogeneousInsertStringTest<F14VectorMap<
1482  std::string,
1483  std::string,
1486  runHeterogeneousInsertStringTest<F14VectorMap<std::string, std::string>>();
1487 }
1488 
1489 TEST(F14FastMap, heterogeneousInsert) {
1490  runHeterogeneousInsertTest<F14FastMap<
1491  Tracked<1>,
1492  int,
1495  runHeterogeneousInsertStringTest<F14FastMap<
1496  std::string,
1497  std::string,
1500  runHeterogeneousInsertStringTest<F14FastMap<std::string, std::string>>();
1501 }
1502 
1504 #endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
1505 
void testCustomSwap()
Definition: F14MapTest.cpp:29
void populate(Vector &v, const pair< int, int > &ss)
*than *hazptr_holder h
Definition: Hazptr.h:116
void verify(int extras)
auto v
std::unique_ptr< int > A
#define EXPECT_THROW(statement, expected_exception)
Definition: gtest.h:1843
char b
void limitTestAllocations(std::size_t allocationsBeforeException=0)
Definition: F14TestUtil.h:328
LogLevel max
Definition: LogLevel.cpp:31
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
const int x
double val
Definition: String.cpp:273
void runVisitContiguousRangesTest(int n)
Definition: F14MapTest.cpp:132
static bool simple
folly::std T
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
#define EXPECT_GE(val1, val2)
Definition: gtest.h:1932
constexpr T const & as_const(T &t) noexcept
Definition: Utility.h:96
static F14TableStats compute(T const &m)
Definition: F14Table.h:108
void unlimitTestAllocations()
Definition: F14TestUtil.h:332
const char * name
Definition: http_parser.c:437
thread_local size_t testAllocatedBlockCount
Definition: F14TestUtil.h:323
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
LogLevel min
Definition: LogLevel.cpp:30
static constexpr F14IntrinsicsMode getF14IntrinsicsMode()
void resetTracking()
Definition: F14TestUtil.h:336
static Map map(mapCap)
TEST(F14Map, customSwap)
Definition: F14MapTest.cpp:46
static map< string, int > m
std::uniform_int_distribution< milliseconds::rep > dist
Definition: Traits.h:594
void free()
thread_local size_t testAllocatedMemorySize
Definition: F14TestUtil.h:322
**Optimized Holders **The template hazptr_array< M > provides most of the functionality *of M hazptr_holder s but with faster construction destruction *for M
Definition: Hazptr.h:104
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
Definition: InvokeTest.cpp:65
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)
constexpr detail::First first
Definition: Base-inl.h:2553
#define EXPECT_GT(val1, val2)
Definition: gtest.h:1934