Coverage Report

Created: 2026-08-13 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/abseil-cpp/absl/container/internal/raw_hash_set.cc
Line
Count
Source
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 <cassert>
20
#include <cstddef>
21
#include <cstdint>
22
#include <cstring>
23
#include <memory>
24
#include <tuple>
25
#include <utility>
26
27
#include "absl/base/attributes.h"
28
#include "absl/base/config.h"
29
#include "absl/base/dynamic_annotations.h"
30
#include "absl/base/internal/endian.h"
31
#include "absl/base/internal/raw_logging.h"
32
#include "absl/base/optimization.h"
33
#include "absl/container/internal/container_memory.h"
34
#include "absl/container/internal/hashtable_control_bytes.h"
35
#include "absl/container/internal/hashtablez_sampler.h"
36
#include "absl/container/internal/raw_hash_set_resize_impl.h"
37
#include "absl/functional/function_ref.h"
38
#include "absl/hash/hash.h"
39
40
namespace absl {
41
ABSL_NAMESPACE_BEGIN
42
namespace container_internal {
43
44
// Represents a control byte corresponding to a full slot with arbitrary hash.
45
0
constexpr ctrl_t ZeroCtrlT() { return static_cast<ctrl_t>(0); }
46
47
// A single byte for default-constructed iterators. We leave it uninitialized
48
// because reading this memory is a bug.
49
ABSL_DLL char kDefaultIterSlot;
50
51
// We need one full byte followed by a sentinel byte for iterator::operator++.
52
ABSL_CONST_INIT ABSL_DLL const ctrl_t kSooControl[2] = {ZeroCtrlT(),
53
                                                        ctrl_t::kSentinel};
54
// We need one full byte followed by a sentinel byte for iterator::operator++.
55
ABSL_CONST_INIT ABSL_DLL const ctrl_t kInsertIteratorControl[2] = {
56
    ZeroCtrlT(), ctrl_t::kSentinel};
57
58
namespace {
59
60
#ifdef ABSL_SWISSTABLE_ASSERT
61
#error ABSL_SWISSTABLE_ASSERT cannot be directly set
62
#else
63
// We use this macro for assertions that users may see when the table is in an
64
// invalid state that sanitizers may help diagnose.
65
#define ABSL_SWISSTABLE_ASSERT(CONDITION) \
66
58.0M
  assert((CONDITION) && "Try enabling sanitizers.")
67
#endif
68
69
void ValidateMaxSize([[maybe_unused]] size_t size,
70
                     [[maybe_unused]] size_t key_size,
71
0
                     [[maybe_unused]] size_t slot_size) {
72
0
  ABSL_SWISSTABLE_ASSERT(size <= MaxValidSize(key_size, slot_size));
73
0
}
74
0
void ValidateMaxCapacity(size_t capacity, size_t key_size, size_t slot_size) {
75
0
  if (capacity <= 1) return;
76
0
  ValidateMaxSize(CapacityToGrowth(PreviousCapacity(capacity)), key_size,
77
0
                  slot_size);
78
0
}
79
80
// Returns "random" seed.
81
56.6k
inline size_t RandomSeed() {
82
56.6k
  constexpr size_t kIncrement = 0xad53;
83
56.6k
#ifdef ABSL_HAVE_THREAD_LOCAL
84
56.6k
  static thread_local size_t counter = 0;
85
56.6k
  counter += kIncrement;
86
56.6k
  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(kIncrement, std::memory_order_relaxed);
90
#endif  // ABSL_HAVE_THREAD_LOCAL
91
56.6k
  return value ^ static_cast<size_t>(reinterpret_cast<uintptr_t>(&counter));
92
56.6k
}
93
94
0
bool ShouldRehashForBugDetection(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
0
  return probe(ProbeCapacity{capacity}, absl::HashOf(RandomSeed()))
101
0
             .offset() < RehashProbabilityConstant();
102
0
}
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 basically using H2 last bits to save on shift operation.
108
125k
size_t SingleGroupTableH1(size_t hash, PerTableSeed seed) {
109
125k
  return hash ^ seed.seed();
110
125k
}
111
112
// Returns the offset of the new element after resize from capacity 1 to 3.
113
56.6k
size_t Resize1To3NewOffset(size_t hash, PerTableSeed seed) {
114
  // After resize from capacity 1 to 3, we always have exactly the slot with
115
  // index 1 occupied, so we need to insert either at index 0 or index 2.
116
56.6k
  static_assert(SooSlotIndex() == 1);
117
56.6k
  return SingleGroupTableH1(hash, seed) & 2;
118
56.6k
}
119
120
// Returns the address of the ith slot in slots where each slot occupies
121
// slot_size.
122
11.1M
inline void* SlotAddress(void* slot_array, size_t slot, size_t slot_size) {
123
11.1M
  return static_cast<void*>(static_cast<char*>(slot_array) +
124
11.1M
                            (slot * slot_size));
125
11.1M
}
126
127
// Returns the address of the slot `i` iterations after `slot` assuming each
128
// slot has the specified size.
129
100k
inline void* NextSlot(void* slot, size_t slot_size, size_t i = 1) {
130
100k
  return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot) +
131
100k
                                 slot_size * i);
132
100k
}
133
134
// Returns the address of the slot just before `slot` assuming each slot has the
135
// specified size.
136
0
inline void* PrevSlot(void* slot, size_t slot_size) {
137
0
  return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot) - slot_size);
