@@ -1976,12 +1976,11 @@ constexpr bool ShouldSampleHashtablezInfoForAlloc() {
19761976 return std::is_same<CharAlloc, std::allocator<char >>::value;
19771977}
19781978
1979- template <typename CharAlloc, bool kSooEnabled >
1979+ template <bool kSooEnabled >
19801980bool ShouldSampleHashtablezInfoOnResize (bool force_sampling,
1981+ bool is_hashtablez_eligible,
19811982 size_t old_capacity, CommonFields& c) {
1982- if (!ShouldSampleHashtablezInfoForAlloc<CharAlloc>()) {
1983- return false ;
1984- }
1983+ if (!is_hashtablez_eligible) return false ;
19851984 // Force sampling is only allowed for SOO tables.
19861985 ABSL_SWISSTABLE_ASSERT (kSooEnabled || !force_sampling);
19871986 if (kSooEnabled && force_sampling) {
@@ -1998,12 +1997,32 @@ bool ShouldSampleHashtablezInfoOnResize(bool force_sampling,
19981997 return false ;
19991998}
20001999
2000+ // Allocates `n` bytes for a backing array.
2001+ template <size_t AlignOfBackingArray, typename CharAlloc>
2002+ ABSL_ATTRIBUTE_NOINLINE void * AllocateBackingArray (void * alloc, size_t n) {
2003+ return Allocate<AlignOfBackingArray>(static_cast <CharAlloc*>(alloc), n);
2004+ }
2005+
2006+ template <size_t AlignOfBackingArray, typename CharAlloc>
2007+ ABSL_ATTRIBUTE_NOINLINE void DeallocateBackingArray (
2008+ void * alloc, size_t capacity, ctrl_t * ctrl, size_t slot_size,
2009+ size_t slot_align, bool had_infoz) {
2010+ RawHashSetLayout layout (capacity, slot_align, had_infoz);
2011+ void * backing_array = ctrl - layout.control_offset ();
2012+ const size_t alloc_size = layout.alloc_size (slot_size);
2013+ // Unpoison before returning the memory to the allocator.
2014+ SanitizerUnpoisonMemoryRegion (backing_array, alloc_size);
2015+ Deallocate<AlignOfBackingArray>(static_cast <CharAlloc*>(alloc), backing_array,
2016+ alloc_size);
2017+ }
2018+
20012019// PolicyFunctions bundles together some information for a particular
20022020// raw_hash_set<T, ...> instantiation. This information is passed to
20032021// type-erased functions that want to do small amounts of type-specific
20042022// work.
20052023struct PolicyFunctions {
2006- size_t slot_size;
2024+ uint32_t slot_size;
2025+ uint32_t slot_align;
20072026
20082027 // Returns the pointer to the hash function stored in the set.
20092028 const void * (*hash_fn)(const CommonFields& common);
@@ -2014,8 +2033,12 @@ struct PolicyFunctions {
20142033 // Transfers the contents of src_slot to dst_slot.
20152034 void (*transfer)(void * set, void * dst_slot, void * src_slot);
20162035
2036+ // Allocates n bytes for the backing store for common.
2037+ void * (*alloc)(void * alloc, size_t n);
2038+
20172039 // Deallocates the backing store from common.
2018- void (*dealloc)(CommonFields& common, const PolicyFunctions& policy);
2040+ void (*dealloc)(void * alloc, size_t capacity, ctrl_t * ctrl, size_t slot_size,
2041+ size_t slot_align, bool had_infoz);
20192042
20202043 // Resizes set to the new capacity.
20212044 // Arguments are used as in raw_hash_set::resize_impl.
@@ -2116,12 +2139,13 @@ constexpr size_t OptimalMemcpySizeForSooSlotTransfer(bool transfer_uses_memcpy,
21162139class HashSetResizeHelper {
21172140 public:
21182141 explicit HashSetResizeHelper (CommonFields& c, bool was_soo, bool had_soo_slot,
2119- bool force_infoz)
2142+ bool force_infoz, bool is_hashtablez_eligible )
21202143 : old_capacity_(c.capacity()),
21212144 had_infoz_(c.has_infoz()),
21222145 was_soo_(was_soo),
21232146 had_soo_slot_(had_soo_slot),
2124- force_infoz_(force_infoz) {}
2147+ force_infoz_(force_infoz),
2148+ is_hashtablez_eligible_(is_hashtablez_eligible) {}
21252149
21262150 // Optimized for small groups version of `find_first_non_full`.
21272151 // Beneficial only right after calling `raw_hash_set::resize`.
@@ -2177,31 +2201,30 @@ class HashSetResizeHelper {
21772201 // infoz.RecordRehash is called if old_capacity == 0.
21782202 //
21792203 // Returns IsGrowingIntoSingleGroupApplicable result to avoid recomputation.
2180- template <typename Alloc,
2181- // The size we are allowed to copy to transfer SOO slot to
2182- // SooSlotIndex(). See OptimalMemcpySizeForSooSlotTransfer().
2183- size_t SooSlotMemcpySize, bool TransferUsesMemcpy, bool SooEnabled,
2184- size_t AlignOfSlot>
2185- ABSL_ATTRIBUTE_NOINLINE bool InitializeSlots (CommonFields& c, Alloc alloc,
2204+ //
2205+ // SooSlotMemcpySize is the size we are allowed to copy to transfer SOO slot
2206+ // to SooSlotIndex(). See OptimalMemcpySizeForSooSlotTransfer().
2207+ template <size_t SooSlotMemcpySize, bool TransferUsesMemcpy, bool SooEnabled>
2208+ ABSL_ATTRIBUTE_NOINLINE bool InitializeSlots (CommonFields& c, void * alloc,
21862209 size_t soo_slot_hash,
21872210 size_t key_size,
21882211 size_t value_size,
21892212 const PolicyFunctions& policy) {
21902213 ABSL_SWISSTABLE_ASSERT (c.capacity ());
21912214 const size_t slot_size = policy.slot_size ;
2215+ const size_t slot_align = policy.slot_align ;
21922216 HashtablezInfoHandle infoz = c.infoz ();
2193- const bool should_sample =
2194- ShouldSampleHashtablezInfoOnResize<Alloc, SooEnabled>(force_infoz_,
2195- old_capacity_, c);
2217+ const bool should_sample = ShouldSampleHashtablezInfoOnResize<SooEnabled>(
2218+ force_infoz_, is_hashtablez_eligible_, old_capacity_, c);
21962219 if (ABSL_PREDICT_FALSE (should_sample)) {
21972220 infoz = ForcedTrySample (slot_size, key_size, value_size,
21982221 SooEnabled ? SooCapacity () : 0 );
21992222 }
22002223 const bool has_infoz = infoz.IsSampled ();
22012224
2202- RawHashSetLayout layout (c.capacity (), AlignOfSlot , has_infoz);
2203- char * mem = static_cast < char *>(Allocate< BackingArrayAlignment (AlignOfSlot)>(
2204- & alloc, layout.alloc_size (slot_size)));
2225+ RawHashSetLayout layout (c.capacity (), slot_align , has_infoz);
2226+ char * mem =
2227+ static_cast < char *>(policy. alloc ( alloc, layout.alloc_size (slot_size)));
22052228 const GenerationType old_generation = c.generation ();
22062229 c.set_generation_ptr (
22072230 reinterpret_cast <GenerationType*>(mem + layout.generation_offset ()));
@@ -2260,7 +2283,8 @@ class HashSetResizeHelper {
22602283 if ((SooEnabled || old_capacity_ != 0 ) && grow_single_group) {
22612284 if (TransferUsesMemcpy) {
22622285 GrowSizeIntoSingleGroupTransferable (c, slot_size);
2263- DeallocateOld<AlignOfSlot>(alloc, slot_size);
2286+ (*policy.dealloc )(alloc, old_capacity_, old_ctrl (), slot_size,
2287+ slot_align, had_infoz_);
22642288 } else {
22652289 GrowIntoSingleGroupShuffleControlBytes (c.control (),
22662290 layout.capacity ());
@@ -2312,12 +2336,9 @@ class HashSetResizeHelper {
23122336
23132337 // Deallocates old backing array.
23142338 template <size_t AlignOfSlot, class CharAlloc >
2315- void DeallocateOld (CharAlloc alloc_ref, size_t slot_size) {
2316- SanitizerUnpoisonMemoryRegion (old_slots (), slot_size * old_capacity_);
2317- auto layout = RawHashSetLayout (old_capacity_, AlignOfSlot, had_infoz_);
2318- Deallocate<BackingArrayAlignment (AlignOfSlot)>(
2319- &alloc_ref, old_ctrl () - layout.control_offset (),
2320- layout.alloc_size (slot_size));
2339+ void DeallocateOld (CharAlloc alloc, size_t slot_size) {
2340+ DeallocateBackingArray<BackingArrayAlignment (AlignOfSlot), CharAlloc>(
2341+ &alloc, old_capacity_, old_ctrl (), slot_size, AlignOfSlot, had_infoz_);
23212342 }
23222343
23232344 private:
@@ -2436,6 +2457,7 @@ class HashSetResizeHelper {
24362457 // True if it is known that table needs to be sampled.
24372458 // Used for sampling SOO tables.
24382459 bool force_infoz_;
2460+ bool is_hashtablez_eligible_;
24392461};
24402462
24412463inline void PrepareInsertCommon (CommonFields& common) {
@@ -2451,29 +2473,11 @@ size_t PrepareInsertAfterSoo(size_t hash, size_t slot_size,
24512473// or creating a new one based on the value of "reuse".
24522474// REQUIRES: c.capacity > 0
24532475void ClearBackingArray (CommonFields& c, const PolicyFunctions& policy,
2454- bool reuse, bool soo_enabled);
2476+ void * alloc, bool reuse, bool soo_enabled);
24552477
24562478// Type-erased version of raw_hash_set::erase_meta_only.
24572479void EraseMetaOnly (CommonFields& c, size_t index, size_t slot_size);
24582480
2459- // Function to place in PolicyFunctions::dealloc for raw_hash_sets
2460- // that are using std::allocator. This allows us to share the same
2461- // function body for raw_hash_set instantiations that have the
2462- // same slot alignment.
2463- template <size_t AlignOfSlot>
2464- ABSL_ATTRIBUTE_NOINLINE void DeallocateStandard (CommonFields& common,
2465- const PolicyFunctions& policy) {
2466- // Unpoison before returning the memory to the allocator.
2467- SanitizerUnpoisonMemoryRegion (common.slot_array (),
2468- policy.slot_size * common.capacity ());
2469-
2470- std::allocator<char > alloc;
2471- common.infoz ().Unregister ();
2472- Deallocate<BackingArrayAlignment (AlignOfSlot)>(
2473- &alloc, common.backing_array_start (),
2474- common.alloc_size (policy.slot_size , AlignOfSlot));
2475- }
2476-
24772481// For trivially relocatable types we use memcpy directly. This allows us to
24782482// share the same function body for raw_hash_set instantiations that have the
24792483// same slot size as long as they are relocatable.
@@ -3802,7 +3806,9 @@ class raw_hash_set {
38023806
38033807 void clear_backing_array (bool reuse) {
38043808 ABSL_SWISSTABLE_ASSERT (capacity () > DefaultCapacity ());
3805- ClearBackingArray (common (), GetPolicyFunctions (), reuse, SooEnabled ());
3809+ CharAlloc alloc (alloc_ref ());
3810+ ClearBackingArray (common (), GetPolicyFunctions (), &alloc, reuse,
3811+ SooEnabled ());
38063812 }
38073813
38083814 void destroy_slots () {
@@ -3819,9 +3825,11 @@ class raw_hash_set {
38193825 // Unpoison before returning the memory to the allocator.
38203826 SanitizerUnpoisonMemoryRegion (slot_array (), sizeof (slot_type) * capacity ());
38213827 infoz ().Unregister ();
3822- Deallocate<BackingArrayAlignment (alignof (slot_type))>(
3823- &alloc_ref (), common ().backing_array_start (),
3824- common ().alloc_size (sizeof (slot_type), alignof (slot_type)));
3828+ CharAlloc alloc (alloc_ref ());
3829+ DeallocateBackingArray<BackingArrayAlignment (alignof (slot_type)),
3830+ CharAlloc>(&alloc, capacity (), control (),
3831+ sizeof (slot_type), alignof (slot_type),
3832+ common ().has_infoz ());
38253833 }
38263834
38273835 void destructor_impl () {
@@ -3887,21 +3895,22 @@ class raw_hash_set {
38873895 const bool had_soo_slot = was_soo && !set->empty ();
38883896 const size_t soo_slot_hash =
38893897 had_soo_slot ? set->hash_of (set->soo_slot ()) : 0 ;
3890- HashSetResizeHelper resize_helper (common, was_soo, had_soo_slot,
3891- force_infoz);
3898+ HashSetResizeHelper resize_helper (
3899+ common, was_soo, had_soo_slot, force_infoz,
3900+ ShouldSampleHashtablezInfoForAlloc<CharAlloc>());
38923901 common.set_capacity (new_capacity);
38933902 // Note that `InitializeSlots` does different number initialization steps
38943903 // depending on the values of `transfer_uses_memcpy` and capacities.
38953904 // Refer to the comment in `InitializeSlots` for more details.
3905+ CharAlloc alloc (set->alloc_ref ());
38963906 const bool grow_single_group =
3897- resize_helper.InitializeSlots <CharAlloc,
3898- OptimalMemcpySizeForSooSlotTransfer (
3907+ resize_helper.InitializeSlots <OptimalMemcpySizeForSooSlotTransfer (
38993908 PolicyTraits::transfer_uses_memcpy (),
39003909 SooEnabled (), sizeof (slot_type)),
39013910 PolicyTraits::transfer_uses_memcpy (),
3902- SooEnabled (), alignof (slot_type) >(
3903- common, CharAlloc (set-> alloc_ref ()), soo_slot_hash ,
3904- sizeof (key_type), sizeof (value_type), GetPolicyFunctions ());
3911+ SooEnabled ()>(
3912+ common, &alloc, soo_slot_hash, sizeof (key_type), sizeof (value_type) ,
3913+ GetPolicyFunctions ());
39053914
39063915 if (resize_helper.old_capacity () == DefaultCapacity ()) {
39073916 // InitializeSlots did all the work including infoz().RecordRehash().
@@ -4288,23 +4297,14 @@ class raw_hash_set {
42884297 auto * h = static_cast <raw_hash_set*>(set);
42894298 h->transfer (static_cast <slot_type*>(dst), static_cast <slot_type*>(src));
42904299 }
4291- // Note: dealloc_fn will only be used if we have a non-standard allocator.
4292- static void dealloc_fn (CommonFields& common, const PolicyFunctions&) {
4293- auto * set = reinterpret_cast <raw_hash_set*>(&common);
4294-
4295- // Unpoison before returning the memory to the allocator.
4296- SanitizerUnpoisonMemoryRegion (common.slot_array (),
4297- sizeof (slot_type) * common.capacity ());
4298-
4299- common.infoz ().Unregister ();
4300- Deallocate<BackingArrayAlignment (alignof (slot_type))>(
4301- &set->alloc_ref (), common.backing_array_start (),
4302- common.alloc_size (sizeof (slot_type), alignof (slot_type)));
4303- }
43044300
43054301 static const PolicyFunctions& GetPolicyFunctions () {
4302+ static_assert (sizeof (slot_type) <= (std::numeric_limits<uint32_t >::max)());
4303+ static_assert (alignof (slot_type) <= (std::numeric_limits<uint32_t >::max)());
4304+ static constexpr size_t kBackingArrayAlignment =
4305+ BackingArrayAlignment (alignof (slot_type));
43064306 static constexpr PolicyFunctions value = {
4307- sizeof (slot_type),
4307+ sizeof (slot_type), alignof (slot_type),
43084308 // TODO(b/328722020): try to type erase
43094309 // for standard layout and alignof(Hash) <= alignof(CommonFields).
43104310 std::is_empty<hasher>::value ? &GetHashRefForEmptyHasher
@@ -4313,11 +4313,9 @@ class raw_hash_set {
43134313 PolicyTraits::transfer_uses_memcpy ()
43144314 ? TransferRelocatable<sizeof (slot_type)>
43154315 : &raw_hash_set::transfer_slot_fn,
4316- (std::is_same<SlotAlloc, std::allocator<slot_type>>::value
4317- ? &DeallocateStandard<alignof (slot_type)>
4318- : &raw_hash_set::dealloc_fn),
4319- &raw_hash_set::resize_impl
4320- };
4316+ &AllocateBackingArray<kBackingArrayAlignment , CharAlloc>,
4317+ &DeallocateBackingArray<kBackingArrayAlignment , CharAlloc>,
4318+ &raw_hash_set::resize_impl};
43214319 return value;
43224320 }
43234321
0 commit comments