6262// pseudo-struct:
6363//
6464// struct BackingArray {
65+ // // The number of elements we can insert before growing the capacity.
66+ // size_t growth_left;
6567// // Control bytes for the "real" slots.
6668// ctrl_t ctrl[capacity];
6769// // Always `ctrl_t::kSentinel`. This is used by iterators to find when to
174176
175177#include < algorithm>
176178#include < cmath>
179+ #include < cstddef>
177180#include < cstdint>
178181#include < cstring>
179182#include < iterator>
@@ -484,13 +487,14 @@ static_assert(ctrl_t::kDeleted == static_cast<ctrl_t>(-2),
484487 " ctrl_t::kDeleted must be -2 to make the implementation of "
485488 " ConvertSpecialToEmptyAndFullToDeleted efficient" );
486489
487- ABSL_DLL extern const ctrl_t kEmptyGroup [16 ];
490+ // See definition comment for why this is size 32.
491+ ABSL_DLL extern const ctrl_t kEmptyGroup [32 ];
488492
489493// Returns a pointer to a control byte group that can be used by empty tables.
490494inline ctrl_t * EmptyGroup () {
491495 // Const must be cast away here; no uses of this function will actually write
492496 // to it, because it is only used for empty tables.
493- return const_cast <ctrl_t *>(kEmptyGroup );
497+ return const_cast <ctrl_t *>(kEmptyGroup + 16 );
494498}
495499
496500// Returns a pointer to a generation to use for an empty hashtable.
@@ -913,33 +917,46 @@ class CommonFields : public CommonFieldsGenerationInfo {
913917 // fields generates less code then calling absl::exchange per field.
914918 control_(that.control()),
915919 slots_(that.slots_ptr()),
916- size_(that.size()),
917920 capacity_(that.capacity()),
918- compressed_tuple_(that.growth_left (), std::move(that.infoz())) {
921+ compressed_tuple_(that.size (), std::move(that.infoz())) {
919922 that.set_control (EmptyGroup ());
920923 that.set_slots (nullptr );
921- that.set_size (0 );
922924 that.set_capacity (0 );
923- that.set_growth_left (0 );
925+ that.set_size (0 );
924926 }
925927 CommonFields& operator =(CommonFields&&) = default ;
926928
927929 ctrl_t * control () const { return control_; }
928930 void set_control (ctrl_t * c) { control_ = c; }
931+ void * backing_array_start () const {
932+ // growth_left is stored before control bytes.
933+ assert (reinterpret_cast <uintptr_t >(control ()) % alignof (size_t ) == 0 );
934+ return control () - sizeof (size_t );
935+ }
936+
929937 // Note: we can't use slots() because Qt defines "slots" as a macro.
930938 void * slots_ptr () const { return slots_; }
931939 void set_slots (void * s) { slots_ = s; }
932- size_t size () const { return size_; }
933- void set_size (size_t s) { size_ = s; }
940+
941+ // The number of filled slots.
942+ size_t size () const { return compressed_tuple_.template get <0 >(); }
943+ void set_size (size_t s) { compressed_tuple_.template get <0 >() = s; }
944+
945+ // The total number of available slots.
934946 size_t capacity () const { return capacity_; }
935947 void set_capacity (size_t c) {
936948 assert (c == 0 || IsValidCapacity (c));
937949 capacity_ = c;
938950 }
939951
940952 // The number of slots we can still fill without needing to rehash.
941- size_t growth_left () const { return compressed_tuple_.template get <0 >(); }
942- void set_growth_left (size_t gl) { compressed_tuple_.template get <0 >() = gl; }
953+ // This is stored in the heap allocation before the control bytes.
954+ size_t growth_left () const {
955+ return *reinterpret_cast <size_t *>(backing_array_start ());
956+ }
957+ void set_growth_left (size_t gl) {
958+ *reinterpret_cast <size_t *>(backing_array_start ()) = gl;
959+ }
943960
944961 HashtablezInfoHandle& infoz () { return compressed_tuple_.template get <1 >(); }
945962 const HashtablezInfoHandle& infoz () const {
@@ -951,32 +968,30 @@ class CommonFields : public CommonFieldsGenerationInfo {
951968 should_rehash_for_bug_detection_on_insert (control (), capacity ());
952969 }
953970 void reset_reserved_growth (size_t reservation) {
954- CommonFieldsGenerationInfo::reset_reserved_growth (reservation, size_ );
971+ CommonFieldsGenerationInfo::reset_reserved_growth (reservation, size () );
955972 }
956973
957974 private:
958975 // TODO(b/259599413): Investigate removing some of these fields:
959976 // - control/slots can be derived from each other
960- // - growth_left can be moved into the slot array
961977 // - we can use 6 bits for capacity since it's always a power of two minus 1
962978
963- // The control bytes (and, also, a pointer to the base of the backing array).
979+ // The control bytes (and, also, a pointer near to the base of the backing
980+ // array).
964981 //
965982 // This contains `capacity + 1 + NumClonedBytes()` entries, even
966983 // when the table is empty (hence EmptyGroup).
984+ //
985+ // Note that growth_left is stored immediately before this pointer.
967986 ctrl_t * control_ = EmptyGroup();
968987
969988 // The beginning of the slots, located at `SlotOffset()` bytes after
970989 // `control`. May be null for empty tables.
971990 void * slots_ = nullptr ;
972991
973- // The number of filled slots.
974- size_t size_ = 0 ;
975-
976- // The total number of available slots.
977992 size_t capacity_ = 0 ;
978993
979- // Bundle together growth_left and HashtablezInfoHandle to ensure EBO for
994+ // Bundle together size and HashtablezInfoHandle to ensure EBO for
980995 // HashtablezInfoHandle when sampling is turned off.
981996 absl::container_internal::CompressedTuple<size_t , HashtablezInfoHandle>
982997 compressed_tuple_{0u , HashtablezInfoHandle{}};
@@ -1318,20 +1333,28 @@ inline void SetCtrl(const CommonFields& common, size_t i, h2_t h,
13181333 SetCtrl (common, i, static_cast <ctrl_t >(h), slot_size);
13191334}
13201335
1336+ // growth_left (which is a size_t) is stored with the backing array.
1337+ constexpr size_t BackingArrayAlignment (size_t align_of_slot) {
1338+ return (std::max)(align_of_slot, alignof (size_t ));
1339+ }
1340+
1341+ // Computes the offset from the start of the backing allocation of the control
1342+ // bytes. growth_left is stored at the beginning of the backing array.
1343+ inline size_t ControlOffset () { return sizeof (size_t ); }
1344+
13211345// Given the capacity of a table, computes the offset (from the start of the
13221346// backing allocation) of the generation counter (if it exists).
13231347inline size_t GenerationOffset (size_t capacity) {
13241348 assert (IsValidCapacity (capacity));
13251349 const size_t num_control_bytes = capacity + 1 + NumClonedBytes ();
1326- return num_control_bytes;
1350+ return ControlOffset () + num_control_bytes;
13271351}
13281352
13291353// Given the capacity of a table, computes the offset (from the start of the
13301354// backing allocation) at which the slots begin.
13311355inline size_t SlotOffset (size_t capacity, size_t slot_align) {
13321356 assert (IsValidCapacity (capacity));
1333- const size_t num_control_bytes = capacity + 1 + NumClonedBytes ();
1334- return (num_control_bytes + NumGenerationBytes () + slot_align - 1 ) &
1357+ return (GenerationOffset (capacity) + NumGenerationBytes () + slot_align - 1 ) &
13351358 (~slot_align + 1 );
13361359}
13371360
@@ -1356,13 +1379,15 @@ ABSL_ATTRIBUTE_NOINLINE void InitializeSlots(CommonFields& c, Alloc alloc) {
13561379 : 0 ;
13571380
13581381 const size_t cap = c.capacity ();
1382+ const size_t alloc_size = AllocSize (cap, SizeOfSlot, AlignOfSlot);
1383+ // growth_left (which is a size_t) is stored with the backing array.
13591384 char * mem = static_cast <char *>(
1360- Allocate<AlignOfSlot>(&alloc, AllocSize (cap, SizeOfSlot, AlignOfSlot) ));
1385+ Allocate<BackingArrayAlignment ( AlignOfSlot) >(&alloc, alloc_size ));
13611386 const GenerationType old_generation = c.generation ();
13621387 c.set_generation_ptr (
13631388 reinterpret_cast <GenerationType*>(mem + GenerationOffset (cap)));
13641389 c.set_generation (NextGeneration (old_generation));
1365- c.set_control (reinterpret_cast <ctrl_t *>(mem));
1390+ c.set_control (reinterpret_cast <ctrl_t *>(mem + ControlOffset () ));
13661391 c.set_slots (mem + SlotOffset (cap, AlignOfSlot));
13671392 ResetCtrl (c, SizeOfSlot);
13681393 if (sample_size) {
@@ -1385,8 +1410,8 @@ struct PolicyFunctions {
13851410 void (*transfer)(void * set, void * dst_slot, void * src_slot);
13861411
13871412 // Deallocate the specified backing store which is sized for n slots.
1388- void (*dealloc)(void * set, const PolicyFunctions& policy, ctrl_t * ctrl,
1389- void * slot_array, size_t n);
1413+ void (*dealloc)(void * set, const PolicyFunctions& policy,
1414+ void * backing_array_start, void * slot_array, size_t n);
13901415};
13911416
13921417// ClearBackingArray clears the backing array, either modifying it in place,
@@ -1405,14 +1430,14 @@ void EraseMetaOnly(CommonFields& c, ctrl_t* it, size_t slot_size);
14051430template <size_t AlignOfSlot>
14061431ABSL_ATTRIBUTE_NOINLINE void DeallocateStandard (void *,
14071432 const PolicyFunctions& policy,
1408- ctrl_t * ctrl, void * slot_array ,
1409- size_t n) {
1433+ void * backing_array_start ,
1434+ void * slot_array, size_t n) {
14101435 // Unpoison before returning the memory to the allocator.
14111436 SanitizerUnpoisonMemoryRegion (slot_array, policy.slot_size * n);
14121437
14131438 std::allocator<char > alloc;
1414- Deallocate<AlignOfSlot>(&alloc, ctrl,
1415- AllocSize (n, policy.slot_size , AlignOfSlot));
1439+ Deallocate<BackingArrayAlignment ( AlignOfSlot)>(
1440+ &alloc, backing_array_start, AllocSize (n, policy.slot_size , AlignOfSlot));
14161441}
14171442
14181443// For trivially relocatable types we use memcpy directly. This allows us to
@@ -1773,7 +1798,9 @@ class raw_hash_set {
17731798
17741799 raw_hash_set (const raw_hash_set& that, const allocator_type& a)
17751800 : raw_hash_set(0 , that.hash_ref(), that.eq_ref(), a) {
1776- reserve (that.size ());
1801+ const size_t size = that.size ();
1802+ if (size == 0 ) return ;
1803+ reserve (size);
17771804 // Because the table is guaranteed to be empty, we can do something faster
17781805 // than a full `insert`.
17791806 for (const auto & v : that) {
@@ -1784,8 +1811,8 @@ class raw_hash_set {
17841811 common ().maybe_increment_generation_on_insert ();
17851812 infoz ().RecordInsert (hash, target.probe_length );
17861813 }
1787- common ().set_size (that. size () );
1788- set_growth_left (growth_left () - that. size () );
1814+ common ().set_size (size);
1815+ set_growth_left (growth_left () - size);
17891816 }
17901817
17911818 ABSL_ATTRIBUTE_NOINLINE raw_hash_set (raw_hash_set&& that) noexcept (
@@ -1838,8 +1865,8 @@ class raw_hash_set {
18381865
18391866 // Unpoison before returning the memory to the allocator.
18401867 SanitizerUnpoisonMemoryRegion (slot_array (), sizeof (slot_type) * cap);
1841- Deallocate<alignof (slot_type)>(
1842- &alloc_ref (), control (),
1868+ Deallocate<BackingArrayAlignment ( alignof (slot_type) )>(
1869+ &alloc_ref (), common (). backing_array_start (),
18431870 AllocSize (cap, sizeof (slot_type), alignof (slot_type)));
18441871
18451872 infoz ().Unregister ();
@@ -2470,8 +2497,8 @@ class raw_hash_set {
24702497 if (old_capacity) {
24712498 SanitizerUnpoisonMemoryRegion (old_slots,
24722499 sizeof (slot_type) * old_capacity);
2473- Deallocate<alignof (slot_type)>(
2474- &alloc_ref (), old_ctrl,
2500+ Deallocate<BackingArrayAlignment ( alignof (slot_type) )>(
2501+ &alloc_ref (), old_ctrl - ControlOffset () ,
24752502 AllocSize (old_capacity, sizeof (slot_type), alignof (slot_type)));
24762503 }
24772504 infoz ().RecordRehash (total_probe_length);
@@ -2707,15 +2734,15 @@ class raw_hash_set {
27072734 static_cast <slot_type*>(src));
27082735 }
27092736 // Note: dealloc_fn will only be used if we have a non-standard allocator.
2710- static void dealloc_fn (void * set, const PolicyFunctions&, ctrl_t * ctrl,
2711- void * slot_mem, size_t n) {
2737+ static void dealloc_fn (void * set, const PolicyFunctions&,
2738+ void * backing_array_start, void * slot_mem, size_t n) {
27122739 auto * h = static_cast <raw_hash_set*>(set);
27132740
27142741 // Unpoison before returning the memory to the allocator.
27152742 SanitizerUnpoisonMemoryRegion (slot_mem, sizeof (slot_type) * n);
27162743
2717- Deallocate<alignof (slot_type)>(
2718- &h->alloc_ref (), ctrl ,
2744+ Deallocate<BackingArrayAlignment ( alignof (slot_type) )>(
2745+ &h->alloc_ref (), backing_array_start ,
27192746 AllocSize (n, sizeof (slot_type), alignof (slot_type)));
27202747 }
27212748
0 commit comments