abseil-cpp/absl/container/flat_hash_map_test.cc
Go to the documentation of this file.
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 <memory>
18 
19 #include "absl/base/internal/raw_logging.h"
20 #include "absl/container/internal/hash_generator_testing.h"
21 #include "absl/container/internal/unordered_map_constructor_test.h"
22 #include "absl/container/internal/unordered_map_lookup_test.h"
23 #include "absl/container/internal/unordered_map_members_test.h"
24 #include "absl/container/internal/unordered_map_modifiers_test.h"
25 #include "absl/types/any.h"
26 
27 namespace absl {
29 namespace container_internal {
30 namespace {
37 
38 // Check that absl::flat_hash_map works in a global constructor.
39 struct BeforeMain {
40  BeforeMain() {
42  x.insert({1, 1});
43  ABSL_RAW_CHECK(x.find(0) == x.end(), "x should not contain 0");
44  auto it = x.find(1);
45  ABSL_RAW_CHECK(it != x.end(), "x should contain 1");
46  ABSL_RAW_CHECK(it->second, "1 should map to 1");
47  }
48 };
49 const BeforeMain before_main;
50 
51 template <class K, class V>
52 using Map = flat_hash_map<K, V, StatefulTestingHash, StatefulTestingEqual,
53  Alloc<std::pair<const K, V>>>;
54 
55 static_assert(!std::is_standard_layout<NonStandardLayout>(), "");
56 
57 using MapTypes =
61 
62 INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ConstructorTest, MapTypes);
63 INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, LookupTest, MapTypes);
64 INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, MembersTest, MapTypes);
65 INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ModifiersTest, MapTypes);
66 
67 using UniquePtrMapTypes = ::testing::Types<Map<int, std::unique_ptr<int>>>;
68 
69 INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, UniquePtrModifiersTest,
70  UniquePtrMapTypes);
71 
72 TEST(FlatHashMap, StandardLayout) {
73  struct Int {
74  explicit Int(size_t value) : value(value) {}
75  Int() : value(0) { ADD_FAILURE(); }
76  Int(const Int& other) : value(other.value) { ADD_FAILURE(); }
77  Int(Int&&) = default;
78  bool operator==(const Int& other) const { return value == other.value; }
79  size_t value;
80  };
81  static_assert(std::is_standard_layout<Int>(), "");
82 
83  struct Hash {
84  size_t operator()(const Int& obj) const { return obj.value; }
85  };
86 
87  // Verify that neither the key nor the value get default-constructed or
88  // copy-constructed.
89  {
90  flat_hash_map<Int, Int, Hash> m;
91  m.try_emplace(Int(1), Int(2));
92  m.try_emplace(Int(3), Int(4));
93  m.erase(Int(1));
94  m.rehash(2 * m.bucket_count());
95  }
96  {
97  flat_hash_map<Int, Int, Hash> m;
98  m.try_emplace(Int(1), Int(2));
99  m.try_emplace(Int(3), Int(4));
100  m.erase(Int(1));
101  m.clear();
102  }
103 }
104 
105 // gcc becomes unhappy if this is inside the method, so pull it out here.
106 struct balast {};
107 
108 TEST(FlatHashMap, IteratesMsan) {
109  // Because SwissTable randomizes on pointer addresses, we keep old tables
110  // around to ensure we don't reuse old memory.
111  std::vector<absl::flat_hash_map<int, balast>> garbage;
112  for (int i = 0; i < 100; ++i) {
114  for (int j = 0; j < 100; ++j) {
115  t[j];
116  for (const auto& p : t) EXPECT_THAT(p, Pair(_, _));
117  }
118  garbage.push_back(std::move(t));
119  }
120 }
121 
122 // Demonstration of the "Lazy Key" pattern. This uses heterogeneous insert to
123 // avoid creating expensive key elements when the item is already present in the
124 // map.
125 struct LazyInt {
126  explicit LazyInt(size_t value, int* tracker)
127  : value(value), tracker(tracker) {}
128 
129  explicit operator size_t() const {
130  ++*tracker;
131  return value;
132  }
133 
134  size_t value;
135  int* tracker;
136 };
137 
138 struct Hash {
139  using is_transparent = void;
140  int* tracker;
141  size_t operator()(size_t obj) const {
142  ++*tracker;
143  return obj;
144  }
145  size_t operator()(const LazyInt& obj) const {
146  ++*tracker;
147  return obj.value;
148  }
149 };
150 
151 struct Eq {
152  using is_transparent = void;
153  bool operator()(size_t lhs, size_t rhs) const {
154  return lhs == rhs;
155  }
156  bool operator()(size_t lhs, const LazyInt& rhs) const {
157  return lhs == rhs.value;
158  }
159 };
160 
161 TEST(FlatHashMap, LazyKeyPattern) {
162  // hashes are only guaranteed in opt mode, we use assertions to track internal
163  // state that can cause extra calls to hash.
164  int conversions = 0;
165  int hashes = 0;
166  flat_hash_map<size_t, size_t, Hash, Eq> m(0, Hash{&hashes});
167  m.reserve(3);
168 
169  m[LazyInt(1, &conversions)] = 1;
171  EXPECT_EQ(conversions, 1);
172 #ifdef NDEBUG
173  EXPECT_EQ(hashes, 1);
174 #endif
175 
176  m[LazyInt(1, &conversions)] = 2;
178  EXPECT_EQ(conversions, 1);
179 #ifdef NDEBUG
180  EXPECT_EQ(hashes, 2);
181 #endif
182 
183  m.try_emplace(LazyInt(2, &conversions), 3);
184  EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3)));
185  EXPECT_EQ(conversions, 2);
186 #ifdef NDEBUG
187  EXPECT_EQ(hashes, 3);
188 #endif
189 
190  m.try_emplace(LazyInt(2, &conversions), 4);
191  EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3)));
192  EXPECT_EQ(conversions, 2);
193 #ifdef NDEBUG
194  EXPECT_EQ(hashes, 4);
195 #endif
196 }
197 
198 TEST(FlatHashMap, BitfieldArgument) {
199  union {
200  int n : 1;
201  };
202  n = 0;
203  flat_hash_map<int, int> m;
204  m.erase(n);
205  m.count(n);
206  m.prefetch(n);
207  m.find(n);
208  m.contains(n);
209  m.equal_range(n);
210  m.insert_or_assign(n, n);
211  m.insert_or_assign(m.end(), n, n);
212  m.try_emplace(n);
213  m.try_emplace(m.end(), n);
214  m.at(n);
215  m[n];
216 }
217 
218 TEST(FlatHashMap, MergeExtractInsert) {
219  // We can't test mutable keys, or non-copyable keys with flat_hash_map.
220  // Test that the nodes have the proper API.
221  absl::flat_hash_map<int, int> m = {{1, 7}, {2, 9}};
222  auto node = m.extract(1);
223  EXPECT_TRUE(node);
224  EXPECT_EQ(node.key(), 1);
225  EXPECT_EQ(node.mapped(), 7);
227 
228  node.mapped() = 17;
229  m.insert(std::move(node));
230  EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 17), Pair(2, 9)));
231 }
232 
233 bool FirstIsEven(std::pair<const int, int> p) { return p.first % 2 == 0; }
234 
235 TEST(FlatHashMap, EraseIf) {
236  // Erase all elements.
237  {
238  flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
239  EXPECT_EQ(erase_if(s, [](std::pair<const int, int>) { return true; }), 5);
240  EXPECT_THAT(s, IsEmpty());
241  }
242  // Erase no elements.
243  {
244  flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
245  EXPECT_EQ(erase_if(s, [](std::pair<const int, int>) { return false; }), 0);
246  EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3),
247  Pair(4, 4), Pair(5, 5)));
248  }
249  // Erase specific elements.
250  {
251  flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
253  [](std::pair<const int, int> kvp) {
254  return kvp.first % 2 == 1;
255  }),
256  3);
257  EXPECT_THAT(s, UnorderedElementsAre(Pair(2, 2), Pair(4, 4)));
258  }
259  // Predicate is function reference.
260  {
261  flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
262  EXPECT_EQ(erase_if(s, FirstIsEven), 2);
263  EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
264  }
265  // Predicate is function pointer.
266  {
267  flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
268  EXPECT_EQ(erase_if(s, &FirstIsEven), 2);
269  EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
270  }
271 }
272 
273 // This test requires std::launder for mutable key access in node handles.
274 #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
275 TEST(FlatHashMap, NodeHandleMutableKeyAccess) {
276  flat_hash_map<std::string, std::string> map;
277 
278  map["key1"] = "mapped";
279 
280  auto nh = map.extract(map.begin());
281  nh.key().resize(3);
282  map.insert(std::move(nh));
283 
284  EXPECT_THAT(map, testing::ElementsAre(Pair("key", "mapped")));
285 }
286 #endif
287 
288 TEST(FlatHashMap, Reserve) {
289  // Verify that if we reserve(size() + n) then we can perform n insertions
290  // without a rehash, i.e., without invalidating any references.
291  for (size_t trial = 0; trial < 20; ++trial) {
292  for (size_t initial = 3; initial < 100; ++initial) {
293  // Fill in `initial` entries, then erase 2 of them, then reserve space for
294  // two inserts and check for reference stability while doing the inserts.
295  flat_hash_map<size_t, size_t> map;
296  for (size_t i = 0; i < initial; ++i) {
297  map[i] = i;
298  }
299  map.erase(0);
300  map.erase(1);
301  map.reserve(map.size() + 2);
302  size_t& a2 = map[2];
303  // In the event of a failure, asan will complain in one of these two
304  // assignments.
305  map[initial] = a2;
306  map[initial + 1] = a2;
307  // Fail even when not under asan:
308  size_t& a2new = map[2];
309  EXPECT_EQ(&a2, &a2new);
310  }
311  }
312 }
313 
314 } // namespace
315 } // namespace container_internal
317 } // namespace absl
ABSL_RAW_CHECK
#define ABSL_RAW_CHECK(condition, message)
Definition: abseil-cpp/absl/base/internal/raw_logging.h:59
obj
OPENSSL_EXPORT const ASN1_OBJECT * obj
Definition: x509.h:1671
Hash
Hash
Definition: abseil-cpp/absl/container/internal/hash_function_defaults_test.cc:339
regen-readme.it
it
Definition: regen-readme.py:15
absl::str_format_internal::LengthMod::j
@ j
value
size_t value
Definition: abseil-cpp/absl/container/flat_hash_map_test.cc:134
Map
Definition: bloaty/third_party/protobuf/ruby/ext/google/protobuf_c/protobuf.h:451
EXPECT_THAT
#define EXPECT_THAT(value, matcher)
absl::TEST
TEST(NotificationTest, SanityTest)
Definition: abseil-cpp/absl/synchronization/notification_test.cc:126
absl::FormatConversionChar::s
@ s
xds_manager.p
p
Definition: xds_manager.py:60
ABSL_NAMESPACE_END
#define ABSL_NAMESPACE_END
Definition: third_party/abseil-cpp/absl/base/config.h:171
absl::container_internal::hash_internal::EnumClass
EnumClass
Definition: abseil-cpp/absl/container/internal/hash_generator_testing.h:58
map
zval * map
Definition: php/ext/google/protobuf/encode_decode.c:480
Enum
struct Enum Enum
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/protobuf.h:642
EXPECT_EQ
#define EXPECT_EQ(a, b)
Definition: iomgr/time_averaged_stats_test.cc:27
testing::ElementsAre
internal::ElementsAreMatcher< ::testing::tuple<> > ElementsAre()
Definition: cares/cares/test/gmock-1.8.0/gmock/gmock.h:13040
testing::internal::ProxyTypeList
Definition: googletest/googletest/include/gtest/internal/gtest-type-util.h:155
ABSL_NAMESPACE_BEGIN
#define ABSL_NAMESPACE_BEGIN
Definition: third_party/abseil-cpp/absl/base/config.h:170
a2
T::first_type a2
Definition: abseil-cpp/absl/container/internal/hash_function_defaults_test.cc:307
absl::move
constexpr absl::remove_reference_t< T > && move(T &&t) noexcept
Definition: abseil-cpp/absl/utility/utility.h:221
testing::Eq
internal::EqMatcher< T > Eq(T x)
Definition: cares/cares/test/gmock-1.8.0/gmock/gmock.h:8561
gmock_output_test._
_
Definition: bloaty/third_party/googletest/googlemock/test/gmock_output_test.py:175
INSTANTIATE_TYPED_TEST_SUITE_P
#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types,...)
Definition: googletest/googletest/include/gtest/gtest-typed-test.h:306
absl::erase_if
btree_map< K, V, C, A >::size_type erase_if(btree_map< K, V, C, A > &map, Pred pred)
Definition: abseil-cpp/absl/container/btree_map.h:483
x
int x
Definition: bloaty/third_party/googletest/googlemock/test/gmock-matchers_test.cc:3610
absl::operator==
bool operator==(const absl::InlinedVector< T, N, A > &a, const absl::InlinedVector< T, N, A > &b)
Definition: abseil-cpp/absl/container/inlined_vector.h:794
ADD_FAILURE
#define ADD_FAILURE()
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:1911
Json::Int
int Int
Definition: third_party/bloaty/third_party/protobuf/conformance/third_party/jsoncpp/json.h:228
n
int n
Definition: abseil-cpp/absl/container/btree_test.cc:1080
testing::Pair
internal::PairMatcher< FirstMatcher, SecondMatcher > Pair(FirstMatcher first_matcher, SecondMatcher second_matcher)
Definition: cares/cares/test/gmock-1.8.0/gmock/gmock.h:9152
absl::container_internal::EraseIf
raw_hash_set< P, H, E, A >::size_type EraseIf(Predicate &pred, raw_hash_set< P, H, E, A > *c)
Definition: abseil-cpp/absl/container/internal/raw_hash_set.h:2287
absl::container_internal::IsEmpty
bool IsEmpty(ctrl_t c)
Definition: abseil-cpp/absl/container/internal/raw_hash_set.h:489
absl::str_format_internal::LengthMod::t
@ t
absl::flat_hash_map
Definition: abseil-cpp/absl/container/flat_hash_map.h:113
testing::UnorderedElementsAre
internal::UnorderedElementsAreMatcher< ::testing::tuple<> > UnorderedElementsAre()
Definition: cares/cares/test/gmock-1.8.0/gmock/gmock.h:13255
EXPECT_TRUE
#define EXPECT_TRUE(condition)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:1967
absl
Definition: abseil-cpp/absl/algorithm/algorithm.h:31
regress.m
m
Definition: regress/regress.py:25
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230
tracker
int * tracker
Definition: abseil-cpp/absl/container/flat_hash_map_test.cc:135


grpc
Author(s):
autogenerated on Fri May 16 2025 02:58:24