138
0
}
139
140
}  // namespace
141
142
// Must be defined out-of-line to avoid MSVC error C2482 on some platforms,
143
// which is caused by non-constexpr initialization.
144
56.6k
uint16_t NextHashTableSeed() {
145
56.6k
  static_assert(PerTableSeed::kBitCount <= 16);
146
56.6k
  return static_cast<uint16_t>(RandomSeed());
147
56.6k
}
148
149
0
GenerationType* EmptyGeneration() {
150
0
  if (SwisstableGenerationsEnabled()) {
151
0
    constexpr size_t kNumEmptyGenerations = 1024;
152
0
    static constexpr GenerationType kEmptyGenerations[kNumEmptyGenerations]{};
153
0
    return const_cast<GenerationType*>(
154
0
        &kEmptyGenerations[RandomSeed() % kNumEmptyGenerations]);
155
0
  }
156
0
  return nullptr;
157
0
}
158
159
bool CommonFieldsGenerationInfoEnabled::
160
0
    should_rehash_for_bug_detection_on_insert(size_t capacity) const {
161
0
  if (reserved_growth_ == kReservedGrowthJustRanOut) return true;
162
0
  if (reserved_growth_ > 0) return false;
163
0
  return ShouldRehashForBugDetection(capacity);
164
0
}
165
166
bool CommonFieldsGenerationInfoEnabled::should_rehash_for_bug_detection_on_move(
167
0
    size_t capacity) const {
168
0
  return ShouldRehashForBugDetection(capacity);
169
0
}
170
171
namespace {
172
173
// Probes an array of control bits using a probe sequence,
174
// and returns the mask corresponding to the first group with a deleted or empty
175
// slot.
176
inline Group::NonIterableBitMaskType probe_till_first_non_full_group(
177
    const ctrl_t* ctrl, probe_seq<Group::kWidth>& seq,
178
124k
    [[maybe_unused]] size_t capacity) {
179
128k
  while (true) {
180
128k
    GroupFullEmptyOrDeleted g{ctrl + seq.offset()};
181
128k
    auto mask = g.MaskEmptyOrDeleted();
182
128k
    if (mask) {
183
124k
      return mask;
184
124k
    }
185
4.30k
    seq.next();
186
4.30k
    ABSL_SWISSTABLE_ASSERT(seq.index() <= capacity && "full table!");
187
4.30k
  }
188
124k
}
189
190
FindInfo find_first_non_full_from_h1(const ctrl_t* ctrl, size_t h1,
191
241k
                                     HashtableCapacity capacity) {
192
241k
  const size_t cap = capacity.capacity();
193
241k
  auto seq = probe_h1(ProbeCapacity{cap}, h1);
194
241k
  if (IsEmptyOrDeleted(ctrl[seq.offset()])) {
195
117k
    return {seq.offset(), /*probe_length=*/0};
196
117k
  }
197
124k
  auto mask = probe_till_first_non_full_group(ctrl, seq, cap);
198
124k
  return {seq.offset(mask.LowestBitSet()), seq.index()};
199
241k
}
200
201
// Probes an array of control bits using a probe sequence derived from `hash`,
202
// and returns the offset corresponding to the first deleted or empty slot.
203
//
204
// Behavior when the entire table is full is undefined.
205
//
206
// NOTE: this function must work with tables having both empty and deleted
207
// slots in the same group. Such tables appear during `erase()`.
208
113k
FindInfo find_first_non_full(const CommonFields& common, size_t hash) {
209
113k
  return find_first_non_full_from_h1(common.control(), H1(hash),
210
113k
                                     common.capacity_impl());
211
113k
}
212
213
// Same as `find_first_non_full`, but returns the mask corresponding to the
214
// first group with a deleted or empty slot.
215
std::pair<FindInfo, Group::NonIterableBitMaskType> find_first_non_full_group(
216
0
    const CommonFields& common, size_t hash) {
217
0
  auto seq = probe(common, hash);
218
0
  auto mask =
219
0
      probe_till_first_non_full_group(common.control(), seq, common.capacity());
220
0
  return {{seq.offset(), seq.index()}, mask};
221
0
}
222
223
// Whether a table fits in half a group. A half-group table fits entirely into a
224
// probing group, i.e., has a capacity < `Group::kWidth`.
225
//
226
// In half-group mode we are able to use the whole capacity. The extra control
227
// bytes give us at least one "empty" control byte to stop the iteration.
228
// This is important to make 1 a valid capacity.
229
//
230
// In half-group mode only the first `capacity` control bytes after the sentinel
231
// are valid. The rest contain dummy ctrl_t::kEmpty values that do not
232
// represent a real slot.
233
0
constexpr bool is_half_group(size_t capacity) {
234
0
  return capacity < Group::kWidth - 1;
235
0
}
236
237
template <class Fn>
238
0
void IterateOverFullSlotsImpl(const CommonFields& c, size_t slot_size, Fn cb) {
239
0
  const size_t cap = c.capacity();
240
0
  ABSL_ASSUME(cap > kMaxSmallCapacity);
241
0
  const ctrl_t* ctrl = c.control();
242
0
  void* slot = c.slot_array(cap);
243
0
  if (is_half_group(cap)) {
244
    // Mirrored/cloned control bytes in half-group table are also located in the
245
    // first group (starting from position 0). We are taking group from position
246
    // `capacity` in order to avoid duplicates.
247
248
    // Half-group tables capacity fits into portable group, where
249
    // GroupPortableImpl::MaskFull is more efficient for the
250
    // capacity <= GroupPortableImpl::kWidth.
251
0
    ABSL_SWISSTABLE_ASSERT(cap <= GroupPortableImpl::kWidth &&
252
0
                           "unexpectedly large half-group capacity");
253
0
    static_assert(Group::kWidth >= GroupPortableImpl::kWidth,
254
0
                  "unexpected group width");
255
    // Group starts from kSentinel slot, so indices in the mask will
256
    // be increased by 1.
257
0
    const auto mask = GroupPortableImpl(ctrl + cap).MaskFull();
258
0
    --ctrl;
259
0
    slot = PrevSlot(slot, slot_size);
260
0
    for (uint32_t i : mask) {
261
0
      cb(ctrl + i, SlotAddress(slot, i, slot_size));
262
0
    }
263
0
    return;
264
0
  }
265
0
  size_t remaining = c.size();
266
0
  ABSL_ATTRIBUTE_UNUSED const size_t original_size_for_assert = remaining;
267
0
  while (remaining != 0) {
268
0
    for (uint32_t i : GroupFullEmptyOrDeleted(ctrl).MaskFull()) {
269
0
      ABSL_SWISSTABLE_ASSERT(IsFull(ctrl[i]) &&
270
0
                             "hash table was modified unexpectedly");
271
0
      cb(ctrl + i, SlotAddress(slot, i, slot_size));
272
0
      --remaining;
273
0
    }
274
0
    ctrl += Group::kWidth;
275
0
    slot = NextSlot(slot, slot_size, Group::kWidth);
276
0
    ABSL_SWISSTABLE_ASSERT(
277
0
        (remaining == 0 || *(ctrl - 1) != ctrl_t::kSentinel) &&
278
0
        "hash table was modified unexpectedly");
279
0
  }
280
  // NOTE: erasure of the current element is allowed in callback for
281
  // absl::erase_if specialization. So we use `>=`.
282
0
  ABSL_SWISSTABLE_ASSERT(original_size_for_assert >= c.size() &&
283
0
                         "hash table was modified unexpectedly");
284
0
}
Unexecuted instantiation: raw_hash_set.cc:void absl::container_internal::(anonymous namespace)::IterateOverFullSlotsImpl<absl::FunctionRef<void (absl::container_internal::ctrl_t const*, void*)> >(absl::container_internal::CommonFields const&, unsigned long, absl::FunctionRef<void (absl::container_internal::ctrl_t const*, void*)>)
Unexecuted instantiation: raw_hash_set.cc:void absl::container_internal::(anonymous namespace)::IterateOverFullSlotsImpl<absl::container_internal::DestroySlots(absl::container_internal::CommonFields&, unsigned long, void (*)(void*, void*))::$_0>(absl::container_internal::CommonFields const&, unsigned long, absl::container_internal::DestroySlots(absl::container_internal::CommonFields&, unsigned long, void (*)(void*, void*))::$_0)
Unexecuted instantiation: raw_hash_set.cc:void absl::container_internal::(anonymous namespace)::IterateOverFullSlotsImpl<absl::container_internal::Copy(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::container_internal::CommonFields const&, absl::FunctionRef<void (void*, void const*)>)::$_0>(absl::container_internal::CommonFields const&, unsigned long, absl::container_internal::Copy(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::container_internal::CommonFields const&, absl::FunctionRef<void (void*, void const*)>)::$_0)
285
286
// NOTE: we don't use structure with bit fields for GrowthInfo because for
287
// correctness we rely on the lower bound being the most significant byte.
288
289
// Returns the increment that needs to be added to the packed full growth info
290
// in order to increase lower bound by lower_bound_increment and increase
291
// overflow growth left by overflow_increment.
292
constexpr uint64_t GetPackedIncrement(uint64_t lower_bound_increment,
293
62.2k
                                      uint64_t overflow_increment) {
294
62.2k
  return (lower_bound_increment << GrowthInfoAccessor::kLowerBoundShift) +
295
62.2k
         overflow_increment;
296
62.2k
}
297
298
// Returns the increment that needs to be added to the packed full growth info
299
// in order to increase lower bound by overflow_to_lower_bound_size and
300
// decrease overflow growth left by overflow_to_lower_bound_size.
301
constexpr uint64_t GetRebalanceIncrement(
302
23.2k
    uint64_t overflow_to_lower_bound_size) {
303
23.2k
  return GetPackedIncrement(overflow_to_lower_bound_size,
304
23.2k
                            0u - overflow_to_lower_bound_size);
305
23.2k
}
306
307
// Returns the number of elements left to grow in the full growth info.
308
constexpr uint64_t GetOverflowGrowthLeftFromPacked(
309
57.7k
    uint64_t packed_full_growth_info) {
310
57.7k
  constexpr uint64_t kFullGrowthMask =
311
57.7k
      (uint64_t{1} << GrowthInfoAccessor::kLowerBoundShift) - 1;
312
57.7k
  return packed_full_growth_info & kFullGrowthMask;
313
57.7k
}
314
315
// Returns the GrowthInfoLowerBound object containing the information
316
// about minimum growth left.
317
constexpr GrowthInfoLowerBound GetGrowthInfoLowerBoundFromPacked(
318
81.0k
    uint64_t packed_full_growth_info) {
319
81.0k
  return GrowthInfoLowerBound(packed_full_growth_info >>
320
81.0k
                              GrowthInfoAccessor::kLowerBoundShift);
321
81.0k
}
322
323
// Returns the number of elements left to grow in the lower bound.
324
constexpr uint64_t GetGrowthLeftLowerBoundFromPacked(
325
57.7k
    uint64_t packed_full_growth_info) {
326
57.7k
  return GetGrowthInfoLowerBoundFromPacked(packed_full_growth_info)
327
57.7k
      .GetGrowthLeft();
328
57.7k
}
329
330
// Returns the total number of elements left to grow in the full growth info.
331
// Assumes that the table has capacity > kMaxGrowthLeftLowerBound.
332
34.5k
uint64_t GetGrowthLeftTotalBigCapacity(void* full_growth_info) {
333
34.5k
  uint64_t packed_full_growth_left = little_endian::Load64(full_growth_info);
334
34.5k
  return GetOverflowGrowthLeftFromPacked(packed_full_growth_left) +
335
34.5k
         GetGrowthLeftLowerBoundFromPacked(packed_full_growth_left);
336
34.5k
}
337
338
}  // namespace
339
340
613k
void CommonFields::AssertNotDebugCapacityImpl() const {
341
613k
  const HashtableCapacity cap = maybe_invalid_capacity();
342
613k
  if (ABSL_PREDICT_TRUE(cap.IsValid())) {
343
613k
    return;
344
613k
  }
345
613k
  assert(!cap.IsReentrance() &&
346
0
         "Reentrant container access during element construction/destruction "
347
0
         "is not allowed.");
348
0
  if (cap.IsDestroyed()) {
349
0
    ABSL_RAW_LOG(FATAL, "Use of destroyed hash table.");
350
0
  }
351
0
  if (SwisstableGenerationsEnabled() && ABSL_PREDICT_FALSE(cap.IsMovedFrom())) {
352
0
    if (cap.IsSelfMovedFrom()) {
353
      // If this log triggers, then a hash table was move-assigned to itself
354
      // and then used again later without being reinitialized.
355
0
      ABSL_RAW_LOG(FATAL, "Use of self-move-assigned hash table.");
356
0
    }
357
0
    ABSL_RAW_LOG(FATAL, "Use of moved-from hash table.");
358
0
  }
359
0
}
360
361
void GrowthInfoAccessor::InitGrowthLeftNoDeleted(size_t growth_left,
362
502k
                                                 size_t capacity) {
363
502k
  if (capacity <= GrowthInfoLowerBound::kMaxGrowthLeftLowerBound) {
364
463k
    *growth_info_lower_bound_ = static_cast<uint8_t>(growth_left);
365
463k
  } else {
366
38.9k
    uint64_t lower_bound =
367
38.9k
        (std::min)(uint64_t{growth_left},
368
38.9k
                   GrowthInfoLowerBound::kMaxGrowthLeftLowerBound);
369
38.9k
    little_endian::Store64(
370
38.9k
        full_growth_info_ptr(),
371
38.9k
        GetPackedIncrement(lower_bound, growth_left - lower_bound));
372
38.9k
  }
373
502k
}
374
375
GrowthInfoLowerBound GrowthInfoAccessor::RebalanceGrowthLeftLowerBound(
376
183k
    size_t capacity) {
377
183k
  auto growth_left_lower_bound = GetGrowthInfoLowerBound();
378
183k
  if (capacity <= GrowthInfoLowerBound::kMaxGrowthLeftLowerBound ||
379
      // For tables with deleted slots, we often call rebalance even if
380
      // we have growth left in the lower bound.
381
159k
      growth_left_lower_bound.HasDeletedAndGrowthLeft()) {
382
159k
    return growth_left_lower_bound;
383
159k
  } else {
384
23.2k
    return RebalanceGrowthLeftLowerBoundLargeCapacity();
385
23.2k
  }
386
183k
}
387
388
177k
size_t GrowthInfoAccessor::GetGrowthLeftTotalSlow(size_t capacity) const {
389
177k
  if (capacity <= GrowthInfoLowerBound::kMaxGrowthLeftLowerBound) {
390
159k
    return GetGrowthLeftLowerBound();
391
159k
  } else {
392
17.2k
    return static_cast<size_t>(
393
17.2k
        GetGrowthLeftTotalBigCapacity(full_growth_info_ptr()));
394
17.2k
  }
395
177k
}
396
397
ABSL_ATTRIBUTE_NOINLINE GrowthInfoLowerBound
398
23.2k
GrowthInfoAccessor::RebalanceGrowthLeftLowerBoundLargeCapacity() {
399
23.2k
  void* full_growth_info = full_growth_info_ptr();
400
23.2k
  uint64_t packed_full_growth_info = little_endian::Load64(full_growth_info);
401
23.2k
  uint64_t overflow_growth_left =
402
23.2k
      GetOverflowGrowthLeftFromPacked(packed_full_growth_info);
403
23.2k
  uint64_t lower_bound_growth_left =
404
23.2k
      GetGrowthLeftLowerBoundFromPacked(packed_full_growth_info);
405
23.2k
  uint64_t overflow_to_lower_bound_size =
406
23.2k
      (std::min)(overflow_growth_left,
407
23.2k
                 GrowthInfoLowerBound::kMaxGrowthLeftLowerBound -
408
23.2k
                     lower_bound_growth_left);
409
23.2k
  packed_full_growth_info +=
410
23.2k
      GetRebalanceIncrement(overflow_to_lower_bound_size);
411
23.2k
  little_endian::Store64(full_growth_info, packed_full_growth_info);
412
23.2k
  auto result = GetGrowthInfoLowerBoundFromPacked(packed_full_growth_info);
413
23.2k
  ABSL_SWISSTABLE_ASSERT(result.HasNoDeleted() ==
414
23.2k
                         GetGrowthInfoLowerBound().HasNoDeleted());
415
23.2k
  ABSL_SWISSTABLE_ASSERT(
416
23.2k
      (result.GetGrowthLeft() > 0 ||
417
23.2k
       GetGrowthLeftTotalBigCapacity(full_growth_info_ptr()) == 0) &&
418
23.2k
      "rebalance may return 0 only if we have absolutely no growth left");
419
23.2k
  return result;
420
23.2k
}
421
422
0
void GrowthInfoAccessor::OverwriteFullAsEmpty() {
423
0
  if (GetGrowthLeftLowerBound() <
424
0
      GrowthInfoLowerBound::kMaxGrowthLeftLowerBound) {
425
0
    ++(*growth_info_lower_bound_);
426
0
  } else {
427
0
    constexpr uint64_t kIncrement = GetPackedIncrement(
428
0
        /*lower_bound_increment=*/0, /*overflow_increment=*/1);
429
0
    void* const full_growth_info = full_growth_info_ptr();
430
0
    little_endian::Store64(
431
0
        full_growth_info, little_endian::Load64(full_growth_info) + kIncrement);
432
0
  }
433
0
}
434
435
0
void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity) {
436
0
  ABSL_SWISSTABLE_ASSERT(ctrl[capacity] == ctrl_t::kSentinel);
437
0
  ABSL_SWISSTABLE_ASSERT(IsValidCapacity(capacity));
438
0
  for (ctrl_t* pos = ctrl; pos < ctrl + capacity; pos += Group::kWidth) {
439
0
    Group{pos}.ConvertSpecialToEmptyAndFullToDeleted(pos);
440
0
  }
441
  // Copy the cloned ctrl bytes.
442
0
  std::memcpy(ctrl + capacity + 1, ctrl, NumClonedBytes());
443
0
  ctrl[capacity] = ctrl_t::kSentinel;
444
0
}
445
446
void IterateOverFullSlots(const CommonFields& c, size_t slot_size,
447
0
                          absl::FunctionRef<void(const ctrl_t*, void*)> cb) {
448
0
  IterateOverFullSlotsImpl(c, slot_size, cb);
449
0
}
450
451
0
HashtablezInfoHandle CommonFields::infoz_ptr() const {
452
  // growth_info is stored before control bytes.
453
0
  ABSL_SWISSTABLE_ASSERT(has_infoz());
454
0
  HashtablezInfoHandle res;
455
0
  void* src = reinterpret_cast<char*>(control()) -
456
0
              MetadataBeforeControlSize(/*has_infoz=*/true, capacity());
457
0
  std::memcpy(&res, src, sizeof(HashtablezInfoHandle));
458
0
  return res;
459
0
}
460
461
0
void CommonFields::set_infoz(HashtablezInfoHandle infoz) {
462
0
  ABSL_SWISSTABLE_ASSERT(has_infoz());
463
0
  void* dst = reinterpret_cast<char*>(control()) -
464
0
              MetadataBeforeControlSize(/*has_infoz=*/true, capacity());
465
0
  std::memcpy(dst, &infoz, sizeof(HashtablezInfoHandle));
466
0
}
467
468
namespace {
469
470
void ResetGrowthLeft(GrowthInfoAccessor growth_info, size_t capacity,
471
445k
                     size_t occupied_elements) {
472
445k
  growth_info.InitGrowthLeftNoDeleted(
473
445k
      CapacityToGrowth(capacity) - occupied_elements, capacity);
474
445k
}
475
476
// Finds guaranteed to exists empty slot from the given position.
477
// NOTE: this function is almost never triggered inside of the
478
// DropDeletesWithoutResize, so we keep it simple.
479
// The table is rather sparse, so empty slot will be found very quickly.
480
0
size_t FindEmptySlot(size_t start, size_t end, const ctrl_t* ctrl) {
481
0
  for (size_t i = start; i < end; ++i) {
482
0
    if (IsEmpty(ctrl[i])) {
483
0
      return i;
484
0
    }
485
0
  }
486
0
  ABSL_UNREACHABLE();
487
0
}
488
489
// Finds guaranteed to exist full slot starting from the given position.
490
// NOTE: this function is only triggered for rehash(0), when we need to
491
// go back to SOO state, so we keep it simple.
492
0
size_t FindFirstFullSlot(size_t start, size_t end, const ctrl_t* ctrl) {
493
0
  for (size_t i = start; i < end; ++i) {
494
0
    if (IsFull(ctrl[i])) {
495
0
      return i;
496
0
    }
497
0
  }
498
0
  ABSL_UNREACHABLE();
499
0
}
500
501
10.9M
void PrepareInsertCommon(CommonFields& common) {
502
10.9M
  common.increment_size();
503
10.9M
  common.maybe_increment_generation_on_insert();
504
10.9M
}
505
506
// Sets sanitizer poisoning for slot corresponding to control byte being set.
507
inline void DoSanitizeOnSetCtrl(const CommonFields& c, size_t i, ctrl_t h,
508
10.9M
                                size_t slot_size) {
509
10.9M
  const size_t cap = c.capacity();
510
10.9M
  ABSL_ASSUME(cap > kMaxSmallCapacity);
511
10.9M
  ABSL_SWISSTABLE_ASSERT(i < cap);
512
10.9M
  auto* slot_i = static_cast<const char*>(c.slot_array(cap)) + i * slot_size;
513
10.9M
  if (IsFull(h)) {
514
10.9M
    SanitizerUnpoisonMemoryRegion(slot_i, slot_size);
515
10.9M
  } else {
516
0
    SanitizerPoisonMemoryRegion(slot_i, slot_size);
517
0
  }
518
10.9M
}
519
520
// Sets `ctrl[i]` to `h`.
521
//
522
// Unlike setting it directly, this function will perform bounds checks and
523
// mirror the value to the cloned tail if necessary.
524
10.6M
inline void SetCtrlNoSanitizeImpl(const CommonFields& c, size_t i, ctrl_t h) {
525
10.6M
  ABSL_SWISSTABLE_ASSERT(i < c.capacity());
526
10.6M
  ctrl_t* ctrl = c.control();
527
10.6M
  const size_t cap = c.capacity();
528
10.6M
  ctrl[i] = h;
529
10.6M
  ctrl[((i - NumClonedBytes()) & cap) + (NumClonedBytes() & cap)] = h;
530
10.6M
}
531
532
inline void SetCtrl(const CommonFields& c, size_t i, ctrl_t h,
533
10.6M
                    size_t slot_size) {
534
10.6M
  ABSL_SWISSTABLE_ASSERT(!c.is_small());
535
10.6M
  DoSanitizeOnSetCtrl(c, i, h, slot_size);
536
10.6M
  SetCtrlNoSanitizeImpl(c, i, h);
537
10.6M
}
538
// Overload for setting to an occupied `h2_t` rather than a special `ctrl_t`.
539
10.6M
inline void SetCtrl(const CommonFields& c, size_t i, h2_t h, size_t slot_size) {
540
10.6M
  SetCtrl(c, i, static_cast<ctrl_t>(h), slot_size);
541
10.6M
}
542
543
// Sets `ctrl[i]` to `ctrl_t::kSentinel`.
544
//
545
// Unlike setting it directly, this function will perform bounds checks and
546
// mirror the value to the cloned tail if necessary.
547
0
inline void BlockCtrl(const CommonFields& c, size_t i) {
548
0
  ABSL_SWISSTABLE_ASSERT(!c.is_small());
549
0
  SetCtrlNoSanitizeImpl(c, i, ctrl_t::kSentinel);
550
0
}
551
552
// Like SetCtrl, but in a single group table, we can save some operations when
553
// setting the cloned control byte.
554
inline void SetCtrlInSingleGroupTable(const CommonFields& c, size_t i, ctrl_t h,
555
69.2k
                                      size_t slot_size) {
556
69.2k
  const size_t cap = c.capacity();
557
69.2k
  ABSL_SWISSTABLE_ASSERT(!c.is_small());
558
69.2k
  ABSL_SWISSTABLE_ASSERT(is_single_group(cap));
559
69.2k
  DoSanitizeOnSetCtrl(c, i, h, slot_size);
560
69.2k
  ctrl_t* ctrl = c.control();
561
69.2k
  ctrl[i] = h;
562
69.2k
  ctrl[i + cap + 1] = h;
563
69.2k
}
564
// Overload for setting to an occupied `h2_t` rather than a special `ctrl_t`.
565
inline void SetCtrlInSingleGroupTable(const CommonFields& c, size_t i, h2_t h,
566
69.2k
                                      size_t slot_size) {
567
69.2k
  SetCtrlInSingleGroupTable(c, i, static_cast<ctrl_t>(h), slot_size);
568
69.2k
}
569
570
// Like SetCtrl, but in a table with capacity >= Group::kWidth - 1,
571
// we can save some operations when setting the cloned control byte.
572
inline void SetCtrlInLargeTable(const CommonFields& c, size_t i, ctrl_t h,
573
241k
                                size_t slot_size) {
574
241k
  ABSL_SWISSTABLE_ASSERT(c.capacity() >= Group::kWidth - 1);
575
241k
  DoSanitizeOnSetCtrl(c, i, h, slot_size);
576
241k
  ctrl_t* ctrl = c.control();
577
241k
  ctrl[i] = h;
578
241k
  ctrl[((i - NumClonedBytes()) & c.capacity()) + NumClonedBytes()] = h;
579
241k
}
580
// Overload for setting to an occupied `h2_t` rather than a special `ctrl_t`.
581
inline void SetCtrlInLargeTable(const CommonFields& c, size_t i, h2_t h,
582
241k
                                size_t slot_size) {
583
241k
  SetCtrlInLargeTable(c, i, static_cast<ctrl_t>(h), slot_size);
584
241k
}
585
586
268k
void BlockControlBytes(CommonFields& common, size_t blocked_element_count) {
587
268k
  const size_t capacity = common.capacity();
588
268k
  while (blocked_element_count > 0) {
589
0
    BlockCtrl(common, capacity - blocked_element_count);
590
0
    --blocked_element_count;
591
0
  }
592
268k
}
593
594
void* DropDeletesWithoutResizeAndPrepareInsert(
595
    CommonFields& common, const PolicyFunctions& __restrict policy,
596
0
    size_t new_hash) {
597
0
  void* set = &common;
598
0
  const size_t capacity = common.capacity();
599
0
  ABSL_SWISSTABLE_ASSERT(IsValidCapacity(capacity));
600
0
  ABSL_SWISSTABLE_ASSERT(!is_single_group(capacity));
601
0
  ABSL_ASSUME(capacity > kMaxSmallCapacity);
602
603
0
  ctrl_t* ctrl = common.control();
604
0
  void* slot_array = common.slot_array(capacity);
605
  // Algorithm:
606
  // - mark all DELETED slots as EMPTY
607
  // - mark all FULL slots as DELETED
608
  // - for each slot marked as DELETED
609
  //     hash = Hash(element)
610
  //     target = find_first_non_full(hash)
611
  //     if target is in the same group
612
  //       mark slot as FULL
613
  //     else if target is EMPTY
614
  //       transfer element to target
615
  //       mark slot as EMPTY
616
  //       mark target as FULL
617
  //     else if target is DELETED
618
  //       swap current element with target element
619
  //       mark target as FULL
620
  //       repeat procedure for current slot with moved from element (target)
621
0
  const size_t blocked_element_count = common.blocked_element_count();
622
0
  ConvertDeletedToEmptyAndFullToDeleted(ctrl, capacity);
623
0
  BlockControlBytes(common, blocked_element_count);
624
0
  const void* hash_fn = policy.hash_fn(common);
625
0
  auto hasher = policy.hash_slot;
626
0
  auto transfer_n = policy.transfer_n;
627
0
  const size_t slot_size = policy.slot_size;
628
629
0
  size_t total_probe_length = 0;
630
0
  void* slot_ptr = SlotAddress(slot_array, 0, slot_size);
631
632
  // The index of an empty slot that can be used as temporary memory for
633
  // the swap operation.
634
0
  constexpr size_t kUnknownId = ~size_t{};
635
0
  size_t tmp_space_id = kUnknownId;
636
637
0
  for (size_t i = 0; i != capacity;
638
0
       ++i, slot_ptr = NextSlot(slot_ptr, slot_size)) {
639
0
    ABSL_SWISSTABLE_ASSERT(slot_ptr == SlotAddress(slot_array, i, slot_size));
640
0
    if (IsEmpty(ctrl[i])) {
641
0
      tmp_space_id = i;
642
0
      continue;
643
0
    }
644
0
    if (!IsDeleted(ctrl[i])) continue;
645
0
    const size_t hash = (*hasher)(hash_fn, slot_ptr, common.seed().seed());
646
0
    const FindInfo target = find_first_non_full(common, hash);
647
0
    const size_t new_i = target.offset;
648
0
    total_probe_length += target.probe_length;
649
650
    // Verify if the old and new i fall within the same group wrt the hash.
651
    // If they do, we don't need to move the object as it falls already in the
652
    // best probe we can.
653
0
    const size_t probe_offset = probe(common, hash).offset();
654
0
    const h2_t h2 = H2(hash);
655
0
    const auto probe_index = [probe_offset, capacity](size_t pos) {
656
0
      return ((pos - probe_offset) & capacity) / Group::kWidth;
657
0
    };
658
659
    // Element doesn't move.
660
0
    if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) {
661
0
      SetCtrlInLargeTable(common, i, h2, slot_size);
662
0
      continue;
663
0
    }
664
665
0
    void* new_slot_ptr = SlotAddress(slot_array, new_i, slot_size);
666
0
    if (IsEmpty(ctrl[new_i])) {
667
      // Transfer element to the empty spot.
668
      // SetCtrl poisons/unpoisons the slots so we have to call it at the
669
      // right time.
670
0
      SetCtrlInLargeTable(common, new_i, h2, slot_size);
671
0
      (*transfer_n)(set, new_slot_ptr, slot_ptr, 1);
672
0
      SetCtrlInLargeTable(common, i, ctrl_t::kEmpty, slot_size);
673
      // Initialize or change empty space id.
674
0
      tmp_space_id = i;
675
0
    } else {
676
0
      ABSL_SWISSTABLE_ASSERT(IsDeleted(ctrl[new_i]));
677
0
      SetCtrlInLargeTable(common, new_i, h2, slot_size);
678
      // Until we are done rehashing, DELETED marks previously FULL slots.
679
680
0
      if (tmp_space_id == kUnknownId) {
681
0
        tmp_space_id = FindEmptySlot(i + 1, capacity, ctrl);
682
0
      }
683
0
      void* tmp_space = SlotAddress(slot_array, tmp_space_id, slot_size);
684
0
      SanitizerUnpoisonMemoryRegion(tmp_space, slot_size);
685
686
      // Swap i and new_i elements.
687
0
      (*transfer_n)(set, tmp_space, new_slot_ptr, 1);
688
0
      (*transfer_n)(set, new_slot_ptr, slot_ptr, 1);
689
0
      (*transfer_n)(set, slot_ptr, tmp_space, 1);
690
691
0
      SanitizerPoisonMemoryRegion(tmp_space, slot_size);
692
693
      // repeat the processing of the ith slot
694
0
      --i;
695
0
      slot_ptr = PrevSlot(slot_ptr, slot_size);
696
0
    }
697
0
  }
698
  // Prepare insert for the new element.
699
0
  PrepareInsertCommon(common);
700
0
  ResetGrowthLeft(common.growth_info(), capacity,
701
0
                  common.size() + blocked_element_count);
702
0
  FindInfo find_info = find_first_non_full(common, new_hash);
703
0
  SetCtrlInLargeTable(common, find_info.offset, H2(new_hash), slot_size);
704
0
  common.infoz().RecordInsertMiss(new_hash, find_info.probe_length);
705
0
  common.infoz().RecordRehash(total_probe_length);
706
0
  return SlotAddress(slot_array, find_info.offset, slot_size);
707
0
}
708
709
0
bool WasNeverFull(CommonFields& c, size_t index) {
710
0
  if (is_single_group(c.capacity())) {
711
0
    return true;
712
0
  }
713
0
  const size_t index_before = (index - Group::kWidth) & c.capacity();
714
0
  const auto empty_after = Group(c.control() + index).MaskEmpty();
715
0
  const auto empty_before = Group(c.control() + index_before).MaskEmpty();
716
717
  // We count how many consecutive non empties we have to the right and to the
718
  // left of `it`. If the sum is >= kWidth then there is at least one probe
719
  // window that might have seen a full group.
720
0
  return empty_before && empty_after &&
721
0
         static_cast<size_t>(empty_after.TrailingZeros()) +
722
0
                 empty_before.LeadingZeros() <
723
0
             Group::kWidth;
724
0
}
725
726
// Updates the control bytes to indicate a completely empty table such that all
727
// control bytes are kEmpty except for the kSentinel bytes.
728
// If the table has blocked elements, last `blocked_element_count` are set to
729
// kSentinel.
730
void ResetCtrl(CommonFields& common, size_t slot_size,
731
268k
               size_t blocked_element_count) {
732
268k
  ABSL_SWISSTABLE_ASSERT(IsCapacityValidForBlockedElements(common.capacity()) ||
733
268k
                         blocked_element_count == 0);
734
268k
  const size_t capacity = common.capacity();
735
268k
  ctrl_t* ctrl = common.control();
736
268k
  static constexpr size_t kTwoGroupCapacity = 2 * Group::kWidth - 1;
737
268k
  if (ABSL_PREDICT_TRUE(capacity <= kTwoGroupCapacity)) {
738
162k
    if (IsSmallCapacity(capacity)) return;
739
162k
    std::memset(ctrl, static_cast<int8_t>(ctrl_t::kEmpty), Group::kWidth);
740
162k
    std::memset(ctrl + capacity, static_cast<int8_t>(ctrl_t::kEmpty),
741
162k
                Group::kWidth);
742
162k
    if (capacity == kTwoGroupCapacity) {
743
37.7k
      std::memset(ctrl + Group::kWidth, static_cast<int8_t>(ctrl_t::kEmpty),
744
37.7k
                  Group::kWidth);
745
37.7k
    }
746
162k
  } else {
747
106k
    std::memset(ctrl, static_cast<int8_t>(ctrl_t::kEmpty),
748
106k
                capacity + 1 + NumClonedBytes());
749
106k
  }
750
268k
  ctrl[capacity] = ctrl_t::kSentinel;
751
268k
  SanitizerPoisonMemoryRegion(common.slot_array(capacity),
752
268k
                              slot_size * (capacity - blocked_element_count));
753
268k
  BlockControlBytes(common, blocked_element_count);
754
268k
}
755
756
// Initializes control bytes for growing from capacity 1 to 3.
757
// `orig_h2` is placed in the position `SooSlotIndex()`.
758
// `new_h2` is placed in the position `new_offset`.
759
ABSL_ATTRIBUTE_ALWAYS_INLINE inline void InitializeThreeElementsControlBytes(
760
56.6k
    h2_t orig_h2, h2_t new_h2, size_t new_offset, ctrl_t* new_ctrl) {
761
56.6k
  static constexpr size_t kNewCapacity = NextCapacity(SooCapacity());
762
56.6k
  static_assert(kNewCapacity == 3);
763
56.6k
  static_assert(is_single_group(kNewCapacity));
764
56.6k
  static_assert(SooSlotIndex() == 1);
765
56.6k
  ABSL_SWISSTABLE_ASSERT(new_offset == 0 || new_offset == 2);
766
767
56.6k
  static constexpr uint64_t kEmptyXorSentinel =
768
56.6k
      static_cast<uint8_t>(ctrl_t::kEmpty) ^
769
56.6k
      static_cast<uint8_t>(ctrl_t::kSentinel);
770
56.6k
  static constexpr uint64_t kEmpty64 = static_cast<uint8_t>(ctrl_t::kEmpty);
771
56.6k
  static constexpr size_t kMirroredSooSlotIndex =
772
56.6k
      SooSlotIndex() + kNewCapacity + 1;
773
  // The first 8 bytes, where SOO slot original and mirrored positions are
774
  // replaced with 0.
775
  // Result will look like: E0ESE0EE
776
56.6k
  static constexpr uint64_t kFirstCtrlBytesWithZeroes =
777
56.6k
      k8EmptyBytes ^ (kEmpty64 << (8 * SooSlotIndex())) ^
778
56.6k
      (kEmptyXorSentinel << (8 * kNewCapacity)) ^
779
56.6k
      (kEmpty64 << (8 * kMirroredSooSlotIndex));
780
781
56.6k
  const uint64_t soo_h2 = static_cast<uint64_t>(orig_h2);
782
56.6k
  const uint64_t new_h2_xor_empty =
783
56.6k
      static_cast<uint64_t>(new_h2 ^ static_cast<uint8_t>(ctrl_t::kEmpty));
784
  // Fill the original and mirrored bytes for SOO slot.
785
  // Result will look like:
786
  // EHESEHEE
787
  // Where H = soo_h2, E = kEmpty, S = kSentinel.
788
56.6k
  uint64_t first_ctrl_bytes =
789
56.6k
      ((soo_h2 << (8 * SooSlotIndex())) | kFirstCtrlBytesWithZeroes) |
790
56.6k
      (soo_h2 << (8 * kMirroredSooSlotIndex));
791
  // Replace original and mirrored empty bytes for the new position.
792
  // Result for new_offset 0 will look like:
793
  // NHESNHEE
794
  // Where H = soo_h2, N = H2(new_hash), E = kEmpty, S = kSentinel.
795
  // Result for new_offset 2 will look like:
796
  // EHNSEHNE
797
56.6k
  first_ctrl_bytes ^= (new_h2_xor_empty << (8 * new_offset));
798
56.6k
  size_t new_mirrored_offset = new_offset + kNewCapacity + 1;
799
56.6k
  first_ctrl_bytes ^= (new_h2_xor_empty << (8 * new_mirrored_offset));
800
801
  // Fill last bytes with kEmpty.
802
56.6k
  std::memset(new_ctrl + kNewCapacity, static_cast<int8_t>(ctrl_t::kEmpty),
803
56.6k
              Group::kWidth);
804
  // Overwrite the first 8 bytes with first_ctrl_bytes.
805
56.6k
  absl::little_endian::Store64(new_ctrl, first_ctrl_bytes);
806
807
  // Example for group size 16:
808
  // new_ctrl after 1st memset =      ???EEEEEEEEEEEEEEEE
809
  // new_offset 0:
810
  // new_ctrl after 2nd store  =      NHESNHEEEEEEEEEEEEE
811
  // new_offset 2:
812
  // new_ctrl after 2nd store  =      EHNSEHNEEEEEEEEEEEE
813
814
  // Example for group size 8:
815
  // new_ctrl after 1st memset =      ???EEEEEEEE
816
  // new_offset 0:
817
  // new_ctrl after 2nd store  =      NHESNHEEEEE
818
  // new_offset 2:
819
  // new_ctrl after 2nd store  =      EHNSEHNEEEE
820
56.6k
}
821
822
// ClearBackingArrayNoReuse clears the backing array and sets the common
823
// fields to the default values for empty non-allocated tables.
824
// REQUIRES: c.capacity > policy.soo_capacity.
825
void ClearBackingArrayNoReuse(CommonFields& c,
826
                              const PolicyFunctions& __restrict policy,
827
17.3k
                              void* alloc) {
828
17.3k
  ABSL_SWISSTABLE_ASSERT(c.capacity() > policy.soo_capacity());
829
  // We need to record infoz before calling dealloc, which will unregister
830
  // infoz.
831
17.3k
  c.infoz().RecordClearedReservation();
832
17.3k
  c.infoz().RecordStorageChanged(0, policy.soo_capacity());
833
17.3k
  c.infoz().Unregister();
834
17.3k
  (*policy.dealloc)(alloc, c.capacity(), c.control(), policy.slot_size,
835
17.3k
                    policy.slot_align, c.has_infoz(),
836
17.3k
                    c.blocked_element_count());
837
17.3k
  c = policy.soo_enabled ? CommonFields{soo_tag_t{}}
838
17.3k
                         : CommonFields{non_soo_tag_t{}};
839
17.3k
}
840
841
template <bool kSooEnabled>
842
135k
void* SingleSlotAddress(CommonFields& c) {
843
135k
  return kSooEnabled ? c.soo_data() : c.slot_array(/*capacity=*/1);
844
135k
}
raw_hash_set.cc:void* absl::container_internal::(anonymous namespace)::SingleSlotAddress<false>(absl::container_internal::CommonFields&)
Line
Count
Source
842
135k
void* SingleSlotAddress(CommonFields& c) {
843
135k
  return kSooEnabled ? c.soo_data() : c.slot_array(/*capacity=*/1);
844
135k
}
Unexecuted instantiation: raw_hash_set.cc:void* absl::container_internal::(anonymous namespace)::SingleSlotAddress<true>(absl::container_internal::CommonFields&)
845
846
template <bool kSooEnabled>
847
148k
void DecrementSmallSize(CommonFields& c) {
848
148k
  if constexpr (kSooEnabled) {
849
13.1k
    c.set_empty_soo();
850
135k
  } else {
851
135k
    c.decrement_size();
852
135k
  }
853
148k
}
raw_hash_set.cc:void absl::container_internal::(anonymous namespace)::DecrementSmallSize<true>(absl::container_internal::CommonFields&)
Line
Count
Source
847
13.1k
void DecrementSmallSize(CommonFields& c) {
848
13.1k
  if constexpr (kSooEnabled) {
849
13.1k
    c.set_empty_soo();
850
  } else {
851
    c.decrement_size();
852
  }
853
13.1k
}
raw_hash_set.cc:void absl::container_internal::(anonymous namespace)::DecrementSmallSize<false>(absl::container_internal::CommonFields&)
Line
Count
Source
847
135k
void DecrementSmallSize(CommonFields& c) {
848
  if constexpr (kSooEnabled) {
849
    c.set_empty_soo();
850
135k
  } else {
851
135k
    c.decrement_size();
852
135k
  }
853
135k
}
854
855
}  // namespace
856
857
0
void EraseMetaOnlySmall(CommonFields& c, bool soo_enabled, size_t slot_size) {
858
0
  ABSL_SWISSTABLE_ASSERT(c.is_small());
859
0
  if (soo_enabled) {
860
0
    c.set_empty_soo();
861
0
    return;
862
0
  }
863
0
  c.decrement_size();
864
0
  c.infoz().RecordErase();
865
0
  SanitizerPoisonMemoryRegion(SingleSlotAddress</*kSooEnabled=*/false>(c),
866
0
                              slot_size);
867
0
}
868
869
0
void EraseMetaOnlyLarge(CommonFields& c, size_t index, size_t slot_size) {
870
0
  ABSL_SWISSTABLE_ASSERT(!c.is_small());
871
0
  ABSL_SWISSTABLE_ASSERT(IsFull(c.control()[index]) &&
872
0
                         "erasing a dangling iterator");
873
0
  c.decrement_size();
874
0
  c.infoz().RecordErase();
875
876
0
  if (WasNeverFull(c, index)) {
877
0
    SetCtrl(c, index, ctrl_t::kEmpty, slot_size);
878
0
    c.growth_info().OverwriteFullAsEmpty();
879
0
    return;
880
0
  }
881
882
0
  c.growth_info().OverwriteFullAsDeleted();
883
0
  SetCtrlInLargeTable(c, index, ctrl_t::kDeleted, slot_size);
884
0
}
885
886
void ClearBackingArray(CommonFields& c,
887
                       const PolicyFunctions& __restrict policy, void* alloc,
888
285k
                       bool reuse) {
889
285k
  ABSL_SWISSTABLE_ASSERT(c.capacity() > kMaxSmallCapacity);
890
285k
  if (reuse) {
891
268k
    const size_t blocked_element_count = c.blocked_element_count();
892
268k
    c.set_size_to_zero();
893
268k
    ABSL_SWISSTABLE_ASSERT(c.capacity() > policy.soo_capacity());
894
268k
    ResetCtrl(c, policy.slot_size, blocked_element_count);
895
268k
    ResetGrowthLeft(c.growth_info(), c.capacity(), blocked_element_count);
896
268k
    ABSL_SWISSTABLE_ASSERT(c.blocked_element_count() == blocked_element_count);
897
268k
    c.infoz().RecordStorageChanged(0, c.capacity());
898
268k
  } else {
899
17.3k
    ClearBackingArrayNoReuse(c, policy, alloc);
900
17.3k
  }
901
285k
}
902
903
void DestroySlots(CommonFields& c, size_t slot_size,
904
0
                  DestroySlotFn destroy_slot) {
905
0
  ABSL_SWISSTABLE_ASSERT(!c.is_small());
906
0
  ABSL_SWISSTABLE_ASSERT(destroy_slot != nullptr);
907
0
  auto destroy_slot_wrapper = [&](const ctrl_t*, void* slot) {
908
0
    destroy_slot(&c, slot);
909
0
  };
910
0
  if constexpr (SwisstableGenerationsOrDebugEnabled()) {
911
0
    CommonFields common_copy(non_soo_tag_t{}, c);
912
0
    c.set_capacity(HashtableCapacity::CreateDestroyed());
913
0
    IterateOverFullSlotsImpl(common_copy, slot_size, destroy_slot_wrapper);
914
0
    c.set_capacity(common_copy.capacity());
915
  } else {
916
    IterateOverFullSlotsImpl(c, slot_size, destroy_slot_wrapper);
917
  }
918
0
}
919
920
void DeallocBackingArray(CommonFields& c, size_t slot_size, size_t slot_align,
921
41.8k
                         DeallocBackingArrayFn dealloc, void* alloc) {
922
41.8k
  const size_t cap = c.capacity();
923
41.8k
  c.infoz().Unregister();
924
41.8k
  dealloc(alloc, cap, c.control(), slot_size, slot_align, c.has_infoz(),
925
41.8k
          c.blocked_element_count());
926
41.8k
}
927
928
template <bool kSooEnabled>
929
void Clear(CommonFields& c, const PolicyFunctions& __restrict policy,
930
613k
           DestroySlotFn destroy_slot, void* alloc) {
931
613k
  if (SwisstableGenerationsEnabled() &&
932
0
      c.maybe_invalid_capacity().IsMovedFrom()) {
933
0
    c.set_capacity(policy.soo_capacity());
934
0
  }
935
613k
  c.AssertNotDebugCapacity();
936
613k
  const size_t cap = c.capacity();
937
613k
  if constexpr (kSooEnabled) {
938
37.6k
    ABSL_ASSUME(cap > 0);
939
37.6k
  }
940
613k
  if (c.is_small()) {
941
327k
    if (!c.empty()) {
942
148k
      if (destroy_slot != nullptr) {
943
0
        destroy_slot(&c, SingleSlotAddress<kSooEnabled>(c));
944
0
      }
945
148k
      DecrementSmallSize<kSooEnabled>(c);
946
148k
      c.infoz().RecordStorageChanged(0, cap);
947
148k
    }
948
327k
  } else {
949
285k
    if (destroy_slot != nullptr) {
950
0
      DestroySlots(c, policy.slot_size, destroy_slot);
951
0
    }
952
    // Iterating over this container is O(bucket_count()). When bucket_count()
953
    // is much greater than size(), iteration becomes prohibitively expensive.
954
    // For clear() it is more important to reuse the allocated array when the
955
    // container is small because allocation takes comparatively long time
956
    // compared to destruction of the elements of the container. So we pick the
957
    // largest bucket_count() threshold for which iteration is still fast and
958
    // past that we simply deallocate the array.
959
285k
    ClearBackingArray(c, policy, alloc, /*reuse=*/cap < 128);
960
285k
  }
961
613k
  c.set_reserved_growth(0);
962
613k
  c.set_reservation_size(0);
963
613k
}
void absl::container_internal::Clear<true>(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, void (*)(void*, void*), void*)
Line
Count
Source
930
37.6k
           DestroySlotFn destroy_slot, void* alloc) {
931
37.6k
  if (SwisstableGenerationsEnabled() &&
932
0
      c.maybe_invalid_capacity().IsMovedFrom()) {
933
0
    c.set_capacity(policy.soo_capacity());
934
0
  }
935
37.6k
  c.AssertNotDebugCapacity();
936
37.6k
  const size_t cap = c.capacity();
937
37.6k
  if constexpr (kSooEnabled) {
938
37.6k
    ABSL_ASSUME(cap > 0);
939
37.6k
  }
940
37.6k
  if (c.is_small()) {
941
13.7k
    if (!c.empty()) {
942
13.1k
      if (destroy_slot != nullptr) {
943
0
        destroy_slot(&c, SingleSlotAddress<kSooEnabled>(c));
944
0
      }
945
13.1k
      DecrementSmallSize<kSooEnabled>(c);
946
13.1k
      c.infoz().RecordStorageChanged(0, cap);
947
13.1k
    }
948
23.8k
  } else {
949
23.8k
    if (destroy_slot != nullptr) {
950
0
      DestroySlots(c, policy.slot_size, destroy_slot);
951
0
    }
952
    // Iterating over this container is O(bucket_count()). When bucket_count()
953
    // is much greater than size(), iteration becomes prohibitively expensive.
954
    // For clear() it is more important to reuse the allocated array when the
955
    // container is small because allocation takes comparatively long time
956
    // compared to destruction of the elements of the container. So we pick the
957
    // largest bucket_count() threshold for which iteration is still fast and
958
    // past that we simply deallocate the array.
959
23.8k
    ClearBackingArray(c, policy, alloc, /*reuse=*/cap < 128);
960
23.8k
  }
961
37.6k
  c.set_reserved_growth(0);
962
37.6k
  c.set_reservation_size(0);
963
37.6k
}
void absl::container_internal::Clear<false>(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, void (*)(void*, void*), void*)
Line
Count
Source
930
576k
           DestroySlotFn destroy_slot, void* alloc) {
931
576k
  if (SwisstableGenerationsEnabled() &&
932
0
      c.maybe_invalid_capacity().IsMovedFrom()) {
933
0
    c.set_capacity(policy.soo_capacity());
934
0
  }
935
576k
  c.AssertNotDebugCapacity();
936
576k
  const size_t cap = c.capacity();
937
  if constexpr (kSooEnabled) {
938
    ABSL_ASSUME(cap > 0);
939
  }
940
576k
  if (c.is_small()) {
941
313k
    if (!c.empty()) {
942
135k
      if (destroy_slot != nullptr) {
943
0
        destroy_slot(&c, SingleSlotAddress<kSooEnabled>(c));
944
0
      }
945
135k
      DecrementSmallSize<kSooEnabled>(c);
946
135k
      c.infoz().RecordStorageChanged(0, cap);
947
135k
    }
948
313k
  } else {
949
262k
    if (destroy_slot != nullptr) {
950
0
      DestroySlots(c, policy.slot_size, destroy_slot);
951
0
    }
952
    // Iterating over this container is O(bucket_count()). When bucket_count()
953
    // is much greater than size(), iteration becomes prohibitively expensive.
954
    // For clear() it is more important to reuse the allocated array when the
955
    // container is small because allocation takes comparatively long time
956
    // compared to destruction of the elements of the container. So we pick the
957
    // largest bucket_count() threshold for which iteration is still fast and
958
    // past that we simply deallocate the array.
959
262k
    ClearBackingArray(c, policy, alloc, /*reuse=*/cap < 128);
960
262k
  }
961
576k
  c.set_reserved_growth(0);
962
576k
  c.set_reservation_size(0);
963
576k
}
964
965
void DestructSoo(CommonFields& c, size_t slot_size, size_t slot_align,
966
                 DestroySlotFn destroy_slot, DeallocBackingArrayFn dealloc,
967
24.9k
                 void* alloc) {
968
24.9k
  ABSL_SWISSTABLE_ASSERT(!c.is_small() || !c.empty());
969
24.9k
  if (c.is_small()) {
970
0
    ABSL_SWISSTABLE_ASSERT(destroy_slot != nullptr);
971
0
    destroy_slot(&c, c.soo_data());
972
0
    return;
973
0
  }
974
24.9k
  if (destroy_slot != nullptr) {
975
0
    DestroySlots(c, slot_size, destroy_slot);
976
0
  }
977
24.9k
  DeallocBackingArray(c, slot_size, slot_align, dealloc, alloc);
978
24.9k
}
979
980
void DestructNonSoo(CommonFields& c, size_t slot_size, size_t slot_align,
981
                    DestroySlotFn destroy_slot, DeallocBackingArrayFn dealloc,
982
16.9k
                    void* alloc) {
983
16.9k
  ABSL_SWISSTABLE_ASSERT(c.capacity() > 0);
984
16.9k
  if (destroy_slot != nullptr) {
985
0
    if (c.is_small()) {
986
0
      if (!c.empty()) {
987
0
        static_assert(kMaxSmallCapacity == 1);
988
0
        destroy_slot(&c, c.slot_array(/*capacity=*/1));
989
0
      }
990
0
    } else {
991
0
      DestroySlots(c, slot_size, destroy_slot);
992
0
    }
993
0
  }
994
16.9k
  DeallocBackingArray(c, slot_size, slot_align, dealloc, alloc);
995
16.9k
}
996
997
namespace {
998
999
// Iterates over full slots in old table, finds new positions for them and
1000
// transfers the slots.
1001
// This function is used for reserving or rehashing non-empty tables.
1002
// This use case is rare so the function is type erased.
1003
// Returns the total probe length.
1004
size_t FindNewPositionsAndTransferSlots(
1005
    CommonFields& common, const PolicyFunctions& __restrict policy,
1006
0
    ctrl_t* old_ctrl, void* old_slots, size_t old_capacity) {
1007
0
  void* new_slots = common.slot_array(common.capacity());
1008
0
  const void* hash_fn = policy.hash_fn(common);
1009
0
  const size_t slot_size = policy.slot_size;
1010
0
  const size_t seed = common.seed().seed();
1011
1012
0
  const auto insert_slot = [&](void* slot) {
1013
0
    size_t hash = policy.hash_slot(hash_fn, slot, seed);
1014
0
    FindInfo target;
1015
0
    if (common.is_small()) {
1016
0
      target = FindInfo{0, 0};
1017
0
    } else {
1018
0
      target = find_first_non_full(common, hash);
1019
0
      SetCtrl(common, target.offset, H2(hash), slot_size);
1020
0
    }
1021
0
    policy.transfer_n(&common, SlotAddress(new_slots, target.offset, slot_size),
1022
0
                      slot, 1);
1023
0
    return target.probe_length;
1024
0
  };
1025
0
  if (IsSmallCapacity(old_capacity)) {
1026
0
    if (common.size() == 1) insert_slot(old_slots);
1027
0
    return 0;
1028
0
  }
1029
0
  size_t total_probe_length = 0;
1030
0
  for (size_t i = 0; i < old_capacity; ++i) {
1031
0
    if (IsFull(old_ctrl[i])) {
1032
0
      total_probe_length += insert_slot(old_slots);
1033
0
    }
1034
0
    old_slots = NextSlot(old_slots, slot_size);
1035
0
  }
1036
0
  return total_probe_length;
1037
0
}
1038
1039
void ReportGrowthToInfozImpl(CommonFields& common, HashtablezInfoHandle infoz,
1040
                             size_t hash, size_t total_probe_length,
1041
0
                             size_t distance_from_desired) {
1042
0
  ABSL_SWISSTABLE_ASSERT(infoz.IsSampled());
1043
0
  infoz.RecordStorageChanged(common.size() - 1, common.capacity());
1044
0
  infoz.RecordRehash(total_probe_length);
1045
0
  infoz.RecordInsertMiss(hash, distance_from_desired);
1046
0
  common.set_has_infoz();
1047
  // TODO(b/413062340): we could potentially store infoz in place of the
1048
  // control pointer for the capacity 1 case.
1049
0
  common.set_infoz(infoz);
1050
0
}
1051
1052
// Specialization to avoid passing two 0s from hot function.
1053
ABSL_ATTRIBUTE_NOINLINE void ReportSingleGroupTableGrowthToInfoz(
1054
0
    CommonFields& common, HashtablezInfoHandle infoz, size_t hash) {
1055
0
  ReportGrowthToInfozImpl(common, infoz, hash, /*total_probe_length=*/0,
1056
0
                          /*distance_from_desired=*/0);
1057
0
}
1058
1059
ABSL_ATTRIBUTE_NOINLINE void ReportGrowthToInfoz(CommonFields& common,
1060
                                                 HashtablezInfoHandle infoz,
1061
                                                 size_t hash,
1062
                                                 size_t total_probe_length,
1063
0
                                                 size_t distance_from_desired) {
1064
0
  ReportGrowthToInfozImpl(common, infoz, hash, total_probe_length,
1065
0
                          distance_from_desired);
1066
0
}
1067
1068
ABSL_ATTRIBUTE_NOINLINE void ReportResizeToInfoz(CommonFields& common,
1069
                                                 HashtablezInfoHandle infoz,
1070
0
                                                 size_t total_probe_length) {
1071
0
  ABSL_SWISSTABLE_ASSERT(infoz.IsSampled());
1072
0
  infoz.RecordStorageChanged(common.size(), common.capacity());
1073
0
  infoz.RecordRehash(total_probe_length);
1074
0
  common.set_has_infoz();
1075
0
  common.set_infoz(infoz);
1076
0
}
1077
1078
struct BackingArrayPtrs {
1079
  ctrl_t* ctrl;
1080
  void* slots;
1081
};
1082
1083
BackingArrayPtrs AllocBackingArray(CommonFields& common,
1084
                                   const PolicyFunctions& __restrict policy,
1085
                                   size_t new_capacity, bool has_infoz,
1086
267k
                                   void* alloc, size_t blocked_element_count) {
1087
267k
  RawHashSetLayout layout(new_capacity, policy.slot_size, policy.slot_align,
1088
267k
                          has_infoz, blocked_element_count);
1089
  // Perform a direct call in the common case to allow for profile-guided
1090
  // heap optimization (PGHO) to understand which allocation function is used.
1091
267k
  constexpr size_t kDefaultAlignment = BackingArrayAlignment(alignof(size_t));
1092
267k
  char* mem = static_cast<char*>(
1093
267k
      ABSL_PREDICT_TRUE(
1094
267k
          policy.alloc ==
1095
267k
          (&AllocateBackingArray<kDefaultAlignment, std::allocator<char>>))
1096
267k
          ? AllocateBackingArray<kDefaultAlignment, std::allocator<char>>(
1097
267k
                alloc, layout.alloc_size())
1098
267k
          : policy.alloc(alloc, layout.alloc_size()));
1099
267k
  const GenerationType old_generation = common.generation();
1100
267k
  common.set_generation_ptr(
1101
267k
      reinterpret_cast<GenerationType*>(mem + layout.generation_offset()));
1102
267k
  common.set_generation(NextGeneration(old_generation));
1103
1104
267k
  return {reinterpret_cast<ctrl_t*>(mem + layout.control_offset()),
1105
267k
          mem + layout.slot_offset()};
1106
267k
}
1107
1108
void ResizeEmptyNonAllocatedTableImpl(CommonFields& common,
1109
                                      const PolicyFunctions& __restrict policy,
1110
                                      size_t new_capacity,
1111
                                      size_t blocked_element_count,
1112
0
                                      bool force_infoz) {
1113
0
  ABSL_SWISSTABLE_ASSERT(IsValidCapacity(new_capacity));
1114
0
  ABSL_SWISSTABLE_ASSERT(new_capacity > policy.soo_capacity());
1115
0
  ABSL_SWISSTABLE_ASSERT(!force_infoz || policy.soo_enabled);
1116
0
  ABSL_SWISSTABLE_ASSERT(common.capacity() == policy.soo_capacity());
1117
0
  ABSL_SWISSTABLE_ASSERT(common.empty());
1118
0
  const size_t slot_size = policy.slot_size;
1119
0
  HashtablezInfoHandle infoz;
1120
0
  const bool should_sample =
1121
0
      policy.is_hashtablez_eligible && (force_infoz || ShouldSampleNextTable());
1122
0
  if (ABSL_PREDICT_FALSE(should_sample)) {
1123
0
    infoz = ForcedTrySample(slot_size, policy.key_size, policy.value_size,
1124
0
                            policy.soo_capacity());
1125
0
  }
1126
0
  const bool has_infoz = infoz.IsSampled();
1127
0
  void* alloc = policy.get_char_alloc(common);
1128
1129
0
  common.set_capacity(new_capacity);
1130
0
  common.init_blocked_element_count(blocked_element_count);
1131
0
  const auto [new_ctrl, new_slots] = AllocBackingArray(
1132
0
      common, policy, new_capacity, has_infoz, alloc, blocked_element_count);
1133
0
  common.set_control(new_ctrl);
1134
0
  common.generate_new_seed(has_infoz);
1135
1136
0
  ResetCtrl(common, slot_size, blocked_element_count);
1137
0
  if (GrowthInfoSizeForCapacity(new_capacity) > 0) {
1138
0
    ResetGrowthLeft(GetGrowthInfoFromControl(new_ctrl), new_capacity,
1139
0
                    blocked_element_count);
1140
0
  }
1141
1142
0
  if (ABSL_PREDICT_FALSE(has_infoz)) {
1143
0
    ReportResizeToInfoz(common, infoz, 0);
1144
0
  }
1145
0
}
1146
1147
// If the table was SOO, initializes new control bytes and transfers slot.
1148
// After transferring the slot, sets control and slots in CommonFields.
1149
// It is rare to resize an SOO table with one element to a large size.
1150
// Requires: `c` contains SOO data.
1151
void InsertOldSooSlotAndInitializeControlBytes(
1152
    CommonFields& c, const PolicyFunctions& __restrict policy, ctrl_t* new_ctrl,
1153
0
    void* new_slots, bool has_infoz) {
1154
0
  ABSL_SWISSTABLE_ASSERT(c.size() == policy.soo_capacity());
1155
0
  ABSL_SWISSTABLE_ASSERT(policy.soo_enabled);
1156
0
  const size_t new_capacity = c.capacity();
1157
1158
0
  c.generate_new_seed(has_infoz);
1159
1160
0
  const size_t soo_slot_hash =
1161
0
      policy.hash_slot(policy.hash_fn(c), c.soo_data(), c.seed().seed());
1162
0
  size_t offset = probe(ProbeCapacity{new_capacity}, soo_slot_hash).offset();
1163
0
  offset = offset == new_capacity ? 0 : offset;
1164
0
  SanitizerPoisonMemoryRegion(new_slots, policy.slot_size * new_capacity);
1165
0
  void* target_slot = SlotAddress(new_slots, offset, policy.slot_size);
1166
0
  SanitizerUnpoisonMemoryRegion(target_slot, policy.slot_size);
1167
0
  policy.transfer_n(&c, target_slot, c.soo_data(), 1);
1168
0
  c.set_control(new_ctrl);
1169
0
  ResetCtrl(c, policy.slot_size, /*blocked_element_count=*/0);
1170
0
  SetCtrl(c, offset, H2(soo_slot_hash), policy.slot_size);
1171
0
}
1172
1173
enum class ResizeFullSooTableSamplingMode {
1174
  kNoSampling,
1175
  // Force sampling. If the table was still not sampled, do not resize.
1176
  kForceSampleNoResizeIfUnsampled,
1177
};
1178
1179
void AssertSoo([[maybe_unused]] CommonFields& common,
1180
25.2k
               [[maybe_unused]] const PolicyFunctions& __restrict policy) {
1181
25.2k
  ABSL_SWISSTABLE_ASSERT(policy.soo_enabled);
1182
25.2k
  ABSL_SWISSTABLE_ASSERT(common.capacity() == policy.soo_capacity());
1183
25.2k
}
1184
void AssertFullSoo([[maybe_unused]] CommonFields& common,
1185
0
                   [[maybe_unused]] const PolicyFunctions& __restrict policy) {
1186
0
  AssertSoo(common, policy);
1187
0
  ABSL_SWISSTABLE_ASSERT(common.size() == policy.soo_capacity());
1188
0
}
1189
1190
void ResizeFullSooTable(CommonFields& common,
1191
                        const PolicyFunctions& __restrict policy,
1192
                        size_t new_capacity,
1193
0
                        ResizeFullSooTableSamplingMode sampling_mode) {
1194
0
  AssertFullSoo(common, policy);
1195
0
  const size_t slot_size = policy.slot_size;
1196
0
  void* alloc = policy.get_char_alloc(common);
1197
0
  constexpr size_t kTableSize = 1;
1198
1199
0
  HashtablezInfoHandle infoz;
1200
0
  bool has_infoz = false;
1201
0
  if (sampling_mode ==
1202
0
      ResizeFullSooTableSamplingMode::kForceSampleNoResizeIfUnsampled) {
1203
0
    if (ABSL_PREDICT_FALSE(policy.is_hashtablez_eligible)) {
1204
0
      infoz = ForcedTrySample(slot_size, policy.key_size, policy.value_size,
1205
0
                              policy.soo_capacity());
1206
0
    }
1207
1208
0
    if (!infoz.IsSampled()) return;
1209
0
    has_infoz = true;
1210
0
  }
1211
1212
0
  common.set_capacity(new_capacity);
1213
1214
  // We do not set control and slots in CommonFields yet to avoid overriding
1215
  // SOO data.
1216
0
  const auto [new_ctrl, new_slots] =
1217
0
      AllocBackingArray(common, policy, new_capacity, has_infoz, alloc,
1218
0
                        /*blocked_element_count=*/0);
1219
1220
0
  InsertOldSooSlotAndInitializeControlBytes(common, policy, new_ctrl, new_slots,
1221
0
                                            has_infoz);
1222
0
  ResetGrowthLeft(common.growth_info(), new_capacity, kTableSize);
1223
0
  if (has_infoz) {
1224
0
    common.set_has_infoz();
1225
0
    common.set_infoz(infoz);
1226
0
    infoz.RecordStorageChanged(kTableSize, new_capacity);
1227
0
  }
1228
0
}
1229
1230
void GrowIntoSingleGroupShuffleControlBytes(ctrl_t* __restrict old_ctrl,
1231
                                            size_t old_capacity,
1232
                                            size_t old_blocked_element_count,
1233
                                            ctrl_t* __restrict new_ctrl,
1234
69.2k
                                            size_t new_capacity) {
1235
69.2k
  ABSL_SWISSTABLE_ASSERT(is_single_group(new_capacity));
1236
69.2k
  constexpr size_t kHalfWidth = Group::kWidth / 2;
1237
69.2k
  ABSL_ASSUME(old_capacity < kHalfWidth);
1238
69.2k
  ABSL_ASSUME(old_capacity > 0);
1239
69.2k
  static_assert(Group::kWidth == 8 || Group::kWidth == 16,
1240
69.2k
                "Group size is not supported.");
1241
1242
  // NOTE: operations are done with compile time known size = 8.
1243
  // Compiler optimizes that into single ASM operation.
1244
1245
  // Load the bytes from old_capacity. This contains
1246
  // - the sentinel byte
1247
  // - all the old control bytes
1248
  // - the rest is filled with kEmpty bytes
1249
  // Example:
1250
  // old_ctrl =     012S012EEEEEEEEE...
1251
  // copied_bytes = S012EEEE
1252
  // Example with blocked elements:
1253
  // old_ctrl =     01SS01SEEEEEEEEE...
1254
  // copied_bytes = S01SEEEE
1255
69.2k
  uint64_t copied_bytes = absl::little_endian::Load64(old_ctrl + old_capacity);
1256
1257
  // We change the sentinel byte to kEmpty before storing to both the start of
1258
  // the new_ctrl, and past the end of the new_ctrl later for the new cloned
1259
  // bytes. Note that this is faster than setting the sentinel byte to kEmpty
1260
  // after the copy directly in new_ctrl because we are limited on store
1261
  // bandwidth.
1262
69.2k
  static constexpr uint64_t kEmptyXorSentinel =
1263
69.2k
      static_cast<uint8_t>(ctrl_t::kEmpty) ^
1264
69.2k
      static_cast<uint8_t>(ctrl_t::kSentinel);
1265
1266
  // Replace the first byte kSentinel with kEmpty.
1267
  // Resulting bytes will be shifted by one byte old control blocks.
1268
  // Example:
1269
  // old_ctrl = 012S012EEEEEEEEE...
1270
  // before =   S012EEEE
1271
  // after  =   E012EEEE
1272
69.2k
  copied_bytes ^= kEmptyXorSentinel;
1273
1274
69.2k
  if (ABSL_PREDICT_FALSE(old_blocked_element_count > 0)) {
1275
    // Replacing blocked sentinel elements with kEmpty.
1276
0
    static constexpr uint64_t kAllBytesEmptyXorSentinel =
1277
0
        kEmptyXorSentinel * uint64_t{0x0101010101010101};
1278
0
    uint64_t blocked_mask = kAllBytesEmptyXorSentinel;
1279
    // Keep old_blocked_element_count bytes in the mask.
1280
0
    blocked_mask >>= 64 - old_blocked_element_count * 8;
1281
    // Shift the mask to the start of the blocked elements bytes.
1282
0
    blocked_mask <<= (old_capacity - old_blocked_element_count + 1) * 8;
1283
    // Example with blocked elements:
1284
    // old_ctrl = 0SSS0SSEEEEEEEEE...
1285
    // before =   E0SSEEEE
1286
    // after  =   E0EEEEEE
1287
0
    copied_bytes ^= blocked_mask;
1288
0
  }
1289
1290
69.2k
  if (Group::kWidth == 8) {
1291
    // With group size 8, we can grow with two write operations.
1292
0
    ABSL_SWISSTABLE_ASSERT(old_capacity < 8 &&
1293
0
                           "old_capacity is too large for group size 8");
1294
0
    absl::little_endian::Store64(new_ctrl, copied_bytes);
1295
1296
0
    static constexpr uint64_t kSentinal64 =
1297
0
        static_cast<uint8_t>(ctrl_t::kSentinel);
1298
1299
    // Prepend kSentinel byte to the beginning of copied_bytes.
1300
    // We have maximum 3 non-empty bytes at the beginning of copied_bytes for
1301
    // group size 8.
1302
    // Example:
1303
    // old_ctrl = 012S012EEEE
1304
    // before =   E012EEEE
1305
    // after  =   SE012EEE
1306
0
    copied_bytes = (copied_bytes << 8) ^ kSentinal64;
1307
0
    absl::little_endian::Store64(new_ctrl + new_capacity, copied_bytes);
1308
    // Example for capacity 3:
1309
    // old_ctrl = 012S012EEEE
1310
    // After the first store:
1311
    //           >!
1312
    // new_ctrl = E012EEEE???????
1313
    // After the second store:
1314
    //                  >!
1315
    // new_ctrl = E012EEESE012EEE
1316
0
    return;
1317
0
  }
1318
1319
69.2k
  ABSL_SWISSTABLE_ASSERT(Group::kWidth == 16);  // NOLINT(misc-static-assert)
1320
1321
  // Fill the second half of the main control bytes with kEmpty.
1322
  // For small capacity that may write into mirrored control bytes.
1323
  // It is fine as we will overwrite all the bytes later.
1324
69.2k
  std::memset(new_ctrl + kHalfWidth, static_cast<int8_t>(ctrl_t::kEmpty),
1325
69.2k
              kHalfWidth);
1326
  // Fill the second half of the mirrored control bytes with kEmpty.
1327
69.2k
  std::memset(new_ctrl + new_capacity + kHalfWidth,
1328
69.2k
              static_cast<int8_t>(ctrl_t::kEmpty), kHalfWidth);
1329
  // Copy the first half of the non-mirrored control bytes.
1330
69.2k
  absl::little_endian::Store64(new_ctrl, copied_bytes);
1331
69.2k
  new_ctrl[new_capacity] = ctrl_t::kSentinel;
1332
  // Copy the first half of the mirrored control bytes.
1333
69.2k
  absl::little_endian::Store64(new_ctrl + new_capacity + 1, copied_bytes);
1334
1335
  // Example for growth capacity 1->3:
1336
  // old_ctrl =                  0S0EEEEEEEEEEEEEE
1337
  // new_ctrl at the end =       E0ESE0EEEEEEEEEEEEE
1338
  //                                    >!
1339
  // new_ctrl after 1st memset = ????????EEEEEEEE???
1340
  //                                       >!
1341
  // new_ctrl after 2nd memset = ????????EEEEEEEEEEE
1342
  //                            >!
1343
  // new_ctrl after 1st store =  E0EEEEEEEEEEEEEEEEE
1344
  // new_ctrl after kSentinel =  E0ESEEEEEEEEEEEEEEE
1345
  //                                >!
1346
  // new_ctrl after 2nd store =  E0ESE0EEEEEEEEEEEEE
1347
1348
  // Example for growth capacity 3->7:
1349
  // old_ctrl =                  012S012EEEEEEEEEEEE
1350
  // new_ctrl at the end =       E012EEESE012EEEEEEEEEEE
1351
  //                                    >!
1352
  // new_ctrl after 1st memset = ????????EEEEEEEE???????
1353
  //                                           >!
1354
  // new_ctrl after 2nd memset = ????????EEEEEEEEEEEEEEE
1355
  //                            >!
1356
  // new_ctrl after 1st store =  E012EEEEEEEEEEEEEEEEEEE
1357
  // new_ctrl after kSentinel =  E012EEESEEEEEEEEEEEEEEE
1358
  //                                >!
1359
  // new_ctrl after 2nd store =  E012EEESE012EEEEEEEEEEE
1360
1361
  // Example for growth capacity 7->15:
1362
  // old_ctrl =                  0123456S0123456EEEEEEEE
1363
  // new_ctrl at the end =       E0123456EEEEEEESE0123456EEEEEEE
1364
  //                                    >!
1365
  // new_ctrl after 1st memset = ????????EEEEEEEE???????????????
1366
  //                                                   >!
1367
  // new_ctrl after 2nd memset = ????????EEEEEEEE???????EEEEEEEE
1368
  //                            >!
1369
  // new_ctrl after 1st store =  E0123456EEEEEEEE???????EEEEEEEE
1370
  // new_ctrl after kSentinel =  E0123456EEEEEEES???????EEEEEEEE
1371
  //                                            >!
1372
  // new_ctrl after 2nd store =  E0123456EEEEEEESE0123456EEEEEEE
1373
69.2k
}
1374
1375
// Size of the buffer we allocate on stack for storing probed elements in
1376
// GrowToNextCapacity algorithm.
1377
constexpr size_t kProbedElementsBufferSize = 512;
1378
1379
// Decodes information about probed elements from contiguous memory.
1380
// Finds new position for each element and transfers it to the new slots.
1381
// Returns the total probe length.
1382
template <typename ProbedItem>
1383
ABSL_ATTRIBUTE_NOINLINE size_t DecodeAndInsertImpl(
1384
    CommonFields& c, const PolicyFunctions& __restrict policy,
1385
47.9k
    const ProbedItem* start, const ProbedItem* end, void* old_slots) {
1386
47.9k
  const HashtableCapacity new_capacity = c.capacity_impl();
1387
1388
47.9k
  void* new_slots = c.slot_array(new_capacity.capacity());
1389
47.9k
  ctrl_t* new_ctrl = c.control();
1390
47.9k
  size_t total_probe_length = 0;
1391
1392
47.9k
  const size_t slot_size = policy.slot_size;
1393
47.9k
  auto transfer_n = policy.transfer_n;
1394
1395
175k
  for (; start < end; ++start) {
1396
127k
    const FindInfo target = find_first_non_full_from_h1(
1397
127k
        new_ctrl, static_cast<size_t>(start->h1), new_capacity);
1398
127k
    total_probe_length += target.probe_length;
1399
127k
    const size_t old_index = static_cast<size_t>(start->source_offset);
1400
127k
    const size_t new_i = target.offset;
1401
127k
    ABSL_SWISSTABLE_ASSERT(old_index < new_capacity.capacity() / 2);
1402
127k
    ABSL_SWISSTABLE_ASSERT(new_i < new_capacity.capacity());
1403
127k
    ABSL_SWISSTABLE_ASSERT(IsEmpty(new_ctrl[new_i]));
1404
127k
    void* src_slot = SlotAddress(old_slots, old_index, slot_size);
1405
127k
    void* dst_slot = SlotAddress(new_slots, new_i, slot_size);
1406
127k
    SanitizerUnpoisonMemoryRegion(dst_slot, slot_size);
1407
127k
    transfer_n(&c, dst_slot, src_slot, 1);
1408
127k
    SetCtrlInLargeTable(c, new_i, static_cast<h2_t>(start->h2), slot_size);
1409
127k
  }
1410
47.9k
  return total_probe_length;
1411
47.9k
}
raw_hash_set.cc:unsigned long absl::container_internal::(anonymous namespace)::DecodeAndInsertImpl<absl::container_internal::ProbedItemImpl<unsigned int, 32ul> >(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::container_internal::ProbedItemImpl<unsigned int, 32ul> const*, absl::container_internal::ProbedItemImpl<unsigned int, 32ul> const*, void*)
Line
Count
Source
1385
47.9k
    const ProbedItem* start, const ProbedItem* end, void* old_slots) {
1386
47.9k
  const HashtableCapacity new_capacity = c.capacity_impl();
1387
1388
47.9k
  void* new_slots = c.slot_array(new_capacity.capacity());
1389
47.9k
  ctrl_t* new_ctrl = c.control();
1390
47.9k
  size_t total_probe_length = 0;
1391
1392
47.9k
  const size_t slot_size = policy.slot_size;
1393
47.9k
  auto transfer_n = policy.transfer_n;
1394
1395
175k
  for (; start < end; ++start) {
1396
127k
    const FindInfo target = find_first_non_full_from_h1(
1397
127k
        new_ctrl, static_cast<size_t>(start->h1), new_capacity);
1398
127k
    total_probe_length += target.probe_length;
1399
127k
    const size_t old_index = static_cast<size_t>(start->source_offset);
1400
127k
    const size_t new_i = target.offset;
1401
127k
    ABSL_SWISSTABLE_ASSERT(old_index < new_capacity.capacity() / 2);
1402
127k
    ABSL_SWISSTABLE_ASSERT(new_i < new_capacity.capacity());
1403
127k
    ABSL_SWISSTABLE_ASSERT(IsEmpty(new_ctrl[new_i]));
1404
127k
    void* src_slot = SlotAddress(old_slots, old_index, slot_size);
1405
127k
    void* dst_slot = SlotAddress(new_slots, new_i, slot_size);
1406
127k
    SanitizerUnpoisonMemoryRegion(dst_slot, slot_size);
1407
127k
    transfer_n(&c, dst_slot, src_slot, 1);
1408
127k
    SetCtrlInLargeTable(c, new_i, static_cast<h2_t>(start->h2), slot_size);
1409
127k
  }
1410
47.9k
  return total_probe_length;
1411
47.9k
}
Unexecuted instantiation: raw_hash_set.cc:unsigned long absl::container_internal::(anonymous namespace)::DecodeAndInsertImpl<absl::container_internal::ProbedItemImpl<unsigned long, 64ul> >(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::container_internal::ProbedItemImpl<unsigned long, 64ul> const*, absl::container_internal::ProbedItemImpl<unsigned long, 64ul> const*, void*)
Unexecuted instantiation: raw_hash_set.cc:unsigned long absl::container_internal::(anonymous namespace)::DecodeAndInsertImpl<absl::container_internal::ProbedItemImpl<unsigned long, 122ul> >(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::container_internal::ProbedItemImpl<unsigned long, 122ul> const*, absl::container_internal::ProbedItemImpl<unsigned long, 122ul> const*, void*)
1412
1413
// Sentinel value for the start of marked elements.
1414
// Signals that there are no marked elements.
1415
constexpr size_t kNoMarkedElementsSentinel = ~size_t{};
1416
1417
// Process probed elements that did not fit into available buffers.
1418
// We marked them in control bytes as kMarkedForSlowTransfer.
1419
// Hash recomputation and full probing is done here.
1420
// This use case should be extremely rare.
1421
ABSL_ATTRIBUTE_NOINLINE size_t ProcessProbedMarkedElements(
1422
    CommonFields& c, const PolicyFunctions& __restrict policy, ctrl_t* old_ctrl,
1423
0
    void* old_slots, size_t start) {
1424
0
  size_t old_capacity = PreviousCapacity(c.capacity());
1425
0
  const size_t slot_size = policy.slot_size;
1426
0
  void* new_slots = c.slot_array(c.capacity());
1427
0
  size_t total_probe_length = 0;
1428
0
  const void* hash_fn = policy.hash_fn(c);
1429
0
  auto hash_slot = policy.hash_slot;
1430
0
  auto transfer_n = policy.transfer_n;
1431
0
  const size_t seed = c.seed().seed();
1432
0
  for (size_t old_index = start; old_index < old_capacity; ++old_index) {
1433
0
    if (old_ctrl[old_index] != ctrl_t::kMarkedForSlowTransfer) {
1434
0
      continue;
1435
0
    }
1436
0
    void* src_slot = SlotAddress(old_slots, old_index, slot_size);
1437
0
    const size_t hash = hash_slot(hash_fn, src_slot, seed);
1438
0
    const FindInfo target = find_first_non_full(c, hash);
1439
0
    total_probe_length += target.probe_length;
1440
0
    const size_t new_i = target.offset;
1441
0
    void* dst_slot = SlotAddress(new_slots, new_i, slot_size);
1442
0
    SetCtrlInLargeTable(c, new_i, H2(hash), slot_size);
1443
0
    transfer_n(&c, dst_slot, src_slot, 1);
1444
0
  }
1445
0
  return total_probe_length;
1446
0
}
1447
1448
// The largest old capacity for which it is guaranteed that all probed elements
1449
// fit in ProbedItemEncoder's local buffer.
1450
// For such tables, `encode_probed_element` is trivial.
1451
constexpr size_t kMaxLocalBufferOldCapacity =
1452
    kProbedElementsBufferSize / sizeof(ProbedItem4Bytes) - 1;
