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"
67template <
typename Key,
71 typename SizeType = uint16_t>
86 template <
typename K,
typename... Args>
87 explicit Node(K&& key, Args&&... args)
89 std::forward_as_tuple(std::forward<K>(key)),
90 std::forward_as_tuple(std::forward<Args>(args)...)) {}
93 using NodeType = Node;
95 using KeyValueType =
decltype(std::declval<NodeType>().key_value);
99 using CalcType = std::
100 conditional_t<
sizeof(SizeType) <
sizeof(uint32_t), uint32_t, uint64_t>;
105 template <
bool kIsConst>
108 using iterator_category = std::forward_iterator_tag;
109 using value_type = KeyValueType;
110 using difference_type = std::ptrdiff_t;
112 std::conditional_t<kIsConst, const value_type&, value_type&>;
114 std::conditional_t<kIsConst, const value_type*, value_type*>;
116 constexpr Iterator() =
default;
118 template <
bool kOtherConst,
119 typename = std::enable_if_t<kIsConst && !kOtherConst>>
120 constexpr Iterator(
const Iterator<kOtherConst>& other) : it_(other.it_) {}
122 reference operator*()
const {
return it_->key_value; }
123 pointer operator->()
const {
return &(it_->key_value); }
125 Iterator operator++() {
129 Iterator operator++(
int) {
130 Iterator tmp = *
this;
134 friend bool operator==(Iterator lhs, Iterator rhs) {
135 return lhs.it_ == rhs.it_;
137 friend bool operator!=(Iterator lhs, Iterator rhs) {
138 return lhs.it_ != rhs.it_;
143 std::conditional_t<kIsConst,
144 typename NodeVectorType::const_iterator,
145 typename NodeVectorType::iterator>;
147 constexpr explicit Iterator(BaseIterator it) : it_(it) {}
149 friend class DynamicHashMap;
155 using key_type = Key;
156 using mapped_type = Value;
157 using value_type = KeyValueType;
158 using size_type = SizeType;
159 using difference_type = std::ptrdiff_t;
161 using key_equal =
Equal;
162 using allocator_type = Allocator;
163 using reference = value_type&;
164 using const_reference =
const value_type&;
165 using pointer = value_type*;
166 using const_pointer =
const value_type*;
167 using iterator = Iterator<false>;
168 using const_iterator = Iterator<true>;
169 using node_type = NodeType;
170 using insert_return_type = std::pair<iterator, bool>;
178 const Equal& equal = Equal()) noexcept
179 : buckets_(allocator), nodes_(allocator), hash_(hash), equal_(equal) {}
191 : buckets_(std::move(other.buckets_)),
192 nodes_(std::move(other.nodes_)),
193 hash_(std::move(other.hash_)),
194 equal_(std::move(other.equal_)),
195 max_load_factor_percent_(other.max_load_factor_percent_),
196 rehash_threshold_(other.rehash_threshold_) {}
201 if (&other ==
this) {
205 buckets_ = std::move(other.buckets_);
206 nodes_ = std::move(other.nodes_);
207 hash_ = std::move(other.hash_);
208 equal_ = std::move(other.equal_);
209 max_load_factor_percent_ = other.max_load_factor_percent_;
210 rehash_threshold_ = other.rehash_threshold_;
219 iterator begin() {
return iterator(nodes_.begin()); }
220 const_iterator begin()
const {
return const_iterator(nodes_.begin()); }
221 const_iterator cbegin()
const {
return begin(); }
222 iterator end() {
return iterator(nodes_.end()); }
223 const_iterator end()
const {
return const_iterator(nodes_.end()); }
224 const_iterator cend()
const {
return end(); }
228 [[nodiscard]]
bool empty()
const {
return nodes_.empty(); }
229 size_type size()
const {
return nodes_.size(); }
230 size_type max_size()
const {
return nodes_.max_size(); }
252 const value_type& value) {
259 std::optional<insert_return_type>
try_insert(value_type&&) =
delete;
262 insert_return_type
insert(
const value_type& value) {
263 return emplace(value.first, value.second);
266 insert_return_type
insert(value_type&& value) {
267 return emplace(std::move(value.first), std::move(value.second));
270 template <
typename InputIt,
271 typename = containers::internal::EnableIfInputIterator<InputIt>>
272 void insert(InputIt first, InputIt last) {
273 for (
auto it = first; it != last; ++it) {
274 emplace(it->first, it->second);
278 void insert(std::initializer_list<value_type> ilist) {
279 insert(ilist.begin(), ilist.end());
285 template <
typename K,
typename... Args>
286 [[nodiscard]] std::optional<std::pair<iterator, bool>>
try_emplace(
287 K&& key, Args&&... args) {
288 return TryEmplaceImpl(std::forward<K>(key), std::forward<Args>(args)...);
292 template <
typename K,
typename... Args>
293 std::pair<iterator, bool>
emplace(K&& key, Args&&... args) {
295 try_emplace(std::forward<K>(key), std::forward<Args>(args)...);
296 PW_ASSERT(result.has_value());
297 return result.value();
308 PW_ASSERT(pos != end());
309 NodeType* node = &(*pos.it_);
310 UnlinkFromBucket(node);
311 return EraseFromVector(node->index);
314 iterator
erase(const_iterator pos) {
315 auto it = iterator(nodes_.begin() + pos.it_->index);
323 iterator
erase(const_iterator first, const_iterator last) {
325 return (first == end()) ? end()
326 : iterator(nodes_.begin() + first.it_->index);
329 size_type index_first = first.it_->index;
330 size_type index_last = (last == end()) ? nodes_.size() : last.it_->index;
331 for (size_type i = index_last - 1; i > index_first; --i) {
332 auto it = iterator(nodes_.begin() + i);
341 template <
typename K>
343 NodeType* node = UnlinkFromBucket(std::forward<K>(key));
344 if (node ==
nullptr) {
347 EraseFromVector(node->index);
353 buckets_.
swap(other.buckets_);
354 nodes_.
swap(other.nodes_);
355 std::swap(hash_, other.hash_);
356 std::swap(equal_, other.equal_);
357 std::swap(max_load_factor_percent_, other.max_load_factor_percent_);
358 std::swap(rehash_threshold_, other.rehash_threshold_);
372 if (&other ==
this) {
376 auto it = other.begin();
377 while (it != other.end()) {
378 auto result =
try_emplace(it->first, std::move(it->second));
379 if (result.has_value() && result.value().second) {
380 it = other.
erase(it);
395 mapped_type&
at(
const key_type& key) {
397 PW_ASSERT(it != end());
401 const mapped_type&
at(
const key_type& key)
const {
403 PW_ASSERT(it != end());
411 template <
typename U = mapped_type,
412 typename = std::enable_if_t<std::is_default_constructible_v<U>>>
414 return emplace(key).first->second;
418 size_type
count(
const key_type& key)
const {
419 return find(key) != end() ? 1 : 0;
425 iterator
find(
const key_type& key) {
426 NodeType* node = FindImpl(key);
427 if (node !=
nullptr) {
428 return iterator(nodes_.begin() + node->index);
433 const_iterator
find(
const key_type& key)
const {
434 NodeType* node = FindImpl(key);
435 if (node !=
nullptr) {
436 return const_iterator(nodes_.begin() + node->index);
443 bool contains(
const key_type& key)
const {
return find(key) != end(); }
451 return std::make_pair(it, it);
453 return std::make_pair(it, std::next(it));
456 std::pair<const_iterator, const_iterator>
equal_range(
457 const key_type& key)
const {
460 return std::make_pair(it, it);
462 return std::make_pair(it, std::next(it));
470 if (buckets_.empty()) {
473 return (nodes_.size() * 100) / buckets_.size();
485 PW_ASSERT(percent > 0 && percent <= kMaxLoadFactorPercentLimit);
486 max_load_factor_percent_ = percent;
487 UpdateRehashThreshold();
495 if (
count < buckets_.size()) {
504 for (
auto& node : nodes_) {
505 size_type bucket_index = hash_(node.key_value.first) % new_buckets.size();
506 node.next = new_buckets[bucket_index];
507 new_buckets[bucket_index] = &node;
510 buckets_ = std::move(new_buckets);
512 UpdateRehashThreshold();
532 const CalcType required_buckets_calc =
535 if (required_buckets_calc > std::numeric_limits<size_type>::max()) {
539 size_type required_buckets =
static_cast<size_type
>(required_buckets_calc);
555 size_type GetBucketIndex(
const key_type& key)
const {
556 PW_ASSERT(!buckets_.empty());
557 return hash_(key) % buckets_.size();
560 size_type CalculateNewBucketCount(size_type current_count)
const {
561 size_type max_bucket_count = buckets_.max_size();
562 if (current_count > max_bucket_count / 2) {
563 return max_bucket_count;
565 return current_count * 2;
568 void UpdateRehashThreshold() {
569 if (buckets_.empty()) {
570 rehash_threshold_ = 0;
572 rehash_threshold_ =
static_cast<size_type
>(
578 NodeType* FindImpl(
const key_type& key)
const {
582 size_type bucket_index = GetBucketIndex(key);
583 NodeType* node = buckets_[bucket_index];
584 while (node !=
nullptr) {
585 if (equal_(node->key_value.first, key)) {
593 template <
typename K,
typename... Args>
594 [[nodiscard]] std::optional<std::pair<iterator, bool>> TryEmplaceImpl(
595 K&& key, Args&&... args) {
596 if (
auto it =
find(key); it != end()) {
597 return std::make_pair(iterator(it),
false);
600 if (size() >= max_size()) {
604 if (buckets_.empty()) {
605 if (!buckets_.
try_assign(kDefaultInitialBuckets,
nullptr)) {
608 UpdateRehashThreshold();
612 std::forward<Args>(args)...)) {
616 NodeType& new_node = nodes_.back();
617 size_type bucket_index = GetBucketIndex(new_node.key_value.first);
618 new_node.next = buckets_[bucket_index];
619 new_node.index = nodes_.size() - 1;
620 buckets_[bucket_index] = &new_node;
622 if (size() > rehash_threshold_) {
623 size_type new_bucket_count = CalculateNewBucketCount(buckets_.size());
627 return std::make_pair(iterator(std::prev(nodes_.end())),
true);
630 iterator EraseFromVector(size_type index_to_remove) {
631 size_type last_index = nodes_.size() - 1;
633 if (index_to_remove != last_index) {
634 nodes_.
swap(index_to_remove, last_index);
635 nodes_[index_to_remove].index = index_to_remove;
639 return iterator(nodes_.begin() + index_to_remove);
642 void UnlinkFromBucket(NodeType* node) {
643 size_type bucket_index = GetBucketIndex(node->key_value.first);
644 FindAndUnlinkFromBucket(bucket_index,
645 [node](NodeType* curr) {
return curr == node; });
648 template <
typename K>
649 NodeType* UnlinkFromBucket(K&& key) {
650 size_type bucket_index = GetBucketIndex(key);
651 return FindAndUnlinkFromBucket(bucket_index, [
this, &key](NodeType* curr) {
652 return equal_(curr->key_value.first, key);
656 template <
typename Predicate>
657 NodeType* FindAndUnlinkFromBucket(size_type bucket_index, Predicate pred) {
658 NodeType* curr = buckets_[bucket_index];
659 NodeType* prev =
nullptr;
661 while (curr !=
nullptr) {
663 if (prev ==
nullptr) {
664 buckets_[bucket_index] = curr->next;
666 prev->next = curr->next;
678 NodeVectorType nodes_;
681 size_type max_load_factor_percent_ = 75;
682 size_type rehash_threshold_ = 0;
683 static constexpr size_type kDefaultInitialBuckets = 11;
691 static constexpr size_type kMaxLoadFactorPercentLimit = 500;
Definition: allocator.h:42
Definition: dynamic_deque.h:68
bool try_assign(size_type count, const value_type &value)
Definition: generic_deque.h:618
Definition: dynamic_hash_map.h:72
Definition: dynamic_ptr_vector.h:50
mapped_type & at(const key_type &key)
Definition: dynamic_hash_map.h:395
void rehash(size_type count)
Definition: dynamic_hash_map.h:518
DynamicHashMap(DynamicHashMap &&other) noexcept
Definition: dynamic_hash_map.h:190
insert_return_type insert(const value_type &value)
Inserts a value into the map. Crashes on allocation failure.
Definition: dynamic_hash_map.h:262
std::pair< iterator, bool > emplace(K &&key, Args &&... args)
Constructs an element in-place. Crashes on allocation failure.
Definition: dynamic_hash_map.h:293
void reserve(size_type count)
Definition: dynamic_hash_map.h:552
DynamicHashMap(const DynamicHashMap &)=delete
mapped_type & operator[](const key_type &key)
Definition: dynamic_hash_map.h:413
allocator_type & get_allocator() const
Returns the allocator used by this map.
Definition: dynamic_hash_map.h:215
constexpr DynamicHashMap(Allocator &allocator, const Hash &hash=Hash(), const Equal &equal=Equal()) noexcept
Definition: dynamic_hash_map.h:176
void swap(DynamicHashMap &other) noexcept
Swaps the contents of two maps. No allocations occur.
Definition: dynamic_hash_map.h:352
void max_load_factor_percent(size_type percent)
Definition: dynamic_hash_map.h:484
std::optional< insert_return_type > try_insert(const value_type &value)
Definition: dynamic_hash_map.h:251
void merge(DynamicHashMap &other)
Definition: dynamic_hash_map.h:371
Node * next
Pointer to the next node in the same hash bucket (collision chain).
Definition: dynamic_hash_map.h:81
size_type count(const key_type &key) const
Returns the number of elements with key key (0 or 1).
Definition: dynamic_hash_map.h:418
size_type erase(K &&key)
Definition: dynamic_hash_map.h:342
bool try_reserve(size_type count)
Definition: dynamic_hash_map.h:531
std::pair< const Key, Value > key_value
The stored key-value pair.
Definition: dynamic_hash_map.h:78
std::optional< std::pair< iterator, bool > > try_emplace(K &&key, Args &&... args)
Definition: dynamic_hash_map.h:286
iterator find(const key_type &key)
Definition: dynamic_hash_map.h:425
bool contains(const key_type &key) const
Definition: dynamic_hash_map.h:443
void reset()
Clears the deque and deallocates its buffer.
Definition: dynamic_hash_map.h:243
size_type max_load_factor_percent() const
Definition: dynamic_hash_map.h:478
iterator erase(iterator pos)
Definition: dynamic_hash_map.h:307
DynamicHashMap & operator=(DynamicHashMap &&other) noexcept
Definition: dynamic_hash_map.h:200
SizeType index
Index of this node in the nodes_ vector, enabling O(1) erasure.
Definition: dynamic_hash_map.h:84
iterator erase(const_iterator first, const_iterator last)
Definition: dynamic_hash_map.h:323
bool try_rehash(size_type count)
Definition: dynamic_hash_map.h:494
void clear()
Definition: dynamic_hash_map.h:237
size_type load_factor_percent() const
Definition: dynamic_hash_map.h:469
std::pair< iterator, iterator > equal_range(const key_type &key)
Definition: dynamic_hash_map.h:448
void swap(DynamicDeque &other) noexcept
Swaps the contents of two deques. No allocations occur.
Definition: dynamic_deque.h:203
void reset()
Clears the deque and deallocates its buffer.
Definition: dynamic_deque.h:190
bool Equal(const C1 &c1, const C2 &c2)
Definition: algorithm.h:248
void reset()
Clears the deque and deallocates its buffer.
Definition: dynamic_ptr_vector.h:405
bool try_emplace_back(Args &&... args)
Definition: dynamic_ptr_vector.h:305
void swap(DynamicPtrVector &other)
Swaps the contents of this DynamicPtrVector with another one.
Definition: dynamic_ptr_vector.h:411
bool try_reserve_ptr(size_type new_capacity)
Definition: dynamic_ptr_vector.h:212
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