Coverage Report

Created: 2026-10-28 06:49

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