1453
static_assert(IsValidCapacity(kMaxLocalBufferOldCapacity));
1454
constexpr size_t kMaxLocalBufferNewCapacity =
1455
    NextCapacity(kMaxLocalBufferOldCapacity);
1456
static_assert(kMaxLocalBufferNewCapacity <= ProbedItem4Bytes::kMaxNewCapacity);
1457
static_assert(NextCapacity(kMaxLocalBufferNewCapacity) <=
1458
              ProbedItem4Bytes::kMaxNewCapacity);
1459
1460
// Initializes mirrored control bytes after
1461
// transfer_unprobed_elements_to_next_capacity.
1462
107k
void InitializeMirroredControlBytes(ctrl_t* new_ctrl, size_t new_capacity) {
1463
107k
  std::memcpy(new_ctrl + new_capacity,
1464
              // We own GrowthInfo just before control bytes. So it is ok
1465
              // to read one byte from it.
1466
107k
              new_ctrl - 1, Group::kWidth);
1467
107k
  new_ctrl[new_capacity] = ctrl_t::kSentinel;
1468
107k
}
1469
1470
// Encodes probed elements into available memory.
1471
// At first, a local (on stack) buffer is used. The size of the buffer is
1472
// kProbedElementsBufferSize bytes.
1473
// When the local buffer is full, we switch to `control_` buffer. We are allowed
1474
// to overwrite `control_` buffer till the `source_offset` byte. In case we have
1475
// no space in `control_` buffer, we fallback to a naive algorithm for all the
1476
// rest of the probed elements. We mark elements as kSentinel in control bytes
1477
// and later process them fully. See ProcessMarkedElements for details. It
1478
// should be extremely rare.
1479
template <typename ProbedItemType,
1480
          // If true, we only use the local buffer and never switch to the
