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