Coverage Report

Created: 2026-07-16 06:43

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