1481
          // control buffer.
1482
          bool kGuaranteedFitToBuffer = false>
1483
class ProbedItemEncoder {
1484
 public:
1485
  using ProbedItem = ProbedItemType;
1486
107k
  explicit ProbedItemEncoder(ctrl_t* control) : control_(control) {}
raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, true>::ProbedItemEncoder(absl::container_internal::ctrl_t*)
Line
Count
Source
1486
90.6k
  explicit ProbedItemEncoder(ctrl_t* control) : control_(control) {}
raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, false>::ProbedItemEncoder(absl::container_internal::ctrl_t*)
Line
Count
Source
1486
17.2k
  explicit ProbedItemEncoder(ctrl_t* control) : control_(control) {}
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 64ul>, false>::ProbedItemEncoder(absl::container_internal::ctrl_t*)
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 122ul>, false>::ProbedItemEncoder(absl::container_internal::ctrl_t*)
1487
1488
  // Encode item into the best available location.
1489
127k
  void EncodeItem(ProbedItem item) {
1490
127k
    if (ABSL_PREDICT_FALSE(!kGuaranteedFitToBuffer && pos_ >= end_)) {
1491
0
      return ProcessEncodeWithOverflow(item);
1492
0
    }
1493
127k
    ABSL_SWISSTABLE_ASSERT(pos_ < end_);
1494
127k
    *pos_ = item;
1495
127k
    ++pos_;
1496
127k
  }
raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, true>::EncodeItem(absl::container_internal::ProbedItemImpl<unsigned int, 32ul>)
Line
Count
Source
1489
93.7k
  void EncodeItem(ProbedItem item) {
1490
93.7k
    if (ABSL_PREDICT_FALSE(!kGuaranteedFitToBuffer && pos_ >= end_)) {
1491
0
      return ProcessEncodeWithOverflow(item);
1492
0
    }
1493
93.7k
    ABSL_SWISSTABLE_ASSERT(pos_ < end_);
1494
93.7k
    *pos_ = item;
1495
93.7k
    ++pos_;
1496
93.7k
  }
raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, false>::EncodeItem(absl::container_internal::ProbedItemImpl<unsigned int, 32ul>)
Line
Count
Source
1489
33.9k
  void EncodeItem(ProbedItem item) {
1490
33.9k
    if (ABSL_PREDICT_FALSE(!kGuaranteedFitToBuffer && pos_ >= end_)) {
1491
0
      return ProcessEncodeWithOverflow(item);
1492
0
    }
1493
33.9k
    ABSL_SWISSTABLE_ASSERT(pos_ < end_);
1494
33.9k
    *pos_ = item;
1495
33.9k
    ++pos_;
1496
33.9k
  }
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 64ul>, false>::EncodeItem(absl::container_internal::ProbedItemImpl<unsigned long, 64ul>)
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 122ul>, false>::EncodeItem(absl::container_internal::ProbedItemImpl<unsigned long, 122ul>)
1497
1498
  // Decodes information about probed elements from all available sources.
1499
  // Finds new position for each element and transfers it to the new slots.
1500
  // Returns the total probe length.
1501
  size_t DecodeAndInsertToTable(CommonFields& common,
1502
                                const PolicyFunctions& __restrict policy,
1503
107k
                                void* old_slots) const {
1504
107k
    if (pos_ == buffer_) {
1505
59.9k
      return 0;
1506
59.9k
    }
1507
47.9k
    if constexpr (kGuaranteedFitToBuffer) {
1508
39.0k
      return DecodeAndInsertImpl(common, policy, buffer_, pos_, old_slots);
1509
39.0k
    }
1510
0
    size_t total_probe_length = DecodeAndInsertImpl(
1511
47.9k
        common, policy, buffer_,
1512
47.9k
        local_buffer_full_ ? buffer_ + kBufferSize : pos_, old_slots);
1513
47.9k
    if (!local_buffer_full_) {
1514
8.90k
      return total_probe_length;
1515
8.90k
    }
1516
39.0k
    total_probe_length +=
1517
39.0k
        DecodeAndInsertToTableOverflow(common, policy, old_slots);
1518
39.0k
    return total_probe_length;
1519
47.9k
  }
raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, true>::DecodeAndInsertToTable(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, void*) const
Line
Count
Source
1503
90.6k
                                void* old_slots) const {
1504
90.6k
    if (pos_ == buffer_) {
1505
51.5k
      return 0;
1506
51.5k
    }
1507
39.0k
    if constexpr (kGuaranteedFitToBuffer) {
1508
39.0k
      return DecodeAndInsertImpl(common, policy, buffer_, pos_, old_slots);
1509
39.0k
    }
1510
0
    size_t total_probe_length = DecodeAndInsertImpl(
1511
39.0k
        common, policy, buffer_,
1512
39.0k
        local_buffer_full_ ? buffer_ + kBufferSize : pos_, old_slots);
1513
39.0k
    if (!local_buffer_full_) {
1514
0
      return total_probe_length;
1515
0
    }
1516
39.0k
    total_probe_length +=
1517
39.0k
        DecodeAndInsertToTableOverflow(common, policy, old_slots);
1518
39.0k
    return total_probe_length;
1519
39.0k
  }
raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, false>::DecodeAndInsertToTable(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, void*) const
Line
Count
Source
1503
17.2k
                                void* old_slots) const {
1504
17.2k
    if (pos_ == buffer_) {
1505
8.35k
      return 0;
1506
8.35k
    }
1507
    if constexpr (kGuaranteedFitToBuffer) {
1508
      return DecodeAndInsertImpl(common, policy, buffer_, pos_, old_slots);
1509
    }
1510
8.90k
    size_t total_probe_length = DecodeAndInsertImpl(
1511
8.90k
        common, policy, buffer_,
1512
8.90k
        local_buffer_full_ ? buffer_ + kBufferSize : pos_, old_slots);
1513
8.90k
    if (!local_buffer_full_) {
1514
8.90k
      return total_probe_length;
1515
8.90k
    }
1516
0
    total_probe_length +=
1517
0
        DecodeAndInsertToTableOverflow(common, policy, old_slots);
1518
0
    return total_probe_length;
1519
8.90k
  }
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 64ul>, false>::DecodeAndInsertToTable(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, void*) const
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 122ul>, false>::DecodeAndInsertToTable(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, void*) const
1520
1521
 private:
1522
0
  static ProbedItem* AlignToNextItem(void* ptr) {
1523
0
    return reinterpret_cast<ProbedItem*>(AlignUpTo(
1524
0
        reinterpret_cast<uintptr_t>(ptr), alignof(ProbedItem)));
1525
0
  }
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, false>::AlignToNextItem(void*)
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 64ul>, false>::AlignToNextItem(void*)
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 122ul>, false>::AlignToNextItem(void*)
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, true>::AlignToNextItem(void*)
1526
1527
0
  ProbedItem* OverflowBufferStart() const {
1528
0
    ABSL_SWISSTABLE_ASSERT(!kGuaranteedFitToBuffer &&
1529
0
                           "OverflowBufferStart should not be called when "
1530
0
                           "kGuaranteedFitToBuffer is true.");
1531
    // We reuse GrowthInfo memory as well.
1532
0
    return AlignToNextItem(
1533
0
        control_ - MetadataBeforeControlSize(/*has_infoz=*/false,
1534
0
                                 NextCapacity(kMaxLocalBufferOldCapacity)));
1535
0
  }
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, false>::OverflowBufferStart() const
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 64ul>, false>::OverflowBufferStart() const
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 122ul>, false>::OverflowBufferStart() const
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, true>::OverflowBufferStart() const
1536
1537
  // Encodes item when previously allocated buffer is full.
1538
  // At first that happens when local buffer is full.
1539
  // We switch from the local buffer to the control buffer.
1540
  // Every time this function is called, the available buffer is extended till
1541
  // `item.source_offset` byte in the control buffer.
1542
  // After the buffer is extended, this function wouldn't be called till the
1543
  // buffer is exhausted.
1544
  //
1545
  // If there's no space in the control buffer, we fallback to naive algorithm
1546
  // and mark probed elements as kMarkedForSlowTransfer in the control buffer.
1547
  // In this case, we will call this function for every subsequent probed
1548
  // element.
1549
0
  ABSL_ATTRIBUTE_NOINLINE void ProcessEncodeWithOverflow(ProbedItem item) {
1550
0
    if (!local_buffer_full_) {
1551
0
      local_buffer_full_ = true;
1552
0
      pos_ = OverflowBufferStart();
1553
0
    }
1554
0
    const size_t source_offset = static_cast<size_t>(item.source_offset);
1555
    // We are in fallback mode so we can't reuse control buffer anymore.
1556
    // Probed elements are marked as kMarkedForSlowTransfer in the control
1557
    // buffer.
1558
0
    if (ABSL_PREDICT_FALSE(marked_elements_starting_position_ !=
1559
0
                           kNoMarkedElementsSentinel)) {
1560
0
      control_[source_offset] = ctrl_t::kMarkedForSlowTransfer;
1561
0
      return;
1562
0
    }
1563
    // Refresh the end pointer to the new available position.
1564
    // Invariant: if pos < end, then we have at least sizeof(ProbedItem) bytes
1565
    // to write.
1566
0
    end_ = control_ + source_offset + 1 - sizeof(ProbedItem);
1567
0
    if (ABSL_PREDICT_TRUE(pos_ < end_)) {
1568
0
      *pos_ = item;
1569
0
      ++pos_;
1570
0
      return;
1571
0
    }
1572
0
    control_[source_offset] = ctrl_t::kMarkedForSlowTransfer;
1573
0
    marked_elements_starting_position_ = source_offset;
1574
    // Now we will always fall down to `ProcessEncodeWithOverflow`.
1575
0
    ABSL_SWISSTABLE_ASSERT(pos_ >= end_);
1576
0
  }
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, false>::ProcessEncodeWithOverflow(absl::container_internal::ProbedItemImpl<unsigned int, 32ul>)
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 64ul>, false>::ProcessEncodeWithOverflow(absl::container_internal::ProbedItemImpl<unsigned long, 64ul>)
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 122ul>, false>::ProcessEncodeWithOverflow(absl::container_internal::ProbedItemImpl<unsigned long, 122ul>)
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, true>::ProcessEncodeWithOverflow(absl::container_internal::ProbedItemImpl<unsigned int, 32ul>)
1577
1578
  // Decodes information about probed elements from control buffer and processes
1579
  // marked elements.
1580
  // Finds new position for each element and transfers it to the new slots.
1581
  // Returns the total probe length.
1582
  ABSL_ATTRIBUTE_NOINLINE size_t DecodeAndInsertToTableOverflow(
1583
      CommonFields& common, const PolicyFunctions& __restrict policy,
1584
0
      void* old_slots) const {
1585
0
    ABSL_SWISSTABLE_ASSERT(local_buffer_full_ &&
1586
0
                           "must not be called when local buffer is not full");
1587
0
    size_t total_probe_length = DecodeAndInsertImpl(
1588
0
        common, policy, OverflowBufferStart(), pos_, old_slots);
1589
0
    if (ABSL_PREDICT_TRUE(marked_elements_starting_position_ ==
1590
0
                          kNoMarkedElementsSentinel)) {
1591
0
      return total_probe_length;
1592
0
    }
1593
0
    total_probe_length +=
1594
0
        ProcessProbedMarkedElements(common, policy, control_, old_slots,
1595
0
                                    marked_elements_starting_position_);
1596
0
    return total_probe_length;
1597
0
  }
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, false>::DecodeAndInsertToTableOverflow(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, void*) const
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 64ul>, false>::DecodeAndInsertToTableOverflow(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, void*) const
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 122ul>, false>::DecodeAndInsertToTableOverflow(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, void*) const
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, true>::DecodeAndInsertToTableOverflow(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, void*) const
1598
1599
  static constexpr size_t kBufferSize =
1600
      kProbedElementsBufferSize / sizeof(ProbedItem);
1601
  ProbedItem buffer_[kBufferSize];
1602
  // If local_buffer_full_ is false, then pos_/end_ are in the local buffer,
1603
  // otherwise, they're in the overflow buffer.
1604
  ProbedItem* pos_ = buffer_;
1605
  const void* end_ = buffer_ + kBufferSize;
1606
  ctrl_t* const control_;
1607
  size_t marked_elements_starting_position_ = kNoMarkedElementsSentinel;
1608
  bool local_buffer_full_ = false;
