@@ -819,11 +819,15 @@ static_assert(kDeleted == -2,
819819// A single block of empty control bytes for tables without any slots allocated.
820820// This enables removing a branch in the hot path of find().
821821// --------------------------------------------------------------------------
822+ template <class std_alloc_t >
822823inline ctrl_t * EmptyGroup () {
823- alignas (16 ) static constexpr ctrl_t empty_group[] = { kSentinel , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty ,
824- kEmpty , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty ,
825- kEmpty , kEmpty , kEmpty , kEmpty };
826- return const_cast <ctrl_t *>(empty_group);
824+ if constexpr (std_alloc_t ::value) {
825+ alignas (16 ) static constexpr ctrl_t empty_group[] = { kSentinel , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty ,
826+ kEmpty , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty ,
827+ kEmpty , kEmpty , kEmpty , kEmpty };
828+ return const_cast <ctrl_t *>(empty_group);
829+ }
830+ return nullptr ;
827831}
828832
829833// --------------------------------------------------------------------------
@@ -1529,6 +1533,8 @@ class raw_hash_set {
15291533 template <class K >
15301534 using key_arg = typename KeyArgImpl::template type<K, key_type>;
15311535
1536+ using std_alloc_t = std::is_same<typename std::decay<Alloc>::type, gtl::priv::Allocator<value_type>>;
1537+
15321538private:
15331539 // Give an early error when key_type is not hashable/eq.
15341540 auto KeyTypeCanBeHashed (const Hash& h, const key_type& k) -> decltype(h(k));
@@ -1635,6 +1641,11 @@ class raw_hash_set {
16351641 , slot_(slot) {}
16361642
16371643 void skip_empty_or_deleted () {
1644+ if constexpr (!std_alloc_t ::value) {
1645+ // ctrl_ could be nullptr
1646+ if (!ctrl_)
1647+ return ;
1648+ }
16381649 while (IsEmptyOrDeleted (*ctrl_)) {
16391650 // ctrl is not necessarily aligned to Group::kWidth. It is also likely
16401651 // to read past the space for ctrl bytes and into slots. This is ok
@@ -1701,7 +1712,7 @@ class raw_hash_set {
17011712 const hasher& hashfn = hasher(),
17021713 const key_equal& eq = key_equal(),
17031714 const allocator_type& alloc = allocator_type())
1704- : ctrl_(EmptyGroup())
1715+ : ctrl_(EmptyGroup< std_alloc_t > ())
17051716 , settings_(0 , hashfn, eq, alloc) {
17061717 if (bucket_cnt) {
17071718 size_t new_capacity = NormalizeCapacity (bucket_cnt);
@@ -1824,7 +1835,7 @@ class raw_hash_set {
18241835 raw_hash_set (raw_hash_set&& that) noexcept (std::is_nothrow_copy_constructible_v<hasher> &&
18251836 std::is_nothrow_copy_constructible_v<key_equal> &&
18261837 std::is_nothrow_copy_constructible_v<allocator_type>)
1827- : ctrl_(std::exchange(that.ctrl_, EmptyGroup()))
1838+ : ctrl_(std::exchange(that.ctrl_, EmptyGroup< std_alloc_t > ()))
18281839 , slots_(std::exchange(that.slots_, nullptr ))
18291840 , size_(std::exchange(that.size_, 0 ))
18301841 , capacity_(std::exchange(that.capacity_, 0 ))
@@ -1839,7 +1850,7 @@ class raw_hash_set {
18391850 }
18401851
18411852 raw_hash_set (raw_hash_set&& that, const allocator_type& a)
1842- : ctrl_(EmptyGroup())
1853+ : ctrl_(EmptyGroup< std_alloc_t > ())
18431854 , slots_(nullptr )
18441855 , size_(0 )
18451856 , capacity_(0 )
@@ -2366,22 +2377,13 @@ class raw_hash_set {
23662377 // NOTE: This is a very low level operation and should not be used without
23672378 // specific benchmarks indicating its importance.
23682379 // -----------------------------------------------------------------------
2369- void prefetch_hash (size_t hashval) const {
2370- (void )hashval;
2371- #if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
2372- auto seq = probe (hashval);
2373- _mm_prefetch ((const char *)(ctrl_ + seq.offset ()), _MM_HINT_NTA);
2374- _mm_prefetch ((const char *)(slots_ + seq.offset ()), _MM_HINT_NTA);
2375- #elif defined(__GNUC__)
2376- auto seq = probe (hashval);
2377- __builtin_prefetch (static_cast <const void *>(ctrl_ + seq.offset ()));
2378- __builtin_prefetch (static_cast <const void *>(slots_ + seq.offset ()));
2379- #endif // __GNUC__
2380+ void prefetch_hash (size_t ) const {
23802381 }
23812382
23822383 template <class K = key_type>
23832384 void prefetch (const key_arg<K>& key) const {
2384- prefetch_hash (this ->hash (key));
2385+ if constexpr (std_alloc_t ::value)
2386+ prefetch_hash (this ->hash (key));
23852387 }
23862388
23872389 // The API of find() has two extensions.
@@ -2490,6 +2492,11 @@ class raw_hash_set {
24902492
24912493 template <class K = key_type>
24922494 bool find_impl (const key_arg<K>& key, size_t hashval, size_t & offset) {
2495+ if constexpr (!std_alloc_t ::value) {
2496+ // ctrl_ could be nullptr
2497+ if (!ctrl_)
2498+ return false ;
2499+ }
24932500 auto seq = probe (hashval);
24942501 while (true ) {
24952502 Group g{ ctrl_ + seq.offset () };
@@ -2652,7 +2659,7 @@ class raw_hash_set {
26522659 // Unpoison before returning the memory to the allocator.
26532660 SanitizerUnpoisonMemoryRegion (slots_, sizeof (slot_type) * capacity_);
26542661 Deallocate<Layout::Alignment ()>(&alloc_ref (), ctrl_, layout.AllocSize ());
2655- ctrl_ = EmptyGroup ();
2662+ ctrl_ = EmptyGroup< std_alloc_t > ();
26562663 slots_ = nullptr ;
26572664 size_ = 0 ;
26582665 capacity_ = 0 ;
@@ -2761,6 +2768,11 @@ class raw_hash_set {
27612768 }
27622769
27632770 bool has_element (const value_type& elem, size_t hashval) const {
2771+ if constexpr (!std_alloc_t ::value) {
2772+ // ctrl_ could be nullptr
2773+ if (!ctrl_)
2774+ return false ;
2775+ }
27642776 auto seq = probe (hashval);
27652777 while (true ) {
27662778 Group g{ ctrl_ + seq.offset () };
@@ -2823,6 +2835,11 @@ class raw_hash_set {
28232835protected:
28242836 template <class K >
28252837 size_t _find_key (const K& key, size_t hashval) {
2838+ if constexpr (!std_alloc_t ::value) {
2839+ // ctrl_ could be nullptr
2840+ if (!ctrl_)
2841+ return (size_t )-1 ;
2842+ }
28262843 auto seq = probe (hashval);
28272844 while (true ) {
28282845 Group g{ ctrl_ + seq.offset () };
@@ -2847,6 +2864,11 @@ class raw_hash_set {
28472864 }
28482865
28492866 size_t prepare_insert (size_t hashval) GTL_ATTRIBUTE_NOINLINE {
2867+ if constexpr (!std_alloc_t ::value) {
2868+ // ctrl_ could be nullptr
2869+ if (!ctrl_)
2870+ rehash_and_grow_if_necessary ();
2871+ }
28502872 auto target = find_first_non_full (hashval);
28512873 if (GTL_PREDICT_FALSE (growth_left () == 0 && !IsDeleted (ctrl_[target.offset ]))) {
28522874 rehash_and_grow_if_necessary ();
@@ -2966,10 +2988,10 @@ class raw_hash_set {
29662988 // - ctrl/slots can be derived from each other
29672989 // - size can be moved into the slot array
29682990 // -------------------------------------------------------------------------
2969- ctrl_t * ctrl_ = EmptyGroup(); // [(capacity + 1) * ctrl_t]
2970- slot_type* slots_ = nullptr ; // [capacity * slot_type]
2971- size_t size_ = 0 ; // number of full slots
2972- size_t capacity_ = 0 ; // total number of slots
2991+ ctrl_t * ctrl_ = EmptyGroup< std_alloc_t > (); // [(capacity + 1) * ctrl_t]
2992+ slot_type* slots_ = nullptr ; // [capacity * slot_type]
2993+ size_t size_ = 0 ; // number of full slots
2994+ size_t capacity_ = 0 ; // total number of slots
29732995 std::tuple<size_t /* growth_left */ , hasher, key_equal, allocator_type> settings_{ 0 ,
29742996 hasher{},
29752997 key_equal{},
@@ -4059,6 +4081,7 @@ class parallel_hash_set {
40594081 // Do not use erase APIs taking iterators when accessing the map concurrently
40604082 // --------------------------------------------------------------------
40614083 iterator erase (iterator it) {
4084+ assert (it != end ());
40624085 _erase (it++);
40634086 return it;
40644087 }
@@ -4991,6 +5014,8 @@ struct HashtableDebugAccess<Set, std::enable_if_t<has_member_type_raw_hash_set<S
49915014 using Slot = typename Traits::slot_type;
49925015
49935016 static size_t GetNumProbes (const Set& set, const typename Set::key_type& key) {
5017+ if (!set.ctrl_ )
5018+ return 0 ;
49945019 size_t num_probes = 0 ;
49955020 size_t hashval = set.hash (key);
49965021 auto seq = set.probe (hashval);
0 commit comments