19#include <initializer_list>
26#include "pw_allocator/allocator.h"
27#include "pw_assert/assert.h"
28#include "pw_containers/dynamic_deque.h"
29#include "pw_containers/dynamic_ptr_vector.h"
30#include "pw_containers/functional.h"
31#include "pw_preprocessor/compiler.h"
57template <
typename Key,
61 typename SizeType = uint16_t>
76 template <
typename K,
typename... Args>
77 explicit Node(K&& key, Args&&... args)
79 std::forward_as_tuple(std::forward<K>(key)),
80 std::forward_as_tuple(std::forward<Args>(args)...)) {}
83 using NodeType = Node;
85 using KeyValueType =
decltype(std::declval<NodeType>().key_value);
89 using CalcType = std::
90 conditional_t<
sizeof(SizeType) <
sizeof(uint32_t), uint32_t, uint64_t>;
95 template <
bool kIsConst>
98 using iterator_category = std::forward_iterator_tag;
99 using value_type = KeyValueType;
100 using difference_type = std::ptrdiff_t;
102 std::conditional_t<kIsConst, const value_type&, value_type&>;
104 std::conditional_t<kIsConst, const value_type*, value_type*>;
106 constexpr Iterator() =
default;
108 template <
bool kOtherConst,
109 typename = std::enable_if_t<kIsConst && !kOtherConst>>
110 constexpr Iterator(
const Iterator<kOtherConst>& other) : it_(other.it_) {}
112 reference operator*()
const {
return it_->key_value; }
113 pointer operator->()
const {
return &(it_->key_value); }
115 Iterator operator++() {
119 Iterator operator++(
int) {
120 Iterator tmp = *
this;
124 friend bool operator==(Iterator lhs, Iterator rhs) {
125 return lhs.it_ == rhs.it_;
127 friend bool operator!=(Iterator lhs, Iterator rhs) {
128 return lhs.it_ != rhs.it_;
133 std::conditional_t<kIsConst,
134 typename NodeVectorType::const_iterator,
135 typename NodeVectorType::iterator>;
137 constexpr explicit Iterator(BaseIterator it) : it_(it) {}
139 friend class DynamicHashMap;
145 using key_type = Key;
146 using mapped_type = Value;
147 using value_type = KeyValueType;
148 using size_type = SizeType;
149 using difference_type = std::ptrdiff_t;
151 using key_equal =
Equal;
152 using allocator_type = Allocator;
153 using reference = value_type&;
154 using const_reference =
const value_type&;
155 using pointer = value_type*;
156 using const_pointer =
const value_type*;
157 using iterator = Iterator<false>;
158 using const_iterator = Iterator<true>;
159 using node_type = NodeType;
160 using insert_return_type = std::pair<iterator, bool>;
168 const Equal& equal = Equal()) noexcept
169 : buckets_(allocator), nodes_(allocator), hash_(hash), equal_(equal) {}
181 : buckets_(std::move(other.buckets_)),
182 nodes_(std::move(other.nodes_)),
183 hash_(std::move(other.hash_)),
184 equal_(std::move(other.equal_)),
185 max_load_factor_percent_(other.max_load_factor_percent_),
186 rehash_threshold_(other.rehash_threshold_) {}
191 if (&other ==
this) {
195 buckets_ = std::move(other.buckets_);
196 nodes_ = std::move(other.nodes_);
197 hash_ = std::move(other.hash_);
198 equal_ = std::move(other.equal_);
199 max_load_factor_percent_ = other.max_load_factor_percent_;
200 rehash_threshold_ = other.rehash_threshold_;
209 iterator begin() {
return iterator(nodes_.begin()); }
210 const_iterator begin()
const {
return const_iterator(nodes_.begin()); }
211 const_iterator cbegin()
const {
return begin(); }
212 iterator end() {
return iterator(nodes_.end()); }
213 const_iterator end()
const {
return const_iterator(nodes_.end()); }
214 const_iterator cend()
const {
return end(); }
218 [[nodiscard]]
bool empty()
const {
return nodes_.empty(); }
219 size_type size()
const {
return nodes_.size(); }
220 size_type max_size()
const {
return nodes_.max_size(); }
236 const value_type& value) {
243 std::optional<insert_return_type>
try_insert(value_type&&) =
delete;
246 insert_return_type
insert(
const value_type& value) {
247 return emplace(value.first, value.second);
250 insert_return_type
insert(value_type&& value) {
251 return emplace(std::move(value.first), std::move(value.second));
254 template <
typename InputIt,
255 typename = containers::internal::EnableIfInputIterator<InputIt>>
256 void insert(InputIt first, InputIt last) {
257 for (
auto it = first; it != last; ++it) {
258 emplace(it->first, it->second);
262 void insert(std::initializer_list<value_type> ilist) {
263 insert(ilist.begin(), ilist.end());
269 template <
typename K,
typename... Args>
270 [[nodiscard]] std::optional<std::pair<iterator, bool>>
try_emplace(
271 K&& key, Args&&... args) {
272 return TryEmplaceImpl(std::forward<K>(key), std::forward<Args>(args)...);
276 template <
typename K,
typename... Args>
277 std::pair<iterator, bool>
emplace(K&& key, Args&&... args) {
279 try_emplace(std::forward<K>(key), std::forward<Args>(args)...);
280 PW_ASSERT(result.has_value());
281 return result.value();
292 PW_ASSERT(pos != end());
293 NodeType* node = &(*pos.it_);
294 UnlinkFromBucket(node);
295 return EraseFromVector(node->index);
298 iterator
erase(const_iterator pos) {
299 auto it = iterator(nodes_.begin() + pos.it_->index);
307 iterator
erase(const_iterator first, const_iterator last) {
309 return (first == end()) ? end()
310 : iterator(nodes_.begin() + first.it_->index);
313 size_type index_first = first.it_->index;
314 size_type index_last = (last == end()) ? nodes_.size() : last.it_->index;
315 for (size_type i = index_last - 1; i > index_first; --i) {
316 auto it = iterator(nodes_.begin() + i);
325 template <
typename K>
327 NodeType* node = UnlinkFromBucket(std::forward<K>(key));
328 if (node ==
nullptr) {
331 EraseFromVector(node->index);
337 buckets_.
swap(other.buckets_);
338 nodes_.
swap(other.nodes_);
339 std::swap(hash_, other.hash_);
340 std::swap(equal_, other.equal_);
341 std::swap(max_load_factor_percent_, other.max_load_factor_percent_);
342 std::swap(rehash_threshold_, other.rehash_threshold_);
356 if (&other ==
this) {
360 auto it = other.begin();
361 while (it != other.end()) {
362 auto result =
try_emplace(it->first, std::move(it->second));
363 if (result.has_value() && result.value().second) {
364 it = other.
erase(it);
379 mapped_type&
at(
const key_type& key) {
381 PW_ASSERT(it != end());
385 const mapped_type&
at(
const key_type& key)
const {
387 PW_ASSERT(it != end());
395 template <
typename U = mapped_type,
396 typename = std::enable_if_t<std::is_default_constructible_v<U>>>
398 return emplace(key).first->second;
402 size_type
count(
const key_type& key)
const {
403 return find(key) != end() ? 1 : 0;
409 iterator
find(
const key_type& key) {
410 NodeType* node = FindImpl(key);
411 if (node !=
nullptr) {
412 return iterator(nodes_.begin() + node->index);
417 const_iterator
find(
const key_type& key)
const {
418 NodeType* node = FindImpl(key);
419 if (node !=
nullptr) {
420 return const_iterator(nodes_.begin() + node->index);
427 bool contains(
const key_type& key)
const {
return find(key) != end(); }
435 return std::make_pair(it, it);
437 return std::make_pair(it, std::next(it));
440 std::pair<const_iterator, const_iterator>
equal_range(
441 const key_type& key)
const {
444 return std::make_pair(it, it);
446 return std::make_pair(it, std::next(it));
454 if (buckets_.empty()) {
457 return (nodes_.size() * 100) / buckets_.
size();
469 PW_ASSERT(percent > 0 && percent <= kMaxLoadFactorPercentLimit);
470 max_load_factor_percent_ = percent;
471 UpdateRehashThreshold();
488 for (
auto& node : nodes_) {
489 size_type bucket_index = hash_(node.key_value.first) % new_buckets.
size();
490 node.next = new_buckets[bucket_index];
491 new_buckets[bucket_index] = &node;
494 buckets_ = std::move(new_buckets);
496 UpdateRehashThreshold();
516 const CalcType required_buckets_calc =
519 if (required_buckets_calc > std::numeric_limits<size_type>::max()) {
523 size_type required_buckets =
static_cast<size_type
>(required_buckets_calc);
539 size_type GetBucketIndex(
const key_type& key)
const {
540 PW_ASSERT(!buckets_.empty());
541 return hash_(key) % buckets_.
size();
544 size_type CalculateNewBucketCount(size_type current_count)
const {
545 size_type max_bucket_count = buckets_.max_size();
546 if (current_count > max_bucket_count / 2) {
547 return max_bucket_count;
549 return current_count * 2;
552 void UpdateRehashThreshold() {
553 if (buckets_.empty()) {
554 rehash_threshold_ = 0;
556 rehash_threshold_ =
static_cast<size_type
>(
562 NodeType* FindImpl(
const key_type& key)
const {
566 size_type bucket_index = GetBucketIndex(key);
567 NodeType* node = buckets_[bucket_index];
568 while (node !=
nullptr) {
569 if (equal_(node->key_value.first, key)) {
577 template <
typename K,
typename... Args>
578 [[nodiscard]] std::optional<std::pair<iterator, bool>> TryEmplaceImpl(
579 K&& key, Args&&... args) {
580 if (
auto it =
find(key); it != end()) {
581 return std::make_pair(iterator(it),
false);
584 if (size() >= max_size()) {
588 if (buckets_.empty()) {
589 if (!buckets_.
try_assign(kDefaultInitialBuckets,
nullptr)) {
592 UpdateRehashThreshold();
596 std::forward<Args>(args)...)) {
600 NodeType& new_node = nodes_.back();
601 size_type bucket_index = GetBucketIndex(new_node.key_value.first);
602 new_node.next = buckets_[bucket_index];
603 new_node.index = nodes_.size() - 1;
604 buckets_[bucket_index] = &new_node;
606 if (size() > rehash_threshold_) {
607 size_type new_bucket_count = CalculateNewBucketCount(buckets_.
size());
611 return std::make_pair(iterator(std::prev(nodes_.end())),
true);
614 iterator EraseFromVector(size_type index_to_remove) {
615 size_type last_index = nodes_.size() - 1;
617 if (index_to_remove != last_index) {
618 nodes_.
swap(index_to_remove, last_index);
619 nodes_[index_to_remove].index = index_to_remove;
623 return iterator(nodes_.begin() + index_to_remove);
626 void UnlinkFromBucket(NodeType* node) {
627 size_type bucket_index = GetBucketIndex(node->key_value.first);
628 FindAndUnlinkFromBucket(bucket_index,
629 [node](NodeType* curr) {
return curr == node; });
632 template <
typename K>
633 NodeType* UnlinkFromBucket(K&& key) {
634 size_type bucket_index = GetBucketIndex(key);
635 return FindAndUnlinkFromBucket(bucket_index, [
this, &key](NodeType* curr) {
636 return equal_(curr->key_value.first, key);
640 template <
typename Predicate>
641 NodeType* FindAndUnlinkFromBucket(size_type bucket_index, Predicate pred) {
642 NodeType* curr = buckets_[bucket_index];
643 NodeType* prev =
nullptr;
645 while (curr !=
nullptr) {
647 if (prev ==
nullptr) {
648 buckets_[bucket_index] = curr->next;
650 prev->next = curr->next;
662 NodeVectorType nodes_;
665 size_type max_load_factor_percent_ = 75;
666 size_type rehash_threshold_ = 0;
667 static constexpr size_type kDefaultInitialBuckets = 11;
675 static constexpr size_type kMaxLoadFactorPercentLimit = 500;
Definition: allocator.h:45
Definition: dynamic_deque.h:60
bool try_assign(size_type count, const value_type &value)
Definition: generic_deque.h:626
Definition: dynamic_hash_map.h:62
Definition: dynamic_ptr_vector.h:39
constexpr size_type size() const noexcept
Returns the number of elements in the deque.
Definition: generic_deque.h:69
mapped_type & at(const key_type &key)
Definition: dynamic_hash_map.h:379
void rehash(size_type count)
Definition: dynamic_hash_map.h:502
DynamicHashMap(DynamicHashMap &&other) noexcept
Definition: dynamic_hash_map.h:180
insert_return_type insert(const value_type &value)
Inserts a value into the map. Crashes on allocation failure.
Definition: dynamic_hash_map.h:246
std::pair< iterator, bool > emplace(K &&key, Args &&... args)
Constructs an element in-place. Crashes on allocation failure.
Definition: dynamic_hash_map.h:277
void reserve(size_type count)
Definition: dynamic_hash_map.h:536
DynamicHashMap(const DynamicHashMap &)=delete
mapped_type & operator[](const key_type &key)
Definition: dynamic_hash_map.h:397
allocator_type & get_allocator() const
Returns the allocator used by this map.
Definition: dynamic_hash_map.h:205
constexpr DynamicHashMap(Allocator &allocator, const Hash &hash=Hash(), const Equal &equal=Equal()) noexcept
Definition: dynamic_hash_map.h:166
void swap(DynamicHashMap &other) noexcept
Swaps the contents of two maps. No allocations occur.
Definition: dynamic_hash_map.h:336
void max_load_factor_percent(size_type percent)
Definition: dynamic_hash_map.h:468
std::optional< insert_return_type > try_insert(const value_type &value)
Definition: dynamic_hash_map.h:235
void merge(DynamicHashMap &other)
Definition: dynamic_hash_map.h:355
Node * next
Pointer to the next node in the same hash bucket (collision chain).
Definition: dynamic_hash_map.h:71
size_type count(const key_type &key) const
Returns the number of elements with key key (0 or 1).
Definition: dynamic_hash_map.h:402
size_type erase(K &&key)
Definition: dynamic_hash_map.h:326
bool try_reserve(size_type count)
Definition: dynamic_hash_map.h:515
std::pair< const Key, Value > key_value
The stored key-value pair.
Definition: dynamic_hash_map.h:68
std::optional< std::pair< iterator, bool > > try_emplace(K &&key, Args &&... args)
Definition: dynamic_hash_map.h:270
iterator find(const key_type &key)
Definition: dynamic_hash_map.h:409
bool contains(const key_type &key) const
Definition: dynamic_hash_map.h:427
size_type max_load_factor_percent() const
Definition: dynamic_hash_map.h:462
iterator erase(iterator pos)
Definition: dynamic_hash_map.h:291
DynamicHashMap & operator=(DynamicHashMap &&other) noexcept
Definition: dynamic_hash_map.h:190
SizeType index
Index of this node in the nodes_ vector, enabling O(1) erasure.
Definition: dynamic_hash_map.h:74
iterator erase(const_iterator first, const_iterator last)
Definition: dynamic_hash_map.h:307
bool try_rehash(size_type count)
Definition: dynamic_hash_map.h:478
void clear()
Definition: dynamic_hash_map.h:227
size_type load_factor_percent() const
Definition: dynamic_hash_map.h:453
std::pair< iterator, iterator > equal_range(const key_type &key)
Definition: dynamic_hash_map.h:432
void swap(DynamicDeque &other) noexcept
Swaps the contents of two deques. No allocations occur.
Definition: dynamic_deque.h:182
bool Equal(const C1 &c1, const C2 &c2)
Definition: algorithm.h:248
bool try_emplace_back(Args &&... args)
Definition: dynamic_ptr_vector.h:294
void swap(DynamicPtrVector &other)
Swaps the contents of this DynamicPtrVector with another one.
Definition: dynamic_ptr_vector.h:394
bool try_reserve_ptr(size_type new_capacity)
Definition: dynamic_ptr_vector.h:201
Status Hash(ConstByteSpan message, ByteSpan out_digest)
Definition: sha256.h:167
#define PW_NO_UNIQUE_ADDRESS
Definition: compiler.h:297
The Pigweed namespace.
Definition: alignment.h:27
Definition: functional.h:228
Definition: functional.h:217