1609
};
1610
1611
// Grows to next capacity with specified encoder type.
1612
// Encoder is used to store probed elements that are processed later.
1613
// Different encoder is used depending on the capacity of the table.
1614
// Returns total probe length.
1615
template <typename Encoder>
1616
size_t GrowToNextCapacity(CommonFields& common,
1617
                          const PolicyFunctions& __restrict policy,
1618
107k
                          ctrl_t* old_ctrl, void* old_slots) {
1619
107k
  using ProbedItem = typename Encoder::ProbedItem;
1620
107k
  ABSL_SWISSTABLE_ASSERT(common.capacity() <= ProbedItem::kMaxNewCapacity);
1621
107k
  Encoder encoder(old_ctrl);
1622
107k
  policy.transfer_unprobed_elements_to_next_capacity(
1623
107k
      common, old_ctrl, old_slots, &encoder,
1624
127k
      [](void* probed_storage, h2_t h2, size_t source_offset, size_t h1) {
1625
127k
        auto encoder_ptr = static_cast<Encoder*>(probed_storage);
1626
127k
        encoder_ptr->EncodeItem(ProbedItem(h2, source_offset, h1));
1627
127k
      });
raw_hash_set.cc:absl::container_internal::(anonymous namespace)::GrowToNextCapacity<absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, true> >(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::container_internal::ctrl_t*, void*)::{lambda(void*, unsigned char, unsigned long, unsigned long)#1}::operator()(void*, unsigned char, unsigned long, unsigned long) const
Line
Count
Source
1624
93.7k
      [](void* probed_storage, h2_t h2, size_t source_offset, size_t h1) {
1625
93.7k
        auto encoder_ptr = static_cast<Encoder*>(probed_storage);
1626
93.7k
        encoder_ptr->EncodeItem(ProbedItem(h2, source_offset, h1));
1627
93.7k
      });
raw_hash_set.cc:absl::container_internal::(anonymous namespace)::GrowToNextCapacity<absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, false> >(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::container_internal::ctrl_t*, void*)::{lambda(void*, unsigned char, unsigned long, unsigned long)#1}::operator()(void*, unsigned char, unsigned long, unsigned long) const
Line
Count
Source
1624
33.9k
      [](void* probed_storage, h2_t h2, size_t source_offset, size_t h1) {
1625
33.9k
        auto encoder_ptr = static_cast<Encoder*>(probed_storage);
1626
33.9k
        encoder_ptr->EncodeItem(ProbedItem(h2, source_offset, h1));
1627
33.9k
      });
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::GrowToNextCapacity<absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 64ul>, false> >(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::container_internal::ctrl_t*, void*)::{lambda(void*, unsigned char, unsigned long, unsigned long)#1}::operator()(void*, unsigned char, unsigned long, unsigned long) const
Unexecuted instantiation: raw_hash_set.cc:absl::container_internal::(anonymous namespace)::GrowToNextCapacity<absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 122ul>, false> >(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::container_internal::ctrl_t*, void*)::{lambda(void*, unsigned char, unsigned long, unsigned long)#1}::operator()(void*, unsigned char, unsigned long, unsigned long) const
1628
107k
  InitializeMirroredControlBytes(common.control(), common.capacity());
1629
107k
  return encoder.DecodeAndInsertToTable(common, policy, old_slots);
1630
107k
}
raw_hash_set.cc:unsigned long absl::container_internal::(anonymous namespace)::GrowToNextCapacity<absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, true> >(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::container_internal::ctrl_t*, void*)
Line
Count
Source
1618
90.6k
                          ctrl_t* old_ctrl, void* old_slots) {
1619
90.6k
  using ProbedItem = typename Encoder::ProbedItem;
1620
90.6k
  ABSL_SWISSTABLE_ASSERT(common.capacity() <= ProbedItem::kMaxNewCapacity);
1621
90.6k
  Encoder encoder(old_ctrl);
1622
90.6k
  policy.transfer_unprobed_elements_to_next_capacity(
1623
90.6k
      common, old_ctrl, old_slots, &encoder,
1624
90.6k
      [](void* probed_storage, h2_t h2, size_t source_offset, size_t h1) {
1625
90.6k
        auto encoder_ptr = static_cast<Encoder*>(probed_storage);
1626
90.6k
        encoder_ptr->EncodeItem(ProbedItem(h2, source_offset, h1));
1627
90.6k
      });
1628
90.6k
  InitializeMirroredControlBytes(common.control(), common.capacity());
1629
90.6k
  return encoder.DecodeAndInsertToTable(common, policy, old_slots);
1630
90.6k
}
raw_hash_set.cc:unsigned long absl::container_internal::(anonymous namespace)::GrowToNextCapacity<absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned int, 32ul>, false> >(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::container_internal::ctrl_t*, void*)
Line
Count
Source
1618
17.2k
                          ctrl_t* old_ctrl, void* old_slots) {
1619
17.2k
  using ProbedItem = typename Encoder::ProbedItem;
1620
17.2k
  ABSL_SWISSTABLE_ASSERT(common.capacity() <= ProbedItem::kMaxNewCapacity);
1621
17.2k
  Encoder encoder(old_ctrl);
1622
17.2k
  policy.transfer_unprobed_elements_to_next_capacity(
1623
17.2k
      common, old_ctrl, old_slots, &encoder,
1624
17.2k
      [](void* probed_storage, h2_t h2, size_t source_offset, size_t h1) {
1625
17.2k
        auto encoder_ptr = static_cast<Encoder*>(probed_storage);
1626
17.2k
        encoder_ptr->EncodeItem(ProbedItem(h2, source_offset, h1));
1627
17.2k
      });
1628
17.2k
  InitializeMirroredControlBytes(common.control(), common.capacity());
1629
17.2k
  return encoder.DecodeAndInsertToTable(common, policy, old_slots);
1630
17.2k
}
Unexecuted instantiation: raw_hash_set.cc:unsigned long absl::container_internal::(anonymous namespace)::GrowToNextCapacity<absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 64ul>, false> >(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::container_internal::ctrl_t*, void*)
Unexecuted instantiation: raw_hash_set.cc:unsigned long absl::container_internal::(anonymous namespace)::GrowToNextCapacity<absl::container_internal::(anonymous namespace)::ProbedItemEncoder<absl::container_internal::ProbedItemImpl<unsigned long, 122ul>, false> >(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::container_internal::ctrl_t*, void*)
1631
1632
// Grows to next capacity for relatively small tables so that even if all
1633
// elements are probed, we don't need to overflow the local buffer.
1634
// Returns total probe length.
1635
size_t GrowToNextCapacityThatFitsInLocalBuffer(
1636
    CommonFields& common, const PolicyFunctions& __restrict policy,
1637
90.6k
    ctrl_t* old_ctrl, void* old_slots) {
1638
90.6k
  ABSL_SWISSTABLE_ASSERT(common.capacity() <= kMaxLocalBufferNewCapacity);
1639
90.6k
  return GrowToNextCapacity<
1640
90.6k
      ProbedItemEncoder<ProbedItem4Bytes, /*kGuaranteedFitToBuffer=*/true>>(
1641
90.6k
      common, policy, old_ctrl, old_slots);
1642
90.6k
}
1643
1644
// Grows to next capacity with different encodings. Returns total probe length.
1645
// These functions are useful to simplify profile analysis.
1646
size_t GrowToNextCapacity4BytesEncoder(CommonFields& common,
1647
                                       const PolicyFunctions& __restrict policy,
1648
17.2k
                                       ctrl_t* old_ctrl, void* old_slots) {
1649
17.2k
  return GrowToNextCapacity<ProbedItemEncoder<ProbedItem4Bytes>>(
1650
17.2k
      common, policy, old_ctrl, old_slots);
1651
17.2k
}
1652
size_t GrowToNextCapacity8BytesEncoder(CommonFields& common,
1653
                                       const PolicyFunctions& __restrict policy,
1654
0
                                       ctrl_t* old_ctrl, void* old_slots) {
1655
0
  return GrowToNextCapacity<ProbedItemEncoder<ProbedItem8Bytes>>(
1656
0
      common, policy, old_ctrl, old_slots);
1657
0
}
1658
size_t GrowToNextCapacity16BytesEncoder(
1659
    CommonFields& common, const PolicyFunctions& __restrict policy,
1660
0
    ctrl_t* old_ctrl, void* old_slots) {
1661
0
  return GrowToNextCapacity<ProbedItemEncoder<ProbedItem16Bytes>>(
1662
0
      common, policy, old_ctrl, old_slots);
1663
0
}
1664
1665
// Grows to next capacity for tables with relatively large capacity so that we
1666
// can't guarantee that all probed elements fit in the local buffer. Returns
1667
// total probe length.
1668
size_t GrowToNextCapacityOverflowLocalBuffer(
1669
    CommonFields& common, const PolicyFunctions& __restrict policy,
1670
17.2k
    ctrl_t* old_ctrl, void* old_slots) {
1671
17.2k
  const size_t new_capacity = common.capacity();
1672
17.2k
  if (ABSL_PREDICT_TRUE(new_capacity <= ProbedItem4Bytes::kMaxNewCapacity)) {
1673
17.2k
    return GrowToNextCapacity4BytesEncoder(common, policy, old_ctrl, old_slots);
1674
17.2k
  }
1675
0
  if (ABSL_PREDICT_TRUE(new_capacity <= ProbedItem8Bytes::kMaxNewCapacity)) {
1676
0
    return GrowToNextCapacity8BytesEncoder(common, policy, old_ctrl, old_slots);
1677
0
  }
1678
  // 16 bytes encoding supports the maximum swisstable capacity.
1679
0
  return GrowToNextCapacity16BytesEncoder(common, policy, old_ctrl, old_slots);
1680
0
}
1681
1682
// Dispatches to the appropriate `GrowToNextCapacity*` function based on the
1683
// capacity of the table. Returns total probe length.
1684
ABSL_ATTRIBUTE_NOINLINE
1685
size_t GrowToNextCapacityDispatch(CommonFields& common,
1686
                                  const PolicyFunctions& __restrict policy,
1687
107k
                                  ctrl_t* old_ctrl, void* old_slots) {
1688
107k
  const size_t new_capacity = common.capacity();
1689
107k
  if (ABSL_PREDICT_TRUE(new_capacity <= kMaxLocalBufferNewCapacity)) {
1690
90.6k
    return GrowToNextCapacityThatFitsInLocalBuffer(common, policy, old_ctrl,
1691
90.6k
                                                   old_slots);
1692
90.6k
  } else {
1693
17.2k
    return GrowToNextCapacityOverflowLocalBuffer(common, policy, old_ctrl,
1694
17.2k
                                                 old_slots);
1695
17.2k
  }
1696
107k
}
1697
1698
void IncrementSmallSizeNonSoo(CommonFields& common,
1699
135k
                              const PolicyFunctions& __restrict policy) {
1700
135k
  ABSL_SWISSTABLE_ASSERT(common.is_small());
1701
135k
  common.increment_size();
1702
135k
  SanitizerUnpoisonMemoryRegion(
1703
135k
      SingleSlotAddress</*kSooEnabled=*/false>(common), policy.slot_size);
1704
135k
}
1705
1706
void IncrementSmallSize(CommonFields& common,
1707
0
                        const PolicyFunctions& __restrict policy) {
1708
0
  ABSL_SWISSTABLE_ASSERT(common.is_small());
1709
0
  if (policy.soo_enabled) {
1710
0
    common.set_full_soo();
1711
0
  } else {
1712
0
    IncrementSmallSizeNonSoo(common, policy);
1713
0
  }
1714
0
}
1715
1716
void* Grow1To3AndPrepareInsert(CommonFields& common,
1717
                               const PolicyFunctions& __restrict policy,
1718
31.3k
                               absl::FunctionRef<size_t(size_t)> get_hash) {
1719
  // TODO(b/413062340): Refactor to reuse more code with
1720
  // GrowSooTableToNextCapacityAndPrepareInsert.
1721
31.3k
  ABSL_SWISSTABLE_ASSERT(common.capacity() == 1);
1722
31.3k
  ABSL_SWISSTABLE_ASSERT(!common.empty());
1723
31.3k
  ABSL_SWISSTABLE_ASSERT(!policy.soo_enabled);
1724
  // 1-element tables can't have any blocked elements.
1725
31.3k
  ABSL_SWISSTABLE_ASSERT(common.blocked_element_count() == 0);
1726
31.3k
  constexpr size_t kOldCapacity = 1;
1727
31.3k
  constexpr size_t kNewCapacity = NextCapacity(kOldCapacity);
1728
31.3k
  void* old_slots = common.slot_array(kOldCapacity);
1729
1730
31.3k
  const size_t slot_size = policy.slot_size;
1731
31.3k
  const size_t slot_align = policy.slot_align;
1732
31.3k
  void* alloc = policy.get_char_alloc(common);
1733
31.3k
  HashtablezInfoHandle infoz = common.infoz();
1734
31.3k
  const bool has_infoz = infoz.IsSampled();
1735
31.3k
  common.set_capacity(kNewCapacity);
1736
1737
31.3k
  const auto [new_ctrl, new_slots] =
1738
31.3k
      AllocBackingArray(common, policy, kNewCapacity, has_infoz, alloc,
1739
31.3k
                        /*blocked_element_count=*/0);
1740
31.3k
  common.set_control(new_ctrl);
1741
31.3k
  SanitizerPoisonMemoryRegion(new_slots, kNewCapacity * slot_size);
1742
1743
31.3k
  if (ABSL_PREDICT_TRUE(!has_infoz)) {
1744
    // When we're sampled, we already have a seed.
1745
31.3k
    common.generate_new_seed(/*has_infoz=*/false);
1746
31.3k
  }
1747
31.3k
  const size_t new_hash = get_hash(common.seed().seed());
1748
31.3k
  h2_t new_h2 = H2(new_hash);
1749
31.3k
  size_t orig_hash =
1750
31.3k
      policy.hash_slot(policy.hash_fn(common), old_slots, common.seed().seed());
1751
31.3k
  size_t offset = Resize1To3NewOffset(new_hash, common.seed());
1752
31.3k
  InitializeThreeElementsControlBytes(H2(orig_hash), new_h2, offset, new_ctrl);
1753
1754
31.3k
  void* old_element_target = NextSlot(new_slots, slot_size);
1755
31.3k
  SanitizerUnpoisonMemoryRegion(old_element_target, slot_size);
1756
31.3k
  policy.transfer_n(&common, old_element_target, old_slots, 1);
1757
1758
31.3k
  void* new_element_target_slot = SlotAddress(new_slots, offset, slot_size);
1759
31.3k
  SanitizerUnpoisonMemoryRegion(new_element_target_slot, slot_size);
1760
1761
31.3k
  policy.dealloc(alloc, kOldCapacity,
1762
                 // old_slots == old_ctrl in case of capacity == 1.
1763
31.3k
                 static_cast<ctrl_t*>(old_slots),
1764
31.3k
                 slot_size, slot_align, has_infoz,
1765
31.3k
                 /*blocked_element_count=*/0);
1766
31.3k
  PrepareInsertCommon(common);
1767
31.3k
  ABSL_SWISSTABLE_ASSERT(common.size() == 2);
1768
31.3k
  GetGrowthInfoFromControl(new_ctrl).InitGrowthLeftNoDeleted(kNewCapacity - 2,
1769
31.3k
                                                             kNewCapacity);
1770
1771
31.3k
  if (ABSL_PREDICT_FALSE(has_infoz)) {
1772
0
    ReportSingleGroupTableGrowthToInfoz(common, infoz, new_hash);
1773
0
  }
1774
31.3k
  return new_element_target_slot;
1775
31.3k
}
1776
1777
// Grows to next capacity and prepares insert for the given new_hash.
1778
// Returns the offset of the new element.
1779
void* GrowToNextCapacityAndPrepareInsert(
1780
    CommonFields& common, const PolicyFunctions& __restrict policy,
1781
177k
    size_t new_hash) {
1782
177k
  const size_t old_capacity = common.capacity();
1783
177k
  ABSL_SWISSTABLE_ASSERT(
1784
177k
      common.growth_info().GetGrowthLeftTotalSlow(old_capacity) == 0);
1785
177k
  ABSL_SWISSTABLE_ASSERT(old_capacity > policy.soo_capacity());
1786
177k
  ABSL_SWISSTABLE_ASSERT(!IsSmallCapacity(old_capacity));
1787
177k
  ABSL_ASSUME(old_capacity > kMaxSmallCapacity);
1788
1789
177k
  const size_t new_capacity = NextCapacity(old_capacity);
1790
177k
  ctrl_t* old_ctrl = common.control();
1791
177k
  void* old_slots = common.slot_array(old_capacity);
1792
177k
  size_t old_blocked_element_count = common.blocked_element_count();
1793
1794
177k
  HashtablezInfoHandle infoz = common.infoz();
1795
177k
  const bool has_infoz = infoz.IsSampled();
1796
177k
  common.set_capacity(new_capacity);
1797
177k
  common.set_blocked_element_count_to_zero();
1798
177k
  const size_t slot_size = policy.slot_size;
1799
177k
  const size_t slot_align = policy.slot_align;
1800
177k
  void* alloc = policy.get_char_alloc(common);
1801
1802
177k
  const auto [new_ctrl, new_slots] =
1803
177k
      AllocBackingArray(common, policy, new_capacity, has_infoz, alloc,
1804
177k
                        /*blocked_element_count=*/0);
1805
177k
  common.set_control(new_ctrl);
1806
177k
  SanitizerPoisonMemoryRegion(new_slots, new_capacity * slot_size);
1807
1808
177k
  h2_t new_h2 = H2(new_hash);
1809
177k
  size_t total_probe_length = 0;
1810
177k
  FindInfo find_info;
1811
177k
  if (ABSL_PREDICT_TRUE(is_single_group(new_capacity))) {
1812
69.2k
    size_t offset;
1813
69.2k
    const size_t old_size = common.size();
1814
69.2k
    GrowIntoSingleGroupShuffleControlBytes(old_ctrl, old_capacity,
1815
69.2k
                                           old_blocked_element_count, new_ctrl,
1816
69.2k
                                           new_capacity);
1817
    // We put the new element either at the beginning or at the end of the
1818
    // table with approximately equal probability.
1819
69.2k
    offset =
1820
69.2k
        SingleGroupTableH1(new_hash, common.seed()) & 1 ? 0 : new_capacity - 1;
1821
1822
69.2k
    ABSL_SWISSTABLE_ASSERT(IsEmpty(new_ctrl[offset]));
1823
69.2k
    SetCtrlInSingleGroupTable(common, offset, new_h2, policy.slot_size);
1824
69.2k
    find_info = FindInfo{offset, 0};
1825
    // Single group tables have all slots full on resize. So we can transfer
1826
    // all slots without checking the control bytes.
1827
69.2k
    ABSL_SWISSTABLE_ASSERT(common.size() + old_blocked_element_count ==
1828
69.2k
                           old_capacity);
1829
69.2k
    void* target = NextSlot(new_slots, slot_size);
1830
69.2k
    SanitizerUnpoisonMemoryRegion(target, old_size * slot_size);
1831
69.2k
    policy.transfer_n(&common, target, old_slots, old_size);
1832
107k
  } else {
1833
107k
    total_probe_length =
1834
107k
        GrowToNextCapacityDispatch(common, policy, old_ctrl, old_slots);
1835
107k
    find_info = find_first_non_full(common, new_hash);
1836
107k
    SetCtrlInLargeTable(common, find_info.offset, new_h2, policy.slot_size);
1837
107k
  }
1838
177k
  ABSL_SWISSTABLE_ASSERT(old_capacity > policy.soo_capacity());
1839
177k
  (*policy.dealloc)(alloc, old_capacity, old_ctrl, slot_size, slot_align,
1840
177k
                    has_infoz, old_blocked_element_count);
1841
177k
  PrepareInsertCommon(common);
1842
177k
  ResetGrowthLeft(GetGrowthInfoFromControl(new_ctrl), new_capacity,
1843
177k
                  common.size());
1844
1845
177k
  if (ABSL_PREDICT_FALSE(has_infoz)) {
1846
0
    ReportGrowthToInfoz(common, infoz, new_hash, total_probe_length,
1847
0
                        find_info.probe_length);
1848
0
  }
1849
177k
  return SlotAddress(new_slots, find_info.offset, policy.slot_size);
1850
177k
}
1851
1852
}  // namespace
1853
1854
void* PrepareInsertSmallNonSoo(CommonFields& common,
1855
                               const PolicyFunctions& __restrict policy,
1856
200k
                               absl::FunctionRef<size_t(size_t)> get_hash) {
1857
200k
  ABSL_SWISSTABLE_ASSERT(common.is_small());
1858
200k
  ABSL_SWISSTABLE_ASSERT(!policy.soo_enabled);
1859
200k
  if (common.capacity() == 1) {
1860
166k
    if (common.empty()) {
1861
135k
      IncrementSmallSizeNonSoo(common, policy);
1862
135k
      if (common.has_infoz()) {
1863
0
        common.infoz().RecordInsertMiss(get_hash(common.seed().seed()),
1864
0
                                        /*distance_from_desired=*/0);
1865
0
      }
1866
135k
      return common.slot_array(/*capacity=*/1);
1867
135k
    } else {
1868
31.3k
      return Grow1To3AndPrepareInsert(common, policy, get_hash);
1869
31.3k
    }
1870
166k
  }
1871
1872
  // Growing from 0 to 1 capacity.
1873
34.0k
  ABSL_SWISSTABLE_ASSERT(common.capacity() == 0);
1874
34.0k
  constexpr size_t kNewCapacity = 1;
1875
1876
34.0k
  common.set_capacity(kNewCapacity);
1877
34.0k
  HashtablezInfoHandle infoz;
1878
34.0k
  const bool should_sample =
1879
34.0k
      policy.is_hashtablez_eligible && ShouldSampleNextTable();
1880
34.0k
  if (ABSL_PREDICT_FALSE(should_sample)) {
1881
0
    infoz = ForcedTrySample(policy.slot_size, policy.key_size,
1882
0
                            policy.value_size, policy.soo_capacity());
1883
0
  }
1884
34.0k
  const bool has_infoz = infoz.IsSampled();
1885
34.0k
  void* alloc = policy.get_char_alloc(common);
1886
1887
34.0k
  const auto [new_ctrl, new_slots] =
1888
34.0k
      AllocBackingArray(common, policy, kNewCapacity, has_infoz, alloc,
1889
34.0k
                        /*blocked_element_count=*/0);
1890
34.0k
  common.set_control(new_ctrl);
1891
1892
34.0k
  static_assert(NextCapacity(0) == 1);
1893
34.0k
  PrepareInsertCommon(common);
1894
1895
34.0k
  if (ABSL_PREDICT_FALSE(has_infoz)) {
1896
0
    common.generate_new_seed(/*has_infoz=*/true);
1897
0
    ReportSingleGroupTableGrowthToInfoz(common, infoz,
1898
0
                                        get_hash(common.seed().seed()));
1899
0
  }
1900
34.0k
  return new_slots;
1901
34.0k
}
1902
1903
namespace {
1904
1905
// Called whenever the table needs to vacate empty slots either by removing
1906
// tombstones via rehash or growth to next capacity.
1907
ABSL_ATTRIBUTE_NOINLINE
1908
void* RehashOrGrowToNextCapacityAndPrepareInsert(
1909
    CommonFields& common, const PolicyFunctions& __restrict policy,
1910
0
    size_t new_hash) {
1911
0
  ABSL_SWISSTABLE_ASSERT(
1912
0
      !common.growth_info().GetGrowthInfoLowerBound().HasNoDeleted());
1913
0
  const size_t cap = common.capacity();
1914
0
  ABSL_ASSUME(cap > 0);
1915
  // Do these calculations in 64-bit to avoid overflow.
1916
0
  if (common.size() * uint64_t{32} <=
1917
0
      (cap - kMaxBlockedElementsForLargeTables) * uint64_t{25}) {
1918
    // Squash DELETED without growing if there is enough capacity.
1919
    //
1920
    // Rehash in place if the current size is <= 25/32 of capacity.
1921
    // Rationale for such a high factor: 1) DropDeletesWithoutResize() is
1922
    // faster than resize, and 2) it takes quite a bit of work to add
1923
    // tombstones.  In the worst case, seems to take approximately 4
1924
    // insert/erase pairs to create a single tombstone and so if we are
1925
    // rehashing because of tombstones, we can afford to rehash-in-place as
1926
    // long as we are reclaiming at least 1/8 the capacity without doing more
1927
    // than 2X the work.  (Where "work" is defined to be size() for rehashing
1928
    // or rehashing in place, and 1 for an insert or erase.)  But rehashing in
1929
    // place is faster per operation than inserting or even doubling the size
1930
    // of the table, so we actually afford to reclaim even less space from a
1931
    // resize-in-place.  The decision is to rehash in place if we can reclaim
1932
    // at about 1/8th of the usable capacity (specifically 3/28 of the
1933
    // capacity) which means that the total cost of rehashing will be a small
1934
    // fraction of the total work.
1935
    //
1936
    // Here is output of an experiment using the BM_CacheInSteadyState
1937
    // benchmark running the old case (where we rehash-in-place only if we can
1938
    // reclaim at least 7/16*capacity) vs. this code (which rehashes in place
1939
    // if we can recover 3/32*capacity).
1940
    //
1941
    // Note that although in the worst-case number of rehashes jumped up from
1942
    // 15 to 190, but the number of operations per second is almost the same.
1943
    //
1944
    // Abridged output of running BM_CacheInSteadyState benchmark from
1945
    // raw_hash_set_benchmark.   N is the number of insert/erase operations.
1946
    //
1947
    //      | OLD (recover >= 7/16        | NEW (recover >= 3/32)
1948
    // size |    N/s LoadFactor NRehashes |    N/s LoadFactor NRehashes
1949
    //  448 | 145284       0.44        18 | 140118       0.44        19
1950
    //  493 | 152546       0.24        11 | 151417       0.48        28
1951
    //  538 | 151439       0.26        11 | 151152       0.53        38
1952
    //  583 | 151765       0.28        11 | 150572       0.57        50
1953
    //  628 | 150241       0.31        11 | 150853       0.61        66
1954
    //  672 | 149602       0.33        12 | 150110       0.66        90
1955
    //  717 | 149998       0.35        12 | 149531       0.70       129
1956
    //  762 | 149836       0.37        13 | 148559       0.74       190
1957
    //  807 | 149736       0.39        14 | 151107       0.39        14
1958
    //  852 | 150204       0.42        15 | 151019       0.42        15
1959
0
    return DropDeletesWithoutResizeAndPrepareInsert(common, policy, new_hash);
1960
0
  } else {
1961
    // Otherwise grow the container.
1962
0
    return GrowToNextCapacityAndPrepareInsert(common, policy, new_hash);
1963
0
  }
1964
0
}
1965
1966
// Slow path for PrepareInsertLarge that is called when the table has deleted
1967
// slots or need to be resized or rehashed.
1968
ABSL_ATTRIBUTE_NOINLINE
1969
void* PrepareInsertLargeSlow(CommonFields& common,
1970
                             const PolicyFunctions& __restrict policy,
1971
183k
                             size_t hash) {
1972
183k
  GrowthInfoAccessor growth_info = common.growth_info();
1973
183k
  const size_t cap = common.capacity();
1974
183k
  ABSL_ASSUME(cap > kMaxSmallCapacity);
1975
183k
  GrowthInfoLowerBound growth_info_lower_bound =
1976
183k
      growth_info.RebalanceGrowthLeftLowerBound(cap);
1977
183k
  if (ABSL_PREDICT_TRUE(
1978
183k
          growth_info_lower_bound.HasNoGrowthLeftAndNoDeleted())) {
1979
    // Table without deleted slots (>95% cases) that needs to be resized.
1980
177k
    return GrowToNextCapacityAndPrepareInsert(common, policy, hash);
1981
177k
  }
1982
6.00k
  if (ABSL_PREDICT_FALSE(
1983
6.00k
          growth_info_lower_bound.HasNoGrowthLeftAndHaveDeleted())) {
1984
    // Table with deleted slots that needs to be rehashed or resized.
1985
0
    return RehashOrGrowToNextCapacityAndPrepareInsert(common, policy, hash);
1986
0
  }
1987
  // Covers two cases:
1988
  // 1. Table with deleted slots that has space for the inserting element.
1989
  // 2. Table without deleted slots that has space and GrowthInfoView was
1990
  //    rebalanced.
1991
6.00k
  FindInfo target = find_first_non_full(common, hash);
1992
6.00k
  PrepareInsertCommon(common);
1993
6.00k
  growth_info.OverwriteControlAsFull(common.control()[target.offset]);
1994
6.00k
  SetCtrlInLargeTable(common, target.offset, H2(hash), policy.slot_size);
1995
6.00k
  common.infoz().RecordInsertMiss(hash, target.probe_length);
1996
6.00k
  return SlotAddress(common.slot_array(cap), target.offset, policy.slot_size);
1997
6.00k
}
1998
1999
// Resizes empty non-allocated SOO table to NextCapacity(SooCapacity()),
2000
// forces the table to be sampled and prepares the insert.
2001
// SOO tables need to switch from SOO to heap in order to store the infoz.
2002
// Requires:
2003
//   1. `c.capacity() == SooCapacity()`.
2004
//   2. `c.empty()`.
2005
ABSL_ATTRIBUTE_NOINLINE void*
2006
GrowEmptySooTableToNextCapacityForceSamplingAndPrepareInsert(
2007
    CommonFields& common, const PolicyFunctions& __restrict policy,
2008
0
    absl::FunctionRef<size_t(size_t)> get_hash) {
2009
0
  const size_t kNewCapacity = NextCapacity(SooCapacity());
2010
0
  ResizeEmptyNonAllocatedTableImpl(common, policy, kNewCapacity,
2011
0
                                   /*blocked_element_count=*/0,
2012
0
                                   /*force_infoz=*/true);
2013
0
  PrepareInsertCommon(common);
2014
0
  common.growth_info().OverwriteEmptyAsFull();
2015
0
  const size_t new_hash = get_hash(common.seed().seed());
2016
0
  SetCtrlInSingleGroupTable(common, SooSlotIndex(), H2(new_hash),
2017
0
                            policy.slot_size);
2018
0
  common.infoz().RecordInsertMiss(new_hash, /*distance_from_desired=*/0);
2019
0
  return SlotAddress(common.slot_array(kNewCapacity), SooSlotIndex(),
2020
0
                     policy.slot_size);
2021
0
}
2022
2023
// Returns the number of elements to block for the given capacity and reserved
2024
// size.
2025
size_t BlockedElementCountForReservedTable(size_t capacity,
2026
0
                                           size_t reserved_size) {
2027
0
  if (!IsCapacityValidForBlockedElements(capacity)) {
2028
0
    return 0;
2029
0
  }
2030
0
  const size_t blocked_elements = CapacityToGrowth(capacity) - reserved_size;
2031
0
  if (is_single_group(capacity)) {
2032
    // Single group tables never probes, so we can block all the slots.
2033
0
    return blocked_elements;
2034
0
  }
2035
0
  return (std::min)(blocked_elements, kMaxBlockedElementsForLargeTables);
2036
0
}
2037
2038
// Resizes empty non-allocated table to the capacity to fit new_size elements.
2039
// Requires:
2040
//   1. `c.capacity() == policy.soo_capacity()`.
2041
//   2. `c.empty()`.
2042
//   3. `new_size > policy.soo_capacity()`.
2043
// The table will be attempted to be sampled.
2044
void ReserveEmptyNonAllocatedTableToFitNewSize(
2045
    CommonFields& common, const PolicyFunctions& __restrict policy,
2046
0
    size_t new_size) {
2047
0
  ValidateMaxSize(new_size, policy.key_size, policy.slot_size);
2048
0
  ABSL_ASSUME(new_size > 0);
2049
0
  const size_t new_capacity = SizeToCapacity(new_size);
2050
0
  ResizeEmptyNonAllocatedTableImpl(
2051
0
      common, policy, new_capacity,
2052
0
      BlockedElementCountForReservedTable(new_capacity, new_size),
2053
0
      /*force_infoz=*/false);
2054
  // This is after resize, to ensure that we have completed the allocation
2055
  // and have potentially sampled the hashtable.
2056
0
  common.infoz().RecordReservation(new_size);
2057
0
}
2058
2059
// Type erased version of raw_hash_set::reserve for tables that have an
2060
// allocated backing array.
2061
//
2062
// Requires:
2063
//   1. `c.capacity() > policy.soo_capacity()` OR `!c.empty()`.
2064
// Reserving already allocated tables is considered to be a rare case.
2065
ABSL_ATTRIBUTE_NOINLINE void ReserveAllocatedTable(
2066
    CommonFields& common, const PolicyFunctions& __restrict policy,
2067
0
    size_t new_size) {
2068
0
  const size_t cap = common.capacity();
2069
0
  ValidateMaxSize(new_size, policy.key_size, policy.slot_size);
2070
0
  ABSL_ASSUME(new_size > 0);
2071
0
  const size_t new_capacity = SizeToCapacity(new_size);
2072
0
  if (cap == policy.soo_capacity()) {
2073
0
    ABSL_SWISSTABLE_ASSERT(!common.empty());
2074
0
    ResizeFullSooTable(common, policy, new_capacity,
2075
0
                       ResizeFullSooTableSamplingMode::kNoSampling);
2076
0
  } else {
2077
0
    ABSL_SWISSTABLE_ASSERT(cap > policy.soo_capacity());
2078
    // TODO(b/382423690): consider using GrowToNextCapacity, when applicable.
2079
0
    ResizeAllocatedTableWithSeedChange(common, policy, new_capacity);
2080
0
  }
2081
0
  common.infoz().RecordReservation(new_size);
2082
0
}
2083
2084
// As `ResizeFullSooTableToNextCapacity`, except that we also force the SOO
2085
// table to be sampled. SOO tables need to switch from SOO to heap in order to
2086
// store the infoz. No-op if sampling is disabled or not possible.
2087
void GrowFullSooTableToNextCapacityForceSampling(
2088
0
    CommonFields& common, const PolicyFunctions& __restrict policy) {
2089
0
  AssertFullSoo(common, policy);
2090
0
  ResizeFullSooTable(
2091
0
      common, policy, NextCapacity(SooCapacity()),
2092
0
      ResizeFullSooTableSamplingMode::kForceSampleNoResizeIfUnsampled);
2093
0
}
2094
2095
}  // namespace
2096
2097
324k
void* GetRefForEmptyClass(CommonFields& common) {
2098
  // Empty base optimization typically make the empty base class address to be
2099
  // the same as the first address of the derived class object.
2100
  // But we generally assume that for empty classes we can return any valid
2101
  // pointer.
2102
324k
  return &common;
2103
324k
}
2104
2105
void ResizeAllocatedTableWithSeedChange(
2106
    CommonFields& common, const PolicyFunctions& __restrict policy,
2107
0
    size_t new_capacity) {
2108
0
  ABSL_SWISSTABLE_ASSERT(IsValidCapacity(new_capacity));
2109
0
  ABSL_SWISSTABLE_ASSERT(new_capacity > policy.soo_capacity());
2110
2111
0
  const size_t old_capacity = common.capacity();
2112
0
  ctrl_t* const old_ctrl = common.control();
2113
0
  void* const old_slots = common.slot_array(old_capacity);
2114
0
  const size_t old_blocked_element_count = common.blocked_element_count();
2115
2116
0
  const size_t slot_size = policy.slot_size;
2117
0
  const size_t slot_align = policy.slot_align;
2118
0
  HashtablezInfoHandle infoz = common.infoz();
2119
0
  const bool has_infoz = infoz.IsSampled();
2120
0
  void* alloc = policy.get_char_alloc(common);
2121
2122
0
  common.set_capacity(new_capacity);
2123
0
  common.set_blocked_element_count_to_zero();
2124
0
  const auto [new_ctrl, new_slots] =
2125
0
      AllocBackingArray(common, policy, new_capacity, has_infoz, alloc,
2126
0
                        /*blocked_element_count=*/0);
2127
0
  common.set_control(new_ctrl);
2128
0
  common.generate_new_seed(has_infoz);
2129
2130
0
  size_t total_probe_length = 0;
2131
0
  ResetCtrl(common, slot_size, /*blocked_element_count=*/0);
2132
0
  ABSL_SWISSTABLE_ASSERT(old_capacity > 0);
2133
0
  total_probe_length = FindNewPositionsAndTransferSlots(
2134
0
      common, policy, old_ctrl, old_slots, old_capacity);
2135
0
  (*policy.dealloc)(alloc, old_capacity, old_ctrl, slot_size, slot_align,
2136
0
                    has_infoz, old_blocked_element_count);
2137
0
  if (GrowthInfoSizeForCapacity(new_capacity) > 0) {
2138
0
    ResetGrowthLeft(GetGrowthInfoFromControl(new_ctrl), new_capacity,
2139
0
                    common.size());
2140
0
  }
2141
2142
0
  if (ABSL_PREDICT_FALSE(has_infoz)) {
2143
0
    ReportResizeToInfoz(common, infoz, total_probe_length);
2144
0
  }
2145
0
}
2146
2147
// Resizes a full SOO table to the NextCapacity(SooCapacity()).
2148
template <size_t SooSlotMemcpySize, bool TransferUsesMemcpy>
2149
void* GrowSooTableToNextCapacityAndPrepareInsert(
2150
    CommonFields& common, const PolicyFunctions& __restrict policy,
2151
25.2k
    absl::FunctionRef<size_t(size_t)> get_hash, bool force_sampling) {
2152
25.2k
  AssertSoo(common, policy);
2153
25.2k
  if (ABSL_PREDICT_FALSE(force_sampling)) {
2154
    // The table is empty, it is only used for forced sampling of SOO tables.
2155
0
    return GrowEmptySooTableToNextCapacityForceSamplingAndPrepareInsert(
2156
0
        common, policy, get_hash);
2157
0
  }
2158
25.2k
  ABSL_SWISSTABLE_ASSERT(common.size() == policy.soo_capacity());
2159
25.2k
  static constexpr size_t kNewCapacity = NextCapacity(SooCapacity());
2160
25.2k
  const size_t slot_size = policy.slot_size;
2161
25.2k
  void* alloc = policy.get_char_alloc(common);
2162
25.2k
  common.set_capacity(kNewCapacity);
2163
2164
  // Since the table is not empty, it will not be sampled.
2165
  // The decision to sample was already made during the first insertion.
2166
  //
2167
  // We do not set control and slots in CommonFields yet to avoid overriding
2168
  // SOO data.
2169
25.2k
  const auto [new_ctrl, new_slots] = AllocBackingArray(
2170
25.2k
      common, policy, kNewCapacity, /*has_infoz=*/false, alloc,
2171
25.2k
      /*blocked_element_count=*/0);
2172
2173
25.2k
  PrepareInsertCommon(common);
2174
25.2k
  ABSL_SWISSTABLE_ASSERT(common.size() == 2);
2175
25.2k
  GetGrowthInfoFromControl(new_ctrl).InitGrowthLeftNoDeleted(kNewCapacity - 2,
2176
25.2k
                                                             kNewCapacity);
2177
25.2k
  common.generate_new_seed(/*has_infoz=*/false);
2178
25.2k
  const h2_t soo_slot_h2 = H2(policy.hash_slot(
2179
25.2k
      policy.hash_fn(common), common.soo_data(), common.seed().seed()));
2180
25.2k
  const size_t new_hash = get_hash(common.seed().seed());
2181
2182
25.2k
  const size_t offset = Resize1To3NewOffset(new_hash, common.seed());
2183
25.2k
  InitializeThreeElementsControlBytes(soo_slot_h2, H2(new_hash), offset,
2184
25.2k
                                      new_ctrl);
2185
2186
25.2k
  SanitizerPoisonMemoryRegion(new_slots, slot_size * kNewCapacity);
2187
25.2k
  void* target_slot = SlotAddress(new_slots, SooSlotIndex(), slot_size);
2188
25.2k
  SanitizerUnpoisonMemoryRegion(target_slot, slot_size);
2189
25.2k
  if constexpr (TransferUsesMemcpy) {
2190
    // Target slot is placed at index 1, but capacity is at
2191
    // minimum 3. So we are allowed to copy at least twice as much
2192
    // memory.
2193
25.2k
    static_assert(SooSlotIndex() == 1);
2194
25.2k
    static_assert(SooSlotMemcpySize > 0);
2195
25.2k
    static_assert(SooSlotMemcpySize <= MaxSooSlotSize());
2196
25.2k
    ABSL_SWISSTABLE_ASSERT(SooSlotMemcpySize <= 2 * slot_size);
2197
25.2k
    ABSL_SWISSTABLE_ASSERT(SooSlotMemcpySize >= slot_size);
2198
25.2k
    void* next_slot = SlotAddress(target_slot, 1, slot_size);
2199
25.2k
    SanitizerUnpoisonMemoryRegion(next_slot, SooSlotMemcpySize - slot_size);
2200
25.2k
    std::memcpy(target_slot, common.soo_data(), SooSlotMemcpySize);
2201
25.2k
    SanitizerPoisonMemoryRegion(next_slot, SooSlotMemcpySize - slot_size);
2202
25.2k
  } else {
2203
0
    static_assert(SooSlotMemcpySize == 0);
2204
0
    policy.transfer_n(&common, target_slot, common.soo_data(), 1);
2205
0
  }
2206
0
  common.set_control(new_ctrl);
2207
2208
  // Full SOO table couldn't be sampled. If SOO table is sampled, it would
2209
  // have been resized to the next capacity.
2210
25.2k
  ABSL_SWISSTABLE_ASSERT(!common.infoz().IsSampled());
2211
25.2k
  void* new_slot = SlotAddress(new_slots, offset, slot_size);
2212
25.2k
  SanitizerUnpoisonMemoryRegion(new_slot, slot_size);
2213
25.2k
  return new_slot;
2214
25.2k
}
Unexecuted instantiation: void* absl::container_internal::GrowSooTableToNextCapacityAndPrepareInsert<0ul, false>(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::FunctionRef<unsigned long (unsigned long)>, bool)
Unexecuted instantiation: void* absl::container_internal::GrowSooTableToNextCapacityAndPrepareInsert<1ul, true>(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::FunctionRef<unsigned long (unsigned long)>, bool)
Unexecuted instantiation: void* absl::container_internal::GrowSooTableToNextCapacityAndPrepareInsert<4ul, true>(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::FunctionRef<unsigned long (unsigned long)>, bool)
void* absl::container_internal::GrowSooTableToNextCapacityAndPrepareInsert<8ul, true>(absl::container_internal::CommonFields&, absl::container_internal::PolicyFunctions const&, absl::FunctionRef<unsigned long (unsigned long)>, bool)
Line
Count
Source
2151
25.2k
    absl::FunctionRef<size_t(size_t)> get_hash, bool force_sampling) {
2152
25.2k
  AssertSoo(common, policy);
2153
25.2k
  if (ABSL_PREDICT_FALSE(force_sampling)) {
2154
    // The table is empty, it is only used for forced sampling of SOO tables.
2155
0
    return GrowEmptySooTableToNextCapacityForceSamplingAndPrepareInsert(
2156
0
        common, policy, get_hash);
2157
0
  }
2158
25.2k
  ABSL_SWISSTABLE_ASSERT(common.size() == policy.soo_capacity());
2159
25.2k
  static constexpr size_t kNewCapacity = NextCapacity(SooCapacity());
2160
25.2k
  const size_t slot_size = policy.slot_size;
2161
25.2k
  void* alloc = policy.get_char_alloc(common);
2162
25.2k
  common.set_capacity(kNewCapacity);
2163
2164
  // Since the table is not empty, it will not be sampled.
2165
  // The decision to sample was already made during the first insertion.
2166
  //
2167
  // We do not set control and slots in CommonFields yet to avoid overriding
2168
  // SOO data.
2169
25.2k
  const auto [new_ctrl, new_slots] = AllocBackingArray(
2170
25.2k
      common, policy, kNewCapacity, /*has_infoz=*/false, alloc,
2171
25.2k
      /*blocked_element_count=*/0);
2172
2173
25.2k
  PrepareInsertCommon(common);
2174
25.2k
  ABSL_SWISSTABLE_ASSERT(common.size() == 2);
2175
25.2k
  GetGrowthInfoFromControl(new_ctrl).InitGrowthLeftNoDeleted(kNewCapacity - 2,
2176
25.2k
                                                             kNewCapacity);
2177
25.2k
  common.generate_new_seed(/*has_infoz=*/false);
2178
25.2k
  const h2_t soo_slot_h2 = H2(policy.hash_slot(
2179
25.2k
      policy.hash_fn(common), common.soo_data(), common.seed().seed()));
2180
25.2k
  const size_t new_hash = get_hash(common.seed().seed());
2181
2182
25.2k
  const size_t offset = Resize1To3NewOffset(new_hash, common.seed());
2183
25.2k
  InitializeThreeElementsControlBytes(soo_slot_h2, H2(new_hash), offset,
2184
25.2k
                                      new_ctrl);
2185
2186
25.2k
  SanitizerPoisonMemoryRegion(new_slots, slot_size * kNewCapacity);
2187
25.2k
  void* target_slot = SlotAddress(new_slots, SooSlotIndex(), slot_size);
2188
25.2k
  SanitizerUnpoisonMemoryRegion(target_slot, slot_size);
2189
25.2k
  if constexpr (TransferUsesMemcpy) {
2190
    // Target slot is placed at index 1, but capacity is at
2191
    // minimum 3. So we are allowed to copy at least twice as much
2192
    // memory.
2193
25.2k
    static_assert(SooSlotIndex() == 1);
2194
25.2k
    static_assert(SooSlotMemcpySize > 0);
2195
25.2k
    static_assert(SooSlotMemcpySize <= MaxSooSlotSize());
2196
25.2k
    ABSL_SWISSTABLE_ASSERT(SooSlotMemcpySize <= 2 * slot_size);
2197
25.2k
    ABSL_SWISSTABLE_ASSERT(SooSlotMemcpySize >= slot_size);
2198
25.2k
    void* next_slot = SlotAddress(target_slot, 1, slot_size);
2199
25.2k
    SanitizerUnpoisonMemoryRegion(next_slot, SooSlotMemcpySize - slot_size);
2200
25.2k
    std::memcpy(target_slot, common.soo_data(), SooSlotMemcpySize);
2201
25.2k
    SanitizerPoisonMemoryRegion(next_slot, SooSlotMemcpySize - slot_size);
2202
  } else {
2203
    static_assert(SooSlotMemcpySize == 0);
2204
    policy.transfer_n(&common, target_slot, common.soo_data(), 1);
2205
  }
2206
0
  common.set_control(new_ctrl);
2207
2208
  // Full SOO table couldn't be sampled. If SOO table is sampled, it would
2209
  // have been resized to the next capacity.
2210
25.2k
  ABSL_SWISSTABLE_ASSERT(!common.infoz().IsSampled());
2211
25.2k
  void* new_slot = SlotAddress(new_slots, offset, slot_size);
2212
25.2k
  SanitizerUnpoisonMemoryRegion(new_slot, slot_size);
2213
25.2k
  return new_slot;
2214
25.2k
}
2215
2216
void Rehash(CommonFields& common, const PolicyFunctions& __restrict policy,
2217
0
            size_t n) {
2218
0
  const size_t cap = common.capacity();
2219
2220
0
  auto clear_backing_array = [&]() {
2221
0
    ClearBackingArrayNoReuse(common, policy, policy.get_char_alloc(common));
2222
0
  };
2223
2224
0
  const size_t slot_size = policy.slot_size;
2225
2226
0
  if (n == 0) {
2227
0
    if (cap <= policy.soo_capacity()) return;
2228
0
    if (common.empty()) {
2229
0
      clear_backing_array();
2230
0
      return;
2231
0
    }
2232
0
    if (common.size() <= policy.soo_capacity()) {
2233
      // When the table is already sampled, we keep it sampled.
2234
0
      if (common.infoz().IsSampled()) {
2235
0
        static constexpr size_t kInitialSampledCapacity =
2236
0
            NextCapacity(SooCapacity());
2237
0
        if (cap > kInitialSampledCapacity) {
2238
0
          ResizeAllocatedTableWithSeedChange(common, policy,
2239
0
                                             kInitialSampledCapacity);
2240
0
        }
2241
        // This asserts that we didn't lose sampling coverage in `resize`.
2242
0
        ABSL_SWISSTABLE_ASSERT(common.infoz().IsSampled());
2243
0
        return;
2244
0
      }
2245
0
      ABSL_SWISSTABLE_ASSERT(slot_size <= sizeof(HeapOrSoo));
2246
0
      ABSL_SWISSTABLE_ASSERT(policy.slot_align <= alignof(HeapOrSoo));
2247
0
      HeapOrSoo tmp_slot;
2248
0
      size_t begin_offset = FindFirstFullSlot(0, cap, common.control());
2249
0
      policy.transfer_n(
2250
0
          &common, &tmp_slot,
2251
0
          SlotAddress(common.slot_array(cap), begin_offset, slot_size), 1);
2252
0
      clear_backing_array();
2253
0
      policy.transfer_n(&common, common.soo_data(), &tmp_slot, 1);
2254
0
      common.set_full_soo();
2255
0
      return;
2256
0
    }
2257
0
  }
2258
2259
  // bitor is a faster way of doing `max` here. We will round up to the next
2260
  // power-of-2-minus-1, so bitor is good enough.
2261
0
  const size_t new_capacity =
2262
0
      NormalizeCapacity(n | SizeToCapacity(common.size()));
2263
0
  ValidateMaxCapacity(new_capacity, policy.key_size, policy.slot_size);
2264
  // n == 0 unconditionally rehashes as per the standard.
2265
0
  if (n == 0 || new_capacity > cap) {
2266
0
    if (cap == policy.soo_capacity()) {
2267
0
      if (common.empty()) {
2268
0
        ResizeEmptyNonAllocatedTableImpl(common, policy, new_capacity,
2269
0
                                         /*blocked_element_count=*/0,
2270
0
                                         /*force_infoz=*/false);
2271
0
      } else {
2272
0
        ResizeFullSooTable(common, policy, new_capacity,
2273
0
                           ResizeFullSooTableSamplingMode::kNoSampling);
2274
0
      }
2275
0
    } else {
2276
0
      ResizeAllocatedTableWithSeedChange(common, policy, new_capacity);
2277
0
    }
2278
    // This is after resize, to ensure that we have completed the allocation
2279
    // and have potentially sampled the hashtable.
2280
0
    common.infoz().RecordReservation(n);
2281
0
  }
2282
0
}
2283
2284
void Copy(CommonFields& common, const PolicyFunctions& __restrict policy,
2285
          const CommonFields& other,
2286
0
          absl::FunctionRef<void(void*, const void*)> copy_fn) {
2287
0
  const size_t size = other.size();
2288
0
  ABSL_SWISSTABLE_ASSERT(size > 0);
2289
0
  const size_t soo_capacity = policy.soo_capacity();
2290
0
  const size_t slot_size = policy.slot_size;
2291
0
  const bool soo_enabled = policy.soo_enabled;
2292
0
  if (size == 1) {
2293
0
    if (!soo_enabled) {
2294
0
      ReserveEmptyNonAllocatedTableToFitNewSize(common, policy, 1);
2295
0
      common.infoz().RecordStorageChanged(1, 1);
2296
0
    }
2297
0
    IncrementSmallSize(common, policy);
2298
0
    const size_t other_capacity = other.capacity();
2299
0
    const void* other_slot =
2300
0
        other_capacity <= soo_capacity ? other.soo_data()
2301
0
        : IsSmallCapacity(other_capacity)
2302
0
            ? other.slot_array(other_capacity)
2303
0
            : SlotAddress(other.slot_array(other_capacity),
2304
0
                          FindFirstFullSlot(0, other_capacity, other.control()),
2305
0
                          slot_size);
2306
0
    copy_fn(soo_enabled ? common.soo_data()
2307
0
                        : SingleSlotAddress</*kSooEnabled=*/false>(common),
2308
0
            other_slot);
2309
2310
0
    if (soo_enabled && policy.is_hashtablez_eligible &&
2311
0
        ShouldSampleNextTable()) {
2312
0
      GrowFullSooTableToNextCapacityForceSampling(common, policy);
2313
0
    }
2314
0
    return;
2315
0
  }
2316
2317
0
  ReserveTableToFitNewSize(common, policy, size);
2318
0
  const size_t blocked_element_count = common.blocked_element_count();
2319
0
  auto infoz = common.infoz();
2320
0
  ABSL_SWISSTABLE_ASSERT(other.capacity() > soo_capacity);
2321
0
  const size_t cap = common.capacity();
2322
0
  ABSL_SWISSTABLE_ASSERT(cap > soo_capacity);
2323
0
  ABSL_ASSUME(cap > kMaxSmallCapacity);
2324
0
  size_t offset = cap;
2325
0
  const void* hash_fn = policy.hash_fn(common);
2326
0
  auto hasher = policy.hash_slot;
2327
0
  const size_t seed = common.seed().seed();
2328
0
  void* target_slot_array = common.slot_array(cap);
2329
0
  IterateOverFullSlotsImpl(
2330
0
      other, slot_size, [&](const ctrl_t*, void* that_slot) {
2331
        // The table is guaranteed to be empty, so we can do faster than
2332
        // a full `insert`.
2333
0
        const size_t hash = (*hasher)(hash_fn, that_slot, seed);
2334
0
        FindInfo target = find_first_non_full(common, hash);
2335
0
        infoz.RecordInsertMiss(hash, target.probe_length);
2336
0
        offset = target.offset;
2337
0
        SetCtrl(common, offset, H2(hash), slot_size);
2338
0
        copy_fn(SlotAddress(target_slot_array, offset, slot_size), that_slot);
2339
0
        common.maybe_increment_generation_on_insert();
2340
0
      });
2341
0
  common.increment_size(size);
2342
0
  ResetGrowthLeft(common.growth_info(), cap, size + blocked_element_count);
2343
0
}
2344
2345
void ReserveTableToFitNewSize(CommonFields& common,
2346
                              const PolicyFunctions& __restrict policy,
2347
0
                              size_t new_size) {
2348
0
  new_size =
2349
0
      std::min(new_size, MaxValidSize(policy.key_size, policy.slot_size));
2350
0
  common.reset_reserved_growth(new_size);
2351
0
  common.set_reservation_size(new_size);
2352
0
  ABSL_SWISSTABLE_ASSERT(new_size > policy.soo_capacity());
2353
0
  const size_t cap = common.capacity();
2354
0
  if (ABSL_PREDICT_TRUE(common.empty() && cap <= policy.soo_capacity())) {
2355
0
    return ReserveEmptyNonAllocatedTableToFitNewSize(common, policy, new_size);
2356
0
  }
2357
2358
0
  ABSL_SWISSTABLE_ASSERT(!common.empty() || cap > policy.soo_capacity());
2359
0
  ABSL_SWISSTABLE_ASSERT(cap > 0);
2360
0
  const size_t max_size_before_growth =
2361
0
      IsSmallCapacity(cap)
2362
0
          ? cap
2363
0
          : common.size() + common.growth_info().GetGrowthLeftTotalSlow(cap);
2364
0
  if (new_size <= max_size_before_growth) {
2365
0
    return;
2366
0
  }
2367
0
  ReserveAllocatedTable(common, policy, new_size);
2368
0
}
2369
2370
namespace {
2371
void* PrepareInsertLargeImpl(CommonFields& common,
2372
                             const PolicyFunctions& __restrict policy,
2373
                             size_t hash,
2374
                             Group::NonIterableBitMaskType mask_empty,
2375
10.8M
                             FindInfo target_group) {
2376
10.8M
  ABSL_SWISSTABLE_ASSERT(!common.is_small());
2377
10.8M
  GrowthInfoAccessor growth_info = common.growth_info();
2378
  // When there are no deleted slots in the table
2379
  // and growth_left is positive, we can insert at the first
2380
  // empty slot in the probe sequence (target).
2381
10.8M
  if (ABSL_PREDICT_FALSE(
2382
10.8M
          !growth_info.GetGrowthInfoLowerBound().HasNoDeletedAndGrowthLeft())) {
2383
183k
    return PrepareInsertLargeSlow(common, policy, hash);
2384
183k
  }
2385
10.6M
  PrepareInsertCommon(common);
2386
10.6M
  growth_info.OverwriteEmptyAsFull();
2387
10.6M
  const size_t cap = common.capacity();
2388
10.6M
  ABSL_ASSUME(cap > kMaxSmallCapacity);
2389
10.6M
  target_group.offset += mask_empty.LowestBitSet();
2390
10.6M
  target_group.offset &= cap;
2391
10.6M
  SetCtrl(common, target_group.offset, H2(hash), policy.slot_size);
2392
10.6M
  common.infoz().RecordInsertMiss(hash, target_group.probe_length);
2393
10.6M
  return SlotAddress(common.slot_array(cap), target_group.offset,
2394
10.6M
                     policy.slot_size);
2395
10.6M
}
2396
}  // namespace
2397
2398
void* PrepareInsertLarge(CommonFields& common,
2399
                         const PolicyFunctions& __restrict policy, size_t hash,
2400
                         Group::NonIterableBitMaskType mask_empty,
2401
10.8M
                         FindInfo target_group) {
2402
  // NOLINTNEXTLINE(misc-static-assert)
2403
10.8M
  ABSL_SWISSTABLE_ASSERT(!SwisstableGenerationsEnabled());
2404
10.8M
  return PrepareInsertLargeImpl(common, policy, hash, mask_empty, target_group);
2405
10.8M
}
2406
2407
void* PrepareInsertLargeGenerationsEnabled(
2408
    CommonFields& common, const PolicyFunctions& __restrict policy, size_t hash,
2409
    Group::NonIterableBitMaskType mask_empty, FindInfo target_group,
2410
0
    absl::FunctionRef<size_t(size_t)> recompute_hash) {
2411
  // NOLINTNEXTLINE(misc-static-assert)
2412
0
  ABSL_SWISSTABLE_ASSERT(SwisstableGenerationsEnabled());
2413
0
  const size_t cap = common.capacity();
2414
0
  const size_t growth_left = common.growth_info().GetGrowthLeftTotalSlow(cap);
2415
  // As an optimization, we avoid calling ShouldRehashForBugDetection if we
2416
  // will end up rehashing anyways.
2417
0
  if (growth_left > 0 && common.should_rehash_for_bug_detection_on_insert()) {
2418
    // Move to a different heap allocation in order to detect bugs.
2419
0
    ResizeAllocatedTableWithSeedChange(common, policy, cap);
2420
0
    hash = recompute_hash(common.seed().seed());
2421
0
    std::tie(target_group, mask_empty) =
2422
0
        find_first_non_full_group(common, hash);
2423
0
  }
2424
0
  return PrepareInsertLargeImpl(common, policy, hash, mask_empty, target_group);
2425
0
}
2426
2427
namespace {
2428
// Returns true if the following is true
2429
// 1. OptimalMemcpySizeForSooSlotTransfer(left) >
2430
//    OptimalMemcpySizeForSooSlotTransfer(left - 1)
2431
// 2. OptimalMemcpySizeForSooSlotTransfer(left) are equal for all i in [left,
2432
// right].
2433
// This function is used to verify that we have all the possible template
2434
// instantiations for GrowFullSooTableToNextCapacity.
2435
// With this verification the problem may be detected at compile time instead of
2436
// link time.
2437
constexpr bool VerifyOptimalMemcpySizeForSooSlotTransferRange(size_t left,
2438
0
                                                              size_t right) {
2439
0
  size_t optimal_size_for_range = OptimalMemcpySizeForSooSlotTransfer(left);
2440
0
  if (optimal_size_for_range <= OptimalMemcpySizeForSooSlotTransfer(left - 1)) {
2441
0
    return false;
2442
0
  }
2443
0
  for (size_t i = left + 1; i <= right; ++i) {
2444
0
    if (OptimalMemcpySizeForSooSlotTransfer(i) != optimal_size_for_range) {
2445
0
      return false;
2446
0
    }
2447
0
  }
2448
0
  return true;
2449
0
}
2450
}  // namespace
2451
2452
// Extern template instantiation for inline function.
2453
template size_t TryFindNewIndexWithoutProbing(size_t h1, size_t old_index,
2454
                                              size_t old_capacity,
2455
                                              ctrl_t* new_ctrl,
2456
                                              size_t new_capacity);
