@@ -329,11 +329,16 @@ static_assert(kDeleted == -2,
329
329
// A single block of empty control bytes for tables without any slots allocated.
330
330
// This enables removing a branch in the hot path of find().
331
331
// --------------------------------------------------------------------------
332
+ template <class std_alloc_t >
332
333
inline ctrl_t * EmptyGroup () {
333
- alignas (16 ) static constexpr ctrl_t empty_group[] = {
334
- kSentinel , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty ,
335
- kEmpty , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty };
336
- return const_cast <ctrl_t *>(empty_group);
334
+ PHMAP_IF_CONSTEXPR (std_alloc_t ::value) {
335
+ alignas (16 ) static constexpr ctrl_t empty_group[] = {
336
+ kSentinel , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty ,
337
+ kEmpty , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty , kEmpty };
338
+
339
+ return const_cast <ctrl_t *>(empty_group);
340
+ }
341
+ return nullptr ;
337
342
}
338
343
339
344
// --------------------------------------------------------------------------
@@ -869,6 +874,8 @@ class raw_hash_set
869
874
template <class K >
870
875
using key_arg = typename KeyArgImpl::template type<K, key_type>;
871
876
877
+ using std_alloc_t = std::is_same<typename std::decay<Alloc>::type, phmap::priv::Allocator<value_type>>;
878
+
872
879
private:
873
880
// Give an early error when key_type is not hashable/eq.
874
881
auto KeyTypeCanBeHashed (const Hash& h, const key_type& k) -> decltype(h(k));
@@ -989,6 +996,11 @@ class raw_hash_set
989
996
iterator (ctrl_t * ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {}
990
997
991
998
void skip_empty_or_deleted () {
999
+ PHMAP_IF_CONSTEXPR (!std_alloc_t ::value) {
1000
+ // ctrl_ could be nullptr
1001
+ if (!ctrl_)
1002
+ return ;
1003
+ }
992
1004
while (IsEmptyOrDeleted (*ctrl_)) {
993
1005
// ctrl is not necessarily aligned to Group::kWidth. It is also likely
994
1006
// to read past the space for ctrl bytes and into slots. This is ok
@@ -1057,7 +1069,7 @@ class raw_hash_set
1057
1069
explicit raw_hash_set (size_t bucket_cnt, const hasher& hashfn = hasher(),
1058
1070
const key_equal& eq = key_equal(),
1059
1071
const allocator_type& alloc = allocator_type())
1060
- : ctrl_(EmptyGroup()), settings_(0 , hashfn, eq, alloc) {
1072
+ : ctrl_(EmptyGroup< std_alloc_t > ()), settings_(0 , hashfn, eq, alloc) {
1061
1073
if (bucket_cnt) {
1062
1074
size_t new_capacity = NormalizeCapacity (bucket_cnt);
1063
1075
reset_growth_left (new_capacity);
@@ -1180,7 +1192,7 @@ class raw_hash_set
1180
1192
std::is_nothrow_copy_constructible<hasher>::value&&
1181
1193
std::is_nothrow_copy_constructible<key_equal>::value&&
1182
1194
std::is_nothrow_copy_constructible<allocator_type>::value)
1183
- : ctrl_(phmap::exchange(that.ctrl_, EmptyGroup())),
1195
+ : ctrl_(phmap::exchange(that.ctrl_, EmptyGroup< std_alloc_t > ())),
1184
1196
slots_ (phmap::exchange(that.slots_, nullptr )),
1185
1197
size_(phmap::exchange(that.size_, 0 )),
1186
1198
capacity_(phmap::exchange(that.capacity_, 0 )),
@@ -1194,7 +1206,7 @@ class raw_hash_set
1194
1206
}
1195
1207
1196
1208
raw_hash_set (raw_hash_set&& that, const allocator_type& a)
1197
- : ctrl_(EmptyGroup()),
1209
+ : ctrl_(EmptyGroup< std_alloc_t > ()),
1198
1210
slots_(nullptr ),
1199
1211
size_(0 ),
1200
1212
capacity_(0 ),
@@ -1615,6 +1627,7 @@ class raw_hash_set
1615
1627
// This overload is necessary because otherwise erase<K>(const K&) would be
1616
1628
// a better match if non-const iterator is passed as an argument.
1617
1629
iterator erase (iterator it) {
1630
+ assert (it != end ());
1618
1631
auto res = it;
1619
1632
++res;
1620
1633
_erase (it);
@@ -1738,7 +1751,8 @@ class raw_hash_set
1738
1751
1739
1752
template <class K = key_type>
1740
1753
void prefetch (const key_arg<K>& key) const {
1741
- prefetch_hash (this ->hash (key));
1754
+ PHMAP_IF_CONSTEXPR (std_alloc_t ::value)
1755
+ prefetch_hash (this ->hash (key));
1742
1756
}
1743
1757
1744
1758
// The API of find() has two extensions.
@@ -1848,6 +1862,11 @@ class raw_hash_set
1848
1862
1849
1863
template <class K = key_type>
1850
1864
bool find_impl (const key_arg<K>& key, size_t hashval, size_t & offset) {
1865
+ PHMAP_IF_CONSTEXPR (!std_alloc_t ::value) {
1866
+ // ctrl_ could be nullptr
1867
+ if (!ctrl_)
1868
+ return false ;
1869
+ }
1851
1870
auto seq = probe (hashval);
1852
1871
while (true ) {
1853
1872
Group g{ ctrl_ + seq.offset () };
@@ -2025,7 +2044,7 @@ class raw_hash_set
2025
2044
// Unpoison before returning the memory to the allocator.
2026
2045
SanitizerUnpoisonMemoryRegion (slots_, sizeof (slot_type) * capacity_);
2027
2046
Deallocate<Layout::Alignment ()>(&alloc_ref (), ctrl_, layout.AllocSize ());
2028
- ctrl_ = EmptyGroup ();
2047
+ ctrl_ = EmptyGroup< std_alloc_t > ();
2029
2048
slots_ = nullptr ;
2030
2049
size_ = 0 ;
2031
2050
capacity_ = 0 ;
@@ -2135,6 +2154,11 @@ class raw_hash_set
2135
2154
}
2136
2155
2137
2156
bool has_element (const value_type& elem, size_t hashval) const {
2157
+ PHMAP_IF_CONSTEXPR (!std_alloc_t ::value) {
2158
+ // ctrl_ could be nullptr
2159
+ if (!ctrl_)
2160
+ return false ;
2161
+ }
2138
2162
auto seq = probe (hashval);
2139
2163
while (true ) {
2140
2164
Group g{ctrl_ + seq.offset ()};
@@ -2197,6 +2221,11 @@ class raw_hash_set
2197
2221
protected:
2198
2222
template <class K >
2199
2223
size_t _find_key (const K& key, size_t hashval) {
2224
+ PHMAP_IF_CONSTEXPR (!std_alloc_t ::value) {
2225
+ // ctrl_ could be nullptr
2226
+ if (!ctrl_)
2227
+ return (size_t )-1 ;
2228
+ }
2200
2229
auto seq = probe (hashval);
2201
2230
while (true ) {
2202
2231
Group g{ctrl_ + seq.offset ()};
@@ -2221,7 +2250,12 @@ class raw_hash_set
2221
2250
}
2222
2251
2223
2252
size_t prepare_insert (size_t hashval) PHMAP_ATTRIBUTE_NOINLINE {
2224
- auto target = find_first_non_full (hashval);
2253
+ PHMAP_IF_CONSTEXPR (!std_alloc_t ::value) {
2254
+ // ctrl_ could be nullptr
2255
+ if (!ctrl_)
2256
+ rehash_and_grow_if_necessary ();
2257
+ }
2258
+ FindInfo target = find_first_non_full (hashval);
2225
2259
if (PHMAP_PREDICT_FALSE (growth_left () == 0 &&
2226
2260
!IsDeleted (ctrl_[target.offset ]))) {
2227
2261
rehash_and_grow_if_necessary ();
@@ -2335,10 +2369,10 @@ class raw_hash_set
2335
2369
// TODO(alkis): Investigate removing some of these fields:
2336
2370
// - ctrl/slots can be derived from each other
2337
2371
// - size can be moved into the slot array
2338
- ctrl_t * ctrl_ = EmptyGroup(); // [(capacity + 1) * ctrl_t]
2339
- slot_type* slots_ = nullptr ; // [capacity * slot_type]
2340
- size_t size_ = 0 ; // number of full slots
2341
- size_t capacity_ = 0 ; // total number of slots
2372
+ ctrl_t * ctrl_ = EmptyGroup< std_alloc_t > (); // [(capacity + 1) * ctrl_t]
2373
+ slot_type* slots_ = nullptr ; // [capacity * slot_type]
2374
+ size_t size_ = 0 ; // number of full slots
2375
+ size_t capacity_ = 0 ; // total number of slots
2342
2376
HashtablezInfoHandle infoz_;
2343
2377
std::tuple<size_t /* growth_left */ , hasher, key_equal, allocator_type>
2344
2378
settings_{0 , hasher{}, key_equal{}, allocator_type{}};
@@ -4576,6 +4610,8 @@ struct HashtableDebugAccess<Set, typename std::enable_if<has_member_type_raw_has
4576
4610
4577
4611
static size_t GetNumProbes (const Set& set,
4578
4612
const typename Set::key_type& key) {
4613
+ if (!set.ctrl_ )
4614
+ return 0 ;
4579
4615
size_t num_probes = 0 ;
4580
4616
size_t hashval = set.hash (key);
4581
4617
auto seq = set.probe (hashval);
0 commit comments