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::__anone1ee0a730111::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 static_assert(
120 absl::is_trivially_relocatable<std::pair<const int, int>>::value, "");
121 static_assert(
122 std::is_same<decltype(absl::container_internal::FlatHashMapPolicy<
123 int, int>::transfer<std::allocator<char>>(nullptr,
124 nullptr,
125 nullptr)),
126 std::true_type>::value,
127 "");
128
129 struct NonRelocatable {
130 NonRelocatable() = default;
131 NonRelocatable(NonRelocatable&&) {}
132 NonRelocatable& operator=(NonRelocatable&&) { return *this; }
133 void* self = nullptr;
134 };
135
136 EXPECT_FALSE(absl::is_trivially_relocatable<NonRelocatable>::value);
137 EXPECT_TRUE(
138 (std::is_same<decltype(absl::container_internal::FlatHashMapPolicy<
139 int, NonRelocatable>::
140 transfer<std::allocator<char>>(nullptr, nullptr,
141 nullptr)),
142 std::false_type>::value));
143 }
144
145 // gcc becomes unhappy if this is inside the method, so pull it out here.
146 struct balast {};
147
TEST(FlatHashMap,IteratesMsan)148 TEST(FlatHashMap, IteratesMsan) {
149 // Because SwissTable randomizes on pointer addresses, we keep old tables
150 // around to ensure we don't reuse old memory.
151 std::vector<absl::flat_hash_map<int, balast>> garbage;
152 for (int i = 0; i < 100; ++i) {
153 absl::flat_hash_map<int, balast> t;
154 for (int j = 0; j < 100; ++j) {
155 t[j];
156 for (const auto& p : t) EXPECT_THAT(p, Pair(_, _));
157 }
158 garbage.push_back(std::move(t));
159 }
160 }
161
162 // Demonstration of the "Lazy Key" pattern. This uses heterogeneous insert to
163 // avoid creating expensive key elements when the item is already present in the
164 // map.
165 struct LazyInt {
LazyIntabsl::container_internal::__anone1ee0a730111::LazyInt166 explicit LazyInt(size_t value, int* tracker)
167 : value(value), tracker(tracker) {}
168
operator size_tabsl::container_internal::__anone1ee0a730111::LazyInt169 explicit operator size_t() const {
170 ++*tracker;
171 return value;
172 }
173
174 size_t value;
175 int* tracker;
176 };
177
178 struct Hash {
179 using is_transparent = void;
180 int* tracker;
operator ()absl::container_internal::__anone1ee0a730111::Hash181 size_t operator()(size_t obj) const {
182 ++*tracker;
183 return obj;
184 }
operator ()absl::container_internal::__anone1ee0a730111::Hash185 size_t operator()(const LazyInt& obj) const {
186 ++*tracker;
187 return obj.value;
188 }
189 };
190
191 struct Eq {
192 using is_transparent = void;
operator ()absl::container_internal::__anone1ee0a730111::Eq193 bool operator()(size_t lhs, size_t rhs) const { return lhs == rhs; }
operator ()absl::container_internal::__anone1ee0a730111::Eq194 bool operator()(size_t lhs, const LazyInt& rhs) const {
195 return lhs == rhs.value;
196 }
197 };
198
TEST(FlatHashMap,LazyKeyPattern)199 TEST(FlatHashMap, LazyKeyPattern) {
200 // hashes are only guaranteed in opt mode, we use assertions to track internal
201 // state that can cause extra calls to hash.
202 int conversions = 0;
203 int hashes = 0;
204 flat_hash_map<size_t, size_t, Hash, Eq> m(0, Hash{&hashes});
205 m.reserve(3);
206
207 m[LazyInt(1, &conversions)] = 1;
208 EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 1)));
209 EXPECT_EQ(conversions, 1);
210 #ifdef NDEBUG
211 EXPECT_EQ(hashes, 1);
212 #endif
213
214 m[LazyInt(1, &conversions)] = 2;
215 EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2)));
216 EXPECT_EQ(conversions, 1);
217 #ifdef NDEBUG
218 EXPECT_EQ(hashes, 2);
219 #endif
220
221 m.try_emplace(LazyInt(2, &conversions), 3);
222 EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3)));
223 EXPECT_EQ(conversions, 2);
224 #ifdef NDEBUG
225 EXPECT_EQ(hashes, 3);
226 #endif
227
228 m.try_emplace(LazyInt(2, &conversions), 4);
229 EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3)));
230 EXPECT_EQ(conversions, 2);
231 #ifdef NDEBUG
232 EXPECT_EQ(hashes, 4);
233 #endif
234 }
235
TEST(FlatHashMap,BitfieldArgument)236 TEST(FlatHashMap, BitfieldArgument) {
237 union {
238 int n : 1;
239 };
240 n = 0;
241 flat_hash_map<int, int> m;
242 m.erase(n);
243 m.count(n);
244 m.prefetch(n);
245 m.find(n);
246 m.contains(n);
247 m.equal_range(n);
248 m.insert_or_assign(n, n);
249 m.insert_or_assign(m.end(), n, n);
250 m.try_emplace(n);
251 m.try_emplace(m.end(), n);
252 m.at(n);
253 m[n];
254 }
255
TEST(FlatHashMap,MergeExtractInsert)256 TEST(FlatHashMap, MergeExtractInsert) {
257 // We can't test mutable keys, or non-copyable keys with flat_hash_map.
258 // Test that the nodes have the proper API.
259 absl::flat_hash_map<int, int> m = {{1, 7}, {2, 9}};
260 auto node = m.extract(1);
261 EXPECT_TRUE(node);
262 EXPECT_EQ(node.key(), 1);
263 EXPECT_EQ(node.mapped(), 7);
264 EXPECT_THAT(m, UnorderedElementsAre(Pair(2, 9)));
265
266 node.mapped() = 17;
267 m.insert(std::move(node));
268 EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 17), Pair(2, 9)));
269 }
270
FirstIsEven(std::pair<const int,int> p)271 bool FirstIsEven(std::pair<const int, int> p) { return p.first % 2 == 0; }
272
TEST(FlatHashMap,EraseIf)273 TEST(FlatHashMap, EraseIf) {
274 // Erase all elements.
275 {
276 flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
277 EXPECT_EQ(erase_if(s, [](std::pair<const int, int>) { return true; }), 5);
278 EXPECT_THAT(s, IsEmpty());
279 }
280 // Erase no 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 false; }), 0);
284 EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3),
285 Pair(4, 4), Pair(5, 5)));
286 }
287 // Erase specific elements.
288 {
289 flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
290 EXPECT_EQ(erase_if(s,
291 [](std::pair<const int, int> kvp) {
292 return kvp.first % 2 == 1;
293 }),
294 3);
295 EXPECT_THAT(s, UnorderedElementsAre(Pair(2, 2), Pair(4, 4)));
296 }
297 // Predicate is function reference.
298 {
299 flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
300 EXPECT_EQ(erase_if(s, FirstIsEven), 2);
301 EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
302 }
303 // Predicate is function pointer.
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 }
310
TEST(FlatHashMap,CForEach)311 TEST(FlatHashMap, CForEach) {
312 flat_hash_map<int, int> m;
313 std::vector<std::pair<int, int>> expected;
314 for (int i = 0; i < 100; ++i) {
315 {
316 SCOPED_TRACE("mutable object iteration");
317 std::vector<std::pair<int, int>> v;
318 absl::container_internal::c_for_each_fast(
319 m, [&v](std::pair<const int, int>& p) { v.push_back(p); });
320 EXPECT_THAT(v, UnorderedElementsAreArray(expected));
321 }
322 {
323 SCOPED_TRACE("const object iteration");
324 std::vector<std::pair<int, int>> v;
325 const flat_hash_map<int, int>& cm = m;
326 absl::container_internal::c_for_each_fast(
327 cm, [&v](const std::pair<const int, int>& p) { v.push_back(p); });
328 EXPECT_THAT(v, UnorderedElementsAreArray(expected));
329 }
330 {
331 SCOPED_TRACE("const object iteration");
332 std::vector<std::pair<int, int>> v;
333 absl::container_internal::c_for_each_fast(
334 flat_hash_map<int, int>(m),
335 [&v](std::pair<const int, int>& p) { v.push_back(p); });
336 EXPECT_THAT(v, UnorderedElementsAreArray(expected));
337 }
338 m[i] = i;
339 expected.emplace_back(i, i);
340 }
341 }
342
TEST(FlatHashMap,CForEachMutate)343 TEST(FlatHashMap, CForEachMutate) {
344 flat_hash_map<int, int> s;
345 std::vector<std::pair<int, int>> expected;
346 for (int i = 0; i < 100; ++i) {
347 std::vector<std::pair<int, int>> v;
348 absl::container_internal::c_for_each_fast(
349 s, [&v](std::pair<const int, int>& p) {
350 v.push_back(p);
351 p.second++;
352 });
353 EXPECT_THAT(v, UnorderedElementsAreArray(expected));
354 for (auto& p : expected) {
355 p.second++;
356 }
357 EXPECT_THAT(s, UnorderedElementsAreArray(expected));
358 s[i] = i;
359 expected.emplace_back(i, i);
360 }
361 }
362
363 // This test requires std::launder for mutable key access in node handles.
364 #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
TEST(FlatHashMap,NodeHandleMutableKeyAccess)365 TEST(FlatHashMap, NodeHandleMutableKeyAccess) {
366 flat_hash_map<std::string, std::string> map;
367
368 map["key1"] = "mapped";
369
370 auto nh = map.extract(map.begin());
371 nh.key().resize(3);
372 map.insert(std::move(nh));
373
374 EXPECT_THAT(map, testing::ElementsAre(Pair("key", "mapped")));
375 }
376 #endif
377
TEST(FlatHashMap,Reserve)378 TEST(FlatHashMap, Reserve) {
379 // Verify that if we reserve(size() + n) then we can perform n insertions
380 // without a rehash, i.e., without invalidating any references.
381 for (size_t trial = 0; trial < 20; ++trial) {
382 for (size_t initial = 3; initial < 100; ++initial) {
383 // Fill in `initial` entries, then erase 2 of them, then reserve space for
384 // two inserts and check for reference stability while doing the inserts.
385 flat_hash_map<size_t, size_t> map;
386 for (size_t i = 0; i < initial; ++i) {
387 map[i] = i;
388 }
389 map.erase(0);
390 map.erase(1);
391 map.reserve(map.size() + 2);
392 size_t& a2 = map[2];
393 // In the event of a failure, asan will complain in one of these two
394 // assignments.
395 map[initial] = a2;
396 map[initial + 1] = a2;
397 // Fail even when not under asan:
398 size_t& a2new = map[2];
399 EXPECT_EQ(&a2, &a2new);
400 }
401 }
402 }
403
TEST(FlatHashMap,RecursiveTypeCompiles)404 TEST(FlatHashMap, RecursiveTypeCompiles) {
405 struct RecursiveType {
406 flat_hash_map<int, RecursiveType> m;
407 };
408 RecursiveType t;
409 t.m[0] = RecursiveType{};
410 }
411
TEST(FlatHashMap,FlatHashMapPolicyDestroyReturnsTrue)412 TEST(FlatHashMap, FlatHashMapPolicyDestroyReturnsTrue) {
413 EXPECT_TRUE(
414 (decltype(FlatHashMapPolicy<int, char>::destroy<std::allocator<char>>(
415 nullptr, nullptr))()));
416 EXPECT_FALSE(
417 (decltype(FlatHashMapPolicy<int, char>::destroy<CountingAllocator<char>>(
418 nullptr, nullptr))()));
419 EXPECT_FALSE((decltype(FlatHashMapPolicy<int, std::unique_ptr<int>>::destroy<
420 std::allocator<char>>(nullptr, nullptr))()));
421 }
422
423 struct InconsistentHashEqType {
InconsistentHashEqTypeabsl::container_internal::__anone1ee0a730111::InconsistentHashEqType424 InconsistentHashEqType(int v1, int v2) : v1(v1), v2(v2) {}
425 template <typename H>
AbslHashValue(H h,InconsistentHashEqType t)426 friend H AbslHashValue(H h, InconsistentHashEqType t) {
427 return H::combine(std::move(h), t.v1);
428 }
operator ==absl::container_internal::__anone1ee0a730111::InconsistentHashEqType429 bool operator==(InconsistentHashEqType t) const { return v2 == t.v2; }
430 int v1, v2;
431 };
432
TEST(Iterator,InconsistentHashEqFunctorsValidation)433 TEST(Iterator, InconsistentHashEqFunctorsValidation) {
434 if (!IsAssertEnabled()) GTEST_SKIP() << "Assertions not enabled.";
435
436 absl::flat_hash_map<InconsistentHashEqType, int> m;
437 for (int i = 0; i < 10; ++i) m[{i, i}] = 1;
438 // We need to insert multiple times to guarantee that we get the assertion
439 // because it's possible for the hash to collide with the inserted element
440 // that has v2==0. In those cases, the new element won't be inserted.
441 auto insert_conflicting_elems = [&] {
442 for (int i = 100; i < 20000; ++i) {
443 EXPECT_EQ((m[{i, 0}]), 1);
444 }
445 };
446
447 const char* crash_message = "hash/eq functors are inconsistent.";
448 #if defined(__arm__) || defined(__aarch64__)
449 // On ARM, the crash message is garbled so don't expect a specific message.
450 crash_message = "";
451 #endif
452 EXPECT_DEATH_IF_SUPPORTED(insert_conflicting_elems(), crash_message);
453 }
454
455 } // namespace
456 } // namespace container_internal
457 ABSL_NAMESPACE_END
458 } // namespace absl
459