22 constexpr
bool operator()(
const T &lhs,
const T &rhs)
const 29 template <
typename KeyT,
typename HasherT = std::hash<KeyT>,
typename EqT = HashSetRHEqualTo<KeyT>>
36 using size_type = size_t;
37 using value_type = KeyT;
38 using reference = KeyT&;
39 using const_reference =
const KeyT&;
40 using HashCode = size_t;
42 static const size_t kMaxLoadFactorPercent = 90;
43 static const HashCode kUnusedHashCode = 0;
44 static const HashCode kTombStoneFlag = HashCode(1) << HashCode(8 *
sizeof(HashCode) - 1);
49 using iterator_category = std::forward_iterator_tag;
50 using difference_type = size_t;
51 using distance_type = size_t;
52 using value_type = KeyT;
53 using pointer = value_type*;
54 using reference = value_type&;
58 iterator(
MyType* hash_set,
size_t bucket) : _set(hash_set), _bucket(bucket)
70 size_t old_index = _bucket;
75 reference operator*()
const 77 return _set->_keys[_bucket];
80 pointer operator->()
const 82 return _set->_keys + _bucket;
87 return this->_bucket == rhs._bucket;
92 return this->_bucket != rhs._bucket;
96 void goto_next_element()
100 }
while (_bucket < _set->_num_buckets && !_is_filled_hash(_set->_hashes[_bucket]));
111 using iterator_category = std::forward_iterator_tag;
112 using difference_type = size_t;
113 using distance_type = size_t;
114 using value_type =
const KeyT;
115 using pointer = value_type*;
116 using reference = value_type&;
136 size_t old_index = _bucket;
141 reference operator*()
const 143 return _set->_keys[_bucket];
146 pointer operator->()
const 148 return _set->_keys + _bucket;
153 return this->_bucket == rhs._bucket;
158 return this->_bucket != rhs._bucket;
162 void goto_next_element()
166 }
while (_bucket < _set->_num_buckets && !_is_filled_hash(_set->_hashes[_bucket]));
180 reserve(other.size());
181 insert(cbegin(other), cend(other));
186 *
this = std::move(other);
192 reserve(other.size());
193 insert(cbegin(other), cend(other));
204 for (
size_t bucket=0; bucket<_num_buckets; ++bucket) {
205 if (_is_filled_hash(_hashes[bucket])) {
206 _keys[bucket].~KeyT();
215 std::swap(_hasher, other._hasher);
216 std::swap(_eq, other._eq);
217 std::swap(_keys, other._keys);
218 std::swap(_hashes, other._hashes);
219 std::swap(_num_buckets, other._num_buckets);
220 std::swap(_num_filled, other._num_filled);
221 std::swap(_rehash_limit, other._rehash_limit);
222 std::swap(_mask, other._mask);
230 while (bucket<_num_buckets && !_is_filled_hash(_hashes[bucket])) {
239 while (bucket<_num_buckets && !_is_filled_hash(_hashes[bucket])) {
246 {
return iterator(
this, _num_buckets); }
258 return _num_filled==0;
261 size_t bucket_count()
const 270 auto bucket = _find_filled_bucket(key);
271 if (bucket == (
size_t)-1) {
279 auto bucket = _find_filled_bucket(key);
280 if (bucket == (
size_t)-1) {
286 bool contains(
const KeyT& k)
const 288 return _find_filled_bucket(k) != (size_t)-1;
291 size_t count(
const KeyT& k)
const 293 return contains(k) ? 1 : 0;
302 std::pair<iterator, bool> insert(KeyT key)
304 const HashCode hash = _hash_key(key);
305 size_t existing_pos = _find_filled_bucket(hash, key);
306 if (existing_pos == (
size_t)-1) {
308 _check_expand_need();
309 size_t pos = _insert_unique_no_expand_check(hash, std::move(key));
313 return {
iterator{
this, existing_pos},
false};
317 template<
class... Args>
318 std::pair<iterator, bool> emplace(Args&&... args)
320 return insert(KeyT(std::forward<Args>(args)...));
325 for (; begin != end; ++begin) {
331 void insert_unique(KeyT key)
333 _check_expand_need();
334 _insert_unique_no_expand_check(_hash_key(key), std::move(key));
341 bool erase(
const KeyT& key)
343 const HashCode hash = _hash_key(key);
344 const size_t pos = _find_filled_bucket(hash, key);
345 if (pos != (
size_t)-1) {
347 _hashes[pos] |= kTombStoneFlag;
357 _keys[it._bucket].~KeyT();
358 _hashes[it._bucket] |= kTombStoneFlag;
366 for (
size_t bucket=0; bucket<_num_buckets; ++bucket) {
367 if (_is_filled_hash(_hashes[bucket])) {
368 _keys[bucket].~KeyT();
372 static_assert(kUnusedHashCode == 0,
"");
373 std::memset(_hashes, 0, _num_buckets *
sizeof(HashCode));
377 void reserve(
size_t num_elems)
379 if (num_elems <= _rehash_limit) {
383 size_t min_required_buckets = 1 + num_elems * 100 / kMaxLoadFactorPercent;
384 size_t num_buckets = 4;
385 while (num_buckets < min_required_buckets) { num_buckets *= 2; }
389 auto new_hashes = (HashCode*)std::malloc(num_buckets *
sizeof(HashCode));
390 auto new_keys = (KeyT*)std::malloc(num_buckets *
sizeof(KeyT));
392 if (new_hashes ==
nullptr || new_keys ==
nullptr) {
393 std::free(new_hashes);
395 throw std::bad_alloc();
399 auto old_num_buckets = _num_buckets;
400 auto old_hashes = _hashes;
401 auto old_keys = _keys;
404 _num_buckets = num_buckets;
405 _rehash_limit = (_num_buckets * kMaxLoadFactorPercent) / 100;
406 _mask = _num_buckets - 1;
407 _hashes = new_hashes;
410 static_assert(kUnusedHashCode == 0,
"");
411 std::memset(_hashes, 0, _num_buckets *
sizeof(HashCode));
413 for (
size_t src_bucket=0; src_bucket<old_num_buckets; ++src_bucket) {
414 auto src_hash = old_hashes[src_bucket];
415 if (_is_filled_hash(src_hash)) {
416 auto& src_key = old_keys[src_bucket];
417 _insert_unique_no_expand_check(src_hash, std::move(src_key));
422 std::free(old_hashes);
428 void _check_expand_need()
430 reserve(_num_filled + 1);
433 uint32_t _hash_key(
const KeyT& key)
const 435 HashCode hash =
static_cast<HashCode
>(_hasher(key));
438 hash &= ~kTombStoneFlag;
441 hash ^= hash==kUnusedHashCode;
446 static bool _is_deleted_hash(HashCode hash)
448 return (hash & kTombStoneFlag) != 0;
451 static bool _is_filled_hash(HashCode hash)
453 return hash != kUnusedHashCode && !_is_deleted_hash(hash);
456 size_t _desired_pos(HashCode hash)
const 461 size_t _probe_distance(HashCode hash, HashCode pos)
const 463 return (pos + _num_buckets - _desired_pos(hash)) & _mask;
466 void _construct(
size_t pos, HashCode hash, KeyT&& key)
468 new (&_keys[pos]) KeyT(std::move(key));
473 size_t _insert_unique_no_expand_check(HashCode hash, KeyT&& key)
475 size_t pos = _desired_pos(hash);
479 if (_hashes[pos] == kUnusedHashCode) {
480 _construct(pos, hash, std::move(key));
486 size_t existing_elem_probe_dist = _probe_distance(_hashes[pos], pos);
487 if (existing_elem_probe_dist < dist) {
488 if (_is_deleted_hash(_hashes[pos])) {
489 _construct(pos, hash, std::move(key));
493 std::swap(hash, _hashes[pos]);
494 std::swap(key, _keys[pos]);
495 dist = existing_elem_probe_dist;
498 pos = (pos + 1) & _mask;
503 size_t _find_filled_bucket(HashCode hash,
const KeyT& key)
const 505 if (_num_buckets == 0) {
return (
size_t)-1; }
506 size_t pos = _desired_pos(hash);
510 if (_hashes[pos] == kUnusedHashCode) {
512 }
else if (dist > _probe_distance(_hashes[pos], pos)) {
514 }
else if (_hashes[pos] == hash && _keys[pos] == key) {
518 pos = (pos + 1) & _mask;
527 KeyT* _keys =
nullptr;
528 HashCode* _hashes =
nullptr;
529 size_t _num_buckets = 0;
530 size_t _num_filled = 0;
531 size_t _rehash_limit = 0;
Definition: hash_set_robin_hood.hpp:108
Definition: hash_set_robin_hood.hpp:20
Definition: hash_set_robin_hood.hpp:46
Definition: hash_set_robin_hood.hpp:30
Definition: coroutine.hpp:18