• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <atomic>
18 #include <cassert>
19 #include <cstddef>
20 #include <cstdint>
21 #include <cstring>
22 
23 #include "absl/base/attributes.h"
24 #include "absl/base/config.h"
25 #include "absl/base/dynamic_annotations.h"
26 #include "absl/base/internal/endian.h"
27 #include "absl/base/internal/raw_logging.h"
28 #include "absl/base/optimization.h"
29 #include "absl/container/internal/container_memory.h"
30 #include "absl/container/internal/hashtable_control_bytes.h"
31 #include "absl/container/internal/hashtablez_sampler.h"
32 #include "absl/functional/function_ref.h"
33 #include "absl/hash/hash.h"
34 #include "absl/numeric/bits.h"
35 
36 namespace absl {
37 ABSL_NAMESPACE_BEGIN
38 namespace container_internal {
39 
40 // Represents a control byte corresponding to a full slot with arbitrary hash.
ZeroCtrlT()41 constexpr ctrl_t ZeroCtrlT() { return static_cast<ctrl_t>(0); }
42 
43 // We have space for `growth_info` before a single block of control bytes. A
44 // single block of empty control bytes for tables without any slots allocated.
45 // This enables removing a branch in the hot path of find(). In order to ensure
46 // that the control bytes are aligned to 16, we have 16 bytes before the control
47 // bytes even though growth_info only needs 8.
48 alignas(16) ABSL_CONST_INIT ABSL_DLL const ctrl_t kEmptyGroup[32] = {
49     ZeroCtrlT(),       ZeroCtrlT(),    ZeroCtrlT(),    ZeroCtrlT(),
50     ZeroCtrlT(),       ZeroCtrlT(),    ZeroCtrlT(),    ZeroCtrlT(),
51     ZeroCtrlT(),       ZeroCtrlT(),    ZeroCtrlT(),    ZeroCtrlT(),
52     ZeroCtrlT(),       ZeroCtrlT(),    ZeroCtrlT(),    ZeroCtrlT(),
53     ctrl_t::kSentinel, ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
54     ctrl_t::kEmpty,    ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
55     ctrl_t::kEmpty,    ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty,
56     ctrl_t::kEmpty,    ctrl_t::kEmpty, ctrl_t::kEmpty, ctrl_t::kEmpty};
57 
58 // We need one full byte followed by a sentinel byte for iterator::operator++ to
59 // work. We have a full group after kSentinel to be safe (in case operator++ is
60 // changed to read a full group).
61 ABSL_CONST_INIT ABSL_DLL const ctrl_t kSooControl[17] = {
62     ZeroCtrlT(),    ctrl_t::kSentinel, ZeroCtrlT(),    ctrl_t::kEmpty,
63     ctrl_t::kEmpty, ctrl_t::kEmpty,    ctrl_t::kEmpty, ctrl_t::kEmpty,
64     ctrl_t::kEmpty, ctrl_t::kEmpty,    ctrl_t::kEmpty, ctrl_t::kEmpty,
65     ctrl_t::kEmpty, ctrl_t::kEmpty,    ctrl_t::kEmpty, ctrl_t::kEmpty,
66     ctrl_t::kEmpty};
67 static_assert(NumControlBytes(SooCapacity()) <= 17,
68               "kSooControl capacity too small");
69 
70 namespace {
71 
HashTableSizeOverflow()72 [[noreturn]] ABSL_ATTRIBUTE_NOINLINE void HashTableSizeOverflow() {
73   ABSL_RAW_LOG(FATAL, "Hash table size overflow");
74 }
75 
ValidateMaxSize(size_t size,size_t slot_size)76 void ValidateMaxSize(size_t size, size_t slot_size) {
77   if (IsAboveValidSize(size, slot_size)) {
78     HashTableSizeOverflow();
79   }
80 }
81 
82 // Returns "random" seed.
RandomSeed()83 inline size_t RandomSeed() {
84 #ifdef ABSL_HAVE_THREAD_LOCAL
85   static thread_local size_t counter = 0;
86   size_t value = ++counter;
87 #else   // ABSL_HAVE_THREAD_LOCAL
88   static std::atomic<size_t> counter(0);
89   size_t value = counter.fetch_add(1, std::memory_order_relaxed);
90 #endif  // ABSL_HAVE_THREAD_LOCAL
91   return value ^ static_cast<size_t>(reinterpret_cast<uintptr_t>(&counter));
92 }
93 
ShouldRehashForBugDetection(PerTableSeed seed,size_t capacity)94 bool ShouldRehashForBugDetection(PerTableSeed seed, size_t capacity) {
95   // Note: we can't use the abseil-random library because abseil-random
96   // depends on swisstable. We want to return true with probability
97   // `min(1, RehashProbabilityConstant() / capacity())`. In order to do this,
98   // we probe based on a random hash and see if the offset is less than
99   // RehashProbabilityConstant().
100   return probe(seed, capacity, absl::HashOf(RandomSeed())).offset() <
101          RehashProbabilityConstant();
102 }
103 
104 // Find a non-deterministic hash for single group table.
105 // Last two bits are used to find a position for a newly inserted element after
106 // resize.
107 // This function is mixing all bits of hash and seed to maximize entropy.
SingleGroupTableH1(size_t hash,PerTableSeed seed)108 size_t SingleGroupTableH1(size_t hash, PerTableSeed seed) {
109   return static_cast<size_t>(absl::popcount(hash ^ seed.seed()));
110 }
111 
112 // Returns the address of the slot `i` iterations after `slot` assuming each
113 // slot has the specified size.
NextSlot(void * slot,size_t slot_size,size_t i=1)114 inline void* NextSlot(void* slot, size_t slot_size, size_t i = 1) {
115   return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot) +
116                                  slot_size * i);
117 }
118 
119 // Returns the address of the slot just before `slot` assuming each slot has the
120 // specified size.
PrevSlot(void * slot,size_t slot_size)121 inline void* PrevSlot(void* slot, size_t slot_size) {
122   return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot) - slot_size);
123 }
124 
125 }  // namespace
126 
EmptyGeneration()127 GenerationType* EmptyGeneration() {
128   if (SwisstableGenerationsEnabled()) {
129     constexpr size_t kNumEmptyGenerations = 1024;
130     static constexpr GenerationType kEmptyGenerations[kNumEmptyGenerations]{};
131     return const_cast<GenerationType*>(
132         &kEmptyGenerations[RandomSeed() % kNumEmptyGenerations]);
133   }
134   return nullptr;
135 }
136 
137 bool CommonFieldsGenerationInfoEnabled::
should_rehash_for_bug_detection_on_insert(PerTableSeed seed,size_t capacity) const138     should_rehash_for_bug_detection_on_insert(PerTableSeed seed,
139                                               size_t capacity) const {
140   if (reserved_growth_ == kReservedGrowthJustRanOut) return true;
141   if (reserved_growth_ > 0) return false;
142   return ShouldRehashForBugDetection(seed, capacity);
143 }
144 
should_rehash_for_bug_detection_on_move(PerTableSeed seed,size_t capacity) const145 bool CommonFieldsGenerationInfoEnabled::should_rehash_for_bug_detection_on_move(
146     PerTableSeed seed, size_t capacity) const {
147   return ShouldRehashForBugDetection(seed, capacity);
148 }
149 
ShouldInsertBackwardsForDebug(size_t capacity,size_t hash,PerTableSeed seed)150 bool ShouldInsertBackwardsForDebug(size_t capacity, size_t hash,
151                                    PerTableSeed seed) {
152   // To avoid problems with weak hashes and single bit tests, we use % 13.
153   // TODO(kfm,sbenza): revisit after we do unconditional mixing
154   return !is_small(capacity) && (H1(hash, seed) ^ RandomSeed()) % 13 > 6;
155 }
156 
IterateOverFullSlots(const CommonFields & c,size_t slot_size,absl::FunctionRef<void (const ctrl_t *,void *)> cb)157 void IterateOverFullSlots(const CommonFields& c, size_t slot_size,
158                           absl::FunctionRef<void(const ctrl_t*, void*)> cb) {
159   const size_t cap = c.capacity();
160   const ctrl_t* ctrl = c.control();
161   void* slot = c.slot_array();
162   if (is_small(cap)) {
163     // Mirrored/cloned control bytes in small table are also located in the
164     // first group (starting from position 0). We are taking group from position
165     // `capacity` in order to avoid duplicates.
166 
167     // Small tables capacity fits into portable group, where
168     // GroupPortableImpl::MaskFull is more efficient for the
169     // capacity <= GroupPortableImpl::kWidth.
170     assert(cap <= GroupPortableImpl::kWidth &&
171            "unexpectedly large small capacity");
172     static_assert(Group::kWidth >= GroupPortableImpl::kWidth,
173                   "unexpected group width");
174     // Group starts from kSentinel slot, so indices in the mask will
175     // be increased by 1.
176     const auto mask = GroupPortableImpl(ctrl + cap).MaskFull();
177     --ctrl;
178     slot = PrevSlot(slot, slot_size);
179     for (uint32_t i : mask) {
180       cb(ctrl + i, SlotAddress(slot, i, slot_size));
181     }
182     return;
183   }
184   size_t remaining = c.size();
185   ABSL_ATTRIBUTE_UNUSED const size_t original_size_for_assert = remaining;
186   while (remaining != 0) {
187     for (uint32_t i : GroupFullEmptyOrDeleted(ctrl).MaskFull()) {
188       assert(IsFull(ctrl[i]) && "hash table was modified unexpectedly");
189       cb(ctrl + i, SlotAddress(slot, i, slot_size));
190       --remaining;
191     }
192     ctrl += Group::kWidth;
193     slot = NextSlot(slot, slot_size, Group::kWidth);
194     assert((remaining == 0 || *(ctrl - 1) != ctrl_t::kSentinel) &&
195            "hash table was modified unexpectedly");
196   }
197   // NOTE: erasure of the current element is allowed in callback for
198   // absl::erase_if specialization. So we use `>=`.
199   assert(original_size_for_assert >= c.size() &&
200          "hash table was modified unexpectedly");
201 }
202 
PrepareInsertAfterSoo(size_t hash,size_t slot_size,CommonFields & common)203 size_t PrepareInsertAfterSoo(size_t hash, size_t slot_size,
204                              CommonFields& common) {
205   assert(common.capacity() == NextCapacity(SooCapacity()));
206   // After resize from capacity 1 to 3, we always have exactly the slot with
207   // index 1 occupied, so we need to insert either at index 0 or index 2.
208   static_assert(SooSlotIndex() == 1, "");
209   PrepareInsertCommon(common);
210   const size_t offset = SingleGroupTableH1(hash, common.seed()) & 2;
211   common.growth_info().OverwriteEmptyAsFull();
212   SetCtrlInSingleGroupTable(common, offset, H2(hash), slot_size);
213   common.infoz().RecordInsert(hash, /*distance_from_desired=*/0);
214   return offset;
215 }
216 
ConvertDeletedToEmptyAndFullToDeleted(ctrl_t * ctrl,size_t capacity)217 void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity) {
218   assert(ctrl[capacity] == ctrl_t::kSentinel);
219   assert(IsValidCapacity(capacity));
220   for (ctrl_t* pos = ctrl; pos < ctrl + capacity; pos += Group::kWidth) {
221     Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos);
222   }
223   // Copy the cloned ctrl bytes.
224   std::memcpy(ctrl + capacity + 1, ctrl, NumClonedBytes());
225   ctrl[capacity] = ctrl_t::kSentinel;
226 }
227 // Extern template instantiation for inline function.
228 template FindInfo find_first_non_full(const CommonFields&, size_t);
229 
find_first_non_full_outofline(const CommonFields & common,size_t hash)230 FindInfo find_first_non_full_outofline(const CommonFields& common,
231                                        size_t hash) {
232   return find_first_non_full(common, hash);
233 }
234 
235 namespace {
236 
ResetGrowthLeft(CommonFields & common)237 void ResetGrowthLeft(CommonFields& common) {
238   common.growth_info().InitGrowthLeftNoDeleted(
239       CapacityToGrowth(common.capacity()) - common.size());
240 }
241 
242 // Finds guaranteed to exists empty slot from the given position.
243 // NOTE: this function is almost never triggered inside of the
244 // DropDeletesWithoutResize, so we keep it simple.
245 // The table is rather sparse, so empty slot will be found very quickly.
FindEmptySlot(size_t start,size_t end,const ctrl_t * ctrl)246 size_t FindEmptySlot(size_t start, size_t end, const ctrl_t* ctrl) {
247   for (size_t i = start; i < end; ++i) {
248     if (IsEmpty(ctrl[i])) {
249       return i;
250     }
251   }
252   ABSL_UNREACHABLE();
253 }
254 
255 // Finds guaranteed to exist full slot starting from the given position.
256 // NOTE: this function is only triggered for rehash(0), when we need to
257 // go back to SOO state, so we keep it simple.
FindFirstFullSlot(size_t start,size_t end,const ctrl_t * ctrl)258 size_t FindFirstFullSlot(size_t start, size_t end, const ctrl_t* ctrl) {
259   for (size_t i = start; i < end; ++i) {
260     if (IsFull(ctrl[i])) {
261       return i;
262     }
263   }
264   ABSL_UNREACHABLE();
265 }
266 
DropDeletesWithoutResizeAndPrepareInsert(CommonFields & common,const PolicyFunctions & policy,size_t new_hash)267 size_t DropDeletesWithoutResizeAndPrepareInsert(CommonFields& common,
268                                                 const PolicyFunctions& policy,
269                                                 size_t new_hash) {
270   void* set = &common;
271   void* slot_array = common.slot_array();
272   const size_t capacity = common.capacity();
273   assert(IsValidCapacity(capacity));
274   assert(!is_small(capacity));
275   // Algorithm:
276   // - mark all DELETED slots as EMPTY
277   // - mark all FULL slots as DELETED
278   // - for each slot marked as DELETED
279   //     hash = Hash(element)
280   //     target = find_first_non_full(hash)
281   //     if target is in the same group
282   //       mark slot as FULL
283   //     else if target is EMPTY
284   //       transfer element to target
285   //       mark slot as EMPTY
286   //       mark target as FULL
287   //     else if target is DELETED
288   //       swap current element with target element
289   //       mark target as FULL
290   //       repeat procedure for current slot with moved from element (target)
291   ctrl_t* ctrl = common.control();
292   ConvertDeletedToEmptyAndFullToDeleted(ctrl, capacity);
293   const void* hash_fn = policy.hash_fn(common);
294   auto hasher = policy.hash_slot;
295   auto transfer = policy.transfer;
296   const size_t slot_size = policy.slot_size;
297 
298   size_t total_probe_length = 0;
299   void* slot_ptr = SlotAddress(slot_array, 0, slot_size);
300 
301   // The index of an empty slot that can be used as temporary memory for
302   // the swap operation.
303   constexpr size_t kUnknownId = ~size_t{};
304   size_t tmp_space_id = kUnknownId;
305 
306   for (size_t i = 0; i != capacity;
307        ++i, slot_ptr = NextSlot(slot_ptr, slot_size)) {
308     assert(slot_ptr == SlotAddress(slot_array, i, slot_size));
309     if (IsEmpty(ctrl[i])) {
310       tmp_space_id = i;
311       continue;
312     }
313     if (!IsDeleted(ctrl[i])) continue;
314     const size_t hash = (*hasher)(hash_fn, slot_ptr);
315     const FindInfo target = find_first_non_full(common, hash);
316     const size_t new_i = target.offset;
317     total_probe_length += target.probe_length;
318 
319     // Verify if the old and new i fall within the same group wrt the hash.
320     // If they do, we don't need to move the object as it falls already in the
321     // best probe we can.
322     const size_t probe_offset = probe(common, hash).offset();
323     const h2_t h2 = H2(hash);
324     const auto probe_index = [probe_offset, capacity](size_t pos) {
325       return ((pos - probe_offset) & capacity) / Group::kWidth;
326     };
327 
328     // Element doesn't move.
329     if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) {
330       SetCtrlInLargeTable(common, i, h2, slot_size);
331       continue;
332     }
333 
334     void* new_slot_ptr = SlotAddress(slot_array, new_i, slot_size);
335     if (IsEmpty(ctrl[new_i])) {
336       // Transfer element to the empty spot.
337       // SetCtrl poisons/unpoisons the slots so we have to call it at the
338       // right time.
339       SetCtrlInLargeTable(common, new_i, h2, slot_size);
340       (*transfer)(set, new_slot_ptr, slot_ptr, 1);
341       SetCtrlInLargeTable(common, i, ctrl_t::kEmpty, slot_size);
342       // Initialize or change empty space id.
343       tmp_space_id = i;
344     } else {
345       assert(IsDeleted(ctrl[new_i]));
346       SetCtrlInLargeTable(common, new_i, h2, slot_size);
347       // Until we are done rehashing, DELETED marks previously FULL slots.
348 
349       if (tmp_space_id == kUnknownId) {
350         tmp_space_id = FindEmptySlot(i + 1, capacity, ctrl);
351       }
352       void* tmp_space = SlotAddress(slot_array, tmp_space_id, slot_size);
353       SanitizerUnpoisonMemoryRegion(tmp_space, slot_size);
354 
355       // Swap i and new_i elements.
356       (*transfer)(set, tmp_space, new_slot_ptr, 1);
357       (*transfer)(set, new_slot_ptr, slot_ptr, 1);
358       (*transfer)(set, slot_ptr, tmp_space, 1);
359 
360       SanitizerPoisonMemoryRegion(tmp_space, slot_size);
361 
362       // repeat the processing of the ith slot
363       --i;
364       slot_ptr = PrevSlot(slot_ptr, slot_size);
365     }
366   }
367   // Prepare insert for the new element.
368   PrepareInsertCommon(common);
369   ResetGrowthLeft(common);
370   FindInfo find_info = find_first_non_full(common, new_hash);
371   SetCtrlInLargeTable(common, find_info.offset, H2(new_hash), policy.slot_size);
372   common.infoz().RecordInsert(new_hash, find_info.probe_length);
373   common.infoz().RecordRehash(total_probe_length);
374   return find_info.offset;
375 }
376 
WasNeverFull(CommonFields & c,size_t index)377 static bool WasNeverFull(CommonFields& c, size_t index) {
378   if (is_single_group(c.capacity())) {
379     return true;
380   }
381   const size_t index_before = (index - Group::kWidth) & c.capacity();
382   const auto empty_after = Group(c.control() + index).MaskEmpty();
383   const auto empty_before = Group(c.control() + index_before).MaskEmpty();
384 
385   // We count how many consecutive non empties we have to the right and to the
386   // left of `it`. If the sum is >= kWidth then there is at least one probe
387   // window that might have seen a full group.
388   return empty_before && empty_after &&
389          static_cast<size_t>(empty_after.TrailingZeros()) +
390                  empty_before.LeadingZeros() <
391              Group::kWidth;
392 }
393 
394 // Initializes control bytes for single element table.
395 // Capacity of the table must be 1.
InitializeSingleElementControlBytes(uint64_t h2,ctrl_t * new_ctrl)396 ABSL_ATTRIBUTE_ALWAYS_INLINE inline void InitializeSingleElementControlBytes(
397     uint64_t h2, ctrl_t* new_ctrl) {
398   static constexpr uint64_t kEmptyXorSentinel =
399       static_cast<uint8_t>(ctrl_t::kEmpty) ^
400       static_cast<uint8_t>(ctrl_t::kSentinel);
401   static constexpr uint64_t kEmpty64 = static_cast<uint8_t>(ctrl_t::kEmpty);
402   // The first 8 bytes, where present slot positions are replaced with 0.
403   static constexpr uint64_t kFirstCtrlBytesWithZeroes =
404       k8EmptyBytes ^ kEmpty64 ^ (kEmptyXorSentinel << 8) ^ (kEmpty64 << 16);
405 
406   // Fill the original 0th and mirrored 2nd bytes with the hash.
407   // Result will look like:
408   // HSHEEEEE
409   // Where H = h2, E = kEmpty, S = kSentinel.
410   const uint64_t first_ctrl_bytes =
411       (h2 | kFirstCtrlBytesWithZeroes) | (h2 << 16);
412   // Fill last bytes with kEmpty.
413   std::memset(new_ctrl + 1, static_cast<int8_t>(ctrl_t::kEmpty), Group::kWidth);
414   // Overwrite the first 3 bytes with HSH. Other bytes will not be changed.
415   absl::little_endian::Store64(new_ctrl, first_ctrl_bytes);
416 }
417 
418 // Initializes control bytes after SOO to the next capacity.
419 // The table must be non-empty SOO.
420 ABSL_ATTRIBUTE_ALWAYS_INLINE inline void
InitializeThreeElementsControlBytesAfterSoo(size_t hash,ctrl_t * new_ctrl)421 InitializeThreeElementsControlBytesAfterSoo(size_t hash, ctrl_t* new_ctrl) {
422   static constexpr size_t kNewCapacity = NextCapacity(SooCapacity());
423   static_assert(kNewCapacity == 3);
424   static_assert(is_single_group(kNewCapacity));
425   static_assert(SooSlotIndex() == 1);
426 
427   static constexpr uint64_t kEmptyXorSentinel =
428       static_cast<uint8_t>(ctrl_t::kEmpty) ^
429       static_cast<uint8_t>(ctrl_t::kSentinel);
430   static constexpr uint64_t kEmpty64 = static_cast<uint8_t>(ctrl_t::kEmpty);
431   static constexpr size_t kMirroredSooSlotIndex =
432       SooSlotIndex() + kNewCapacity + 1;
433   // The first 8 bytes, where present slot positions are replaced with 0.
434   static constexpr uint64_t kFirstCtrlBytesWithZeroes =
435       k8EmptyBytes ^ (kEmpty64 << (8 * SooSlotIndex())) ^
436       (kEmptyXorSentinel << (8 * kNewCapacity)) ^
437       (kEmpty64 << (8 * kMirroredSooSlotIndex));
438 
439   const uint64_t h2 = static_cast<uint64_t>(H2(hash));
440   // Fill the original 0th and mirrored 2nd bytes with the hash.
441   // Result will look like:
442   // EHESEHEE
443   // Where H = h2, E = kEmpty, S = kSentinel.
444   const uint64_t first_ctrl_bytes =
445       ((h2 << (8 * SooSlotIndex())) | kFirstCtrlBytesWithZeroes) |
446       (h2 << (8 * kMirroredSooSlotIndex));
447 
448   // Fill last bytes with kEmpty.
449   std::memset(new_ctrl + kNewCapacity, static_cast<int8_t>(ctrl_t::kEmpty),
450               Group::kWidth);
451   // Overwrite the first 8 bytes with first_ctrl_bytes.
452   absl::little_endian::Store64(new_ctrl, first_ctrl_bytes);
453 
454   // Example for group size 16:
455   // new_ctrl after 1st memset =      ???EEEEEEEEEEEEEEEE
456   // new_ctrl after 2nd store  =      EHESEHEEEEEEEEEEEEE
457 
458   // Example for group size 8:
459   // new_ctrl after 1st memset =      ???EEEEEEEE
460   // new_ctrl after 2nd store  =      EHESEHEEEEE
461 }
462 
463 }  // namespace
464 
EraseMetaOnly(CommonFields & c,size_t index,size_t slot_size)465 void EraseMetaOnly(CommonFields& c, size_t index, size_t slot_size) {
466   assert(IsFull(c.control()[index]) && "erasing a dangling iterator");
467   c.decrement_size();
468   c.infoz().RecordErase();
469 
470   if (WasNeverFull(c, index)) {
471     SetCtrl(c, index, ctrl_t::kEmpty, slot_size);
472     c.growth_info().OverwriteFullAsEmpty();
473     return;
474   }
475 
476   c.growth_info().OverwriteFullAsDeleted();
477   SetCtrlInLargeTable(c, index, ctrl_t::kDeleted, slot_size);
478 }
479 
ClearBackingArray(CommonFields & c,const PolicyFunctions & policy,void * alloc,bool reuse,bool soo_enabled)480 void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
481                        void* alloc, bool reuse, bool soo_enabled) {
482   if (reuse) {
483     c.set_size_to_zero();
484     assert(!soo_enabled || c.capacity() > SooCapacity());
485     ResetCtrl(c, policy.slot_size);
486     ResetGrowthLeft(c);
487     c.infoz().RecordStorageChanged(0, c.capacity());
488   } else {
489     // We need to record infoz before calling dealloc, which will unregister
490     // infoz.
491     c.infoz().RecordClearedReservation();
492     c.infoz().RecordStorageChanged(0, soo_enabled ? SooCapacity() : 0);
493     c.infoz().Unregister();
494     (*policy.dealloc)(alloc, c.capacity(), c.control(), policy.slot_size,
495                       policy.slot_align, c.has_infoz());
496     c = soo_enabled ? CommonFields{soo_tag_t{}} : CommonFields{non_soo_tag_t{}};
497   }
498 }
499 
500 namespace {
501 
502 // Poisons empty slots. It is useful when slots are transferred via memcpy.
503 // PRECONDITIONs: common.control() is fully initialized.
PoisonEmptySlots(CommonFields & c,size_t slot_size)504 void PoisonEmptySlots(CommonFields& c, size_t slot_size) {
505   for (size_t i = 0; i < c.capacity(); ++i) {
506     if (!IsFull(c.control()[i])) {
507       SanitizerPoisonMemoryRegion(SlotAddress(c.slot_array(), i, slot_size),
508                                   slot_size);
509     }
510   }
511 }
512 
513 enum class ResizeNonSooMode {
514   kGuaranteedEmpty,
515   kGuaranteedAllocated,
516 };
517 
518 template <ResizeNonSooMode kMode>
ResizeNonSooImpl(CommonFields & common,const PolicyFunctions & policy,size_t new_capacity,HashtablezInfoHandle infoz)519 void ResizeNonSooImpl(CommonFields& common, const PolicyFunctions& policy,
520                       size_t new_capacity, HashtablezInfoHandle infoz) {
521   assert(IsValidCapacity(new_capacity));
522   assert(new_capacity > policy.soo_capacity);
523 
524   const size_t old_capacity = common.capacity();
525   [[maybe_unused]] ctrl_t* old_ctrl = common.control();
526   [[maybe_unused]] void* old_slots = common.slot_array();
527 
528   const size_t slot_size = policy.slot_size;
529   const size_t slot_align = policy.slot_align;
530   const bool has_infoz = infoz.IsSampled();
531 
532   common.set_capacity(new_capacity);
533   RawHashSetLayout layout(new_capacity, slot_size, slot_align, has_infoz);
534   void* alloc = policy.get_char_alloc(common);
535   char* mem = static_cast<char*>(policy.alloc(alloc, layout.alloc_size()));
536   const GenerationType old_generation = common.generation();
537   common.set_generation_ptr(
538       reinterpret_cast<GenerationType*>(mem + layout.generation_offset()));
539   common.set_generation(NextGeneration(old_generation));
540 
541   common.set_control</*kGenerateSeed=*/true>(
542       reinterpret_cast<ctrl_t*>(mem + layout.control_offset()));
543   common.set_slots(mem + layout.slot_offset());
544 
545   size_t total_probe_length = 0;
546   ResetCtrl(common, slot_size);
547   assert(kMode != ResizeNonSooMode::kGuaranteedEmpty ||
548          old_capacity == policy.soo_capacity);
549   assert(kMode != ResizeNonSooMode::kGuaranteedAllocated || old_capacity > 0);
550   if constexpr (kMode == ResizeNonSooMode::kGuaranteedAllocated) {
551     total_probe_length = policy.find_new_positions_and_transfer_slots(
552         common, old_ctrl, old_slots, old_capacity);
553     (*policy.dealloc)(alloc, old_capacity, old_ctrl, slot_size, slot_align,
554                       has_infoz);
555   }
556 
557   ResetGrowthLeft(common);
558   if (has_infoz) {
559     common.set_has_infoz();
560     infoz.RecordStorageChanged(common.size(), new_capacity);
561     infoz.RecordRehash(total_probe_length);
562     common.set_infoz(infoz);
563   }
564 }
565 
ResizeEmptyNonAllocatedTableImpl(CommonFields & common,const PolicyFunctions & policy,size_t new_capacity,bool force_infoz)566 void ResizeEmptyNonAllocatedTableImpl(CommonFields& common,
567                                       const PolicyFunctions& policy,
568                                       size_t new_capacity, bool force_infoz) {
569   assert(IsValidCapacity(new_capacity));
570   assert(new_capacity > policy.soo_capacity);
571   assert(!force_infoz || policy.soo_capacity > 0);
572   assert(common.capacity() <= policy.soo_capacity);
573   assert(common.empty());
574   const size_t slot_size = policy.slot_size;
575   HashtablezInfoHandle infoz;
576   const bool should_sample =
577       policy.is_hashtablez_eligible && (force_infoz || ShouldSampleNextTable());
578   if (ABSL_PREDICT_FALSE(should_sample)) {
579     infoz = ForcedTrySample(slot_size, policy.key_size, policy.value_size,
580                             policy.soo_capacity);
581   }
582   ResizeNonSooImpl<ResizeNonSooMode::kGuaranteedEmpty>(common, policy,
583                                                        new_capacity, infoz);
584 }
585 
586 // If the table was SOO, initializes new control bytes and transfers slot.
587 // After transferring the slot, sets control and slots in CommonFields.
588 // It is rare to resize an SOO table with one element to a large size.
589 // Requires: `c` contains SOO data.
InsertOldSooSlotAndInitializeControlBytes(CommonFields & c,const PolicyFunctions & policy,size_t hash,ctrl_t * new_ctrl,void * new_slots)590 void InsertOldSooSlotAndInitializeControlBytes(CommonFields& c,
591                                                const PolicyFunctions& policy,
592                                                size_t hash, ctrl_t* new_ctrl,
593                                                void* new_slots) {
594   assert(c.size() == policy.soo_capacity);
595   assert(policy.soo_capacity == SooCapacity());
596   size_t new_capacity = c.capacity();
597 
598   c.generate_new_seed();
599   size_t offset = probe(c.seed(), new_capacity, hash).offset();
600   offset = offset == new_capacity ? 0 : offset;
601   SanitizerPoisonMemoryRegion(new_slots, policy.slot_size * new_capacity);
602   void* target_slot = SlotAddress(new_slots, offset, policy.slot_size);
603   SanitizerUnpoisonMemoryRegion(target_slot, policy.slot_size);
604   policy.transfer(&c, target_slot, c.soo_data(), 1);
605   c.set_control</*kGenerateSeed=*/false>(new_ctrl);
606   c.set_slots(new_slots);
607   ResetCtrl(c, policy.slot_size);
608   SetCtrl(c, offset, H2(hash), policy.slot_size);
609 }
610 
611 enum class ResizeFullSooTableSamplingMode {
612   kNoSampling,
613   // Force sampling. If the table was still not sampled, do not resize.
614   kForceSampleNoResizeIfUnsampled,
615 };
616 
ResizeFullSooTable(CommonFields & common,const PolicyFunctions & policy,size_t new_capacity,ResizeFullSooTableSamplingMode sampling_mode)617 void ResizeFullSooTable(CommonFields& common, const PolicyFunctions& policy,
618                         size_t new_capacity,
619                         ResizeFullSooTableSamplingMode sampling_mode) {
620   assert(common.capacity() == policy.soo_capacity);
621   assert(common.size() == policy.soo_capacity);
622   assert(policy.soo_capacity == SooCapacity());
623   const size_t slot_size = policy.slot_size;
624   const size_t slot_align = policy.slot_align;
625 
626   HashtablezInfoHandle infoz;
627   if (sampling_mode ==
628       ResizeFullSooTableSamplingMode::kForceSampleNoResizeIfUnsampled) {
629     if (ABSL_PREDICT_FALSE(policy.is_hashtablez_eligible)) {
630       infoz = ForcedTrySample(slot_size, policy.key_size, policy.value_size,
631                               policy.soo_capacity);
632     }
633 
634     if (!infoz.IsSampled()) {
635       return;
636     }
637   }
638 
639   const bool has_infoz = infoz.IsSampled();
640 
641   common.set_capacity(new_capacity);
642 
643   RawHashSetLayout layout(new_capacity, slot_size, slot_align, has_infoz);
644   void* alloc = policy.get_char_alloc(common);
645   char* mem = static_cast<char*>(policy.alloc(alloc, layout.alloc_size()));
646   const GenerationType old_generation = common.generation();
647   common.set_generation_ptr(
648       reinterpret_cast<GenerationType*>(mem + layout.generation_offset()));
649   common.set_generation(NextGeneration(old_generation));
650 
651   // We do not set control and slots in CommonFields yet to avoid overriding
652   // SOO data.
653   ctrl_t* new_ctrl = reinterpret_cast<ctrl_t*>(mem + layout.control_offset());
654   void* new_slots = mem + layout.slot_offset();
655 
656   const size_t soo_slot_hash =
657       policy.hash_slot(policy.hash_fn(common), common.soo_data());
658 
659   InsertOldSooSlotAndInitializeControlBytes(common, policy, soo_slot_hash,
660                                             new_ctrl, new_slots);
661   ResetGrowthLeft(common);
662   if (has_infoz) {
663     common.set_has_infoz();
664     common.set_infoz(infoz);
665   }
666 }
667 
GrowIntoSingleGroupShuffleControlBytes(ctrl_t * __restrict old_ctrl,size_t old_capacity,ctrl_t * __restrict new_ctrl,size_t new_capacity)668 void GrowIntoSingleGroupShuffleControlBytes(ctrl_t* __restrict old_ctrl,
669                                             size_t old_capacity,
670                                             ctrl_t* __restrict new_ctrl,
671                                             size_t new_capacity) {
672   assert(is_single_group(new_capacity));
673   constexpr size_t kHalfWidth = Group::kWidth / 2;
674   ABSL_ASSUME(old_capacity < kHalfWidth);
675   ABSL_ASSUME(old_capacity > 0);
676   static_assert(Group::kWidth == 8 || Group::kWidth == 16,
677                 "Group size is not supported.");
678 
679   // NOTE: operations are done with compile time known size = 8.
680   // Compiler optimizes that into single ASM operation.
681 
682   // Load the bytes from old_capacity. This contains
683   // - the sentinel byte
684   // - all the old control bytes
685   // - the rest is filled with kEmpty bytes
686   // Example:
687   // old_ctrl =     012S012EEEEEEEEE...
688   // copied_bytes = S012EEEE
689   uint64_t copied_bytes = absl::little_endian::Load64(old_ctrl + old_capacity);
690 
691   // We change the sentinel byte to kEmpty before storing to both the start of
692   // the new_ctrl, and past the end of the new_ctrl later for the new cloned
693   // bytes. Note that this is faster than setting the sentinel byte to kEmpty
694   // after the copy directly in new_ctrl because we are limited on store
695   // bandwidth.
696   static constexpr uint64_t kEmptyXorSentinel =
697       static_cast<uint8_t>(ctrl_t::kEmpty) ^
698       static_cast<uint8_t>(ctrl_t::kSentinel);
699 
700   // Replace the first byte kSentinel with kEmpty.
701   // Resulting bytes will be shifted by one byte old control blocks.
702   // Example:
703   // old_ctrl = 012S012EEEEEEEEE...
704   // before =   S012EEEE
705   // after  =   E012EEEE
706   copied_bytes ^= kEmptyXorSentinel;
707 
708   if (Group::kWidth == 8) {
709     // With group size 8, we can grow with two write operations.
710     assert(old_capacity < 8 && "old_capacity is too large for group size 8");
711     absl::little_endian::Store64(new_ctrl, copied_bytes);
712 
713     static constexpr uint64_t kSentinal64 =
714         static_cast<uint8_t>(ctrl_t::kSentinel);
715 
716     // Prepend kSentinel byte to the beginning of copied_bytes.
717     // We have maximum 3 non-empty bytes at the beginning of copied_bytes for
718     // group size 8.
719     // Example:
720     // old_ctrl = 012S012EEEE
721     // before =   E012EEEE
722     // after  =   SE012EEE
723     copied_bytes = (copied_bytes << 8) ^ kSentinal64;
724     absl::little_endian::Store64(new_ctrl + new_capacity, copied_bytes);
725     // Example for capacity 3:
726     // old_ctrl = 012S012EEEE
727     // After the first store:
728     //           >!
729     // new_ctrl = E012EEEE???????
730     // After the second store:
731     //                  >!
732     // new_ctrl = E012EEESE012EEE
733     return;
734   }
735 
736   assert(Group::kWidth == 16);
737 
738   // Fill the second half of the main control bytes with kEmpty.
739   // For small capacity that may write into mirrored control bytes.
740   // It is fine as we will overwrite all the bytes later.
741   std::memset(new_ctrl + kHalfWidth, static_cast<int8_t>(ctrl_t::kEmpty),
742               kHalfWidth);
743   // Fill the second half of the mirrored control bytes with kEmpty.
744   std::memset(new_ctrl + new_capacity + kHalfWidth,
745               static_cast<int8_t>(ctrl_t::kEmpty), kHalfWidth);
746   // Copy the first half of the non-mirrored control bytes.
747   absl::little_endian::Store64(new_ctrl, copied_bytes);
748   new_ctrl[new_capacity] = ctrl_t::kSentinel;
749   // Copy the first half of the mirrored control bytes.
750   absl::little_endian::Store64(new_ctrl + new_capacity + 1, copied_bytes);
751 
752   // Example for growth capacity 1->3:
753   // old_ctrl =                  0S0EEEEEEEEEEEEEE
754   // new_ctrl at the end =       E0ESE0EEEEEEEEEEEEE
755   //                                    >!
756   // new_ctrl after 1st memset = ????????EEEEEEEE???
757   //                                       >!
758   // new_ctrl after 2nd memset = ????????EEEEEEEEEEE
759   //                            >!
760   // new_ctrl after 1st store =  E0EEEEEEEEEEEEEEEEE
761   // new_ctrl after kSentinel =  E0ESEEEEEEEEEEEEEEE
762   //                                >!
763   // new_ctrl after 2nd store =  E0ESE0EEEEEEEEEEEEE
764 
765   // Example for growth capacity 3->7:
766   // old_ctrl =                  012S012EEEEEEEEEEEE
767   // new_ctrl at the end =       E012EEESE012EEEEEEEEEEE
768   //                                    >!
769   // new_ctrl after 1st memset = ????????EEEEEEEE???????
770   //                                           >!
771   // new_ctrl after 2nd memset = ????????EEEEEEEEEEEEEEE
772   //                            >!
773   // new_ctrl after 1st store =  E012EEEEEEEEEEEEEEEEEEE
774   // new_ctrl after kSentinel =  E012EEESEEEEEEEEEEEEEEE
775   //                                >!
776   // new_ctrl after 2nd store =  E012EEESE012EEEEEEEEEEE
777 
778   // Example for growth capacity 7->15:
779   // old_ctrl =                  0123456S0123456EEEEEEEE
780   // new_ctrl at the end =       E0123456EEEEEEESE0123456EEEEEEE
781   //                                    >!
782   // new_ctrl after 1st memset = ????????EEEEEEEE???????????????
783   //                                                   >!
784   // new_ctrl after 2nd memset = ????????EEEEEEEE???????EEEEEEEE
785   //                            >!
786   // new_ctrl after 1st store =  E0123456EEEEEEEE???????EEEEEEEE
787   // new_ctrl after kSentinel =  E0123456EEEEEEES???????EEEEEEEE
788   //                                            >!
789   // new_ctrl after 2nd store =  E0123456EEEEEEESE0123456EEEEEEE
790 }
791 
GrowToNextCapacityAndPrepareInsert(CommonFields & common,const PolicyFunctions & policy,size_t new_hash)792 size_t GrowToNextCapacityAndPrepareInsert(CommonFields& common,
793                                           const PolicyFunctions& policy,
794                                           size_t new_hash) {
795   assert(common.growth_left() == 0);
796   const size_t old_capacity = common.capacity();
797   assert(old_capacity == 0 || old_capacity > policy.soo_capacity);
798 
799   const size_t new_capacity = NextCapacity(old_capacity);
800   assert(IsValidCapacity(new_capacity));
801   assert(new_capacity > policy.soo_capacity);
802 
803   ctrl_t* old_ctrl = common.control();
804   void* old_slots = common.slot_array();
805 
806   common.set_capacity(new_capacity);
807   const size_t slot_size = policy.slot_size;
808   const size_t slot_align = policy.slot_align;
809   HashtablezInfoHandle infoz;
810   if (old_capacity > 0) {
811     infoz = common.infoz();
812   } else {
813     const bool should_sample =
814         policy.is_hashtablez_eligible && ShouldSampleNextTable();
815     if (ABSL_PREDICT_FALSE(should_sample)) {
816       infoz = ForcedTrySample(slot_size, policy.key_size, policy.value_size,
817                               policy.soo_capacity);
818     }
819   }
820   const bool has_infoz = infoz.IsSampled();
821 
822   RawHashSetLayout layout(new_capacity, slot_size, slot_align, has_infoz);
823   void* alloc = policy.get_char_alloc(common);
824   char* mem = static_cast<char*>(policy.alloc(alloc, layout.alloc_size()));
825   const GenerationType old_generation = common.generation();
826   common.set_generation_ptr(
827       reinterpret_cast<GenerationType*>(mem + layout.generation_offset()));
828   common.set_generation(NextGeneration(old_generation));
829 
830   ctrl_t* new_ctrl = reinterpret_cast<ctrl_t*>(mem + layout.control_offset());
831   void* new_slots = mem + layout.slot_offset();
832   common.set_control</*kGenerateSeed=*/false>(new_ctrl);
833   common.set_slots(new_slots);
834 
835   h2_t new_h2 = H2(new_hash);
836   size_t total_probe_length = 0;
837   FindInfo find_info;
838   if (old_capacity == 0) {
839     static_assert(NextCapacity(0) == 1);
840     InitializeSingleElementControlBytes(new_h2, new_ctrl);
841     common.generate_new_seed();
842     find_info = FindInfo{0, 0};
843   } else {
844     if (is_single_group(new_capacity)) {
845       GrowIntoSingleGroupShuffleControlBytes(old_ctrl, old_capacity, new_ctrl,
846                                              new_capacity);
847       // Single group tables have no deleted slots, so we can transfer all slots
848       // without checking the control bytes.
849       assert(common.size() == old_capacity);
850       policy.transfer(&common, NextSlot(new_slots, slot_size), old_slots,
851                       old_capacity);
852       PoisonEmptySlots(common, slot_size);
853       // We put the new element either at the beginning or at the end of the
854       // table with approximately equal probability.
855       size_t offset = SingleGroupTableH1(new_hash, common.seed()) & 1
856                           ? 0
857                           : new_capacity - 1;
858 
859       assert(IsEmpty(new_ctrl[offset]));
860       SetCtrlInSingleGroupTable(common, offset, new_h2, policy.slot_size);
861       find_info = FindInfo{offset, 0};
862     } else {
863       ResetCtrl(common, slot_size);
864       total_probe_length = policy.find_new_positions_and_transfer_slots(
865           common, old_ctrl, old_slots, old_capacity);
866       find_info = find_first_non_full(common, new_hash);
867       SetCtrlInLargeTable(common, find_info.offset, new_h2, policy.slot_size);
868     }
869     assert(old_capacity > policy.soo_capacity);
870     (*policy.dealloc)(alloc, old_capacity, old_ctrl, slot_size, slot_align,
871                       has_infoz);
872   }
873   PrepareInsertCommon(common);
874   ResetGrowthLeft(common);
875 
876   if (has_infoz) {
877     common.set_has_infoz();
878     infoz.RecordStorageChanged(common.size() - 1, new_capacity);
879     infoz.RecordRehash(total_probe_length);
880     infoz.RecordInsert(new_hash, find_info.probe_length);
881     common.set_infoz(infoz);
882   }
883   return find_info.offset;
884 }
885 
886 // Called whenever the table needs to vacate empty slots either by removing
887 // tombstones via rehash or growth to next capacity.
888 ABSL_ATTRIBUTE_NOINLINE
RehashOrGrowToNextCapacityAndPrepareInsert(CommonFields & common,const PolicyFunctions & policy,size_t new_hash)889 size_t RehashOrGrowToNextCapacityAndPrepareInsert(CommonFields& common,
890                                                   const PolicyFunctions& policy,
891                                                   size_t new_hash) {
892   const size_t cap = common.capacity();
893   ABSL_ASSUME(cap > 0);
894   if (cap > Group::kWidth &&
895       // Do these calculations in 64-bit to avoid overflow.
896       common.size() * uint64_t{32} <= cap * uint64_t{25}) {
897     // Squash DELETED without growing if there is enough capacity.
898     //
899     // Rehash in place if the current size is <= 25/32 of capacity.
900     // Rationale for such a high factor: 1) DropDeletesWithoutResize() is
901     // faster than resize, and 2) it takes quite a bit of work to add
902     // tombstones.  In the worst case, seems to take approximately 4
903     // insert/erase pairs to create a single tombstone and so if we are
904     // rehashing because of tombstones, we can afford to rehash-in-place as
905     // long as we are reclaiming at least 1/8 the capacity without doing more
906     // than 2X the work.  (Where "work" is defined to be size() for rehashing
907     // or rehashing in place, and 1 for an insert or erase.)  But rehashing in
908     // place is faster per operation than inserting or even doubling the size
909     // of the table, so we actually afford to reclaim even less space from a
910     // resize-in-place.  The decision is to rehash in place if we can reclaim
911     // at about 1/8th of the usable capacity (specifically 3/28 of the
912     // capacity) which means that the total cost of rehashing will be a small
913     // fraction of the total work.
914     //
915     // Here is output of an experiment using the BM_CacheInSteadyState
916     // benchmark running the old case (where we rehash-in-place only if we can
917     // reclaim at least 7/16*capacity) vs. this code (which rehashes in place
918     // if we can recover 3/32*capacity).
919     //
920     // Note that although in the worst-case number of rehashes jumped up from
921     // 15 to 190, but the number of operations per second is almost the same.
922     //
923     // Abridged output of running BM_CacheInSteadyState benchmark from
924     // raw_hash_set_benchmark.   N is the number of insert/erase operations.
925     //
926     //      | OLD (recover >= 7/16        | NEW (recover >= 3/32)
927     // size |    N/s LoadFactor NRehashes |    N/s LoadFactor NRehashes
928     //  448 | 145284       0.44        18 | 140118       0.44        19
929     //  493 | 152546       0.24        11 | 151417       0.48        28
930     //  538 | 151439       0.26        11 | 151152       0.53        38
931     //  583 | 151765       0.28        11 | 150572       0.57        50
932     //  628 | 150241       0.31        11 | 150853       0.61        66
933     //  672 | 149602       0.33        12 | 150110       0.66        90
934     //  717 | 149998       0.35        12 | 149531       0.70       129
935     //  762 | 149836       0.37        13 | 148559       0.74       190
936     //  807 | 149736       0.39        14 | 151107       0.39        14
937     //  852 | 150204       0.42        15 | 151019       0.42        15
938     return DropDeletesWithoutResizeAndPrepareInsert(common, policy, new_hash);
939   } else {
940     // Otherwise grow the container.
941     return GrowToNextCapacityAndPrepareInsert(common, policy, new_hash);
942   }
943 }
944 
945 // Slow path for PrepareInsertNonSoo that is called when the table has deleted
946 // slots or need to be resized or rehashed.
PrepareInsertNonSooSlow(CommonFields & common,const PolicyFunctions & policy,size_t hash)947 size_t PrepareInsertNonSooSlow(CommonFields& common,
948                                const PolicyFunctions& policy, size_t hash) {
949   const GrowthInfo growth_info = common.growth_info();
950   assert(!growth_info.HasNoDeletedAndGrowthLeft());
951   if (ABSL_PREDICT_TRUE(growth_info.HasNoGrowthLeftAndNoDeleted())) {
952     // Table without deleted slots (>95% cases) that needs to be resized.
953     assert(growth_info.HasNoDeleted() && growth_info.GetGrowthLeft() == 0);
954     return GrowToNextCapacityAndPrepareInsert(common, policy, hash);
955   }
956   if (ABSL_PREDICT_FALSE(growth_info.HasNoGrowthLeftAssumingMayHaveDeleted())) {
957     // Table with deleted slots that needs to be rehashed or resized.
958     return RehashOrGrowToNextCapacityAndPrepareInsert(common, policy, hash);
959   }
960   // Table with deleted slots that has space for the inserting element.
961   FindInfo target = find_first_non_full(common, hash);
962   PrepareInsertCommon(common);
963   common.growth_info().OverwriteControlAsFull(common.control()[target.offset]);
964   SetCtrlInLargeTable(common, target.offset, H2(hash), policy.slot_size);
965   common.infoz().RecordInsert(hash, target.probe_length);
966   return target.offset;
967 }
968 
969 }  // namespace
970 
GetRefForEmptyClass(CommonFields & common)971 void* GetRefForEmptyClass(CommonFields& common) {
972   // Empty base optimization typically make the empty base class address to be
973   // the same as the first address of the derived class object.
974   // But we generally assume that for empty classes we can return any valid
975   // pointer.
976   return &common;
977 }
978 
ResizeAllocatedTableWithSeedChange(CommonFields & common,const PolicyFunctions & policy,size_t new_capacity)979 void ResizeAllocatedTableWithSeedChange(CommonFields& common,
980                                         const PolicyFunctions& policy,
981                                         size_t new_capacity) {
982   ResizeNonSooImpl<ResizeNonSooMode::kGuaranteedAllocated>(
983       common, policy, new_capacity, common.infoz());
984 }
985 
ReserveEmptyNonAllocatedTableToFitNewSize(CommonFields & common,const PolicyFunctions & policy,size_t new_size)986 void ReserveEmptyNonAllocatedTableToFitNewSize(CommonFields& common,
987                                                const PolicyFunctions& policy,
988                                                size_t new_size) {
989   ValidateMaxSize(new_size, policy.slot_size);
990   ResizeEmptyNonAllocatedTableImpl(
991       common, policy, NormalizeCapacity(GrowthToLowerboundCapacity(new_size)),
992       /*force_infoz=*/false);
993   // This is after resize, to ensure that we have completed the allocation
994   // and have potentially sampled the hashtable.
995   common.infoz().RecordReservation(new_size);
996   common.reset_reserved_growth(new_size);
997   common.set_reservation_size(new_size);
998 }
999 
ReserveEmptyNonAllocatedTableToFitBucketCount(CommonFields & common,const PolicyFunctions & policy,size_t bucket_count)1000 void ReserveEmptyNonAllocatedTableToFitBucketCount(
1001     CommonFields& common, const PolicyFunctions& policy, size_t bucket_count) {
1002   size_t new_capacity = NormalizeCapacity(bucket_count);
1003   ValidateMaxSize(CapacityToGrowth(new_capacity), policy.slot_size);
1004   ResizeEmptyNonAllocatedTableImpl(common, policy, new_capacity,
1005                                    /*force_infoz=*/false);
1006 }
1007 
GrowEmptySooTableToNextCapacityForceSampling(CommonFields & common,const PolicyFunctions & policy)1008 void GrowEmptySooTableToNextCapacityForceSampling(
1009     CommonFields& common, const PolicyFunctions& policy) {
1010   ResizeEmptyNonAllocatedTableImpl(common, policy, NextCapacity(SooCapacity()),
1011                                    /*force_infoz=*/true);
1012 }
1013 
1014 // Resizes a full SOO table to the NextCapacity(SooCapacity()).
1015 template <size_t SooSlotMemcpySize, bool TransferUsesMemcpy>
GrowFullSooTableToNextCapacity(CommonFields & common,const PolicyFunctions & policy,size_t soo_slot_hash)1016 void GrowFullSooTableToNextCapacity(CommonFields& common,
1017                                     const PolicyFunctions& policy,
1018                                     size_t soo_slot_hash) {
1019   assert(common.capacity() == policy.soo_capacity);
1020   assert(common.size() == policy.soo_capacity);
1021   static constexpr size_t kNewCapacity = NextCapacity(SooCapacity());
1022   assert(kNewCapacity > policy.soo_capacity);
1023   assert(policy.soo_capacity == SooCapacity());
1024   const size_t slot_size = policy.slot_size;
1025   const size_t slot_align = policy.slot_align;
1026   common.set_capacity(kNewCapacity);
1027 
1028   // Since the table is not empty, it will not be sampled.
1029   // The decision to sample was already made during the first insertion.
1030   RawHashSetLayout layout(kNewCapacity, slot_size, slot_align,
1031                           /*has_infoz=*/false);
1032   void* alloc = policy.get_char_alloc(common);
1033   char* mem = static_cast<char*>(policy.alloc(alloc, layout.alloc_size()));
1034   const GenerationType old_generation = common.generation();
1035   common.set_generation_ptr(
1036       reinterpret_cast<GenerationType*>(mem + layout.generation_offset()));
1037   common.set_generation(NextGeneration(old_generation));
1038 
1039   // We do not set control and slots in CommonFields yet to avoid overriding
1040   // SOO data.
1041   ctrl_t* new_ctrl = reinterpret_cast<ctrl_t*>(mem + layout.control_offset());
1042   void* new_slots = mem + layout.slot_offset();
1043 
1044   InitializeThreeElementsControlBytesAfterSoo(soo_slot_hash, new_ctrl);
1045 
1046   SanitizerPoisonMemoryRegion(new_slots, slot_size * kNewCapacity);
1047   void* target_slot = SlotAddress(new_slots, SooSlotIndex(), slot_size);
1048   SanitizerUnpoisonMemoryRegion(target_slot, slot_size);
1049   if constexpr (TransferUsesMemcpy) {
1050     // Target slot is placed at index 1, but capacity is at
1051     // minimum 3. So we are allowed to copy at least twice as much
1052     // memory.
1053     static_assert(SooSlotIndex() == 1);
1054     static_assert(SooSlotMemcpySize > 0);
1055     static_assert(SooSlotMemcpySize <= MaxSooSlotSize());
1056     assert(SooSlotMemcpySize <= 2 * slot_size);
1057     assert(SooSlotMemcpySize >= slot_size);
1058     void* next_slot = SlotAddress(target_slot, 1, slot_size);
1059     SanitizerUnpoisonMemoryRegion(next_slot, SooSlotMemcpySize - slot_size);
1060     std::memcpy(target_slot, common.soo_data(), SooSlotMemcpySize);
1061     SanitizerPoisonMemoryRegion(next_slot, SooSlotMemcpySize - slot_size);
1062   } else {
1063     static_assert(SooSlotMemcpySize == 0);
1064     policy.transfer(&common, target_slot, common.soo_data(), 1);
1065   }
1066   common.set_control</*kGenerateSeed=*/true>(new_ctrl);
1067   common.set_slots(new_slots);
1068 
1069   ResetGrowthLeft(common);
1070 }
1071 
GrowFullSooTableToNextCapacityForceSampling(CommonFields & common,const PolicyFunctions & policy)1072 void GrowFullSooTableToNextCapacityForceSampling(
1073     CommonFields& common, const PolicyFunctions& policy) {
1074   assert(common.capacity() == policy.soo_capacity);
1075   assert(common.size() == policy.soo_capacity);
1076   assert(policy.soo_capacity == SooCapacity());
1077   ResizeFullSooTable(
1078       common, policy, NextCapacity(SooCapacity()),
1079       ResizeFullSooTableSamplingMode::kForceSampleNoResizeIfUnsampled);
1080 }
1081 
Rehash(CommonFields & common,const PolicyFunctions & policy,size_t n)1082 void Rehash(CommonFields& common, const PolicyFunctions& policy, size_t n) {
1083   const size_t cap = common.capacity();
1084 
1085   auto clear_backing_array = [&]() {
1086     ClearBackingArray(common, policy, policy.get_char_alloc(common),
1087                       /*reuse=*/false, policy.soo_capacity > 0);
1088   };
1089 
1090   const size_t slot_size = policy.slot_size;
1091 
1092   if (n == 0) {
1093     if (cap <= policy.soo_capacity) return;
1094     if (common.empty()) {
1095       clear_backing_array();
1096       return;
1097     }
1098     if (common.size() <= policy.soo_capacity) {
1099       // When the table is already sampled, we keep it sampled.
1100       if (common.infoz().IsSampled()) {
1101         static constexpr size_t kInitialSampledCapacity =
1102             NextCapacity(SooCapacity());
1103         if (cap > kInitialSampledCapacity) {
1104           ResizeAllocatedTableWithSeedChange(common, policy,
1105                                              kInitialSampledCapacity);
1106         }
1107         // This asserts that we didn't lose sampling coverage in `resize`.
1108         assert(common.infoz().IsSampled());
1109         return;
1110       }
1111       assert(slot_size <= sizeof(HeapOrSoo));
1112       assert(policy.slot_align <= alignof(HeapOrSoo));
1113       HeapOrSoo tmp_slot(uninitialized_tag_t{});
1114       size_t begin_offset = FindFirstFullSlot(0, cap, common.control());
1115       policy.transfer(&common, &tmp_slot,
1116                       SlotAddress(common.slot_array(), begin_offset, slot_size),
1117                       1);
1118       clear_backing_array();
1119       policy.transfer(&common, common.soo_data(), &tmp_slot, 1);
1120       common.set_full_soo();
1121       return;
1122     }
1123   }
1124 
1125   // bitor is a faster way of doing `max` here. We will round up to the next
1126   // power-of-2-minus-1, so bitor is good enough.
1127   size_t new_size = n | GrowthToLowerboundCapacity(common.size());
1128   ValidateMaxSize(n, policy.slot_size);
1129   const size_t new_capacity = NormalizeCapacity(new_size);
1130   // n == 0 unconditionally rehashes as per the standard.
1131   if (n == 0 || new_capacity > cap) {
1132     if (cap == policy.soo_capacity) {
1133       if (common.empty()) {
1134         ResizeEmptyNonAllocatedTableImpl(common, policy, new_capacity,
1135                                          /*force_infoz=*/false);
1136       } else {
1137         ResizeFullSooTable(common, policy, new_capacity,
1138                            ResizeFullSooTableSamplingMode::kNoSampling);
1139       }
1140     } else {
1141       ResizeAllocatedTableWithSeedChange(common, policy, new_capacity);
1142     }
1143     // This is after resize, to ensure that we have completed the allocation
1144     // and have potentially sampled the hashtable.
1145     common.infoz().RecordReservation(n);
1146   }
1147 }
1148 
ReserveAllocatedTable(CommonFields & common,const PolicyFunctions & policy,size_t n)1149 void ReserveAllocatedTable(CommonFields& common, const PolicyFunctions& policy,
1150                            size_t n) {
1151   common.reset_reserved_growth(n);
1152   common.set_reservation_size(n);
1153 
1154   const size_t cap = common.capacity();
1155   assert(!common.empty() || cap > policy.soo_capacity);
1156   assert(cap > 0);
1157   const size_t max_size_before_growth =
1158       cap <= policy.soo_capacity ? policy.soo_capacity
1159                                  : common.size() + common.growth_left();
1160   if (n <= max_size_before_growth) {
1161     return;
1162   }
1163   ValidateMaxSize(n, policy.slot_size);
1164   const size_t new_capacity = NormalizeCapacity(GrowthToLowerboundCapacity(n));
1165   if (cap == policy.soo_capacity) {
1166     assert(!common.empty());
1167     ResizeFullSooTable(common, policy, new_capacity,
1168                        ResizeFullSooTableSamplingMode::kNoSampling);
1169   } else {
1170     assert(cap > policy.soo_capacity);
1171     ResizeAllocatedTableWithSeedChange(common, policy, new_capacity);
1172   }
1173   common.infoz().RecordReservation(n);
1174 }
1175 
PrepareInsertNonSoo(CommonFields & common,const PolicyFunctions & policy,size_t hash,FindInfo target)1176 size_t PrepareInsertNonSoo(CommonFields& common, const PolicyFunctions& policy,
1177                            size_t hash, FindInfo target) {
1178   const bool rehash_for_bug_detection =
1179       common.should_rehash_for_bug_detection_on_insert() &&
1180       // Required to allow use of ResizeAllocatedTable.
1181       common.capacity() > 0;
1182   if (rehash_for_bug_detection) {
1183     // Move to a different heap allocation in order to detect bugs.
1184     const size_t cap = common.capacity();
1185     ResizeAllocatedTableWithSeedChange(
1186         common, policy, common.growth_left() > 0 ? cap : NextCapacity(cap));
1187     target = find_first_non_full(common, hash);
1188   }
1189 
1190   const GrowthInfo growth_info = common.growth_info();
1191   // When there are no deleted slots in the table
1192   // and growth_left is positive, we can insert at the first
1193   // empty slot in the probe sequence (target).
1194   if (ABSL_PREDICT_FALSE(!growth_info.HasNoDeletedAndGrowthLeft())) {
1195     return PrepareInsertNonSooSlow(common, policy, hash);
1196   }
1197   PrepareInsertCommon(common);
1198   common.growth_info().OverwriteEmptyAsFull();
1199   SetCtrl(common, target.offset, H2(hash), policy.slot_size);
1200   common.infoz().RecordInsert(hash, target.probe_length);
1201   return target.offset;
1202 }
1203 
1204 namespace {
1205 // Returns true if the following is true
1206 // 1. OptimalMemcpySizeForSooSlotTransfer(left) >
1207 //    OptimalMemcpySizeForSooSlotTransfer(left - 1)
1208 // 2. OptimalMemcpySizeForSooSlotTransfer(left) are equal for all i in [left,
1209 // right].
1210 // This function is used to verify that we have all the possible template
1211 // instantiations for GrowFullSooTableToNextCapacity.
1212 // With this verification the problem may be detected at compile time instead of
1213 // link time.
VerifyOptimalMemcpySizeForSooSlotTransferRange(size_t left,size_t right)1214 constexpr bool VerifyOptimalMemcpySizeForSooSlotTransferRange(size_t left,
1215                                                               size_t right) {
1216   size_t optimal_size_for_range = OptimalMemcpySizeForSooSlotTransfer(left);
1217   if (optimal_size_for_range <= OptimalMemcpySizeForSooSlotTransfer(left - 1)) {
1218     return false;
1219   }
1220   for (size_t i = left + 1; i <= right; ++i) {
1221     if (OptimalMemcpySizeForSooSlotTransfer(i) != optimal_size_for_range) {
1222       return false;
1223     }
1224   }
1225   return true;
1226 }
1227 }  // namespace
1228 
1229 // We need to instantiate ALL possible template combinations because we define
1230 // the function in the cc file.
1231 template void GrowFullSooTableToNextCapacity<0, false>(CommonFields&,
1232                                                        const PolicyFunctions&,
1233                                                        size_t);
1234 template void
1235 GrowFullSooTableToNextCapacity<OptimalMemcpySizeForSooSlotTransfer(1), true>(
1236     CommonFields&, const PolicyFunctions&, size_t);
1237 
1238 static_assert(VerifyOptimalMemcpySizeForSooSlotTransferRange(2, 3));
1239 template void
1240 GrowFullSooTableToNextCapacity<OptimalMemcpySizeForSooSlotTransfer(3), true>(
1241     CommonFields&, const PolicyFunctions&, size_t);
1242 
1243 static_assert(VerifyOptimalMemcpySizeForSooSlotTransferRange(4, 8));
1244 template void
1245 GrowFullSooTableToNextCapacity<OptimalMemcpySizeForSooSlotTransfer(8), true>(
1246     CommonFields&, const PolicyFunctions&, size_t);
1247 
1248 #if UINTPTR_MAX == UINT32_MAX
1249 static_assert(MaxSooSlotSize() == 8);
1250 #else
1251 static_assert(VerifyOptimalMemcpySizeForSooSlotTransferRange(9, 16));
1252 template void
1253 GrowFullSooTableToNextCapacity<OptimalMemcpySizeForSooSlotTransfer(16), true>(
1254     CommonFields&, const PolicyFunctions&, size_t);
1255 static_assert(MaxSooSlotSize() == 16);
1256 #endif
1257 
1258 }  // namespace container_internal
1259 ABSL_NAMESPACE_END
1260 }  // namespace absl
1261