• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "absl/container/flat_hash_map.h"
16 
17 #include <cstddef>
18 #include <memory>
19 #include <string>
20 #include <type_traits>
21 #include <utility>
22 #include <vector>
23 
24 #include "gmock/gmock.h"
25 #include "gtest/gtest.h"
26 #include "absl/base/config.h"
27 #include "absl/container/internal/hash_generator_testing.h"
28 #include "absl/container/internal/hash_policy_testing.h"
29 #include "absl/container/internal/test_allocator.h"
30 #include "absl/container/internal/unordered_map_constructor_test.h"
31 #include "absl/container/internal/unordered_map_lookup_test.h"
32 #include "absl/container/internal/unordered_map_members_test.h"
33 #include "absl/container/internal/unordered_map_modifiers_test.h"
34 #include "absl/log/check.h"
35 #include "absl/meta/type_traits.h"
36 #include "absl/types/any.h"
37 
38 namespace absl {
39 ABSL_NAMESPACE_BEGIN
40 namespace container_internal {
41 namespace {
42 using ::absl::container_internal::hash_internal::Enum;
43 using ::absl::container_internal::hash_internal::EnumClass;
44 using ::testing::_;
45 using ::testing::IsEmpty;
46 using ::testing::Pair;
47 using ::testing::UnorderedElementsAre;
48 using ::testing::UnorderedElementsAreArray;
49 
50 // Check that absl::flat_hash_map works in a global constructor.
51 struct BeforeMain {
BeforeMainabsl::container_internal::__anond0ab68820111::BeforeMain52   BeforeMain() {
53     absl::flat_hash_map<int, int> x;
54     x.insert({1, 1});
55     CHECK(x.find(0) == x.end()) << "x should not contain 0";
56     auto it = x.find(1);
57     CHECK(it != x.end()) << "x should contain 1";
58     CHECK(it->second) << "1 should map to 1";
59   }
60 };
61 const BeforeMain before_main;
62 
63 template <class K, class V>
64 using Map = flat_hash_map<K, V, StatefulTestingHash, StatefulTestingEqual,
65                           Alloc<std::pair<const K, V>>>;
66 
67 static_assert(!std::is_standard_layout<NonStandardLayout>(), "");
68 
69 using MapTypes =
70     ::testing::Types<Map<int, int>, Map<std::string, int>,
71                      Map<Enum, std::string>, Map<EnumClass, int>,
72                      Map<int, NonStandardLayout>, Map<NonStandardLayout, int>>;
73 
74 INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ConstructorTest, MapTypes);
75 INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, LookupTest, MapTypes);
76 INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, MembersTest, MapTypes);
77 INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ModifiersTest, MapTypes);
78 
79 using UniquePtrMapTypes = ::testing::Types<Map<int, std::unique_ptr<int>>>;
80 
81 INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, UniquePtrModifiersTest,
82                                UniquePtrMapTypes);
83 
TEST(FlatHashMap,StandardLayout)84 TEST(FlatHashMap, StandardLayout) {
85   struct Int {
86     explicit Int(size_t value) : value(value) {}
87     Int() : value(0) { ADD_FAILURE(); }
88     Int(const Int& other) : value(other.value) { ADD_FAILURE(); }
89     Int(Int&&) = default;
90     bool operator==(const Int& other) const { return value == other.value; }
91     size_t value;
92   };
93   static_assert(std::is_standard_layout<Int>(), "");
94 
95   struct Hash {
96     size_t operator()(const Int& obj) const { return obj.value; }
97   };
98 
99   // Verify that neither the key nor the value get default-constructed or
100   // copy-constructed.
101   {
102     flat_hash_map<Int, Int, Hash> m;
103     m.try_emplace(Int(1), Int(2));
104     m.try_emplace(Int(3), Int(4));
105     m.erase(Int(1));
106     m.rehash(2 * m.bucket_count());
107   }
108   {
109     flat_hash_map<Int, Int, Hash> m;
110     m.try_emplace(Int(1), Int(2));
111     m.try_emplace(Int(3), Int(4));
112     m.erase(Int(1));
113     m.clear();
114   }
115 }
116 
TEST(FlatHashMap,Relocatability)117 TEST(FlatHashMap, Relocatability) {
118   static_assert(absl::is_trivially_relocatable<int>::value);
119 #if ABSL_INTERNAL_CPLUSPLUS_LANG <= 202002L
120   // std::pair is not trivially copyable in C++23 in some standard
121   // library versions.
122   // See https://github.com/llvm/llvm-project/pull/95444 for instance.
123   // container_memory.h contains a workaround so what really matters
124   // is the transfer test below.
125   static_assert(
126       absl::is_trivially_relocatable<std::pair<const int, int>>::value);
127 #endif
128   static_assert(
129       std::is_same<decltype(absl::container_internal::FlatHashMapPolicy<
130                             int, int>::transfer<std::allocator<char>>(nullptr,
131                                                                       nullptr,
132                                                                       nullptr)),
133                    std::true_type>::value);
134 
135   struct NonRelocatable {
136     NonRelocatable() = default;
137     NonRelocatable(NonRelocatable&&) {}
138     NonRelocatable& operator=(NonRelocatable&&) { return *this; }
139     void* self = nullptr;
140   };
141 
142   EXPECT_FALSE(absl::is_trivially_relocatable<NonRelocatable>::value);
143   EXPECT_TRUE(
144       (std::is_same<decltype(absl::container_internal::FlatHashMapPolicy<
145                             int, NonRelocatable>::
146                                 transfer<std::allocator<char>>(nullptr, nullptr,
147                                                                nullptr)),
148                    std::false_type>::value));
149 }
150 
151 // gcc becomes unhappy if this is inside the method, so pull it out here.
152 struct balast {};
153 
TEST(FlatHashMap,IteratesMsan)154 TEST(FlatHashMap, IteratesMsan) {
155   // Because SwissTable randomizes on pointer addresses, we keep old tables
156   // around to ensure we don't reuse old memory.
157   std::vector<absl::flat_hash_map<int, balast>> garbage;
158   for (int i = 0; i < 100; ++i) {
159     absl::flat_hash_map<int, balast> t;
160     for (int j = 0; j < 100; ++j) {
161       t[j];
162       for (const auto& p : t) EXPECT_THAT(p, Pair(_, _));
163     }
164     garbage.push_back(std::move(t));
165   }
166 }
167 
168 // Demonstration of the "Lazy Key" pattern.  This uses heterogeneous insert to
169 // avoid creating expensive key elements when the item is already present in the
170 // map.
171 struct LazyInt {
LazyIntabsl::container_internal::__anond0ab68820111::LazyInt172   explicit LazyInt(size_t value, int* tracker)
173       : value(value), tracker(tracker) {}
174 
operator size_tabsl::container_internal::__anond0ab68820111::LazyInt175   explicit operator size_t() const {
176     ++*tracker;
177     return value;
178   }
179 
180   size_t value;
181   int* tracker;
182 };
183 
184 struct Hash {
185   using is_transparent = void;
186   int* tracker;
operator ()absl::container_internal::__anond0ab68820111::Hash187   size_t operator()(size_t obj) const {
188     ++*tracker;
189     return obj;
190   }
operator ()absl::container_internal::__anond0ab68820111::Hash191   size_t operator()(const LazyInt& obj) const {
192     ++*tracker;
193     return obj.value;
194   }
195 };
196 
197 struct Eq {
198   using is_transparent = void;
operator ()absl::container_internal::__anond0ab68820111::Eq199   bool operator()(size_t lhs, size_t rhs) const { return lhs == rhs; }
operator ()absl::container_internal::__anond0ab68820111::Eq200   bool operator()(size_t lhs, const LazyInt& rhs) const {
201     return lhs == rhs.value;
202   }
203 };
204 
TEST(FlatHashMap,LazyKeyPattern)205 TEST(FlatHashMap, LazyKeyPattern) {
206   // hashes are only guaranteed in opt mode, we use assertions to track internal
207   // state that can cause extra calls to hash.
208   int conversions = 0;
209   int hashes = 0;
210   flat_hash_map<size_t, size_t, Hash, Eq> m(0, Hash{&hashes});
211   m.reserve(3);
212 
213   m[LazyInt(1, &conversions)] = 1;
214   EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 1)));
215   EXPECT_EQ(conversions, 1);
216 #ifdef NDEBUG
217   EXPECT_EQ(hashes, 1);
218 #endif
219 
220   m[LazyInt(1, &conversions)] = 2;
221   EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2)));
222   EXPECT_EQ(conversions, 1);
223 #ifdef NDEBUG
224   EXPECT_EQ(hashes, 2);
225 #endif
226 
227   m.try_emplace(LazyInt(2, &conversions), 3);
228   EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3)));
229   EXPECT_EQ(conversions, 2);
230 #ifdef NDEBUG
231   EXPECT_EQ(hashes, 3);
232 #endif
233 
234   m.try_emplace(LazyInt(2, &conversions), 4);
235   EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3)));
236   EXPECT_EQ(conversions, 2);
237 #ifdef NDEBUG
238   EXPECT_EQ(hashes, 4);
239 #endif
240 }
241 
TEST(FlatHashMap,BitfieldArgument)242 TEST(FlatHashMap, BitfieldArgument) {
243   union {
244     int n : 1;
245   };
246   n = 0;
247   flat_hash_map<int, int> m;
248   m.erase(n);
249   m.count(n);
250   m.prefetch(n);
251   m.find(n);
252   m.contains(n);
253   m.equal_range(n);
254   m.insert_or_assign(n, n);
255   m.insert_or_assign(m.end(), n, n);
256   m.try_emplace(n);
257   m.try_emplace(m.end(), n);
258   m.at(n);
259   m[n];
260 }
261 
TEST(FlatHashMap,MergeExtractInsert)262 TEST(FlatHashMap, MergeExtractInsert) {
263   // We can't test mutable keys, or non-copyable keys with flat_hash_map.
264   // Test that the nodes have the proper API.
265   absl::flat_hash_map<int, int> m = {{1, 7}, {2, 9}};
266   auto node = m.extract(1);
267   EXPECT_TRUE(node);
268   EXPECT_EQ(node.key(), 1);
269   EXPECT_EQ(node.mapped(), 7);
270   EXPECT_THAT(m, UnorderedElementsAre(Pair(2, 9)));
271 
272   node.mapped() = 17;
273   m.insert(std::move(node));
274   EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 17), Pair(2, 9)));
275 }
276 
FirstIsEven(std::pair<const int,int> p)277 bool FirstIsEven(std::pair<const int, int> p) { return p.first % 2 == 0; }
278 
TEST(FlatHashMap,EraseIf)279 TEST(FlatHashMap, EraseIf) {
280   // Erase all elements.
281   {
282     flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
283     EXPECT_EQ(erase_if(s, [](std::pair<const int, int>) { return true; }), 5);
284     EXPECT_THAT(s, IsEmpty());
285   }
286   // Erase no elements.
287   {
288     flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
289     EXPECT_EQ(erase_if(s, [](std::pair<const int, int>) { return false; }), 0);
290     EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3),
291                                         Pair(4, 4), Pair(5, 5)));
292   }
293   // Erase specific elements.
294   {
295     flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
296     EXPECT_EQ(erase_if(s,
297                        [](std::pair<const int, int> kvp) {
298                          return kvp.first % 2 == 1;
299                        }),
300               3);
301     EXPECT_THAT(s, UnorderedElementsAre(Pair(2, 2), Pair(4, 4)));
302   }
303   // Predicate is function reference.
304   {
305     flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
306     EXPECT_EQ(erase_if(s, FirstIsEven), 2);
307     EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
308   }
309   // Predicate is function pointer.
310   {
311     flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
312     EXPECT_EQ(erase_if(s, &FirstIsEven), 2);
313     EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
314   }
315 }
316 
TEST(FlatHashMap,CForEach)317 TEST(FlatHashMap, CForEach) {
318   flat_hash_map<int, int> m;
319   std::vector<std::pair<int, int>> expected;
320   for (int i = 0; i < 100; ++i) {
321     {
322       SCOPED_TRACE("mutable object iteration");
323       std::vector<std::pair<int, int>> v;
324       absl::container_internal::c_for_each_fast(
325           m, [&v](std::pair<const int, int>& p) { v.push_back(p); });
326       EXPECT_THAT(v, UnorderedElementsAreArray(expected));
327     }
328     {
329       SCOPED_TRACE("const object iteration");
330       std::vector<std::pair<int, int>> v;
331       const flat_hash_map<int, int>& cm = m;
332       absl::container_internal::c_for_each_fast(
333           cm, [&v](const std::pair<const int, int>& p) { v.push_back(p); });
334       EXPECT_THAT(v, UnorderedElementsAreArray(expected));
335     }
336     {
337       SCOPED_TRACE("const object iteration");
338       std::vector<std::pair<int, int>> v;
339       absl::container_internal::c_for_each_fast(
340           flat_hash_map<int, int>(m),
341           [&v](std::pair<const int, int>& p) { v.push_back(p); });
342       EXPECT_THAT(v, UnorderedElementsAreArray(expected));
343     }
344     m[i] = i;
345     expected.emplace_back(i, i);
346   }
347 }
348 
TEST(FlatHashMap,CForEachMutate)349 TEST(FlatHashMap, CForEachMutate) {
350   flat_hash_map<int, int> s;
351   std::vector<std::pair<int, int>> expected;
352   for (int i = 0; i < 100; ++i) {
353     std::vector<std::pair<int, int>> v;
354     absl::container_internal::c_for_each_fast(
355         s, [&v](std::pair<const int, int>& p) {
356           v.push_back(p);
357           p.second++;
358         });
359     EXPECT_THAT(v, UnorderedElementsAreArray(expected));
360     for (auto& p : expected) {
361       p.second++;
362     }
363     EXPECT_THAT(s, UnorderedElementsAreArray(expected));
364     s[i] = i;
365     expected.emplace_back(i, i);
366   }
367 }
368 
TEST(FlatHashMap,NodeHandleMutableKeyAccess)369 TEST(FlatHashMap, NodeHandleMutableKeyAccess) {
370   flat_hash_map<std::string, std::string> map;
371 
372   map["key1"] = "mapped";
373 
374   auto nh = map.extract(map.begin());
375   nh.key().resize(3);
376   map.insert(std::move(nh));
377 
378   EXPECT_THAT(map, testing::ElementsAre(Pair("key", "mapped")));
379 }
380 
TEST(FlatHashMap,Reserve)381 TEST(FlatHashMap, Reserve) {
382   // Verify that if we reserve(size() + n) then we can perform n insertions
383   // without a rehash, i.e., without invalidating any references.
384   for (size_t trial = 0; trial < 20; ++trial) {
385     for (size_t initial = 3; initial < 100; ++initial) {
386       // Fill in `initial` entries, then erase 2 of them, then reserve space for
387       // two inserts and check for reference stability while doing the inserts.
388       flat_hash_map<size_t, size_t> map;
389       for (size_t i = 0; i < initial; ++i) {
390         map[i] = i;
391       }
392       map.erase(0);
393       map.erase(1);
394       map.reserve(map.size() + 2);
395       size_t& a2 = map[2];
396       // In the event of a failure, asan will complain in one of these two
397       // assignments.
398       map[initial] = a2;
399       map[initial + 1] = a2;
400       // Fail even when not under asan:
401       size_t& a2new = map[2];
402       EXPECT_EQ(&a2, &a2new);
403     }
404   }
405 }
406 
TEST(FlatHashMap,RecursiveTypeCompiles)407 TEST(FlatHashMap, RecursiveTypeCompiles) {
408   struct RecursiveType {
409     flat_hash_map<int, RecursiveType> m;
410   };
411   RecursiveType t;
412   t.m[0] = RecursiveType{};
413 }
414 
TEST(FlatHashMap,FlatHashMapPolicyDestroyReturnsTrue)415 TEST(FlatHashMap, FlatHashMapPolicyDestroyReturnsTrue) {
416   EXPECT_TRUE(
417       (decltype(FlatHashMapPolicy<int, char>::destroy<std::allocator<char>>(
418           nullptr, nullptr))()));
419   EXPECT_FALSE(
420       (decltype(FlatHashMapPolicy<int, char>::destroy<CountingAllocator<char>>(
421           nullptr, nullptr))()));
422   EXPECT_FALSE((decltype(FlatHashMapPolicy<int, std::unique_ptr<int>>::destroy<
423                          std::allocator<char>>(nullptr, nullptr))()));
424 }
425 
426 struct InconsistentHashEqType {
InconsistentHashEqTypeabsl::container_internal::__anond0ab68820111::InconsistentHashEqType427   InconsistentHashEqType(int v1, int v2) : v1(v1), v2(v2) {}
428   template <typename H>
AbslHashValue(H h,InconsistentHashEqType t)429   friend H AbslHashValue(H h, InconsistentHashEqType t) {
430     return H::combine(std::move(h), t.v1);
431   }
operator ==absl::container_internal::__anond0ab68820111::InconsistentHashEqType432   bool operator==(InconsistentHashEqType t) const { return v2 == t.v2; }
433   int v1, v2;
434 };
435 
TEST(Iterator,InconsistentHashEqFunctorsValidation)436 TEST(Iterator, InconsistentHashEqFunctorsValidation) {
437   if (!IsAssertEnabled()) GTEST_SKIP() << "Assertions not enabled.";
438 
439   absl::flat_hash_map<InconsistentHashEqType, int> m;
440   for (int i = 0; i < 10; ++i) m[{i, i}] = 1;
441   // We need to insert multiple times to guarantee that we get the assertion
442   // because it's possible for the hash to collide with the inserted element
443   // that has v2==0. In those cases, the new element won't be inserted.
444   auto insert_conflicting_elems = [&] {
445     for (int i = 100; i < 20000; ++i) {
446       EXPECT_EQ((m[{i, 0}]), 1);
447     }
448   };
449 
450   const char* crash_message = "hash/eq functors are inconsistent.";
451 #if defined(__arm__) || defined(__aarch64__)
452   // On ARM, the crash message is garbled so don't expect a specific message.
453   crash_message = "";
454 #endif
455   EXPECT_DEATH_IF_SUPPORTED(insert_conflicting_elems(), crash_message);
456 }
457 
458 }  // namespace
459 }  // namespace container_internal
460 ABSL_NAMESPACE_END
461 }  // namespace absl
462