flat_hash_map_test.cc 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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. #include "absl/container/flat_hash_map.h"
  15. #include <memory>
  16. #include "absl/base/internal/raw_logging.h"
  17. #include "absl/container/internal/hash_generator_testing.h"
  18. #include "absl/container/internal/unordered_map_constructor_test.h"
  19. #include "absl/container/internal/unordered_map_lookup_test.h"
  20. #include "absl/container/internal/unordered_map_members_test.h"
  21. #include "absl/container/internal/unordered_map_modifiers_test.h"
  22. #include "absl/types/any.h"
  23. namespace absl {
  24. ABSL_NAMESPACE_BEGIN
  25. namespace container_internal {
  26. namespace {
  27. using ::absl::container_internal::hash_internal::Enum;
  28. using ::absl::container_internal::hash_internal::EnumClass;
  29. using ::testing::_;
  30. using ::testing::IsEmpty;
  31. using ::testing::Pair;
  32. using ::testing::UnorderedElementsAre;
  33. // Check that absl::flat_hash_map works in a global constructor.
  34. struct BeforeMain {
  35. BeforeMain() {
  36. absl::flat_hash_map<int, int> x;
  37. x.insert({1, 1});
  38. ABSL_RAW_CHECK(x.find(0) == x.end(), "x should not contain 0");
  39. auto it = x.find(1);
  40. ABSL_RAW_CHECK(it != x.end(), "x should contain 1");
  41. ABSL_RAW_CHECK(it->second, "1 should map to 1");
  42. }
  43. };
  44. const BeforeMain before_main;
  45. template <class K, class V>
  46. using Map = flat_hash_map<K, V, StatefulTestingHash, StatefulTestingEqual,
  47. Alloc<std::pair<const K, V>>>;
  48. static_assert(!std::is_standard_layout<NonStandardLayout>(), "");
  49. using MapTypes =
  50. ::testing::Types<Map<int, int>, Map<std::string, int>,
  51. Map<Enum, std::string>, Map<EnumClass, int>,
  52. Map<int, NonStandardLayout>, Map<NonStandardLayout, int>>;
  53. INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ConstructorTest, MapTypes);
  54. INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, LookupTest, MapTypes);
  55. INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, MembersTest, MapTypes);
  56. INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, ModifiersTest, MapTypes);
  57. using UniquePtrMapTypes = ::testing::Types<Map<int, std::unique_ptr<int>>>;
  58. INSTANTIATE_TYPED_TEST_SUITE_P(FlatHashMap, UniquePtrModifiersTest,
  59. UniquePtrMapTypes);
  60. TEST(FlatHashMap, StandardLayout) {
  61. struct Int {
  62. explicit Int(size_t value) : value(value) {}
  63. Int() : value(0) { ADD_FAILURE(); }
  64. Int(const Int& other) : value(other.value) { ADD_FAILURE(); }
  65. Int(Int&&) = default;
  66. bool operator==(const Int& other) const { return value == other.value; }
  67. size_t value;
  68. };
  69. static_assert(std::is_standard_layout<Int>(), "");
  70. struct Hash {
  71. size_t operator()(const Int& obj) const { return obj.value; }
  72. };
  73. // Verify that neither the key nor the value get default-constructed or
  74. // copy-constructed.
  75. {
  76. flat_hash_map<Int, Int, Hash> m;
  77. m.try_emplace(Int(1), Int(2));
  78. m.try_emplace(Int(3), Int(4));
  79. m.erase(Int(1));
  80. m.rehash(2 * m.bucket_count());
  81. }
  82. {
  83. flat_hash_map<Int, Int, Hash> m;
  84. m.try_emplace(Int(1), Int(2));
  85. m.try_emplace(Int(3), Int(4));
  86. m.erase(Int(1));
  87. m.clear();
  88. }
  89. }
  90. // gcc becomes unhappy if this is inside the method, so pull it out here.
  91. struct balast {};
  92. TEST(FlatHashMap, IteratesMsan) {
  93. // Because SwissTable randomizes on pointer addresses, we keep old tables
  94. // around to ensure we don't reuse old memory.
  95. std::vector<absl::flat_hash_map<int, balast>> garbage;
  96. for (int i = 0; i < 100; ++i) {
  97. absl::flat_hash_map<int, balast> t;
  98. for (int j = 0; j < 100; ++j) {
  99. t[j];
  100. for (const auto& p : t) EXPECT_THAT(p, Pair(_, _));
  101. }
  102. garbage.push_back(std::move(t));
  103. }
  104. }
  105. // Demonstration of the "Lazy Key" pattern. This uses heterogeneous insert to
  106. // avoid creating expensive key elements when the item is already present in the
  107. // map.
  108. struct LazyInt {
  109. explicit LazyInt(size_t value, int* tracker)
  110. : value(value), tracker(tracker) {}
  111. explicit operator size_t() const {
  112. ++*tracker;
  113. return value;
  114. }
  115. size_t value;
  116. int* tracker;
  117. };
  118. struct Hash {
  119. using is_transparent = void;
  120. int* tracker;
  121. size_t operator()(size_t obj) const {
  122. ++*tracker;
  123. return obj;
  124. }
  125. size_t operator()(const LazyInt& obj) const {
  126. ++*tracker;
  127. return obj.value;
  128. }
  129. };
  130. struct Eq {
  131. using is_transparent = void;
  132. bool operator()(size_t lhs, size_t rhs) const {
  133. return lhs == rhs;
  134. }
  135. bool operator()(size_t lhs, const LazyInt& rhs) const {
  136. return lhs == rhs.value;
  137. }
  138. };
  139. TEST(FlatHashMap, LazyKeyPattern) {
  140. // hashes are only guaranteed in opt mode, we use assertions to track internal
  141. // state that can cause extra calls to hash.
  142. int conversions = 0;
  143. int hashes = 0;
  144. flat_hash_map<size_t, size_t, Hash, Eq> m(0, Hash{&hashes});
  145. m.reserve(3);
  146. m[LazyInt(1, &conversions)] = 1;
  147. EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 1)));
  148. EXPECT_EQ(conversions, 1);
  149. #ifdef NDEBUG
  150. EXPECT_EQ(hashes, 1);
  151. #endif
  152. m[LazyInt(1, &conversions)] = 2;
  153. EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2)));
  154. EXPECT_EQ(conversions, 1);
  155. #ifdef NDEBUG
  156. EXPECT_EQ(hashes, 2);
  157. #endif
  158. m.try_emplace(LazyInt(2, &conversions), 3);
  159. EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3)));
  160. EXPECT_EQ(conversions, 2);
  161. #ifdef NDEBUG
  162. EXPECT_EQ(hashes, 3);
  163. #endif
  164. m.try_emplace(LazyInt(2, &conversions), 4);
  165. EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 2), Pair(2, 3)));
  166. EXPECT_EQ(conversions, 2);
  167. #ifdef NDEBUG
  168. EXPECT_EQ(hashes, 4);
  169. #endif
  170. }
  171. TEST(FlatHashMap, BitfieldArgument) {
  172. union {
  173. int n : 1;
  174. };
  175. n = 0;
  176. flat_hash_map<int, int> m;
  177. m.erase(n);
  178. m.count(n);
  179. m.prefetch(n);
  180. m.find(n);
  181. m.contains(n);
  182. m.equal_range(n);
  183. m.insert_or_assign(n, n);
  184. m.insert_or_assign(m.end(), n, n);
  185. m.try_emplace(n);
  186. m.try_emplace(m.end(), n);
  187. m.at(n);
  188. m[n];
  189. }
  190. TEST(FlatHashMap, MergeExtractInsert) {
  191. // We can't test mutable keys, or non-copyable keys with flat_hash_map.
  192. // Test that the nodes have the proper API.
  193. absl::flat_hash_map<int, int> m = {{1, 7}, {2, 9}};
  194. auto node = m.extract(1);
  195. EXPECT_TRUE(node);
  196. EXPECT_EQ(node.key(), 1);
  197. EXPECT_EQ(node.mapped(), 7);
  198. EXPECT_THAT(m, UnorderedElementsAre(Pair(2, 9)));
  199. node.mapped() = 17;
  200. m.insert(std::move(node));
  201. EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 17), Pair(2, 9)));
  202. }
  203. bool FirstIsEven(std::pair<const int, int> p) { return p.first % 2 == 0; }
  204. TEST(FlatHashMap, EraseIf) {
  205. // Erase all elements.
  206. {
  207. flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
  208. erase_if(s, [](std::pair<const int, int>) { return true; });
  209. EXPECT_THAT(s, IsEmpty());
  210. }
  211. // Erase no elements.
  212. {
  213. flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
  214. erase_if(s, [](std::pair<const int, int>) { return false; });
  215. EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3),
  216. Pair(4, 4), Pair(5, 5)));
  217. }
  218. // Erase specific elements.
  219. {
  220. flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
  221. erase_if(s,
  222. [](std::pair<const int, int> kvp) { return kvp.first % 2 == 1; });
  223. EXPECT_THAT(s, UnorderedElementsAre(Pair(2, 2), Pair(4, 4)));
  224. }
  225. // Predicate is function reference.
  226. {
  227. flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
  228. erase_if(s, FirstIsEven);
  229. EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
  230. }
  231. // Predicate is function pointer.
  232. {
  233. flat_hash_map<int, int> s = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}};
  234. erase_if(s, &FirstIsEven);
  235. EXPECT_THAT(s, UnorderedElementsAre(Pair(1, 1), Pair(3, 3), Pair(5, 5)));
  236. }
  237. }
  238. // This test requires std::launder for mutable key access in node handles.
  239. #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
  240. TEST(FlatHashMap, NodeHandleMutableKeyAccess) {
  241. flat_hash_map<std::string, std::string> map;
  242. map["key1"] = "mapped";
  243. auto nh = map.extract(map.begin());
  244. nh.key().resize(3);
  245. map.insert(std::move(nh));
  246. EXPECT_THAT(map, testing::ElementsAre(Pair("key", "mapped")));
  247. }
  248. #endif
  249. TEST(FlatHashMap, Reserve) {
  250. // Verify that if we reserve(size() + n) then we can perform n insertions
  251. // without a rehash, i.e., without invalidating any references.
  252. for (size_t trial = 0; trial < 20; ++trial) {
  253. for (size_t initial = 3; initial < 100; ++initial) {
  254. // Fill in `initial` entries, then erase 2 of them, then reserve space for
  255. // two inserts and check for reference stability while doing the inserts.
  256. flat_hash_map<size_t, size_t> map;
  257. for (size_t i = 0; i < initial; ++i) {
  258. map[i] = i;
  259. }
  260. map.erase(0);
  261. map.erase(1);
  262. map.reserve(map.size() + 2);
  263. size_t& a2 = map[2];
  264. // In the event of a failure, asan will complain in one of these two
  265. // assignments.
  266. map[initial] = a2;
  267. map[initial + 1] = a2;
  268. // Fail even when not under asan:
  269. size_t& a2new = map[2];
  270. EXPECT_EQ(&a2, &a2new);
  271. }
  272. }
  273. }
  274. } // namespace
  275. } // namespace container_internal
  276. ABSL_NAMESPACE_END
  277. } // namespace absl