2457
2458
// We need to instantiate ALL possible template combinations because we define
2459
// the function in the cc file.
2460
template void* GrowSooTableToNextCapacityAndPrepareInsert<0, false>(
2461
    CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
2462
    bool);
2463
template void* GrowSooTableToNextCapacityAndPrepareInsert<
2464
    OptimalMemcpySizeForSooSlotTransfer(1), true>(
2465
    CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
2466
    bool);
2467
2468
static_assert(VerifyOptimalMemcpySizeForSooSlotTransferRange(2, 3));
2469
template void* GrowSooTableToNextCapacityAndPrepareInsert<
2470
    OptimalMemcpySizeForSooSlotTransfer(3), true>(
2471
    CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
2472
    bool);
2473
2474
#if UINTPTR_MAX == UINT32_MAX
2475
static_assert(MaxSooSlotSize() == 4);
2476
static_assert(VerifyOptimalMemcpySizeForSooSlotTransferRange(2, 4));
2477
#else
2478
static_assert(VerifyOptimalMemcpySizeForSooSlotTransferRange(4, 8));
2479
template void* GrowSooTableToNextCapacityAndPrepareInsert<
2480
    OptimalMemcpySizeForSooSlotTransfer(8), true>(
2481
    CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
2482
    bool);
2483
static_assert(MaxSooSlotSize() == 8);
2484
#endif
2485
2486
template void* AllocateBackingArray<BackingArrayAlignment(alignof(size_t)),
2487
                                    std::allocator<char>>(void* alloc,
2488
                                                          size_t n);
2489
template void DeallocateBackingArray<BackingArrayAlignment(alignof(size_t)),
2490
                                     std::allocator<char>>(
2491
    void* alloc, size_t capacity, ctrl_t* ctrl, size_t slot_size,
2492
    size_t slot_align, bool had_infoz, size_t blocked_element_count);
2493
2494
template void Clear<true>(CommonFields& c, const PolicyFunctions& policy,
2495
                          DestroySlotFn destroy_slot, void* alloc);
2496
template void Clear<false>(CommonFields& c, const PolicyFunctions& policy,
2497
                           DestroySlotFn destroy_slot, void* alloc);
2498
2499
}  // namespace container_internal
2500
ABSL_NAMESPACE_END
2501
}  // namespace absl