21 constexpr
bool operator()(
const T &lhs,
const T &rhs)
const 28 template <
typename KeyT,
typename ValueT,
typename HashT = std::hash<KeyT>,
typename EqT = HashMapEqualTo<KeyT>>
34 using PairT = std::pair<KeyT, ValueT>;
36 using size_type = size_t;
37 using value_type = PairT;
38 using reference = PairT&;
39 using const_reference =
const PairT&;
44 using iterator_category = std::forward_iterator_tag;
45 using difference_type = size_t;
46 using distance_type = size_t;
47 using value_type = std::pair<KeyT, ValueT>;
48 using pointer = value_type*;
49 using reference = value_type&;
53 iterator(
MyType* hash_map,
size_t bucket) : _map(hash_map), _bucket(bucket)
59 this->goto_next_element();
65 size_t old_index = _bucket;
66 this->goto_next_element();
70 reference operator*()
const 72 return _map->_pairs[_bucket];
75 pointer operator->()
const 77 return _map->_pairs + _bucket;
80 bool operator==(
const iterator& rhs)
const 82 DCHECK_EQ_F(_map, rhs._map);
83 return this->_bucket == rhs._bucket;
86 bool operator!=(
const iterator& rhs)
const 88 DCHECK_EQ_F(_map, rhs._map);
89 return this->_bucket != rhs._bucket;
93 void goto_next_element()
95 DCHECK_LT_F(_bucket, _map->_num_buckets);
98 }
while (_bucket < _map->_num_buckets && _map->_states[_bucket] != State::FILLED);
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 std::pair<KeyT, ValueT>;
115 using pointer = value_type*;
116 using reference = value_type&;
130 this->goto_next_element();
136 size_t old_index = _bucket;
137 this->goto_next_element();
141 reference operator*()
const 143 return _map->_pairs[_bucket];
146 pointer operator->()
const 148 return _map->_pairs + _bucket;
153 DCHECK_EQ_F(_map, rhs._map);
154 return this->_bucket == rhs._bucket;
159 DCHECK_EQ_F(_map, rhs._map);
160 return this->_bucket != rhs._bucket;
164 void goto_next_element()
166 DCHECK_LT_F(_bucket, _map->_num_buckets);
169 }
while (_bucket < _map->_num_buckets && _map->_states[_bucket] != State::FILLED);
185 reserve(other.size());
186 insert(cbegin(other), cend(other));
191 *
this = std::move(other);
197 reserve(other.size());
198 insert(cbegin(other), cend(other));
202 void operator=(
HashMap&& other)
209 for (
size_t bucket=0; bucket<_num_buckets; ++bucket) {
210 if (_states[bucket] == State::FILLED) {
211 _pairs[bucket].~PairT();
220 std::swap(_hasher, other._hasher);
221 std::swap(_eq, other._eq);
222 std::swap(_states, other._states);
223 std::swap(_pairs, other._pairs);
224 std::swap(_num_buckets, other._num_buckets);
225 std::swap(_num_filled, other._num_filled);
226 std::swap(_max_probe_length, other._max_probe_length);
227 std::swap(_mask, other._mask);
235 while (bucket<_num_buckets && _states[bucket] != State::FILLED) {
244 while (bucket<_num_buckets && _states[bucket] != State::FILLED) {
251 {
return iterator(
this, _num_buckets); }
263 return _num_filled==0;
270 auto bucket = this->find_filled_bucket(key);
271 if (bucket == (
size_t)-1) {
279 auto bucket = this->find_filled_bucket(key);
280 if (bucket == (
size_t)-1)
287 bool contains(
const KeyT& k)
const 289 return find_filled_bucket(k) != (size_t)-1;
292 size_t count(
const KeyT& k)
const 294 return find_filled_bucket(k) != (size_t)-1 ? 1 : 0;
300 auto bucket = find_filled_bucket(k);
301 if (bucket != (
size_t)-1) {
302 return &_pairs[bucket].second;
311 auto bucket = find_filled_bucket(k);
312 if (bucket != (
size_t)-1) {
313 return &_pairs[bucket].second;
322 const ValueT* ret = try_get(k);
335 std::pair<iterator, bool>
insert(
const KeyT& key,
const ValueT& value)
339 auto bucket = find_or_allocate(key);
341 if (_states[bucket] == State::FILLED) {
342 return {
iterator(
this, bucket),
false };
344 _states[bucket] = State::FILLED;
345 new(_pairs + bucket) PairT(key, value);
347 return {
iterator(
this, bucket),
true };
351 std::pair<iterator, bool> insert(
const std::pair<KeyT, ValueT>& p)
353 return insert(p.first, p.second);
358 for (; begin != end; ++begin) {
359 insert(begin->first, begin->second);
366 DCHECK_F(!contains(key));
368 auto bucket = find_empty_bucket(key);
369 _states[bucket] = State::FILLED;
370 new(_pairs + bucket) PairT(std::move(key), std::move(value));
374 void insert_unique(std::pair<KeyT, ValueT>&& p)
376 insert_unique(std::move(p.first), std::move(p.second));
380 ValueT
set_get(
const KeyT& key,
const ValueT& new_value)
384 auto bucket = find_or_allocate(key);
387 if (_states[bucket] == State::FILLED) {
388 ValueT old_value = _pairs[bucket].second;
389 _pairs[bucket] = new_value.second;
392 _states[bucket] = State::FILLED;
393 new(_pairs + bucket) PairT(key, new_value);
404 auto bucket = find_or_allocate(key);
407 if (_states[bucket] != State::FILLED) {
408 _states[bucket] = State::FILLED;
409 new(_pairs + bucket) PairT(key, ValueT());
413 return _pairs[bucket].second;
422 auto bucket = find_filled_bucket(key);
423 if (bucket != (
size_t)-1) {
424 _states[bucket] = State::ACTIVE;
425 _pairs[bucket].~PairT();
437 DCHECK_EQ_F(it._map,
this);
438 DCHECK_LT_F(it._bucket, _num_buckets);
439 _states[it._bucket] = State::ACTIVE;
440 _pairs[it._bucket].~PairT();
448 for (
size_t bucket=0; bucket<_num_buckets; ++bucket) {
449 if (_states[bucket] == State::FILLED) {
450 _states[bucket] = State::INACTIVE;
451 _pairs[bucket].~PairT();
455 _max_probe_length = -1;
461 size_t required_buckets = num_elems + num_elems/2 + 1;
462 if (required_buckets <= _num_buckets) {
465 size_t num_buckets = 4;
466 while (num_buckets < required_buckets) { num_buckets *= 2; }
468 auto new_states = (State*)malloc(num_buckets *
sizeof(State));
469 auto new_pairs = (PairT*)malloc(num_buckets *
sizeof(PairT));
471 if (!new_states || !new_pairs) {
474 throw std::bad_alloc();
478 auto old_num_buckets = _num_buckets;
479 auto old_states = _states;
480 auto old_pairs = _pairs;
483 _num_buckets = num_buckets;
484 _mask = _num_buckets - 1;
485 _states = new_states;
488 std::fill_n(_states, num_buckets, State::INACTIVE);
490 _max_probe_length = -1;
492 for (
size_t src_bucket=0; src_bucket<old_num_buckets; src_bucket++) {
493 if (old_states[src_bucket] == State::FILLED) {
494 auto& src_pair = old_pairs[src_bucket];
496 auto dst_bucket = find_empty_bucket(src_pair.first);
497 DCHECK_NE_F(dst_bucket, (
size_t)-1);
498 DCHECK_NE_F(_states[dst_bucket], State::FILLED);
499 _states[dst_bucket] = State::FILLED;
500 new(_pairs + dst_bucket) PairT(std::move(src_pair));
515 void check_expand_need()
517 reserve(_num_filled + 1);
521 size_t find_filled_bucket(
const KeyT& key)
const 523 if (empty()) {
return (
size_t)-1; }
525 auto hash_value = _hasher(key);
526 for (
int offset=0; offset<=_max_probe_length; ++offset) {
527 auto bucket = (hash_value + offset) & _mask;
528 if (_states[bucket] == State::FILLED) {
529 if (_eq(_pairs[bucket].first, key)) {
532 }
else if (_states[bucket] == State::INACTIVE) {
541 size_t find_or_allocate(
const KeyT& key)
543 auto hash_value = _hasher(key);
544 size_t hole = (size_t)-1;
546 for (; offset<=_max_probe_length; ++offset) {
547 auto bucket = (hash_value + offset) & _mask;
549 if (_states[bucket] == State::FILLED) {
550 if (_eq(_pairs[bucket].first, key)) {
553 }
else if (_states[bucket] == State::INACTIVE) {
557 if (hole == (
size_t)-1) {
565 DCHECK_EQ_F(offset, _max_probe_length+1);
567 if (hole != (
size_t)-1) {
573 auto bucket = (hash_value + offset) & _mask;
575 if (_states[bucket] != State::FILLED) {
576 _max_probe_length = offset;
583 size_t find_empty_bucket(
const KeyT& key)
585 auto hash_value = _hasher(key);
586 for (
int offset=0; ; ++offset) {
587 auto bucket = (hash_value + offset) & _mask;
588 if (_states[bucket] != State::FILLED) {
589 if (offset > _max_probe_length) {
590 _max_probe_length = offset;
598 enum class State : uint8_t
607 State* _states =
nullptr;
608 PairT* _pairs =
nullptr;
609 size_t _num_buckets = 0;
610 size_t _num_filled = 0;
611 int _max_probe_length = -1;
bool erase(const KeyT &key)
Definition: hash_map.hpp:420
const ValueT * try_get(const KeyT &k) const
Const version of the above.
Definition: hash_map.hpp:309
ValueT set_get(const KeyT &key, const ValueT &new_value)
Return the old value or ValueT() if it didn't exist.
Definition: hash_map.hpp:380
iterator erase(iterator it)
Definition: hash_map.hpp:435
void insert_unique(KeyT &&key, ValueT &&value)
Same as above, but contains(key) MUST be false.
Definition: hash_map.hpp:364
like std::equal_to but no need to #include <functional>
Definition: hash_map.hpp:19
ValueT & operator[](const KeyT &key)
Like std::map<KeyT,ValueT>::operator[].
Definition: hash_map.hpp:400
void clear()
Remove all elements, keeping full capacity.
Definition: hash_map.hpp:446
void reserve(size_t num_elems)
Make room for this many elements.
Definition: hash_map.hpp:459
std::pair< iterator, bool > insert(const KeyT &key, const ValueT &value)
Definition: hash_map.hpp:335
ValueT * try_get(const KeyT &k)
Returns the matching ValueT or nullptr if k isn't found.
Definition: hash_map.hpp:298
const ValueT get_or_return_default(const KeyT &k) const
Convenience function.
Definition: hash_map.hpp:320
Definition: hash_map.hpp:41
A cache-friendly hash table with open addressing, linear probing and power-of-two capacity...
Definition: hash_map.hpp:29
Definition: hash_map.hpp:108
Definition: coroutine.hpp:18