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/internal/raw_hash_set.h"
16
17 #include <algorithm>
18 #include <atomic>
19 #include <cmath>
20 #include <cstddef>
21 #include <cstdint>
22 #include <deque>
23 #include <functional>
24 #include <iostream>
25 #include <iterator>
26 #include <list>
27 #include <map>
28 #include <memory>
29 #include <numeric>
30 #include <ostream>
31 #include <random>
32 #include <string>
33 #include <type_traits>
34 #include <unordered_map>
35 #include <unordered_set>
36 #include <utility>
37 #include <vector>
38
39 #include "gmock/gmock.h"
40 #include "gtest/gtest.h"
41 #include "absl/base/attributes.h"
42 #include "absl/base/config.h"
43 #include "absl/base/internal/cycleclock.h"
44 #include "absl/base/prefetch.h"
45 #include "absl/container/flat_hash_map.h"
46 #include "absl/container/flat_hash_set.h"
47 #include "absl/container/internal/container_memory.h"
48 #include "absl/container/internal/hash_function_defaults.h"
49 #include "absl/container/internal/hash_policy_testing.h"
50 #include "absl/container/internal/hashtable_debug.h"
51 #include "absl/log/log.h"
52 #include "absl/strings/string_view.h"
53
54 namespace absl {
55 ABSL_NAMESPACE_BEGIN
56 namespace container_internal {
57
58 struct RawHashSetTestOnlyAccess {
59 template <typename C>
GetSlotsabsl::container_internal::RawHashSetTestOnlyAccess60 static auto GetSlots(const C& c) -> decltype(c.slot_array()) {
61 return c.slot_array();
62 }
63 template <typename C>
CountTombstonesabsl::container_internal::RawHashSetTestOnlyAccess64 static size_t CountTombstones(const C& c) {
65 return c.common().TombstonesCount();
66 }
67 };
68
69 namespace {
70
71 using ::testing::ElementsAre;
72 using ::testing::Eq;
73 using ::testing::Ge;
74 using ::testing::Lt;
75 using ::testing::Pair;
76 using ::testing::UnorderedElementsAre;
77
78 // Convenience function to static cast to ctrl_t.
CtrlT(int i)79 ctrl_t CtrlT(int i) { return static_cast<ctrl_t>(i); }
80
TEST(Util,NormalizeCapacity)81 TEST(Util, NormalizeCapacity) {
82 EXPECT_EQ(1, NormalizeCapacity(0));
83 EXPECT_EQ(1, NormalizeCapacity(1));
84 EXPECT_EQ(3, NormalizeCapacity(2));
85 EXPECT_EQ(3, NormalizeCapacity(3));
86 EXPECT_EQ(7, NormalizeCapacity(4));
87 EXPECT_EQ(7, NormalizeCapacity(7));
88 EXPECT_EQ(15, NormalizeCapacity(8));
89 EXPECT_EQ(15, NormalizeCapacity(15));
90 EXPECT_EQ(15 * 2 + 1, NormalizeCapacity(15 + 1));
91 EXPECT_EQ(15 * 2 + 1, NormalizeCapacity(15 + 2));
92 }
93
TEST(Util,GrowthAndCapacity)94 TEST(Util, GrowthAndCapacity) {
95 // Verify that GrowthToCapacity gives the minimum capacity that has enough
96 // growth.
97 for (size_t growth = 0; growth < 10000; ++growth) {
98 SCOPED_TRACE(growth);
99 size_t capacity = NormalizeCapacity(GrowthToLowerboundCapacity(growth));
100 // The capacity is large enough for `growth`.
101 EXPECT_THAT(CapacityToGrowth(capacity), Ge(growth));
102 // For (capacity+1) < kWidth, growth should equal capacity.
103 if (capacity + 1 < Group::kWidth) {
104 EXPECT_THAT(CapacityToGrowth(capacity), Eq(capacity));
105 } else {
106 EXPECT_THAT(CapacityToGrowth(capacity), Lt(capacity));
107 }
108 if (growth != 0 && capacity > 1) {
109 // There is no smaller capacity that works.
110 EXPECT_THAT(CapacityToGrowth(capacity / 2), Lt(growth));
111 }
112 }
113
114 for (size_t capacity = Group::kWidth - 1; capacity < 10000;
115 capacity = 2 * capacity + 1) {
116 SCOPED_TRACE(capacity);
117 size_t growth = CapacityToGrowth(capacity);
118 EXPECT_THAT(growth, Lt(capacity));
119 EXPECT_LE(GrowthToLowerboundCapacity(growth), capacity);
120 EXPECT_EQ(NormalizeCapacity(GrowthToLowerboundCapacity(growth)), capacity);
121 }
122 }
123
TEST(Util,probe_seq)124 TEST(Util, probe_seq) {
125 probe_seq<16> seq(0, 127);
126 auto gen = [&]() {
127 size_t res = seq.offset();
128 seq.next();
129 return res;
130 };
131 std::vector<size_t> offsets(8);
132 std::generate_n(offsets.begin(), 8, gen);
133 EXPECT_THAT(offsets, ElementsAre(0, 16, 48, 96, 32, 112, 80, 64));
134 seq = probe_seq<16>(128, 127);
135 std::generate_n(offsets.begin(), 8, gen);
136 EXPECT_THAT(offsets, ElementsAre(0, 16, 48, 96, 32, 112, 80, 64));
137 }
138
TEST(BitMask,Smoke)139 TEST(BitMask, Smoke) {
140 EXPECT_FALSE((BitMask<uint8_t, 8>(0)));
141 EXPECT_TRUE((BitMask<uint8_t, 8>(5)));
142
143 EXPECT_THAT((BitMask<uint8_t, 8>(0)), ElementsAre());
144 EXPECT_THAT((BitMask<uint8_t, 8>(0x1)), ElementsAre(0));
145 EXPECT_THAT((BitMask<uint8_t, 8>(0x2)), ElementsAre(1));
146 EXPECT_THAT((BitMask<uint8_t, 8>(0x3)), ElementsAre(0, 1));
147 EXPECT_THAT((BitMask<uint8_t, 8>(0x4)), ElementsAre(2));
148 EXPECT_THAT((BitMask<uint8_t, 8>(0x5)), ElementsAre(0, 2));
149 EXPECT_THAT((BitMask<uint8_t, 8>(0x55)), ElementsAre(0, 2, 4, 6));
150 EXPECT_THAT((BitMask<uint8_t, 8>(0xAA)), ElementsAre(1, 3, 5, 7));
151 }
152
TEST(BitMask,WithShift)153 TEST(BitMask, WithShift) {
154 // See the non-SSE version of Group for details on what this math is for.
155 uint64_t ctrl = 0x1716151413121110;
156 uint64_t hash = 0x12;
157 constexpr uint64_t msbs = 0x8080808080808080ULL;
158 constexpr uint64_t lsbs = 0x0101010101010101ULL;
159 auto x = ctrl ^ (lsbs * hash);
160 uint64_t mask = (x - lsbs) & ~x & msbs;
161 EXPECT_EQ(0x0000000080800000, mask);
162
163 BitMask<uint64_t, 8, 3> b(mask);
164 EXPECT_EQ(*b, 2);
165 }
166
TEST(BitMask,LeadingTrailing)167 TEST(BitMask, LeadingTrailing) {
168 EXPECT_EQ((BitMask<uint32_t, 16>(0x00001a40).LeadingZeros()), 3);
169 EXPECT_EQ((BitMask<uint32_t, 16>(0x00001a40).TrailingZeros()), 6);
170
171 EXPECT_EQ((BitMask<uint32_t, 16>(0x00000001).LeadingZeros()), 15);
172 EXPECT_EQ((BitMask<uint32_t, 16>(0x00000001).TrailingZeros()), 0);
173
174 EXPECT_EQ((BitMask<uint32_t, 16>(0x00008000).LeadingZeros()), 0);
175 EXPECT_EQ((BitMask<uint32_t, 16>(0x00008000).TrailingZeros()), 15);
176
177 EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).LeadingZeros()), 3);
178 EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).TrailingZeros()), 1);
179
180 EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000000000000080).LeadingZeros()), 7);
181 EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000000000000080).TrailingZeros()), 0);
182
183 EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x8000000000000000).LeadingZeros()), 0);
184 EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x8000000000000000).TrailingZeros()), 7);
185 }
186
TEST(Group,EmptyGroup)187 TEST(Group, EmptyGroup) {
188 for (h2_t h = 0; h != 128; ++h) EXPECT_FALSE(Group{EmptyGroup()}.Match(h));
189 }
190
TEST(Group,Match)191 TEST(Group, Match) {
192 if (Group::kWidth == 16) {
193 ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3),
194 ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
195 CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1),
196 CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)};
197 EXPECT_THAT(Group{group}.Match(0), ElementsAre());
198 EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 11, 12, 13, 14, 15));
199 EXPECT_THAT(Group{group}.Match(3), ElementsAre(3, 10));
200 EXPECT_THAT(Group{group}.Match(5), ElementsAre(5, 9));
201 EXPECT_THAT(Group{group}.Match(7), ElementsAre(7, 8));
202 } else if (Group::kWidth == 8) {
203 ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2),
204 ctrl_t::kDeleted, CtrlT(2), CtrlT(1),
205 ctrl_t::kSentinel, CtrlT(1)};
206 EXPECT_THAT(Group{group}.Match(0), ElementsAre());
207 EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 5, 7));
208 EXPECT_THAT(Group{group}.Match(2), ElementsAre(2, 4));
209 } else {
210 FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
211 }
212 }
213
TEST(Group,MaskEmpty)214 TEST(Group, MaskEmpty) {
215 if (Group::kWidth == 16) {
216 ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted, CtrlT(3),
217 ctrl_t::kEmpty, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
218 CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1),
219 CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)};
220 EXPECT_THAT(Group{group}.MaskEmpty().LowestBitSet(), 0);
221 EXPECT_THAT(Group{group}.MaskEmpty().HighestBitSet(), 4);
222 } else if (Group::kWidth == 8) {
223 ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2),
224 ctrl_t::kDeleted, CtrlT(2), CtrlT(1),
225 ctrl_t::kSentinel, CtrlT(1)};
226 EXPECT_THAT(Group{group}.MaskEmpty().LowestBitSet(), 0);
227 EXPECT_THAT(Group{group}.MaskEmpty().HighestBitSet(), 0);
228 } else {
229 FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
230 }
231 }
232
TEST(Group,MaskEmptyOrDeleted)233 TEST(Group, MaskEmptyOrDeleted) {
234 if (Group::kWidth == 16) {
235 ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), ctrl_t::kEmpty, CtrlT(3),
236 ctrl_t::kDeleted, CtrlT(5), ctrl_t::kSentinel, CtrlT(7),
237 CtrlT(7), CtrlT(5), CtrlT(3), CtrlT(1),
238 CtrlT(1), CtrlT(1), CtrlT(1), CtrlT(1)};
239 EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().LowestBitSet(), 0);
240 EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().HighestBitSet(), 4);
241 } else if (Group::kWidth == 8) {
242 ctrl_t group[] = {ctrl_t::kEmpty, CtrlT(1), CtrlT(2),
243 ctrl_t::kDeleted, CtrlT(2), CtrlT(1),
244 ctrl_t::kSentinel, CtrlT(1)};
245 EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().LowestBitSet(), 0);
246 EXPECT_THAT(Group{group}.MaskEmptyOrDeleted().HighestBitSet(), 3);
247 } else {
248 FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
249 }
250 }
251
TEST(Batch,DropDeletes)252 TEST(Batch, DropDeletes) {
253 constexpr size_t kCapacity = 63;
254 constexpr size_t kGroupWidth = container_internal::Group::kWidth;
255 std::vector<ctrl_t> ctrl(kCapacity + 1 + kGroupWidth);
256 ctrl[kCapacity] = ctrl_t::kSentinel;
257 std::vector<ctrl_t> pattern = {
258 ctrl_t::kEmpty, CtrlT(2), ctrl_t::kDeleted, CtrlT(2),
259 ctrl_t::kEmpty, CtrlT(1), ctrl_t::kDeleted};
260 for (size_t i = 0; i != kCapacity; ++i) {
261 ctrl[i] = pattern[i % pattern.size()];
262 if (i < kGroupWidth - 1)
263 ctrl[i + kCapacity + 1] = pattern[i % pattern.size()];
264 }
265 ConvertDeletedToEmptyAndFullToDeleted(ctrl.data(), kCapacity);
266 ASSERT_EQ(ctrl[kCapacity], ctrl_t::kSentinel);
267 for (size_t i = 0; i < kCapacity + kGroupWidth; ++i) {
268 ctrl_t expected = pattern[i % (kCapacity + 1) % pattern.size()];
269 if (i == kCapacity) expected = ctrl_t::kSentinel;
270 if (expected == ctrl_t::kDeleted) expected = ctrl_t::kEmpty;
271 if (IsFull(expected)) expected = ctrl_t::kDeleted;
272 EXPECT_EQ(ctrl[i], expected)
273 << i << " " << static_cast<int>(pattern[i % pattern.size()]);
274 }
275 }
276
TEST(Group,CountLeadingEmptyOrDeleted)277 TEST(Group, CountLeadingEmptyOrDeleted) {
278 const std::vector<ctrl_t> empty_examples = {ctrl_t::kEmpty, ctrl_t::kDeleted};
279 const std::vector<ctrl_t> full_examples = {
280 CtrlT(0), CtrlT(1), CtrlT(2), CtrlT(3),
281 CtrlT(5), CtrlT(9), CtrlT(127), ctrl_t::kSentinel};
282
283 for (ctrl_t empty : empty_examples) {
284 std::vector<ctrl_t> e(Group::kWidth, empty);
285 EXPECT_EQ(Group::kWidth, Group{e.data()}.CountLeadingEmptyOrDeleted());
286 for (ctrl_t full : full_examples) {
287 for (size_t i = 0; i != Group::kWidth; ++i) {
288 std::vector<ctrl_t> f(Group::kWidth, empty);
289 f[i] = full;
290 EXPECT_EQ(i, Group{f.data()}.CountLeadingEmptyOrDeleted());
291 }
292 std::vector<ctrl_t> f(Group::kWidth, empty);
293 f[Group::kWidth * 2 / 3] = full;
294 f[Group::kWidth / 2] = full;
295 EXPECT_EQ(
296 Group::kWidth / 2, Group{f.data()}.CountLeadingEmptyOrDeleted());
297 }
298 }
299 }
300
301 template <class T>
302 struct ValuePolicy {
303 using slot_type = T;
304 using key_type = T;
305 using init_type = T;
306
307 template <class Allocator, class... Args>
constructabsl::container_internal::__anon0a0f3e1e0111::ValuePolicy308 static void construct(Allocator* alloc, slot_type* slot, Args&&... args) {
309 absl::allocator_traits<Allocator>::construct(*alloc, slot,
310 std::forward<Args>(args)...);
311 }
312
313 template <class Allocator>
destroyabsl::container_internal::__anon0a0f3e1e0111::ValuePolicy314 static void destroy(Allocator* alloc, slot_type* slot) {
315 absl::allocator_traits<Allocator>::destroy(*alloc, slot);
316 }
317
318 template <class Allocator>
transferabsl::container_internal::__anon0a0f3e1e0111::ValuePolicy319 static void transfer(Allocator* alloc, slot_type* new_slot,
320 slot_type* old_slot) {
321 construct(alloc, new_slot, std::move(*old_slot));
322 destroy(alloc, old_slot);
323 }
324
elementabsl::container_internal::__anon0a0f3e1e0111::ValuePolicy325 static T& element(slot_type* slot) { return *slot; }
326
327 template <class F, class... Args>
328 static decltype(absl::container_internal::DecomposeValue(
329 std::declval<F>(), std::declval<Args>()...))
applyabsl::container_internal::__anon0a0f3e1e0111::ValuePolicy330 apply(F&& f, Args&&... args) {
331 return absl::container_internal::DecomposeValue(
332 std::forward<F>(f), std::forward<Args>(args)...);
333 }
334 };
335
336 using IntPolicy = ValuePolicy<int64_t>;
337 using Uint8Policy = ValuePolicy<uint8_t>;
338
339 class StringPolicy {
340 template <class F, class K, class V,
341 class = typename std::enable_if<
342 std::is_convertible<const K&, absl::string_view>::value>::type>
343 decltype(std::declval<F>()(
344 std::declval<const absl::string_view&>(), std::piecewise_construct,
345 std::declval<std::tuple<K>>(),
apply_impl(F && f,std::pair<std::tuple<K>,V> p)346 std::declval<V>())) static apply_impl(F&& f,
347 std::pair<std::tuple<K>, V> p) {
348 const absl::string_view& key = std::get<0>(p.first);
349 return std::forward<F>(f)(key, std::piecewise_construct, std::move(p.first),
350 std::move(p.second));
351 }
352
353 public:
354 struct slot_type {
355 struct ctor {};
356
357 template <class... Ts>
slot_typeabsl::container_internal::__anon0a0f3e1e0111::StringPolicy::slot_type358 explicit slot_type(ctor, Ts&&... ts) : pair(std::forward<Ts>(ts)...) {}
359
360 std::pair<std::string, std::string> pair;
361 };
362
363 using key_type = std::string;
364 using init_type = std::pair<std::string, std::string>;
365
366 template <class allocator_type, class... Args>
construct(allocator_type * alloc,slot_type * slot,Args...args)367 static void construct(allocator_type* alloc, slot_type* slot, Args... args) {
368 std::allocator_traits<allocator_type>::construct(
369 *alloc, slot, typename slot_type::ctor(), std::forward<Args>(args)...);
370 }
371
372 template <class allocator_type>
destroy(allocator_type * alloc,slot_type * slot)373 static void destroy(allocator_type* alloc, slot_type* slot) {
374 std::allocator_traits<allocator_type>::destroy(*alloc, slot);
375 }
376
377 template <class allocator_type>
transfer(allocator_type * alloc,slot_type * new_slot,slot_type * old_slot)378 static void transfer(allocator_type* alloc, slot_type* new_slot,
379 slot_type* old_slot) {
380 construct(alloc, new_slot, std::move(old_slot->pair));
381 destroy(alloc, old_slot);
382 }
383
element(slot_type * slot)384 static std::pair<std::string, std::string>& element(slot_type* slot) {
385 return slot->pair;
386 }
387
388 template <class F, class... Args>
apply(F && f,Args &&...args)389 static auto apply(F&& f, Args&&... args)
390 -> decltype(apply_impl(std::forward<F>(f),
391 PairArgs(std::forward<Args>(args)...))) {
392 return apply_impl(std::forward<F>(f),
393 PairArgs(std::forward<Args>(args)...));
394 }
395 };
396
397 struct StringHash : absl::Hash<absl::string_view> {
398 using is_transparent = void;
399 };
400 struct StringEq : std::equal_to<absl::string_view> {
401 using is_transparent = void;
402 };
403
404 struct StringTable
405 : raw_hash_set<StringPolicy, StringHash, StringEq, std::allocator<int>> {
406 using Base = typename StringTable::raw_hash_set;
407 StringTable() = default;
408 using Base::Base;
409 };
410
411 struct IntTable
412 : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
413 std::equal_to<int64_t>, std::allocator<int64_t>> {
414 using Base = typename IntTable::raw_hash_set;
415 using Base::Base;
416 };
417
418 struct Uint8Table
419 : raw_hash_set<Uint8Policy, container_internal::hash_default_hash<uint8_t>,
420 std::equal_to<uint8_t>, std::allocator<uint8_t>> {
421 using Base = typename Uint8Table::raw_hash_set;
422 using Base::Base;
423 };
424
425 template <typename T>
426 struct CustomAlloc : std::allocator<T> {
427 CustomAlloc() = default;
428
429 template <typename U>
CustomAllocabsl::container_internal::__anon0a0f3e1e0111::CustomAlloc430 explicit CustomAlloc(const CustomAlloc<U>& /*other*/) {}
431
432 template<class U> struct rebind {
433 using other = CustomAlloc<U>;
434 };
435 };
436
437 struct CustomAllocIntTable
438 : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
439 std::equal_to<int64_t>, CustomAlloc<int64_t>> {
440 using Base = typename CustomAllocIntTable::raw_hash_set;
441 using Base::Base;
442 };
443
444 // Tries to allocate memory at the minimum alignment even when the default
445 // allocator uses a higher alignment.
446 template <typename T>
447 struct MinimumAlignmentAlloc : std::allocator<T> {
448 MinimumAlignmentAlloc() = default;
449
450 template <typename U>
MinimumAlignmentAllocabsl::container_internal::__anon0a0f3e1e0111::MinimumAlignmentAlloc451 explicit MinimumAlignmentAlloc(const MinimumAlignmentAlloc<U>& /*other*/) {}
452
453 template <class U>
454 struct rebind {
455 using other = MinimumAlignmentAlloc<U>;
456 };
457
allocateabsl::container_internal::__anon0a0f3e1e0111::MinimumAlignmentAlloc458 T* allocate(size_t n) {
459 T* ptr = std::allocator<T>::allocate(n + 1);
460 char* cptr = reinterpret_cast<char*>(ptr);
461 cptr += alignof(T);
462 return reinterpret_cast<T*>(cptr);
463 }
464
deallocateabsl::container_internal::__anon0a0f3e1e0111::MinimumAlignmentAlloc465 void deallocate(T* ptr, size_t n) {
466 char* cptr = reinterpret_cast<char*>(ptr);
467 cptr -= alignof(T);
468 std::allocator<T>::deallocate(reinterpret_cast<T*>(cptr), n + 1);
469 }
470 };
471
472 struct MinimumAlignmentUint8Table
473 : raw_hash_set<Uint8Policy, container_internal::hash_default_hash<uint8_t>,
474 std::equal_to<uint8_t>, MinimumAlignmentAlloc<uint8_t>> {
475 using Base = typename MinimumAlignmentUint8Table::raw_hash_set;
476 using Base::Base;
477 };
478
479 // Allows for freezing the allocator to expect no further allocations.
480 template <typename T>
481 struct FreezableAlloc : std::allocator<T> {
FreezableAllocabsl::container_internal::__anon0a0f3e1e0111::FreezableAlloc482 explicit FreezableAlloc(bool* f) : frozen(f) {}
483
484 template <typename U>
FreezableAllocabsl::container_internal::__anon0a0f3e1e0111::FreezableAlloc485 explicit FreezableAlloc(const FreezableAlloc<U>& other)
486 : frozen(other.frozen) {}
487
488 template <class U>
489 struct rebind {
490 using other = FreezableAlloc<U>;
491 };
492
allocateabsl::container_internal::__anon0a0f3e1e0111::FreezableAlloc493 T* allocate(size_t n) {
494 EXPECT_FALSE(*frozen);
495 return std::allocator<T>::allocate(n);
496 }
497
498 bool* frozen;
499 };
500
501 struct BadFastHash {
502 template <class T>
operator ()absl::container_internal::__anon0a0f3e1e0111::BadFastHash503 size_t operator()(const T&) const {
504 return 0;
505 }
506 };
507
508 struct BadHashFreezableIntTable
509 : raw_hash_set<IntPolicy, BadFastHash, std::equal_to<int64_t>,
510 FreezableAlloc<int64_t>> {
511 using Base = typename BadHashFreezableIntTable::raw_hash_set;
512 using Base::Base;
513 };
514
515 struct BadTable : raw_hash_set<IntPolicy, BadFastHash, std::equal_to<int>,
516 std::allocator<int>> {
517 using Base = typename BadTable::raw_hash_set;
518 BadTable() = default;
519 using Base::Base;
520 };
521
TEST(Table,EmptyFunctorOptimization)522 TEST(Table, EmptyFunctorOptimization) {
523 static_assert(std::is_empty<std::equal_to<absl::string_view>>::value, "");
524 static_assert(std::is_empty<std::allocator<int>>::value, "");
525
526 struct MockTable {
527 void* infoz;
528 void* ctrl;
529 void* slots;
530 size_t size;
531 size_t capacity;
532 };
533 struct MockTableInfozDisabled {
534 void* ctrl;
535 void* slots;
536 size_t size;
537 size_t capacity;
538 };
539 struct StatelessHash {
540 size_t operator()(absl::string_view) const { return 0; }
541 };
542 struct StatefulHash : StatelessHash {
543 size_t dummy;
544 };
545
546 struct GenerationData {
547 size_t reserved_growth;
548 size_t reservation_size;
549 GenerationType* generation;
550 };
551
552 // Ignore unreachable-code warning. Compiler thinks one branch of each ternary
553 // conditional is unreachable.
554 #if defined(__clang__)
555 #pragma clang diagnostic push
556 #pragma clang diagnostic ignored "-Wunreachable-code"
557 #endif
558 constexpr size_t mock_size = std::is_empty<HashtablezInfoHandle>()
559 ? sizeof(MockTableInfozDisabled)
560 : sizeof(MockTable);
561 constexpr size_t generation_size =
562 SwisstableGenerationsEnabled() ? sizeof(GenerationData) : 0;
563 #if defined(__clang__)
564 #pragma clang diagnostic pop
565 #endif
566
567 EXPECT_EQ(
568 mock_size + generation_size,
569 sizeof(
570 raw_hash_set<StringPolicy, StatelessHash,
571 std::equal_to<absl::string_view>, std::allocator<int>>));
572
573 EXPECT_EQ(
574 mock_size + sizeof(StatefulHash) + generation_size,
575 sizeof(
576 raw_hash_set<StringPolicy, StatefulHash,
577 std::equal_to<absl::string_view>, std::allocator<int>>));
578 }
579
TEST(Table,Empty)580 TEST(Table, Empty) {
581 IntTable t;
582 EXPECT_EQ(0, t.size());
583 EXPECT_TRUE(t.empty());
584 }
585
TEST(Table,LookupEmpty)586 TEST(Table, LookupEmpty) {
587 IntTable t;
588 auto it = t.find(0);
589 EXPECT_TRUE(it == t.end());
590 }
591
TEST(Table,Insert1)592 TEST(Table, Insert1) {
593 IntTable t;
594 EXPECT_TRUE(t.find(0) == t.end());
595 auto res = t.emplace(0);
596 EXPECT_TRUE(res.second);
597 EXPECT_THAT(*res.first, 0);
598 EXPECT_EQ(1, t.size());
599 EXPECT_THAT(*t.find(0), 0);
600 }
601
TEST(Table,Insert2)602 TEST(Table, Insert2) {
603 IntTable t;
604 EXPECT_TRUE(t.find(0) == t.end());
605 auto res = t.emplace(0);
606 EXPECT_TRUE(res.second);
607 EXPECT_THAT(*res.first, 0);
608 EXPECT_EQ(1, t.size());
609 EXPECT_TRUE(t.find(1) == t.end());
610 res = t.emplace(1);
611 EXPECT_TRUE(res.second);
612 EXPECT_THAT(*res.first, 1);
613 EXPECT_EQ(2, t.size());
614 EXPECT_THAT(*t.find(0), 0);
615 EXPECT_THAT(*t.find(1), 1);
616 }
617
TEST(Table,InsertCollision)618 TEST(Table, InsertCollision) {
619 BadTable t;
620 EXPECT_TRUE(t.find(1) == t.end());
621 auto res = t.emplace(1);
622 EXPECT_TRUE(res.second);
623 EXPECT_THAT(*res.first, 1);
624 EXPECT_EQ(1, t.size());
625
626 EXPECT_TRUE(t.find(2) == t.end());
627 res = t.emplace(2);
628 EXPECT_THAT(*res.first, 2);
629 EXPECT_TRUE(res.second);
630 EXPECT_EQ(2, t.size());
631
632 EXPECT_THAT(*t.find(1), 1);
633 EXPECT_THAT(*t.find(2), 2);
634 }
635
636 // Test that we do not add existent element in case we need to search through
637 // many groups with deleted elements
TEST(Table,InsertCollisionAndFindAfterDelete)638 TEST(Table, InsertCollisionAndFindAfterDelete) {
639 BadTable t; // all elements go to the same group.
640 // Have at least 2 groups with Group::kWidth collisions
641 // plus some extra collisions in the last group.
642 constexpr size_t kNumInserts = Group::kWidth * 2 + 5;
643 for (size_t i = 0; i < kNumInserts; ++i) {
644 auto res = t.emplace(i);
645 EXPECT_TRUE(res.second);
646 EXPECT_THAT(*res.first, i);
647 EXPECT_EQ(i + 1, t.size());
648 }
649
650 // Remove elements one by one and check
651 // that we still can find all other elements.
652 for (size_t i = 0; i < kNumInserts; ++i) {
653 EXPECT_EQ(1, t.erase(i)) << i;
654 for (size_t j = i + 1; j < kNumInserts; ++j) {
655 EXPECT_THAT(*t.find(j), j);
656 auto res = t.emplace(j);
657 EXPECT_FALSE(res.second) << i << " " << j;
658 EXPECT_THAT(*res.first, j);
659 EXPECT_EQ(kNumInserts - i - 1, t.size());
660 }
661 }
662 EXPECT_TRUE(t.empty());
663 }
664
TEST(Table,InsertWithinCapacity)665 TEST(Table, InsertWithinCapacity) {
666 IntTable t;
667 t.reserve(10);
668 const size_t original_capacity = t.capacity();
669 const auto addr = [&](int i) {
670 return reinterpret_cast<uintptr_t>(&*t.find(i));
671 };
672 // Inserting an element does not change capacity.
673 t.insert(0);
674 EXPECT_THAT(t.capacity(), original_capacity);
675 const uintptr_t original_addr_0 = addr(0);
676 // Inserting another element does not rehash.
677 t.insert(1);
678 EXPECT_THAT(t.capacity(), original_capacity);
679 EXPECT_THAT(addr(0), original_addr_0);
680 // Inserting lots of duplicate elements does not rehash.
681 for (int i = 0; i < 100; ++i) {
682 t.insert(i % 10);
683 }
684 EXPECT_THAT(t.capacity(), original_capacity);
685 EXPECT_THAT(addr(0), original_addr_0);
686 // Inserting a range of duplicate elements does not rehash.
687 std::vector<int> dup_range;
688 for (int i = 0; i < 100; ++i) {
689 dup_range.push_back(i % 10);
690 }
691 t.insert(dup_range.begin(), dup_range.end());
692 EXPECT_THAT(t.capacity(), original_capacity);
693 EXPECT_THAT(addr(0), original_addr_0);
694 }
695
TEST(Table,LazyEmplace)696 TEST(Table, LazyEmplace) {
697 StringTable t;
698 bool called = false;
699 auto it = t.lazy_emplace("abc", [&](const StringTable::constructor& f) {
700 called = true;
701 f("abc", "ABC");
702 });
703 EXPECT_TRUE(called);
704 EXPECT_THAT(*it, Pair("abc", "ABC"));
705 called = false;
706 it = t.lazy_emplace("abc", [&](const StringTable::constructor& f) {
707 called = true;
708 f("abc", "DEF");
709 });
710 EXPECT_FALSE(called);
711 EXPECT_THAT(*it, Pair("abc", "ABC"));
712 }
713
TEST(Table,ContainsEmpty)714 TEST(Table, ContainsEmpty) {
715 IntTable t;
716
717 EXPECT_FALSE(t.contains(0));
718 }
719
TEST(Table,Contains1)720 TEST(Table, Contains1) {
721 IntTable t;
722
723 EXPECT_TRUE(t.insert(0).second);
724 EXPECT_TRUE(t.contains(0));
725 EXPECT_FALSE(t.contains(1));
726
727 EXPECT_EQ(1, t.erase(0));
728 EXPECT_FALSE(t.contains(0));
729 }
730
TEST(Table,Contains2)731 TEST(Table, Contains2) {
732 IntTable t;
733
734 EXPECT_TRUE(t.insert(0).second);
735 EXPECT_TRUE(t.contains(0));
736 EXPECT_FALSE(t.contains(1));
737
738 t.clear();
739 EXPECT_FALSE(t.contains(0));
740 }
741
742 int decompose_constructed;
743 int decompose_copy_constructed;
744 int decompose_copy_assigned;
745 int decompose_move_constructed;
746 int decompose_move_assigned;
747 struct DecomposeType {
DecomposeTypeabsl::container_internal::__anon0a0f3e1e0111::DecomposeType748 DecomposeType(int i = 0) : i(i) { // NOLINT
749 ++decompose_constructed;
750 }
751
DecomposeTypeabsl::container_internal::__anon0a0f3e1e0111::DecomposeType752 explicit DecomposeType(const char* d) : DecomposeType(*d) {}
753
DecomposeTypeabsl::container_internal::__anon0a0f3e1e0111::DecomposeType754 DecomposeType(const DecomposeType& other) : i(other.i) {
755 ++decompose_copy_constructed;
756 }
operator =absl::container_internal::__anon0a0f3e1e0111::DecomposeType757 DecomposeType& operator=(const DecomposeType& other) {
758 ++decompose_copy_assigned;
759 i = other.i;
760 return *this;
761 }
DecomposeTypeabsl::container_internal::__anon0a0f3e1e0111::DecomposeType762 DecomposeType(DecomposeType&& other) : i(other.i) {
763 ++decompose_move_constructed;
764 }
operator =absl::container_internal::__anon0a0f3e1e0111::DecomposeType765 DecomposeType& operator=(DecomposeType&& other) {
766 ++decompose_move_assigned;
767 i = other.i;
768 return *this;
769 }
770
771 int i;
772 };
773
774 struct DecomposeHash {
775 using is_transparent = void;
operator ()absl::container_internal::__anon0a0f3e1e0111::DecomposeHash776 size_t operator()(const DecomposeType& a) const { return a.i; }
operator ()absl::container_internal::__anon0a0f3e1e0111::DecomposeHash777 size_t operator()(int a) const { return a; }
operator ()absl::container_internal::__anon0a0f3e1e0111::DecomposeHash778 size_t operator()(const char* a) const { return *a; }
779 };
780
781 struct DecomposeEq {
782 using is_transparent = void;
operator ()absl::container_internal::__anon0a0f3e1e0111::DecomposeEq783 bool operator()(const DecomposeType& a, const DecomposeType& b) const {
784 return a.i == b.i;
785 }
operator ()absl::container_internal::__anon0a0f3e1e0111::DecomposeEq786 bool operator()(const DecomposeType& a, int b) const { return a.i == b; }
operator ()absl::container_internal::__anon0a0f3e1e0111::DecomposeEq787 bool operator()(const DecomposeType& a, const char* b) const {
788 return a.i == *b;
789 }
790 };
791
792 struct DecomposePolicy {
793 using slot_type = DecomposeType;
794 using key_type = DecomposeType;
795 using init_type = DecomposeType;
796
797 template <typename T>
constructabsl::container_internal::__anon0a0f3e1e0111::DecomposePolicy798 static void construct(void*, DecomposeType* slot, T&& v) {
799 ::new (slot) DecomposeType(std::forward<T>(v));
800 }
destroyabsl::container_internal::__anon0a0f3e1e0111::DecomposePolicy801 static void destroy(void*, DecomposeType* slot) { slot->~DecomposeType(); }
elementabsl::container_internal::__anon0a0f3e1e0111::DecomposePolicy802 static DecomposeType& element(slot_type* slot) { return *slot; }
803
804 template <class F, class T>
applyabsl::container_internal::__anon0a0f3e1e0111::DecomposePolicy805 static auto apply(F&& f, const T& x) -> decltype(std::forward<F>(f)(x, x)) {
806 return std::forward<F>(f)(x, x);
807 }
808 };
809
810 template <typename Hash, typename Eq>
TestDecompose(bool construct_three)811 void TestDecompose(bool construct_three) {
812 DecomposeType elem{0};
813 const int one = 1;
814 const char* three_p = "3";
815 const auto& three = three_p;
816 const int elem_vector_count = 256;
817 std::vector<DecomposeType> elem_vector(elem_vector_count, DecomposeType{0});
818 std::iota(elem_vector.begin(), elem_vector.end(), 0);
819
820 using DecomposeSet =
821 raw_hash_set<DecomposePolicy, Hash, Eq, std::allocator<int>>;
822 DecomposeSet set1;
823
824 decompose_constructed = 0;
825 int expected_constructed = 0;
826 EXPECT_EQ(expected_constructed, decompose_constructed);
827 set1.insert(elem);
828 EXPECT_EQ(expected_constructed, decompose_constructed);
829 set1.insert(1);
830 EXPECT_EQ(++expected_constructed, decompose_constructed);
831 set1.emplace("3");
832 EXPECT_EQ(++expected_constructed, decompose_constructed);
833 EXPECT_EQ(expected_constructed, decompose_constructed);
834
835 { // insert(T&&)
836 set1.insert(1);
837 EXPECT_EQ(expected_constructed, decompose_constructed);
838 }
839
840 { // insert(const T&)
841 set1.insert(one);
842 EXPECT_EQ(expected_constructed, decompose_constructed);
843 }
844
845 { // insert(hint, T&&)
846 set1.insert(set1.begin(), 1);
847 EXPECT_EQ(expected_constructed, decompose_constructed);
848 }
849
850 { // insert(hint, const T&)
851 set1.insert(set1.begin(), one);
852 EXPECT_EQ(expected_constructed, decompose_constructed);
853 }
854
855 { // emplace(...)
856 set1.emplace(1);
857 EXPECT_EQ(expected_constructed, decompose_constructed);
858 set1.emplace("3");
859 expected_constructed += construct_three;
860 EXPECT_EQ(expected_constructed, decompose_constructed);
861 set1.emplace(one);
862 EXPECT_EQ(expected_constructed, decompose_constructed);
863 set1.emplace(three);
864 expected_constructed += construct_three;
865 EXPECT_EQ(expected_constructed, decompose_constructed);
866 }
867
868 { // emplace_hint(...)
869 set1.emplace_hint(set1.begin(), 1);
870 EXPECT_EQ(expected_constructed, decompose_constructed);
871 set1.emplace_hint(set1.begin(), "3");
872 expected_constructed += construct_three;
873 EXPECT_EQ(expected_constructed, decompose_constructed);
874 set1.emplace_hint(set1.begin(), one);
875 EXPECT_EQ(expected_constructed, decompose_constructed);
876 set1.emplace_hint(set1.begin(), three);
877 expected_constructed += construct_three;
878 EXPECT_EQ(expected_constructed, decompose_constructed);
879 }
880
881 decompose_copy_constructed = 0;
882 decompose_copy_assigned = 0;
883 decompose_move_constructed = 0;
884 decompose_move_assigned = 0;
885 int expected_copy_constructed = 0;
886 int expected_move_constructed = 0;
887 { // raw_hash_set(first, last) with random-access iterators
888 DecomposeSet set2(elem_vector.begin(), elem_vector.end());
889 // Expect exactly one copy-constructor call for each element if no
890 // rehashing is done.
891 expected_copy_constructed += elem_vector_count;
892 EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed);
893 EXPECT_EQ(expected_move_constructed, decompose_move_constructed);
894 EXPECT_EQ(0, decompose_move_assigned);
895 EXPECT_EQ(0, decompose_copy_assigned);
896 }
897
898 { // raw_hash_set(first, last) with forward iterators
899 std::list<DecomposeType> elem_list(elem_vector.begin(), elem_vector.end());
900 expected_copy_constructed = decompose_copy_constructed;
901 DecomposeSet set2(elem_list.begin(), elem_list.end());
902 // Expect exactly N elements copied into set, expect at most 2*N elements
903 // moving internally for all resizing needed (for a growth factor of 2).
904 expected_copy_constructed += elem_vector_count;
905 EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed);
906 expected_move_constructed += elem_vector_count;
907 EXPECT_LT(expected_move_constructed, decompose_move_constructed);
908 expected_move_constructed += elem_vector_count;
909 EXPECT_GE(expected_move_constructed, decompose_move_constructed);
910 EXPECT_EQ(0, decompose_move_assigned);
911 EXPECT_EQ(0, decompose_copy_assigned);
912 expected_copy_constructed = decompose_copy_constructed;
913 expected_move_constructed = decompose_move_constructed;
914 }
915
916 { // insert(first, last)
917 DecomposeSet set2;
918 set2.insert(elem_vector.begin(), elem_vector.end());
919 // Expect exactly N elements copied into set, expect at most 2*N elements
920 // moving internally for all resizing needed (for a growth factor of 2).
921 const int expected_new_elements = elem_vector_count;
922 const int expected_max_element_moves = 2 * elem_vector_count;
923 expected_copy_constructed += expected_new_elements;
924 EXPECT_EQ(expected_copy_constructed, decompose_copy_constructed);
925 expected_move_constructed += expected_max_element_moves;
926 EXPECT_GE(expected_move_constructed, decompose_move_constructed);
927 EXPECT_EQ(0, decompose_move_assigned);
928 EXPECT_EQ(0, decompose_copy_assigned);
929 expected_copy_constructed = decompose_copy_constructed;
930 expected_move_constructed = decompose_move_constructed;
931 }
932 }
933
TEST(Table,Decompose)934 TEST(Table, Decompose) {
935 if (SwisstableGenerationsEnabled()) {
936 GTEST_SKIP() << "Generations being enabled causes extra rehashes.";
937 }
938
939 TestDecompose<DecomposeHash, DecomposeEq>(false);
940
941 struct TransparentHashIntOverload {
942 size_t operator()(const DecomposeType& a) const { return a.i; }
943 size_t operator()(int a) const { return a; }
944 };
945 struct TransparentEqIntOverload {
946 bool operator()(const DecomposeType& a, const DecomposeType& b) const {
947 return a.i == b.i;
948 }
949 bool operator()(const DecomposeType& a, int b) const { return a.i == b; }
950 };
951 TestDecompose<TransparentHashIntOverload, DecomposeEq>(true);
952 TestDecompose<TransparentHashIntOverload, TransparentEqIntOverload>(true);
953 TestDecompose<DecomposeHash, TransparentEqIntOverload>(true);
954 }
955
956 // Returns the largest m such that a table with m elements has the same number
957 // of buckets as a table with n elements.
MaxDensitySize(size_t n)958 size_t MaxDensitySize(size_t n) {
959 IntTable t;
960 t.reserve(n);
961 for (size_t i = 0; i != n; ++i) t.emplace(i);
962 const size_t c = t.bucket_count();
963 while (c == t.bucket_count()) t.emplace(n++);
964 return t.size() - 1;
965 }
966
967 struct Modulo1000Hash {
operator ()absl::container_internal::__anon0a0f3e1e0111::Modulo1000Hash968 size_t operator()(int x) const { return x % 1000; }
969 };
970
971 struct Modulo1000HashTable
972 : public raw_hash_set<IntPolicy, Modulo1000Hash, std::equal_to<int>,
973 std::allocator<int>> {};
974
975 // Test that rehash with no resize happen in case of many deleted slots.
TEST(Table,RehashWithNoResize)976 TEST(Table, RehashWithNoResize) {
977 if (SwisstableGenerationsEnabled()) {
978 GTEST_SKIP() << "Generations being enabled causes extra rehashes.";
979 }
980
981 Modulo1000HashTable t;
982 // Adding the same length (and the same hash) strings
983 // to have at least kMinFullGroups groups
984 // with Group::kWidth collisions. Then fill up to MaxDensitySize;
985 const size_t kMinFullGroups = 7;
986 std::vector<int> keys;
987 for (size_t i = 0; i < MaxDensitySize(Group::kWidth * kMinFullGroups); ++i) {
988 int k = i * 1000;
989 t.emplace(k);
990 keys.push_back(k);
991 }
992 const size_t capacity = t.capacity();
993
994 // Remove elements from all groups except the first and the last one.
995 // All elements removed from full groups will be marked as ctrl_t::kDeleted.
996 const size_t erase_begin = Group::kWidth / 2;
997 const size_t erase_end = (t.size() / Group::kWidth - 1) * Group::kWidth;
998 for (size_t i = erase_begin; i < erase_end; ++i) {
999 EXPECT_EQ(1, t.erase(keys[i])) << i;
1000 }
1001 keys.erase(keys.begin() + erase_begin, keys.begin() + erase_end);
1002
1003 auto last_key = keys.back();
1004 size_t last_key_num_probes = GetHashtableDebugNumProbes(t, last_key);
1005
1006 // Make sure that we have to make a lot of probes for last key.
1007 ASSERT_GT(last_key_num_probes, kMinFullGroups);
1008
1009 int x = 1;
1010 // Insert and erase one element, before inplace rehash happen.
1011 while (last_key_num_probes == GetHashtableDebugNumProbes(t, last_key)) {
1012 t.emplace(x);
1013 ASSERT_EQ(capacity, t.capacity());
1014 // All elements should be there.
1015 ASSERT_TRUE(t.find(x) != t.end()) << x;
1016 for (const auto& k : keys) {
1017 ASSERT_TRUE(t.find(k) != t.end()) << k;
1018 }
1019 t.erase(x);
1020 ++x;
1021 }
1022 }
1023
TEST(Table,InsertEraseStressTest)1024 TEST(Table, InsertEraseStressTest) {
1025 IntTable t;
1026 const size_t kMinElementCount = 250;
1027 std::deque<int> keys;
1028 size_t i = 0;
1029 for (; i < MaxDensitySize(kMinElementCount); ++i) {
1030 t.emplace(i);
1031 keys.push_back(i);
1032 }
1033 const size_t kNumIterations = 1000000;
1034 for (; i < kNumIterations; ++i) {
1035 ASSERT_EQ(1, t.erase(keys.front()));
1036 keys.pop_front();
1037 t.emplace(i);
1038 keys.push_back(i);
1039 }
1040 }
1041
TEST(Table,InsertOverloads)1042 TEST(Table, InsertOverloads) {
1043 StringTable t;
1044 // These should all trigger the insert(init_type) overload.
1045 t.insert({{}, {}});
1046 t.insert({"ABC", {}});
1047 t.insert({"DEF", "!!!"});
1048
1049 EXPECT_THAT(t, UnorderedElementsAre(Pair("", ""), Pair("ABC", ""),
1050 Pair("DEF", "!!!")));
1051 }
1052
TEST(Table,LargeTable)1053 TEST(Table, LargeTable) {
1054 IntTable t;
1055 for (int64_t i = 0; i != 100000; ++i) t.emplace(i << 40);
1056 for (int64_t i = 0; i != 100000; ++i) ASSERT_EQ(i << 40, *t.find(i << 40));
1057 }
1058
1059 // Timeout if copy is quadratic as it was in Rust.
TEST(Table,EnsureNonQuadraticAsInRust)1060 TEST(Table, EnsureNonQuadraticAsInRust) {
1061 static const size_t kLargeSize = 1 << 15;
1062
1063 IntTable t;
1064 for (size_t i = 0; i != kLargeSize; ++i) {
1065 t.insert(i);
1066 }
1067
1068 // If this is quadratic, the test will timeout.
1069 IntTable t2;
1070 for (const auto& entry : t) t2.insert(entry);
1071 }
1072
TEST(Table,ClearBug)1073 TEST(Table, ClearBug) {
1074 if (SwisstableGenerationsEnabled()) {
1075 GTEST_SKIP() << "Generations being enabled causes extra rehashes.";
1076 }
1077
1078 IntTable t;
1079 constexpr size_t capacity = container_internal::Group::kWidth - 1;
1080 constexpr size_t max_size = capacity / 2 + 1;
1081 for (size_t i = 0; i < max_size; ++i) {
1082 t.insert(i);
1083 }
1084 ASSERT_EQ(capacity, t.capacity());
1085 intptr_t original = reinterpret_cast<intptr_t>(&*t.find(2));
1086 t.clear();
1087 ASSERT_EQ(capacity, t.capacity());
1088 for (size_t i = 0; i < max_size; ++i) {
1089 t.insert(i);
1090 }
1091 ASSERT_EQ(capacity, t.capacity());
1092 intptr_t second = reinterpret_cast<intptr_t>(&*t.find(2));
1093 // We are checking that original and second are close enough to each other
1094 // that they are probably still in the same group. This is not strictly
1095 // guaranteed.
1096 EXPECT_LT(static_cast<size_t>(std::abs(original - second)),
1097 capacity * sizeof(IntTable::value_type));
1098 }
1099
TEST(Table,Erase)1100 TEST(Table, Erase) {
1101 IntTable t;
1102 EXPECT_TRUE(t.find(0) == t.end());
1103 auto res = t.emplace(0);
1104 EXPECT_TRUE(res.second);
1105 EXPECT_EQ(1, t.size());
1106 t.erase(res.first);
1107 EXPECT_EQ(0, t.size());
1108 EXPECT_TRUE(t.find(0) == t.end());
1109 }
1110
TEST(Table,EraseMaintainsValidIterator)1111 TEST(Table, EraseMaintainsValidIterator) {
1112 IntTable t;
1113 const int kNumElements = 100;
1114 for (int i = 0; i < kNumElements; i ++) {
1115 EXPECT_TRUE(t.emplace(i).second);
1116 }
1117 EXPECT_EQ(t.size(), kNumElements);
1118
1119 int num_erase_calls = 0;
1120 auto it = t.begin();
1121 while (it != t.end()) {
1122 t.erase(it++);
1123 num_erase_calls++;
1124 }
1125
1126 EXPECT_TRUE(t.empty());
1127 EXPECT_EQ(num_erase_calls, kNumElements);
1128 }
1129
TEST(Table,EraseBeginEnd)1130 TEST(Table, EraseBeginEnd) {
1131 IntTable t;
1132 for (int i = 0; i < 10; ++i) t.insert(i);
1133 EXPECT_EQ(t.size(), 10);
1134 t.erase(t.begin(), t.end());
1135 EXPECT_EQ(t.size(), 0);
1136 }
1137
1138 // Collect N bad keys by following algorithm:
1139 // 1. Create an empty table and reserve it to 2 * N.
1140 // 2. Insert N random elements.
1141 // 3. Take first Group::kWidth - 1 to bad_keys array.
1142 // 4. Clear the table without resize.
1143 // 5. Go to point 2 while N keys not collected
CollectBadMergeKeys(size_t N)1144 std::vector<int64_t> CollectBadMergeKeys(size_t N) {
1145 static constexpr int kGroupSize = Group::kWidth - 1;
1146
1147 auto topk_range = [](size_t b, size_t e,
1148 IntTable* t) -> std::vector<int64_t> {
1149 for (size_t i = b; i != e; ++i) {
1150 t->emplace(i);
1151 }
1152 std::vector<int64_t> res;
1153 res.reserve(kGroupSize);
1154 auto it = t->begin();
1155 for (size_t i = b; i != e && i != b + kGroupSize; ++i, ++it) {
1156 res.push_back(*it);
1157 }
1158 return res;
1159 };
1160
1161 std::vector<int64_t> bad_keys;
1162 bad_keys.reserve(N);
1163 IntTable t;
1164 t.reserve(N * 2);
1165
1166 for (size_t b = 0; bad_keys.size() < N; b += N) {
1167 auto keys = topk_range(b, b + N, &t);
1168 bad_keys.insert(bad_keys.end(), keys.begin(), keys.end());
1169 t.erase(t.begin(), t.end());
1170 EXPECT_TRUE(t.empty());
1171 }
1172 return bad_keys;
1173 }
1174
1175 struct ProbeStats {
1176 // Number of elements with specific probe length over all tested tables.
1177 std::vector<size_t> all_probes_histogram;
1178 // Ratios total_probe_length/size for every tested table.
1179 std::vector<double> single_table_ratios;
1180
1181 // Average ratio total_probe_length/size over tables.
AvgRatioabsl::container_internal::__anon0a0f3e1e0111::ProbeStats1182 double AvgRatio() const {
1183 return std::accumulate(single_table_ratios.begin(),
1184 single_table_ratios.end(), 0.0) /
1185 single_table_ratios.size();
1186 }
1187
1188 // Maximum ratio total_probe_length/size over tables.
MaxRatioabsl::container_internal::__anon0a0f3e1e0111::ProbeStats1189 double MaxRatio() const {
1190 return *std::max_element(single_table_ratios.begin(),
1191 single_table_ratios.end());
1192 }
1193
1194 // Percentile ratio total_probe_length/size over tables.
PercentileRatioabsl::container_internal::__anon0a0f3e1e0111::ProbeStats1195 double PercentileRatio(double Percentile = 0.95) const {
1196 auto r = single_table_ratios;
1197 auto mid = r.begin() + static_cast<size_t>(r.size() * Percentile);
1198 if (mid != r.end()) {
1199 std::nth_element(r.begin(), mid, r.end());
1200 return *mid;
1201 } else {
1202 return MaxRatio();
1203 }
1204 }
1205
1206 // Maximum probe length over all elements and all tables.
MaxProbeabsl::container_internal::__anon0a0f3e1e0111::ProbeStats1207 size_t MaxProbe() const { return all_probes_histogram.size(); }
1208
1209 // Fraction of elements with specified probe length.
ProbeNormalizedHistogramabsl::container_internal::__anon0a0f3e1e0111::ProbeStats1210 std::vector<double> ProbeNormalizedHistogram() const {
1211 double total_elements = std::accumulate(all_probes_histogram.begin(),
1212 all_probes_histogram.end(), 0ull);
1213 std::vector<double> res;
1214 for (size_t p : all_probes_histogram) {
1215 res.push_back(p / total_elements);
1216 }
1217 return res;
1218 }
1219
PercentileProbeabsl::container_internal::__anon0a0f3e1e0111::ProbeStats1220 size_t PercentileProbe(double Percentile = 0.99) const {
1221 size_t idx = 0;
1222 for (double p : ProbeNormalizedHistogram()) {
1223 if (Percentile > p) {
1224 Percentile -= p;
1225 ++idx;
1226 } else {
1227 return idx;
1228 }
1229 }
1230 return idx;
1231 }
1232
operator <<(std::ostream & out,const ProbeStats & s)1233 friend std::ostream& operator<<(std::ostream& out, const ProbeStats& s) {
1234 out << "{AvgRatio:" << s.AvgRatio() << ", MaxRatio:" << s.MaxRatio()
1235 << ", PercentileRatio:" << s.PercentileRatio()
1236 << ", MaxProbe:" << s.MaxProbe() << ", Probes=[";
1237 for (double p : s.ProbeNormalizedHistogram()) {
1238 out << p << ",";
1239 }
1240 out << "]}";
1241
1242 return out;
1243 }
1244 };
1245
1246 struct ExpectedStats {
1247 double avg_ratio;
1248 double max_ratio;
1249 std::vector<std::pair<double, double>> pecentile_ratios;
1250 std::vector<std::pair<double, double>> pecentile_probes;
1251
operator <<(std::ostream & out,const ExpectedStats & s)1252 friend std::ostream& operator<<(std::ostream& out, const ExpectedStats& s) {
1253 out << "{AvgRatio:" << s.avg_ratio << ", MaxRatio:" << s.max_ratio
1254 << ", PercentileRatios: [";
1255 for (auto el : s.pecentile_ratios) {
1256 out << el.first << ":" << el.second << ", ";
1257 }
1258 out << "], PercentileProbes: [";
1259 for (auto el : s.pecentile_probes) {
1260 out << el.first << ":" << el.second << ", ";
1261 }
1262 out << "]}";
1263
1264 return out;
1265 }
1266 };
1267
VerifyStats(size_t size,const ExpectedStats & exp,const ProbeStats & stats)1268 void VerifyStats(size_t size, const ExpectedStats& exp,
1269 const ProbeStats& stats) {
1270 EXPECT_LT(stats.AvgRatio(), exp.avg_ratio) << size << " " << stats;
1271 EXPECT_LT(stats.MaxRatio(), exp.max_ratio) << size << " " << stats;
1272 for (auto pr : exp.pecentile_ratios) {
1273 EXPECT_LE(stats.PercentileRatio(pr.first), pr.second)
1274 << size << " " << pr.first << " " << stats;
1275 }
1276
1277 for (auto pr : exp.pecentile_probes) {
1278 EXPECT_LE(stats.PercentileProbe(pr.first), pr.second)
1279 << size << " " << pr.first << " " << stats;
1280 }
1281 }
1282
1283 using ProbeStatsPerSize = std::map<size_t, ProbeStats>;
1284
1285 // Collect total ProbeStats on num_iters iterations of the following algorithm:
1286 // 1. Create new table and reserve it to keys.size() * 2
1287 // 2. Insert all keys xored with seed
1288 // 3. Collect ProbeStats from final table.
CollectProbeStatsOnKeysXoredWithSeed(const std::vector<int64_t> & keys,size_t num_iters)1289 ProbeStats CollectProbeStatsOnKeysXoredWithSeed(
1290 const std::vector<int64_t>& keys, size_t num_iters) {
1291 const size_t reserve_size = keys.size() * 2;
1292
1293 ProbeStats stats;
1294
1295 int64_t seed = 0x71b1a19b907d6e33;
1296 while (num_iters--) {
1297 seed = static_cast<int64_t>(static_cast<uint64_t>(seed) * 17 + 13);
1298 IntTable t1;
1299 t1.reserve(reserve_size);
1300 for (const auto& key : keys) {
1301 t1.emplace(key ^ seed);
1302 }
1303
1304 auto probe_histogram = GetHashtableDebugNumProbesHistogram(t1);
1305 stats.all_probes_histogram.resize(
1306 std::max(stats.all_probes_histogram.size(), probe_histogram.size()));
1307 std::transform(probe_histogram.begin(), probe_histogram.end(),
1308 stats.all_probes_histogram.begin(),
1309 stats.all_probes_histogram.begin(), std::plus<size_t>());
1310
1311 size_t total_probe_seq_length = 0;
1312 for (size_t i = 0; i < probe_histogram.size(); ++i) {
1313 total_probe_seq_length += i * probe_histogram[i];
1314 }
1315 stats.single_table_ratios.push_back(total_probe_seq_length * 1.0 /
1316 keys.size());
1317 t1.erase(t1.begin(), t1.end());
1318 }
1319 return stats;
1320 }
1321
XorSeedExpectedStats()1322 ExpectedStats XorSeedExpectedStats() {
1323 constexpr bool kRandomizesInserts =
1324 #ifdef NDEBUG
1325 false;
1326 #else // NDEBUG
1327 true;
1328 #endif // NDEBUG
1329
1330 // The effective load factor is larger in non-opt mode because we insert
1331 // elements out of order.
1332 switch (container_internal::Group::kWidth) {
1333 case 8:
1334 if (kRandomizesInserts) {
1335 return {0.05,
1336 1.0,
1337 {{0.95, 0.5}},
1338 {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
1339 } else {
1340 return {0.05,
1341 2.0,
1342 {{0.95, 0.1}},
1343 {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
1344 }
1345 case 16:
1346 if (kRandomizesInserts) {
1347 return {0.1,
1348 2.0,
1349 {{0.95, 0.1}},
1350 {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}};
1351 } else {
1352 return {0.05,
1353 1.0,
1354 {{0.95, 0.05}},
1355 {{0.95, 0}, {0.99, 1}, {0.999, 4}, {0.9999, 10}}};
1356 }
1357 }
1358 LOG(FATAL) << "Unknown Group width";
1359 return {};
1360 }
1361
1362 // TODO(b/80415403): Figure out why this test is so flaky, esp. on MSVC
TEST(Table,DISABLED_EnsureNonQuadraticTopNXorSeedByProbeSeqLength)1363 TEST(Table, DISABLED_EnsureNonQuadraticTopNXorSeedByProbeSeqLength) {
1364 ProbeStatsPerSize stats;
1365 std::vector<size_t> sizes = {Group::kWidth << 5, Group::kWidth << 10};
1366 for (size_t size : sizes) {
1367 stats[size] =
1368 CollectProbeStatsOnKeysXoredWithSeed(CollectBadMergeKeys(size), 200);
1369 }
1370 auto expected = XorSeedExpectedStats();
1371 for (size_t size : sizes) {
1372 auto& stat = stats[size];
1373 VerifyStats(size, expected, stat);
1374 LOG(INFO) << size << " " << stat;
1375 }
1376 }
1377
1378 // Collect total ProbeStats on num_iters iterations of the following algorithm:
1379 // 1. Create new table
1380 // 2. Select 10% of keys and insert 10 elements key * 17 + j * 13
1381 // 3. Collect ProbeStats from final table
CollectProbeStatsOnLinearlyTransformedKeys(const std::vector<int64_t> & keys,size_t num_iters)1382 ProbeStats CollectProbeStatsOnLinearlyTransformedKeys(
1383 const std::vector<int64_t>& keys, size_t num_iters) {
1384 ProbeStats stats;
1385
1386 std::random_device rd;
1387 std::mt19937 rng(rd());
1388 auto linear_transform = [](size_t x, size_t y) { return x * 17 + y * 13; };
1389 std::uniform_int_distribution<size_t> dist(0, keys.size()-1);
1390 while (num_iters--) {
1391 IntTable t1;
1392 size_t num_keys = keys.size() / 10;
1393 size_t start = dist(rng);
1394 for (size_t i = 0; i != num_keys; ++i) {
1395 for (size_t j = 0; j != 10; ++j) {
1396 t1.emplace(linear_transform(keys[(i + start) % keys.size()], j));
1397 }
1398 }
1399
1400 auto probe_histogram = GetHashtableDebugNumProbesHistogram(t1);
1401 stats.all_probes_histogram.resize(
1402 std::max(stats.all_probes_histogram.size(), probe_histogram.size()));
1403 std::transform(probe_histogram.begin(), probe_histogram.end(),
1404 stats.all_probes_histogram.begin(),
1405 stats.all_probes_histogram.begin(), std::plus<size_t>());
1406
1407 size_t total_probe_seq_length = 0;
1408 for (size_t i = 0; i < probe_histogram.size(); ++i) {
1409 total_probe_seq_length += i * probe_histogram[i];
1410 }
1411 stats.single_table_ratios.push_back(total_probe_seq_length * 1.0 /
1412 t1.size());
1413 t1.erase(t1.begin(), t1.end());
1414 }
1415 return stats;
1416 }
1417
LinearTransformExpectedStats()1418 ExpectedStats LinearTransformExpectedStats() {
1419 constexpr bool kRandomizesInserts =
1420 #ifdef NDEBUG
1421 false;
1422 #else // NDEBUG
1423 true;
1424 #endif // NDEBUG
1425
1426 // The effective load factor is larger in non-opt mode because we insert
1427 // elements out of order.
1428 switch (container_internal::Group::kWidth) {
1429 case 8:
1430 if (kRandomizesInserts) {
1431 return {0.1,
1432 0.5,
1433 {{0.95, 0.3}},
1434 {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}};
1435 } else {
1436 return {0.4,
1437 0.6,
1438 {{0.95, 0.5}},
1439 {{0.95, 1}, {0.99, 14}, {0.999, 23}, {0.9999, 26}}};
1440 }
1441 case 16:
1442 if (kRandomizesInserts) {
1443 return {0.1,
1444 0.4,
1445 {{0.95, 0.3}},
1446 {{0.95, 1}, {0.99, 2}, {0.999, 9}, {0.9999, 15}}};
1447 } else {
1448 return {0.05,
1449 0.2,
1450 {{0.95, 0.1}},
1451 {{0.95, 0}, {0.99, 1}, {0.999, 6}, {0.9999, 10}}};
1452 }
1453 }
1454 LOG(FATAL) << "Unknown Group width";
1455 return {};
1456 }
1457
1458 // TODO(b/80415403): Figure out why this test is so flaky.
TEST(Table,DISABLED_EnsureNonQuadraticTopNLinearTransformByProbeSeqLength)1459 TEST(Table, DISABLED_EnsureNonQuadraticTopNLinearTransformByProbeSeqLength) {
1460 ProbeStatsPerSize stats;
1461 std::vector<size_t> sizes = {Group::kWidth << 5, Group::kWidth << 10};
1462 for (size_t size : sizes) {
1463 stats[size] = CollectProbeStatsOnLinearlyTransformedKeys(
1464 CollectBadMergeKeys(size), 300);
1465 }
1466 auto expected = LinearTransformExpectedStats();
1467 for (size_t size : sizes) {
1468 auto& stat = stats[size];
1469 VerifyStats(size, expected, stat);
1470 LOG(INFO) << size << " " << stat;
1471 }
1472 }
1473
TEST(Table,EraseCollision)1474 TEST(Table, EraseCollision) {
1475 BadTable t;
1476
1477 // 1 2 3
1478 t.emplace(1);
1479 t.emplace(2);
1480 t.emplace(3);
1481 EXPECT_THAT(*t.find(1), 1);
1482 EXPECT_THAT(*t.find(2), 2);
1483 EXPECT_THAT(*t.find(3), 3);
1484 EXPECT_EQ(3, t.size());
1485
1486 // 1 DELETED 3
1487 t.erase(t.find(2));
1488 EXPECT_THAT(*t.find(1), 1);
1489 EXPECT_TRUE(t.find(2) == t.end());
1490 EXPECT_THAT(*t.find(3), 3);
1491 EXPECT_EQ(2, t.size());
1492
1493 // DELETED DELETED 3
1494 t.erase(t.find(1));
1495 EXPECT_TRUE(t.find(1) == t.end());
1496 EXPECT_TRUE(t.find(2) == t.end());
1497 EXPECT_THAT(*t.find(3), 3);
1498 EXPECT_EQ(1, t.size());
1499
1500 // DELETED DELETED DELETED
1501 t.erase(t.find(3));
1502 EXPECT_TRUE(t.find(1) == t.end());
1503 EXPECT_TRUE(t.find(2) == t.end());
1504 EXPECT_TRUE(t.find(3) == t.end());
1505 EXPECT_EQ(0, t.size());
1506 }
1507
TEST(Table,EraseInsertProbing)1508 TEST(Table, EraseInsertProbing) {
1509 BadTable t(100);
1510
1511 // 1 2 3 4
1512 t.emplace(1);
1513 t.emplace(2);
1514 t.emplace(3);
1515 t.emplace(4);
1516
1517 // 1 DELETED 3 DELETED
1518 t.erase(t.find(2));
1519 t.erase(t.find(4));
1520
1521 // 1 10 3 11 12
1522 t.emplace(10);
1523 t.emplace(11);
1524 t.emplace(12);
1525
1526 EXPECT_EQ(5, t.size());
1527 EXPECT_THAT(t, UnorderedElementsAre(1, 10, 3, 11, 12));
1528 }
1529
TEST(Table,Clear)1530 TEST(Table, Clear) {
1531 IntTable t;
1532 EXPECT_TRUE(t.find(0) == t.end());
1533 t.clear();
1534 EXPECT_TRUE(t.find(0) == t.end());
1535 auto res = t.emplace(0);
1536 EXPECT_TRUE(res.second);
1537 EXPECT_EQ(1, t.size());
1538 t.clear();
1539 EXPECT_EQ(0, t.size());
1540 EXPECT_TRUE(t.find(0) == t.end());
1541 }
1542
TEST(Table,Swap)1543 TEST(Table, Swap) {
1544 IntTable t;
1545 EXPECT_TRUE(t.find(0) == t.end());
1546 auto res = t.emplace(0);
1547 EXPECT_TRUE(res.second);
1548 EXPECT_EQ(1, t.size());
1549 IntTable u;
1550 t.swap(u);
1551 EXPECT_EQ(0, t.size());
1552 EXPECT_EQ(1, u.size());
1553 EXPECT_TRUE(t.find(0) == t.end());
1554 EXPECT_THAT(*u.find(0), 0);
1555 }
1556
TEST(Table,Rehash)1557 TEST(Table, Rehash) {
1558 IntTable t;
1559 EXPECT_TRUE(t.find(0) == t.end());
1560 t.emplace(0);
1561 t.emplace(1);
1562 EXPECT_EQ(2, t.size());
1563 t.rehash(128);
1564 EXPECT_EQ(2, t.size());
1565 EXPECT_THAT(*t.find(0), 0);
1566 EXPECT_THAT(*t.find(1), 1);
1567 }
1568
TEST(Table,RehashDoesNotRehashWhenNotNecessary)1569 TEST(Table, RehashDoesNotRehashWhenNotNecessary) {
1570 IntTable t;
1571 t.emplace(0);
1572 t.emplace(1);
1573 auto* p = &*t.find(0);
1574 t.rehash(1);
1575 EXPECT_EQ(p, &*t.find(0));
1576 }
1577
TEST(Table,RehashZeroDoesNotAllocateOnEmptyTable)1578 TEST(Table, RehashZeroDoesNotAllocateOnEmptyTable) {
1579 IntTable t;
1580 t.rehash(0);
1581 EXPECT_EQ(0, t.bucket_count());
1582 }
1583
TEST(Table,RehashZeroDeallocatesEmptyTable)1584 TEST(Table, RehashZeroDeallocatesEmptyTable) {
1585 IntTable t;
1586 t.emplace(0);
1587 t.clear();
1588 EXPECT_NE(0, t.bucket_count());
1589 t.rehash(0);
1590 EXPECT_EQ(0, t.bucket_count());
1591 }
1592
TEST(Table,RehashZeroForcesRehash)1593 TEST(Table, RehashZeroForcesRehash) {
1594 IntTable t;
1595 t.emplace(0);
1596 t.emplace(1);
1597 auto* p = &*t.find(0);
1598 t.rehash(0);
1599 EXPECT_NE(p, &*t.find(0));
1600 }
1601
TEST(Table,ConstructFromInitList)1602 TEST(Table, ConstructFromInitList) {
1603 using P = std::pair<std::string, std::string>;
1604 struct Q {
1605 operator P() const { return {}; } // NOLINT
1606 };
1607 StringTable t = {P(), Q(), {}, {{}, {}}};
1608 }
1609
TEST(Table,CopyConstruct)1610 TEST(Table, CopyConstruct) {
1611 IntTable t;
1612 t.emplace(0);
1613 EXPECT_EQ(1, t.size());
1614 {
1615 IntTable u(t);
1616 EXPECT_EQ(1, u.size());
1617 EXPECT_THAT(*u.find(0), 0);
1618 }
1619 {
1620 IntTable u{t};
1621 EXPECT_EQ(1, u.size());
1622 EXPECT_THAT(*u.find(0), 0);
1623 }
1624 {
1625 IntTable u = t;
1626 EXPECT_EQ(1, u.size());
1627 EXPECT_THAT(*u.find(0), 0);
1628 }
1629 }
1630
TEST(Table,CopyConstructWithAlloc)1631 TEST(Table, CopyConstructWithAlloc) {
1632 StringTable t;
1633 t.emplace("a", "b");
1634 EXPECT_EQ(1, t.size());
1635 StringTable u(t, Alloc<std::pair<std::string, std::string>>());
1636 EXPECT_EQ(1, u.size());
1637 EXPECT_THAT(*u.find("a"), Pair("a", "b"));
1638 }
1639
1640 struct ExplicitAllocIntTable
1641 : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
1642 std::equal_to<int64_t>, Alloc<int64_t>> {
1643 ExplicitAllocIntTable() = default;
1644 };
1645
TEST(Table,AllocWithExplicitCtor)1646 TEST(Table, AllocWithExplicitCtor) {
1647 ExplicitAllocIntTable t;
1648 EXPECT_EQ(0, t.size());
1649 }
1650
TEST(Table,MoveConstruct)1651 TEST(Table, MoveConstruct) {
1652 {
1653 StringTable t;
1654 t.emplace("a", "b");
1655 EXPECT_EQ(1, t.size());
1656
1657 StringTable u(std::move(t));
1658 EXPECT_EQ(1, u.size());
1659 EXPECT_THAT(*u.find("a"), Pair("a", "b"));
1660 }
1661 {
1662 StringTable t;
1663 t.emplace("a", "b");
1664 EXPECT_EQ(1, t.size());
1665
1666 StringTable u{std::move(t)};
1667 EXPECT_EQ(1, u.size());
1668 EXPECT_THAT(*u.find("a"), Pair("a", "b"));
1669 }
1670 {
1671 StringTable t;
1672 t.emplace("a", "b");
1673 EXPECT_EQ(1, t.size());
1674
1675 StringTable u = std::move(t);
1676 EXPECT_EQ(1, u.size());
1677 EXPECT_THAT(*u.find("a"), Pair("a", "b"));
1678 }
1679 }
1680
TEST(Table,MoveConstructWithAlloc)1681 TEST(Table, MoveConstructWithAlloc) {
1682 StringTable t;
1683 t.emplace("a", "b");
1684 EXPECT_EQ(1, t.size());
1685 StringTable u(std::move(t), Alloc<std::pair<std::string, std::string>>());
1686 EXPECT_EQ(1, u.size());
1687 EXPECT_THAT(*u.find("a"), Pair("a", "b"));
1688 }
1689
TEST(Table,CopyAssign)1690 TEST(Table, CopyAssign) {
1691 StringTable t;
1692 t.emplace("a", "b");
1693 EXPECT_EQ(1, t.size());
1694 StringTable u;
1695 u = t;
1696 EXPECT_EQ(1, u.size());
1697 EXPECT_THAT(*u.find("a"), Pair("a", "b"));
1698 }
1699
TEST(Table,CopySelfAssign)1700 TEST(Table, CopySelfAssign) {
1701 StringTable t;
1702 t.emplace("a", "b");
1703 EXPECT_EQ(1, t.size());
1704 t = *&t;
1705 EXPECT_EQ(1, t.size());
1706 EXPECT_THAT(*t.find("a"), Pair("a", "b"));
1707 }
1708
TEST(Table,MoveAssign)1709 TEST(Table, MoveAssign) {
1710 StringTable t;
1711 t.emplace("a", "b");
1712 EXPECT_EQ(1, t.size());
1713 StringTable u;
1714 u = std::move(t);
1715 EXPECT_EQ(1, u.size());
1716 EXPECT_THAT(*u.find("a"), Pair("a", "b"));
1717 }
1718
TEST(Table,Equality)1719 TEST(Table, Equality) {
1720 StringTable t;
1721 std::vector<std::pair<std::string, std::string>> v = {{"a", "b"},
1722 {"aa", "bb"}};
1723 t.insert(std::begin(v), std::end(v));
1724 StringTable u = t;
1725 EXPECT_EQ(u, t);
1726 }
1727
TEST(Table,Equality2)1728 TEST(Table, Equality2) {
1729 StringTable t;
1730 std::vector<std::pair<std::string, std::string>> v1 = {{"a", "b"},
1731 {"aa", "bb"}};
1732 t.insert(std::begin(v1), std::end(v1));
1733 StringTable u;
1734 std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"},
1735 {"aa", "aa"}};
1736 u.insert(std::begin(v2), std::end(v2));
1737 EXPECT_NE(u, t);
1738 }
1739
TEST(Table,Equality3)1740 TEST(Table, Equality3) {
1741 StringTable t;
1742 std::vector<std::pair<std::string, std::string>> v1 = {{"b", "b"},
1743 {"bb", "bb"}};
1744 t.insert(std::begin(v1), std::end(v1));
1745 StringTable u;
1746 std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"},
1747 {"aa", "aa"}};
1748 u.insert(std::begin(v2), std::end(v2));
1749 EXPECT_NE(u, t);
1750 }
1751
TEST(Table,NumDeletedRegression)1752 TEST(Table, NumDeletedRegression) {
1753 IntTable t;
1754 t.emplace(0);
1755 t.erase(t.find(0));
1756 // construct over a deleted slot.
1757 t.emplace(0);
1758 t.clear();
1759 }
1760
TEST(Table,FindFullDeletedRegression)1761 TEST(Table, FindFullDeletedRegression) {
1762 IntTable t;
1763 for (int i = 0; i < 1000; ++i) {
1764 t.emplace(i);
1765 t.erase(t.find(i));
1766 }
1767 EXPECT_EQ(0, t.size());
1768 }
1769
TEST(Table,ReplacingDeletedSlotDoesNotRehash)1770 TEST(Table, ReplacingDeletedSlotDoesNotRehash) {
1771 size_t n;
1772 {
1773 // Compute n such that n is the maximum number of elements before rehash.
1774 IntTable t;
1775 t.emplace(0);
1776 size_t c = t.bucket_count();
1777 for (n = 1; c == t.bucket_count(); ++n) t.emplace(n);
1778 --n;
1779 }
1780 IntTable t;
1781 t.rehash(n);
1782 const size_t c = t.bucket_count();
1783 for (size_t i = 0; i != n; ++i) t.emplace(i);
1784 EXPECT_EQ(c, t.bucket_count()) << "rehashing threshold = " << n;
1785 t.erase(0);
1786 t.emplace(0);
1787 EXPECT_EQ(c, t.bucket_count()) << "rehashing threshold = " << n;
1788 }
1789
TEST(Table,NoThrowMoveConstruct)1790 TEST(Table, NoThrowMoveConstruct) {
1791 ASSERT_TRUE(
1792 std::is_nothrow_copy_constructible<absl::Hash<absl::string_view>>::value);
1793 ASSERT_TRUE(std::is_nothrow_copy_constructible<
1794 std::equal_to<absl::string_view>>::value);
1795 ASSERT_TRUE(std::is_nothrow_copy_constructible<std::allocator<int>>::value);
1796 EXPECT_TRUE(std::is_nothrow_move_constructible<StringTable>::value);
1797 }
1798
TEST(Table,NoThrowMoveAssign)1799 TEST(Table, NoThrowMoveAssign) {
1800 ASSERT_TRUE(
1801 std::is_nothrow_move_assignable<absl::Hash<absl::string_view>>::value);
1802 ASSERT_TRUE(
1803 std::is_nothrow_move_assignable<std::equal_to<absl::string_view>>::value);
1804 ASSERT_TRUE(std::is_nothrow_move_assignable<std::allocator<int>>::value);
1805 ASSERT_TRUE(
1806 absl::allocator_traits<std::allocator<int>>::is_always_equal::value);
1807 EXPECT_TRUE(std::is_nothrow_move_assignable<StringTable>::value);
1808 }
1809
TEST(Table,NoThrowSwappable)1810 TEST(Table, NoThrowSwappable) {
1811 ASSERT_TRUE(
1812 container_internal::IsNoThrowSwappable<absl::Hash<absl::string_view>>());
1813 ASSERT_TRUE(container_internal::IsNoThrowSwappable<
1814 std::equal_to<absl::string_view>>());
1815 ASSERT_TRUE(container_internal::IsNoThrowSwappable<std::allocator<int>>());
1816 EXPECT_TRUE(container_internal::IsNoThrowSwappable<StringTable>());
1817 }
1818
TEST(Table,HeterogeneousLookup)1819 TEST(Table, HeterogeneousLookup) {
1820 struct Hash {
1821 size_t operator()(int64_t i) const { return i; }
1822 size_t operator()(double i) const {
1823 ADD_FAILURE();
1824 return i;
1825 }
1826 };
1827 struct Eq {
1828 bool operator()(int64_t a, int64_t b) const { return a == b; }
1829 bool operator()(double a, int64_t b) const {
1830 ADD_FAILURE();
1831 return a == b;
1832 }
1833 bool operator()(int64_t a, double b) const {
1834 ADD_FAILURE();
1835 return a == b;
1836 }
1837 bool operator()(double a, double b) const {
1838 ADD_FAILURE();
1839 return a == b;
1840 }
1841 };
1842
1843 struct THash {
1844 using is_transparent = void;
1845 size_t operator()(int64_t i) const { return i; }
1846 size_t operator()(double i) const { return i; }
1847 };
1848 struct TEq {
1849 using is_transparent = void;
1850 bool operator()(int64_t a, int64_t b) const { return a == b; }
1851 bool operator()(double a, int64_t b) const { return a == b; }
1852 bool operator()(int64_t a, double b) const { return a == b; }
1853 bool operator()(double a, double b) const { return a == b; }
1854 };
1855
1856 raw_hash_set<IntPolicy, Hash, Eq, Alloc<int64_t>> s{0, 1, 2};
1857 // It will convert to int64_t before the query.
1858 EXPECT_EQ(1, *s.find(double{1.1}));
1859
1860 raw_hash_set<IntPolicy, THash, TEq, Alloc<int64_t>> ts{0, 1, 2};
1861 // It will try to use the double, and fail to find the object.
1862 EXPECT_TRUE(ts.find(1.1) == ts.end());
1863 }
1864
1865 template <class Table>
1866 using CallFind = decltype(std::declval<Table&>().find(17));
1867
1868 template <class Table>
1869 using CallErase = decltype(std::declval<Table&>().erase(17));
1870
1871 template <class Table>
1872 using CallExtract = decltype(std::declval<Table&>().extract(17));
1873
1874 template <class Table>
1875 using CallPrefetch = decltype(std::declval<Table&>().prefetch(17));
1876
1877 template <class Table>
1878 using CallCount = decltype(std::declval<Table&>().count(17));
1879
1880 template <template <typename> class C, class Table, class = void>
1881 struct VerifyResultOf : std::false_type {};
1882
1883 template <template <typename> class C, class Table>
1884 struct VerifyResultOf<C, Table, absl::void_t<C<Table>>> : std::true_type {};
1885
TEST(Table,HeterogeneousLookupOverloads)1886 TEST(Table, HeterogeneousLookupOverloads) {
1887 using NonTransparentTable =
1888 raw_hash_set<StringPolicy, absl::Hash<absl::string_view>,
1889 std::equal_to<absl::string_view>, std::allocator<int>>;
1890
1891 EXPECT_FALSE((VerifyResultOf<CallFind, NonTransparentTable>()));
1892 EXPECT_FALSE((VerifyResultOf<CallErase, NonTransparentTable>()));
1893 EXPECT_FALSE((VerifyResultOf<CallExtract, NonTransparentTable>()));
1894 EXPECT_FALSE((VerifyResultOf<CallPrefetch, NonTransparentTable>()));
1895 EXPECT_FALSE((VerifyResultOf<CallCount, NonTransparentTable>()));
1896
1897 using TransparentTable = raw_hash_set<
1898 StringPolicy,
1899 absl::container_internal::hash_default_hash<absl::string_view>,
1900 absl::container_internal::hash_default_eq<absl::string_view>,
1901 std::allocator<int>>;
1902
1903 EXPECT_TRUE((VerifyResultOf<CallFind, TransparentTable>()));
1904 EXPECT_TRUE((VerifyResultOf<CallErase, TransparentTable>()));
1905 EXPECT_TRUE((VerifyResultOf<CallExtract, TransparentTable>()));
1906 EXPECT_TRUE((VerifyResultOf<CallPrefetch, TransparentTable>()));
1907 EXPECT_TRUE((VerifyResultOf<CallCount, TransparentTable>()));
1908 }
1909
TEST(Iterator,IsDefaultConstructible)1910 TEST(Iterator, IsDefaultConstructible) {
1911 StringTable::iterator i;
1912 EXPECT_TRUE(i == StringTable::iterator());
1913 }
1914
TEST(ConstIterator,IsDefaultConstructible)1915 TEST(ConstIterator, IsDefaultConstructible) {
1916 StringTable::const_iterator i;
1917 EXPECT_TRUE(i == StringTable::const_iterator());
1918 }
1919
TEST(Iterator,ConvertsToConstIterator)1920 TEST(Iterator, ConvertsToConstIterator) {
1921 StringTable::iterator i;
1922 EXPECT_TRUE(i == StringTable::const_iterator());
1923 }
1924
TEST(Iterator,Iterates)1925 TEST(Iterator, Iterates) {
1926 IntTable t;
1927 for (size_t i = 3; i != 6; ++i) EXPECT_TRUE(t.emplace(i).second);
1928 EXPECT_THAT(t, UnorderedElementsAre(3, 4, 5));
1929 }
1930
TEST(Table,Merge)1931 TEST(Table, Merge) {
1932 StringTable t1, t2;
1933 t1.emplace("0", "-0");
1934 t1.emplace("1", "-1");
1935 t2.emplace("0", "~0");
1936 t2.emplace("2", "~2");
1937
1938 EXPECT_THAT(t1, UnorderedElementsAre(Pair("0", "-0"), Pair("1", "-1")));
1939 EXPECT_THAT(t2, UnorderedElementsAre(Pair("0", "~0"), Pair("2", "~2")));
1940
1941 t1.merge(t2);
1942 EXPECT_THAT(t1, UnorderedElementsAre(Pair("0", "-0"), Pair("1", "-1"),
1943 Pair("2", "~2")));
1944 EXPECT_THAT(t2, UnorderedElementsAre(Pair("0", "~0")));
1945 }
1946
TEST(Table,IteratorEmplaceConstructibleRequirement)1947 TEST(Table, IteratorEmplaceConstructibleRequirement) {
1948 struct Value {
1949 explicit Value(absl::string_view view) : value(view) {}
1950 std::string value;
1951
1952 bool operator==(const Value& other) const { return value == other.value; }
1953 };
1954 struct H {
1955 size_t operator()(const Value& v) const {
1956 return absl::Hash<std::string>{}(v.value);
1957 }
1958 };
1959
1960 struct Table : raw_hash_set<ValuePolicy<Value>, H, std::equal_to<Value>,
1961 std::allocator<Value>> {
1962 using Base = typename Table::raw_hash_set;
1963 using Base::Base;
1964 };
1965
1966 std::string input[3]{"A", "B", "C"};
1967
1968 Table t(std::begin(input), std::end(input));
1969 EXPECT_THAT(t, UnorderedElementsAre(Value{"A"}, Value{"B"}, Value{"C"}));
1970
1971 input[0] = "D";
1972 input[1] = "E";
1973 input[2] = "F";
1974 t.insert(std::begin(input), std::end(input));
1975 EXPECT_THAT(t, UnorderedElementsAre(Value{"A"}, Value{"B"}, Value{"C"},
1976 Value{"D"}, Value{"E"}, Value{"F"}));
1977 }
1978
TEST(Nodes,EmptyNodeType)1979 TEST(Nodes, EmptyNodeType) {
1980 using node_type = StringTable::node_type;
1981 node_type n;
1982 EXPECT_FALSE(n);
1983 EXPECT_TRUE(n.empty());
1984
1985 EXPECT_TRUE((std::is_same<node_type::allocator_type,
1986 StringTable::allocator_type>::value));
1987 }
1988
TEST(Nodes,ExtractInsert)1989 TEST(Nodes, ExtractInsert) {
1990 constexpr char k0[] = "Very long string zero.";
1991 constexpr char k1[] = "Very long string one.";
1992 constexpr char k2[] = "Very long string two.";
1993 StringTable t = {{k0, ""}, {k1, ""}, {k2, ""}};
1994 EXPECT_THAT(t,
1995 UnorderedElementsAre(Pair(k0, ""), Pair(k1, ""), Pair(k2, "")));
1996
1997 auto node = t.extract(k0);
1998 EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
1999 EXPECT_TRUE(node);
2000 EXPECT_FALSE(node.empty());
2001
2002 StringTable t2;
2003 StringTable::insert_return_type res = t2.insert(std::move(node));
2004 EXPECT_TRUE(res.inserted);
2005 EXPECT_THAT(*res.position, Pair(k0, ""));
2006 EXPECT_FALSE(res.node);
2007 EXPECT_THAT(t2, UnorderedElementsAre(Pair(k0, "")));
2008
2009 // Not there.
2010 EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
2011 node = t.extract("Not there!");
2012 EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
2013 EXPECT_FALSE(node);
2014
2015 // Inserting nothing.
2016 res = t2.insert(std::move(node));
2017 EXPECT_FALSE(res.inserted);
2018 EXPECT_EQ(res.position, t2.end());
2019 EXPECT_FALSE(res.node);
2020 EXPECT_THAT(t2, UnorderedElementsAre(Pair(k0, "")));
2021
2022 t.emplace(k0, "1");
2023 node = t.extract(k0);
2024
2025 // Insert duplicate.
2026 res = t2.insert(std::move(node));
2027 EXPECT_FALSE(res.inserted);
2028 EXPECT_THAT(*res.position, Pair(k0, ""));
2029 EXPECT_TRUE(res.node);
2030 EXPECT_FALSE(node); // NOLINT(bugprone-use-after-move)
2031 }
2032
TEST(Nodes,HintInsert)2033 TEST(Nodes, HintInsert) {
2034 IntTable t = {1, 2, 3};
2035 auto node = t.extract(1);
2036 EXPECT_THAT(t, UnorderedElementsAre(2, 3));
2037 auto it = t.insert(t.begin(), std::move(node));
2038 EXPECT_THAT(t, UnorderedElementsAre(1, 2, 3));
2039 EXPECT_EQ(*it, 1);
2040 EXPECT_FALSE(node); // NOLINT(bugprone-use-after-move)
2041
2042 node = t.extract(2);
2043 EXPECT_THAT(t, UnorderedElementsAre(1, 3));
2044 // reinsert 2 to make the next insert fail.
2045 t.insert(2);
2046 EXPECT_THAT(t, UnorderedElementsAre(1, 2, 3));
2047 it = t.insert(t.begin(), std::move(node));
2048 EXPECT_EQ(*it, 2);
2049 // The node was not emptied by the insert call.
2050 EXPECT_TRUE(node); // NOLINT(bugprone-use-after-move)
2051 }
2052
MakeSimpleTable(size_t size)2053 IntTable MakeSimpleTable(size_t size) {
2054 IntTable t;
2055 while (t.size() < size) t.insert(t.size());
2056 return t;
2057 }
2058
OrderOfIteration(const IntTable & t)2059 std::vector<int> OrderOfIteration(const IntTable& t) {
2060 return {t.begin(), t.end()};
2061 }
2062
2063 // These IterationOrderChanges tests depend on non-deterministic behavior.
2064 // We are injecting non-determinism from the pointer of the table, but do so in
2065 // a way that only the page matters. We have to retry enough times to make sure
2066 // we are touching different memory pages to cause the ordering to change.
2067 // We also need to keep the old tables around to avoid getting the same memory
2068 // blocks over and over.
TEST(Table,IterationOrderChangesByInstance)2069 TEST(Table, IterationOrderChangesByInstance) {
2070 for (size_t size : {2, 6, 12, 20}) {
2071 const auto reference_table = MakeSimpleTable(size);
2072 const auto reference = OrderOfIteration(reference_table);
2073
2074 std::vector<IntTable> tables;
2075 bool found_difference = false;
2076 for (int i = 0; !found_difference && i < 5000; ++i) {
2077 tables.push_back(MakeSimpleTable(size));
2078 found_difference = OrderOfIteration(tables.back()) != reference;
2079 }
2080 if (!found_difference) {
2081 FAIL()
2082 << "Iteration order remained the same across many attempts with size "
2083 << size;
2084 }
2085 }
2086 }
2087
TEST(Table,IterationOrderChangesOnRehash)2088 TEST(Table, IterationOrderChangesOnRehash) {
2089 std::vector<IntTable> garbage;
2090 for (int i = 0; i < 5000; ++i) {
2091 auto t = MakeSimpleTable(20);
2092 const auto reference = OrderOfIteration(t);
2093 // Force rehash to the same size.
2094 t.rehash(0);
2095 auto trial = OrderOfIteration(t);
2096 if (trial != reference) {
2097 // We are done.
2098 return;
2099 }
2100 garbage.push_back(std::move(t));
2101 }
2102 FAIL() << "Iteration order remained the same across many attempts.";
2103 }
2104
2105 // Verify that pointers are invalidated as soon as a second element is inserted.
2106 // This prevents dependency on pointer stability on small tables.
TEST(Table,UnstablePointers)2107 TEST(Table, UnstablePointers) {
2108 IntTable table;
2109
2110 const auto addr = [&](int i) {
2111 return reinterpret_cast<uintptr_t>(&*table.find(i));
2112 };
2113
2114 table.insert(0);
2115 const uintptr_t old_ptr = addr(0);
2116
2117 // This causes a rehash.
2118 table.insert(1);
2119
2120 EXPECT_NE(old_ptr, addr(0));
2121 }
2122
IsAssertEnabled()2123 bool IsAssertEnabled() {
2124 // Use an assert with side-effects to figure out if they are actually enabled.
2125 bool assert_enabled = false;
2126 assert([&]() { // NOLINT
2127 assert_enabled = true;
2128 return true;
2129 }());
2130 return assert_enabled;
2131 }
2132
TEST(TableDeathTest,InvalidIteratorAsserts)2133 TEST(TableDeathTest, InvalidIteratorAsserts) {
2134 if (!IsAssertEnabled() && !SwisstableGenerationsEnabled())
2135 GTEST_SKIP() << "Assertions not enabled.";
2136
2137 IntTable t;
2138 // Extra simple "regexp" as regexp support is highly varied across platforms.
2139 EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()),
2140 "erase.* called on end.. iterator.");
2141 typename IntTable::iterator iter;
2142 EXPECT_DEATH_IF_SUPPORTED(
2143 ++iter, "operator.* called on default-constructed iterator.");
2144 t.insert(0);
2145 iter = t.begin();
2146 t.erase(iter);
2147 const char* const kErasedDeathMessage =
2148 SwisstableGenerationsEnabled()
2149 ? "operator.* called on invalid iterator.*was likely erased"
2150 : "operator.* called on invalid iterator.*might have been "
2151 "erased.*config=asan";
2152 EXPECT_DEATH_IF_SUPPORTED(++iter, kErasedDeathMessage);
2153 }
2154
2155 // Invalid iterator use can trigger heap-use-after-free in asan,
2156 // use-of-uninitialized-value in msan, or invalidated iterator assertions.
2157 constexpr const char* kInvalidIteratorDeathMessage =
2158 "heap-use-after-free|use-of-uninitialized-value|invalidated "
2159 "iterator|Invalid iterator|invalid iterator";
2160
2161 // MSVC doesn't support | in regex.
2162 #if defined(_MSC_VER)
2163 constexpr bool kMsvc = true;
2164 #else
2165 constexpr bool kMsvc = false;
2166 #endif
2167
TEST(TableDeathTest,IteratorInvalidAssertsEqualityOperator)2168 TEST(TableDeathTest, IteratorInvalidAssertsEqualityOperator) {
2169 if (!IsAssertEnabled() && !SwisstableGenerationsEnabled())
2170 GTEST_SKIP() << "Assertions not enabled.";
2171
2172 IntTable t;
2173 t.insert(1);
2174 t.insert(2);
2175 t.insert(3);
2176 auto iter1 = t.begin();
2177 auto iter2 = std::next(iter1);
2178 ASSERT_NE(iter1, t.end());
2179 ASSERT_NE(iter2, t.end());
2180 t.erase(iter1);
2181 // Extra simple "regexp" as regexp support is highly varied across platforms.
2182 const char* const kErasedDeathMessage =
2183 SwisstableGenerationsEnabled()
2184 ? "Invalid iterator comparison.*was likely erased"
2185 : "Invalid iterator comparison.*might have been erased.*config=asan";
2186 EXPECT_DEATH_IF_SUPPORTED(void(iter1 == iter2), kErasedDeathMessage);
2187 EXPECT_DEATH_IF_SUPPORTED(void(iter2 != iter1), kErasedDeathMessage);
2188 t.erase(iter2);
2189 EXPECT_DEATH_IF_SUPPORTED(void(iter1 == iter2), kErasedDeathMessage);
2190
2191 IntTable t1, t2;
2192 t1.insert(0);
2193 t2.insert(0);
2194 iter1 = t1.begin();
2195 iter2 = t2.begin();
2196 const char* const kContainerDiffDeathMessage =
2197 SwisstableGenerationsEnabled()
2198 ? "Invalid iterator comparison.*iterators from different hashtables"
2199 : "Invalid iterator comparison.*may be from different "
2200 ".*containers.*config=asan";
2201 EXPECT_DEATH_IF_SUPPORTED(void(iter1 == iter2), kContainerDiffDeathMessage);
2202 EXPECT_DEATH_IF_SUPPORTED(void(iter2 == iter1), kContainerDiffDeathMessage);
2203
2204 for (int i = 0; i < 10; ++i) t1.insert(i);
2205 // There should have been a rehash in t1.
2206 if (kMsvc) return; // MSVC doesn't support | in regex.
2207
2208 // NOTE(b/293887834): After rehashing, iterators will contain pointers to
2209 // freed memory, which may be detected by ThreadSanitizer.
2210 const char* const kRehashedDeathMessage =
2211 SwisstableGenerationsEnabled()
2212 ? kInvalidIteratorDeathMessage
2213 : "Invalid iterator comparison.*might have rehashed.*config=asan"
2214 "|ThreadSanitizer: heap-use-after-free";
2215 EXPECT_DEATH_IF_SUPPORTED(void(iter1 == t1.begin()), kRehashedDeathMessage);
2216 }
2217
2218 #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
TEST(RawHashSamplerTest,Sample)2219 TEST(RawHashSamplerTest, Sample) {
2220 // Enable the feature even if the prod default is off.
2221 SetHashtablezEnabled(true);
2222 SetHashtablezSampleParameter(100);
2223
2224 auto& sampler = GlobalHashtablezSampler();
2225 size_t start_size = 0;
2226 absl::flat_hash_set<const HashtablezInfo*> preexisting_info;
2227 start_size += sampler.Iterate([&](const HashtablezInfo& info) {
2228 preexisting_info.insert(&info);
2229 ++start_size;
2230 });
2231
2232 std::vector<IntTable> tables;
2233 for (int i = 0; i < 1000000; ++i) {
2234 tables.emplace_back();
2235
2236 const bool do_reserve = (i % 10 > 5);
2237 const bool do_rehash = !do_reserve && (i % 10 > 0);
2238
2239 if (do_reserve) {
2240 // Don't reserve on all tables.
2241 tables.back().reserve(10 * (i % 10));
2242 }
2243
2244 tables.back().insert(1);
2245 tables.back().insert(i % 5);
2246
2247 if (do_rehash) {
2248 // Rehash some other tables.
2249 tables.back().rehash(10 * (i % 10));
2250 }
2251 }
2252 size_t end_size = 0;
2253 absl::flat_hash_map<size_t, int> observed_checksums;
2254 absl::flat_hash_map<ssize_t, int> reservations;
2255 end_size += sampler.Iterate([&](const HashtablezInfo& info) {
2256 if (preexisting_info.count(&info) == 0) {
2257 observed_checksums[info.hashes_bitwise_xor.load(
2258 std::memory_order_relaxed)]++;
2259 reservations[info.max_reserve.load(std::memory_order_relaxed)]++;
2260 }
2261 EXPECT_EQ(info.inline_element_size, sizeof(int64_t));
2262 ++end_size;
2263 });
2264
2265 EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()),
2266 0.01, 0.005);
2267 EXPECT_EQ(observed_checksums.size(), 5);
2268 for (const auto& [_, count] : observed_checksums) {
2269 EXPECT_NEAR((100 * count) / static_cast<double>(tables.size()), 0.2, 0.05);
2270 }
2271
2272 EXPECT_EQ(reservations.size(), 10);
2273 for (const auto& [reservation, count] : reservations) {
2274 EXPECT_GE(reservation, 0);
2275 EXPECT_LT(reservation, 100);
2276
2277 EXPECT_NEAR((100 * count) / static_cast<double>(tables.size()), 0.1, 0.05)
2278 << reservation;
2279 }
2280 }
2281 #endif // ABSL_INTERNAL_HASHTABLEZ_SAMPLE
2282
TEST(RawHashSamplerTest,DoNotSampleCustomAllocators)2283 TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) {
2284 // Enable the feature even if the prod default is off.
2285 SetHashtablezEnabled(true);
2286 SetHashtablezSampleParameter(100);
2287
2288 auto& sampler = GlobalHashtablezSampler();
2289 size_t start_size = 0;
2290 start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; });
2291
2292 std::vector<CustomAllocIntTable> tables;
2293 for (int i = 0; i < 1000000; ++i) {
2294 tables.emplace_back();
2295 tables.back().insert(1);
2296 }
2297 size_t end_size = 0;
2298 end_size += sampler.Iterate([&](const HashtablezInfo&) { ++end_size; });
2299
2300 EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()),
2301 0.00, 0.001);
2302 }
2303
2304 #ifdef ABSL_HAVE_ADDRESS_SANITIZER
TEST(Sanitizer,PoisoningUnused)2305 TEST(Sanitizer, PoisoningUnused) {
2306 IntTable t;
2307 t.reserve(5);
2308 // Insert something to force an allocation.
2309 int64_t& v1 = *t.insert(0).first;
2310
2311 // Make sure there is something to test.
2312 ASSERT_GT(t.capacity(), 1);
2313
2314 int64_t* slots = RawHashSetTestOnlyAccess::GetSlots(t);
2315 for (size_t i = 0; i < t.capacity(); ++i) {
2316 EXPECT_EQ(slots + i != &v1, __asan_address_is_poisoned(slots + i));
2317 }
2318 }
2319
TEST(Sanitizer,PoisoningOnErase)2320 TEST(Sanitizer, PoisoningOnErase) {
2321 IntTable t;
2322 int64_t& v = *t.insert(0).first;
2323
2324 EXPECT_FALSE(__asan_address_is_poisoned(&v));
2325 t.erase(0);
2326 EXPECT_TRUE(__asan_address_is_poisoned(&v));
2327 }
2328 #endif // ABSL_HAVE_ADDRESS_SANITIZER
2329
2330 template <typename T>
2331 class AlignOneTest : public ::testing::Test {};
2332 using AlignOneTestTypes =
2333 ::testing::Types<Uint8Table, MinimumAlignmentUint8Table>;
2334 TYPED_TEST_SUITE(AlignOneTest, AlignOneTestTypes);
2335
TYPED_TEST(AlignOneTest,AlignOne)2336 TYPED_TEST(AlignOneTest, AlignOne) {
2337 // We previously had a bug in which we were copying a control byte over the
2338 // first slot when alignof(value_type) is 1. We test repeated
2339 // insertions/erases and verify that the behavior is correct.
2340 TypeParam t;
2341 std::unordered_set<uint8_t> verifier; // NOLINT
2342
2343 // Do repeated insertions/erases from the table.
2344 for (int64_t i = 0; i < 100000; ++i) {
2345 SCOPED_TRACE(i);
2346 const uint8_t u = (i * -i) & 0xFF;
2347 auto it = t.find(u);
2348 auto verifier_it = verifier.find(u);
2349 if (it == t.end()) {
2350 ASSERT_EQ(verifier_it, verifier.end());
2351 t.insert(u);
2352 verifier.insert(u);
2353 } else {
2354 ASSERT_NE(verifier_it, verifier.end());
2355 t.erase(it);
2356 verifier.erase(verifier_it);
2357 }
2358 }
2359
2360 EXPECT_EQ(t.size(), verifier.size());
2361 for (uint8_t u : t) {
2362 EXPECT_EQ(verifier.count(u), 1);
2363 }
2364 }
2365
TEST(Iterator,InvalidUseCrashesWithSanitizers)2366 TEST(Iterator, InvalidUseCrashesWithSanitizers) {
2367 if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
2368 if (kMsvc) GTEST_SKIP() << "MSVC doesn't support | in regexp.";
2369
2370 IntTable t;
2371 // Start with 1 element so that `it` is never an end iterator.
2372 t.insert(-1);
2373 for (int i = 0; i < 10; ++i) {
2374 auto it = t.begin();
2375 t.insert(i);
2376 EXPECT_DEATH_IF_SUPPORTED(*it, kInvalidIteratorDeathMessage);
2377 EXPECT_DEATH_IF_SUPPORTED(void(it == t.begin()),
2378 kInvalidIteratorDeathMessage);
2379 }
2380 }
2381
TEST(Iterator,InvalidUseWithReserveCrashesWithSanitizers)2382 TEST(Iterator, InvalidUseWithReserveCrashesWithSanitizers) {
2383 if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
2384 if (kMsvc) GTEST_SKIP() << "MSVC doesn't support | in regexp.";
2385
2386 IntTable t;
2387 t.reserve(10);
2388 t.insert(0);
2389 auto it = t.begin();
2390 // Reserved growth can't rehash.
2391 for (int i = 1; i < 10; ++i) {
2392 t.insert(i);
2393 EXPECT_EQ(*it, 0);
2394 }
2395 // ptr will become invalidated on rehash.
2396 const int64_t* ptr = &*it;
2397 (void)ptr;
2398
2399 // erase decreases size but does not decrease reserved growth so the next
2400 // insertion still invalidates iterators.
2401 t.erase(0);
2402 // The first insert after reserved growth is 0 is guaranteed to rehash when
2403 // generations are enabled.
2404 t.insert(10);
2405 EXPECT_DEATH_IF_SUPPORTED(*it, kInvalidIteratorDeathMessage);
2406 EXPECT_DEATH_IF_SUPPORTED(void(it == t.begin()),
2407 kInvalidIteratorDeathMessage);
2408 #ifdef ABSL_HAVE_ADDRESS_SANITIZER
2409 EXPECT_DEATH_IF_SUPPORTED(std::cout << *ptr, "heap-use-after-free");
2410 #endif
2411 }
2412
TEST(Table,ReservedGrowthUpdatesWhenTableDoesntGrow)2413 TEST(Table, ReservedGrowthUpdatesWhenTableDoesntGrow) {
2414 IntTable t;
2415 for (int i = 0; i < 8; ++i) t.insert(i);
2416 // Want to insert twice without invalidating iterators so reserve.
2417 const size_t cap = t.capacity();
2418 t.reserve(t.size() + 2);
2419 // We want to be testing the case in which the reserve doesn't grow the table.
2420 ASSERT_EQ(cap, t.capacity());
2421 auto it = t.find(0);
2422 t.insert(100);
2423 t.insert(200);
2424 // `it` shouldn't have been invalidated.
2425 EXPECT_EQ(*it, 0);
2426 }
2427
TEST(Table,EraseBeginEndResetsReservedGrowth)2428 TEST(Table, EraseBeginEndResetsReservedGrowth) {
2429 bool frozen = false;
2430 BadHashFreezableIntTable t{FreezableAlloc<int64_t>(&frozen)};
2431 t.reserve(100);
2432 const size_t cap = t.capacity();
2433 frozen = true; // no further allocs allowed
2434
2435 for (int i = 0; i < 10; ++i) {
2436 // Create a long run (hash function returns constant).
2437 for (int j = 0; j < 100; ++j) t.insert(j);
2438 // Erase elements from the middle of the long run, which creates tombstones.
2439 for (int j = 30; j < 60; ++j) t.erase(j);
2440 EXPECT_EQ(t.size(), 70);
2441 EXPECT_EQ(t.capacity(), cap);
2442 ASSERT_EQ(RawHashSetTestOnlyAccess::CountTombstones(t), 30);
2443
2444 t.erase(t.begin(), t.end());
2445
2446 EXPECT_EQ(t.size(), 0);
2447 EXPECT_EQ(t.capacity(), cap);
2448 ASSERT_EQ(RawHashSetTestOnlyAccess::CountTombstones(t), 0);
2449 }
2450 }
2451
TEST(Table,GenerationInfoResetsOnClear)2452 TEST(Table, GenerationInfoResetsOnClear) {
2453 if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
2454 if (kMsvc) GTEST_SKIP() << "MSVC doesn't support | in regexp.";
2455
2456 IntTable t;
2457 for (int i = 0; i < 1000; ++i) t.insert(i);
2458 t.reserve(t.size() + 100);
2459
2460 t.clear();
2461
2462 t.insert(0);
2463 auto it = t.begin();
2464 t.insert(1);
2465 EXPECT_DEATH_IF_SUPPORTED(*it, kInvalidIteratorDeathMessage);
2466 }
2467
TEST(Table,InvalidReferenceUseCrashesWithSanitizers)2468 TEST(Table, InvalidReferenceUseCrashesWithSanitizers) {
2469 if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
2470 #ifdef ABSL_HAVE_MEMORY_SANITIZER
2471 GTEST_SKIP() << "MSan fails to detect some of these rehashes.";
2472 #endif
2473
2474 IntTable t;
2475 t.insert(0);
2476 // Rehashing is guaranteed on every insertion while capacity is less than
2477 // RehashProbabilityConstant().
2478 int64_t i = 0;
2479 while (t.capacity() <= RehashProbabilityConstant()) {
2480 // ptr will become invalidated on rehash.
2481 const int64_t* ptr = &*t.begin();
2482 t.insert(++i);
2483 EXPECT_DEATH_IF_SUPPORTED(std::cout << *ptr, "heap-use-after-free") << i;
2484 }
2485 }
2486
TEST(Iterator,InvalidComparisonDifferentTables)2487 TEST(Iterator, InvalidComparisonDifferentTables) {
2488 if (!SwisstableGenerationsEnabled()) GTEST_SKIP() << "Generations disabled.";
2489
2490 IntTable t1, t2;
2491 IntTable::iterator default_constructed_iter;
2492 // We randomly use one of N empty generations for generations from empty
2493 // hashtables. In general, we won't always detect when iterators from
2494 // different empty hashtables are compared, but in this test case, we
2495 // should deterministically detect the error due to our randomness yielding
2496 // consecutive random generations.
2497 EXPECT_DEATH_IF_SUPPORTED(void(t1.end() == t2.end()),
2498 "Invalid iterator comparison.*empty hashtables");
2499 EXPECT_DEATH_IF_SUPPORTED(void(t1.end() == default_constructed_iter),
2500 "Invalid iterator comparison.*default-constructed");
2501 t1.insert(0);
2502 EXPECT_DEATH_IF_SUPPORTED(void(t1.begin() == t2.end()),
2503 "Invalid iterator comparison.*empty hashtable");
2504 EXPECT_DEATH_IF_SUPPORTED(void(t1.begin() == default_constructed_iter),
2505 "Invalid iterator comparison.*default-constructed");
2506 t2.insert(0);
2507 EXPECT_DEATH_IF_SUPPORTED(void(t1.begin() == t2.end()),
2508 "Invalid iterator comparison.*end.. iterator");
2509 EXPECT_DEATH_IF_SUPPORTED(void(t1.begin() == t2.begin()),
2510 "Invalid iterator comparison.*non-end");
2511 }
2512
2513 } // namespace
2514 } // namespace container_internal
2515 ABSL_NAMESPACE_END
2516 } // namespace absl
2517