libhatchet
Loading...
Searching...
No Matches
hxhash_table.hpp
Go to the documentation of this file.
1#pragma once
2// SPDX-FileCopyrightText: © 2017-2026 Adrian Johnston.
3// SPDX-License-Identifier: MIT
4// This file is licensed under the MIT license found in the LICENSE.md file.
5
11
12#include "libhatchet.h"
13
14// HX_USE_MACROS_WITH_MODULE allows including macros alongside the module.
15#if HX_USE_MACROS_WITH_MODULE
16#error Header does not provide macros alone.
17#endif
18
19#include "hxkey.hpp"
20#include "hxptr.hpp"
21#include "hxallocator.hpp"
22#include "hxrange.hpp"
23
24HX_NS_BEGIN_
25
26#include "detail/hxpow2_allocator.hpp"
27
29
30#if HX_CPLUSPLUS >= 202002L
32template<typename node_t_>
33concept hxhash_table_concept_ =
34 requires(node_t_& node_, const node_t_& const_node_) {
35 sizeof(typename node_t_::key_t); // node must publish a key_t type
36 { &node_ } -> hxconvertible_to<hxhash_node_base*>; // node_t must derive from hxhash_node_base
37 // returns the node's key
38 { const_node_.hash_key() } -> hxconvertible_to<const typename node_t_::key_t&>;
39 };
41#else
42#define hxhash_table_concept_ typename
43#endif
44
48protected:
49 friend class hxhash_table_base_;
50 template<hxhash_table_concept_ node_t_, typename deleter_t_, bool multi_t_,
51 uint32_t table_size_bits_> friend class hxhash_table;
52
56
59};
60
65template<typename key_t_>
67public:
68 using key_t = key_t_;
69
72 template<typename ref_t_>
73 hxhash_table_set_node(ref_t_&& key_)
74 : hxhash_node_base(0), m_key_(hxforward<ref_t_>(key_)) {
75 this->hash_val = hxkey_hash(m_key_);
76 }
77
79 hxhash_table_set_node(void) = delete;
82
85 bool operator==(const hxhash_table_set_node& x_) const { return hxkey_equal(m_key_, x_.m_key_); }
86
90 bool operator<(const hxhash_table_set_node& x_) const { return hxkey_less(m_key_, x_.m_key_); }
91
93 const key_t_& hash_key(void) const { return m_key_; }
94
97 hxhash_t hash_value(void) const { return this->hash_val; }
98
102 static hxhash_t hash_value(const key_t_& key_) { return hxkey_hash(key_); }
103
104private:
105 key_t_ m_key_;
106};
107
109template<typename key_t_, typename value_t_>
111public:
112 using key_t = key_t_;
113 using value_t = value_t_;
114
118 hxhash_table_map_node(const key_t_& key_) :
119 hxhash_table_set_node<key_t_>(key_) { }
120
124 template<typename ref_t_>
125 hxhash_table_map_node(const key_t_& key_, ref_t_&& value_) :
126 hxhash_table_set_node<key_t_>(key_), m_value_(hxforward<ref_t_>(value_)) { }
127
131 bool operator==(const hxhash_table_map_node& x_) const;
132
136 bool operator<(const hxhash_table_map_node& x_) const;
137
139 const value_t_& value(void) const { return m_value_; }
140
142 value_t_& value(void) { return m_value_; }
143
144private:
145 value_t_ m_value_;
146};
147
149// Internal. Type erased shared code.
150class hxhash_table_base_ {
151private:
152 template<hxhash_table_concept_ node_t_, typename deleter_t_, bool multi_t_, uint32_t table_size_bits_>
153 friend class hxhash_table;
154 using hxhash_equal_fn_ = bool(*)(const hxhash_node_base* node_, const void* key_);
155 using hxhash_deleter_fn_ = void(*)(hxhash_node_base* node_, void* context_);
156 static hxsize_t chain_count_multi_(const hxhash_node_base* head_, const void* key_,
157 hxhash_equal_fn_ equal_);
158 static void chain_erase_at_(hxhash_node_base** head_, hxhash_node_base* node_);
159 static hxsize_t chain_erase_multi_(hxhash_node_base** head_, const void* key_,
160 hxhash_equal_fn_ equal_, hxhash_deleter_fn_ deleter_, void* deleter_context_);
161 static hxhash_node_base* chain_extract_(hxhash_node_base** head_, const void* key_,
162 hxhash_equal_fn_ equal_);
163 static hxsize_t chain_length_(const hxhash_node_base* head_);
164 static hxhash_node_base* chain_replace_(hxhash_node_base** head_, hxhash_node_base* node_,
165 const void* key_, hxhash_equal_fn_ equal_);
166 static void table_clear_(hxhash_node_base** table_, hxsize_t bucket_count_,
167 hxhash_deleter_fn_ deleter_, void* deleter_context_);
168};
170
219template<hxhash_table_concept_ node_t_,
220 typename deleter_t_=hxdefault_delete,
221 bool multi_t_ = false,
222 uint32_t table_size_bits_=hxallocator_dynamic_capacity>
223class hxhash_table : private deleter_t_, private hxhash_table_base_ {
224public:
225 using node_t = node_t_;
226 using key_t = typename node_t_::key_t;
227
234 {
235 public:
237 const_iterator(void) : m_next_bucket_(hxnull), m_bucket_end_(hxnull),
238 m_current_node_(hxnull) { }
239
242 const_iterator operator++(int) { const_iterator t_(*this); operator++(); return t_; }
245 bool operator==(const const_iterator& x_) const;
246#if HX_CPLUSPLUS < 202002L // C++20 defaults != from ==.
249 bool operator!=(const const_iterator& x_) const { return !(*this == x_); }
250#endif
252 const node_t_& operator*(void) const;
254 const node_t_* operator->(void) const;
255 private:
257 friend class hxhash_table;
258 const_iterator(const hxhash_table* table_);
259 const_iterator(const hxhash_table* table_, node_t_* node_);
260 void next_bucket_(void);
261 hxhash_node_base** m_next_bucket_;
262 hxhash_node_base** m_bucket_end_;
263 protected:
264 node_t_* m_current_node_;
266 };
267
271 {
272 public:
274 iterator(void) { }
278 iterator operator++(int) { iterator t_(*this); const_iterator::operator++(); return t_; }
280 node_t_& operator*(void) const;
282 node_t_* operator->(void) const;
283 private:
285 friend class hxhash_table;
286 iterator(hxhash_table* table_) : const_iterator(table_) { }
287 iterator(hxhash_table* table_, node_t_* node_) : const_iterator(table_, node_) { }
288 iterator(const const_iterator& src_) : const_iterator(src_) { }
290 };
291
295 explicit hxhash_table(deleter_t_ deleter_=deleter_t_());
296
298 ~hxhash_table(void) { this->clear(this->deleter()); }
299
305 template<hxrange_concept_ range_t_,
306 hxenable_if_t<!hxis_lvalue_reference<range_t_>(), int> = 0>
307 void add_range(range_t_&& range_) noexcept;
308
309#if HX_CPLUSPLUS >= 202302L
315 template<typename self_t_, typename callable_t_>
317 this self_t_&& self_, const key_t& key_, callable_t_&& callable_)
318 -> hxremove_cvref_t<decltype(
319 hxforward<callable_t_>(callable_)(
321
325 template<typename self_t_, typename callable_t_>
327 this self_t_&& self_, const_iterator it_, callable_t_&& callable_)
328 -> hxremove_cvref_t<decltype(
331#endif // HX_CPLUSPLUS >= 202302L
332
334 const_iterator begin(void) const { return const_iterator(this); }
335
337 iterator begin(void) { return iterator(this); }
338
340 hxattr_nodiscard hxsize_t bucket_count(void) const { return m_table_.capacity(); }
341
343 const_iterator cbegin(void) const { return const_iterator(this); }
344
346 const_iterator cend(void) const { return const_iterator(); }
347
351 template<typename deleter_u_>
352 void clear(deleter_u_&& deleter_) noexcept;
353
355 void clear(void) noexcept { this->clear(this->deleter()); }
356
359 hxattr_nodiscard hxsize_t count(const typename node_t_::key_t& key_) const;
360
362 hxattr_nodiscard const deleter_t_& deleter(void) const;
363
364 hxattr_nodiscard deleter_t_& deleter(void);
365
374 hxalignment_t align_=hxalignment, bool multi_=multi_t_, class... args_t_>
375 hxenable_if_t<multi_, iterator> emplace(args_t_&&... args_) noexcept;
376
378 hxattr_nodiscard bool empty(void) const { return m_size_ == 0; }
379
381 const_iterator end(void) const { return const_iterator(); }
382
384 iterator end(void) { return iterator(); }
385
393 template<typename deleter_u_>
394 hxsize_t erase(const typename node_t_::key_t& key_, deleter_u_&& deleter_) noexcept;
395
398 hxsize_t erase(const typename node_t_::key_t& key_) noexcept;
399
403 iterator erase(const const_iterator& it_) noexcept;
404
408 hxptr<node_t_, deleter_t_> extract(const typename node_t_::key_t& key_) noexcept;
409
414
423 const typename node_t_::key_t& key_, const const_iterator& previous_=const_iterator()) const;
424
426 const typename node_t_::key_t& key_, const const_iterator& previous_=const_iterator());
427
430 hxattr_nodiscard bool has_value(const key_t& key_) const;
431
437 iterator insert(node_t_* ptr_) noexcept;
438
443 template<typename deleter_u_>
445
447 hxattr_nodiscard float load_factor(void) const;
448
451
452#if HX_CPLUSPLUS >= 202302L
458 template<typename self_t_, typename callable_t_>
460 this self_t_&& self_, const key_t& key_, callable_t_&& callable_)
461 -> decltype(self_.end());
462
467 template<typename self_t_, typename callable_t_>
469 this self_t_&& self_, const_iterator it_, callable_t_&& callable_)
470 -> decltype(self_.end());
471#endif // HX_CPLUSPLUS >= 202302L
472
474 void release_all(void);
475
478 hxsize_t release_key(const typename node_t_::key_t& key_);
479
486 hxptr<node_t_, deleter_t_> replace(node_t_* ptr_) noexcept;
487
492
494 hxattr_nodiscard hxsize_t size(void) const { return m_size_; }
495
506 hxalignment_t align_=hxalignment, bool multi_=multi_t_, class... args_t_>
507 hxenable_if_t<!multi_, iterator> try_emplace(
508 const typename node_t_::key_t& key_, args_t_&&... args_) noexcept;
509
510#if HX_CPLUSPLUS >= 202302L
515 template<typename self_t_> hxattr_nodiscard
516 auto value_or(this self_t_&& self_, const key_t& key_, const node_t_* default_value_)
517 -> decltype(self_.end());
518
522 template<typename self_t_> hxattr_nodiscard
523 auto value_or(this self_t_&& self_, const_iterator it_, const node_t_* default_value_)
524 -> decltype(self_.end());
525#endif // HX_CPLUSPLUS >= 202302L
526
527private:
528 static_assert(table_size_bits_ < hxhash_bits, "Hash bits must be [0..hxhash_bits)");
529
530 // Deleted for being bug prone.
531 hxhash_table(const hxhash_table&) = delete;
532
533 hxhash_node_base** get_bucket_head_(hxhash_t hash_);
534 const hxhash_node_base*const* get_bucket_head_(hxhash_t hash_) const;
535
536 hxsize_t m_size_;
537 hxdetail_::hxpow2_allocator_<hxhash_node_base*, table_size_bits_, true> m_table_;
538};
539
540#include "detail/hxhash_table.inl"
541HX_NS_END_
hxdefault_delete - A callable that deletes objects of type T using hxdelete.
Definition hxmemory_manager.h:265
hxhash_node_base - Intrusive hash-chain node base.
Definition hxhash_table.hpp:47
hxhash_t hash_val
Definition hxhash_table.hpp:58
friend class hxhash_table
Definition hxhash_table.hpp:51
hxhash_node_base * hash_next
Definition hxhash_table.hpp:57
friend class hxhash_table_base_
Definition hxhash_table.hpp:49
hxhash_node_base(hxhash_t hash_)
Constructs an unlinked node with the given cached hash.
Definition hxhash_table.hpp:55
const_iterator - A const forward iterator over the elements of the hash table.
Definition hxhash_table.hpp:234
bool operator==(const const_iterator &x_) const
Compares two iterators for equality.
const node_t_ * operator->(void) const
Dereferences the iterator to access the current node_t's pointer.
const_iterator operator++(int)
Advances the iterator to the next element (post-increment).
Definition hxhash_table.hpp:242
const_iterator(void)
Constructs an iterator pointing to the end of the hash table.
Definition hxhash_table.hpp:237
const_iterator & operator++(void)
Advances the iterator to the next element.
const node_t_ & operator*(void) const
Dereferences the iterator to access the current node_t.
iterator - A mutable iterator that can modify the elements of the hash table.
Definition hxhash_table.hpp:271
node_t_ & operator*(void) const
Dereferences the iterator to access the current node_t.
iterator(void)
Constructs an iterator pointing to the end of the hash table.
Definition hxhash_table.hpp:274
iterator operator++(int)
Advances the iterator to the next element (post-increment).
Definition hxhash_table.hpp:278
iterator & operator++(void)
Advances the iterator to the next element.
Definition hxhash_table.hpp:276
node_t_ * operator->(void) const
Dereferences the iterator to access the current node_t's pointer.
hxhash_table_map_node(const key_t_ &key_)
Default-initializes a node.
Definition hxhash_table.hpp:118
hxhash_table_map_node(const key_t_ &key_, ref_t_ &&value_)
Constructs a node whose value is copy- or move-initialized.
Definition hxhash_table.hpp:125
const value_t_ & value(void) const
Returns the stored value.
Definition hxhash_table.hpp:139
value_t_ & value(void)
Returns the stored value, allowing mutation.
Definition hxhash_table.hpp:142
key_t_ key_t
Definition hxhash_table.hpp:112
value_t_ value_t
Definition hxhash_table.hpp:113
bool operator<(const hxhash_table_map_node &x_) const
Returns true if this node is ordered before x, comparing keys first and then values with hxkey_equal ...
bool operator==(const hxhash_table_map_node &x_) const
Returns true if this node and x have equal keys and values, using hxkey_equal.
hxhash_t hash_value(void) const
Returns the cached hash value for the stored key.
Definition hxhash_table.hpp:97
const key_t_ & hash_key(void) const
The key and hash identify the node_t and should not change once added.
Definition hxhash_table.hpp:93
bool operator<(const hxhash_table_set_node &x_) const
Returns true if this node's key is ordered before x's key, using hxkey_less.
Definition hxhash_table.hpp:90
hxhash_table_set_node(ref_t_ &&key_)
Constructs a node from the key and caches its hash for reuse.
Definition hxhash_table.hpp:73
static hxhash_t hash_value(const key_t_ &key_)
Returns the hash value computed for key.
Definition hxhash_table.hpp:102
key_t_ key_t
Definition hxhash_table.hpp:68
bool operator==(const hxhash_table_set_node &x_) const
Returns true if this node and x have equal keys, using hxkey_equal.
Definition hxhash_table.hpp:85
hxhash_table_set_node(void)=delete
Assignment is undefined. The key is immutable.
hxhash_table_set_node & operator=(const hxhash_table_set_node &x_)=delete
hxhash_table_set_node(const hxhash_table_set_node &src_)=delete
const deleter_t_ & deleter(void) const
Returns a const reference to the stored deleter.
void clear(deleter_u_ &&deleter_) noexcept
Removes all nodes and if deleter is true then calls deleter() on every node.
bool empty(void) const
Checks if the hash table is empty.
Definition hxhash_table.hpp:378
~hxhash_table(void)
Destructs the hash table and deletes all resources.
Definition hxhash_table.hpp:298
hxhash_table(deleter_t_ deleter_=deleter_t_())
Constructs an empty hash table with a capacity of 2^table_size_bits and an optional deleter instance.
hxsize_t bucket_count(void) const
Returns the number of buckets in the hash table.
Definition hxhash_table.hpp:340
auto value_or(this self_t_ &&self_, const key_t &key_, const node_t_ *default_value_) -> decltype(self_.end())
Returns an iterator to the first node matching key, or an iterator to default_value when lookup fails...
hxenable_if_t<!multi_, iterator > try_emplace(const typename node_t_::key_t &key_, args_t_ &&... args_) noexcept
try_emplace - Returns an iterator to the node if it was inserted, or an iterator to the existing node...
iterator erase(const const_iterator &it_) noexcept
Removes and calls the stored deleter on the node_t referenced by it.
hxsize_t erase(const typename node_t_::key_t &key_) noexcept
Removes and calls the stored deleter on nodes with an equivalent key.
iterator find(const typename node_t_::key_t &key_, const const_iterator &previous_=const_iterator())
hxsize_t count(const typename node_t_::key_t &key_) const
Counts the number of nodes with the given key.
deleter_t_ & deleter(void)
hxptr< node_t_, deleter_t_ > replace(node_t_ *ptr_) noexcept
replace - Replaces the first node_t matching ptr's key, if any, with ptr and returns an hxptr owning ...
void add_range(range_t_ &&range_) noexcept
Links each node_t from a temporary range into this hash table by address, exactly as insert(node_t*) ...
iterator insert(hxptr< node_t_, deleter_u_ > &&ptr_) noexcept
insert - Returns an iterator to the inserted node.
hxsize_t release_key(const typename node_t_::key_t &key_)
Removes all nodes matching the given key without deleting them.
hxsize_t size(void) const
Returns the number of elements in the hash table.
Definition hxhash_table.hpp:494
bool has_value(const key_t &key_) const
Returns true if a node matching key exists.
auto and_then(this self_t_ &&self_, const_iterator it_, callable_t_ &&callable_) -> hxremove_cvref_t< decltype(hxforward< callable_t_ >(callable_)(hxforward_like< self_t_ >(hxdeclval< const node_t_ & >())))>
Iterator overload of and_then.
iterator end(void)
Returns an iterator pointing to the end of the hash table.
Definition hxhash_table.hpp:384
hxptr< node_t_, deleter_t_ > extract(const typename node_t_::key_t &key_) noexcept
extract - Returns an hxptr owning the first node_t with the given key, or an empty hxptr if no matchi...
iterator begin(void)
Returns an iterator pointing to the beginning of the hash table.
Definition hxhash_table.hpp:337
hxptr< node_t_, deleter_t_ > extract(const const_iterator &it_) noexcept
Removes the node_t referenced by it and returns an hxptr owning it.
const_iterator cbegin(void) const
Returns a const iterator pointing to the beginning of the hash table.
Definition hxhash_table.hpp:343
node_t_ node_t
Definition hxhash_table.hpp:225
iterator insert(node_t_ *ptr_) noexcept
insert - Returns an iterator to the inserted node.
const_iterator find(const typename node_t_::key_t &key_, const const_iterator &previous_=const_iterator()) const
Returns an iterator to a node_t matching key, or end() if none exists.
const_iterator end(void) const
Returns a const iterator pointing to the end of the hash table.
Definition hxhash_table.hpp:381
const_iterator begin(void) const
Returns a const iterator pointing to the beginning of the hash table.
Definition hxhash_table.hpp:334
auto or_else(this self_t_ &&self_, const_iterator it_, callable_t_ &&callable_) -> decltype(self_.end())
Iterator overload of or_else.
const_iterator cend(void) const
Returns a const iterator pointing to the end of the hash table.
Definition hxhash_table.hpp:346
void release_all(void)
Clears the hash table without deleting any nodes.
hxsize_t erase(const typename node_t_::key_t &key_, deleter_u_ &&deleter_) noexcept
Releases all nodes matching key and calls deleter on every node.
auto and_then(this self_t_ &&self_, const key_t &key_, callable_t_ &&callable_) -> hxremove_cvref_t< decltype(hxforward< callable_t_ >(callable_)(hxforward_like< self_t_ >(hxdeclval< node_t_ & >())))>
Returns the result of calling callable with the first node matching key, otherwise returns hxnil.
hxsize_t load_max(void) const
Returns the size of the largest bucket.
void set_size_bits(hxhash_t bits_)
Sets the number of hash bits and allocate memory for the table.
void clear(void) noexcept
Removes all nodes and calls the stored deleter on every node.
Definition hxhash_table.hpp:355
auto or_else(this self_t_ &&self_, const key_t &key_, callable_t_ &&callable_) -> decltype(self_.end())
Returns an iterator to the first node matching key, or the result of calling callable when lookup fai...
auto value_or(this self_t_ &&self_, const_iterator it_, const node_t_ *default_value_) -> decltype(self_.end())
Iterator overload of value_or.
typename node_t_::key_t key_t
Definition hxhash_table.hpp:226
float load_factor(void) const
Returns the average number of nodes per bucket.
hxenable_if_t< multi_, iterator > emplace(args_t_ &&... args_) noexcept
emplace - Returns an iterator to the node constructed with hxnew.
hxptr<T, deleter_t> - A unique owning pointer.
Definition hxptr.hpp:30
hxconvertible_to - A concept that requires one type to be convertible to another.
Definition hxutility.h:255
Similar to std::allocator.
HX_NS_BEGIN_ constexpr hxsize_t hxallocator_dynamic_capacity
hxallocator_dynamic_capacity - A capacity value that allows for dynamic allocation.
Definition hxallocator.hpp:20
User-specializable key-equal, key-less, and key-hash callables.
constexpr bool hxkey_equal(const A_ &a_, const B_ &b_)
hxkey_equal - Returns true if a and b are equivalent, deducing A and invoking hxkey_equal_t<A>.
Definition hxkey.hpp:80
hxhash_t hxkey_hash(const T &x_)
hxkey_hash - Returns the hash of x, deducing T and invoking hxkey_hash_t<T>.
Definition hxkey.hpp:179
constexpr bool hxkey_less(const A_ &a_, const B_ &b_)
hxkey_less - Returns true if a is less than b, deducing A and invoking hxkey_less_t<A>.
Definition hxkey.hpp:115
@ hxsystem_allocator_current
hxsystem_allocator_current - Use current allocation scope.
Definition hxmemory_manager.h:80
HX_C_BEGIN_ typedef unsigned int hxalignment_t
hxalignment_t - A positive integer power of 2 for aligning allocations.
Definition hxmemory_manager.h:62
int hxsystem_allocator_t
hxsystem_allocator_t - This is extendable by the application.
Definition hxmemory_manager.h:75
hxattr_noexcept hxsystem_allocator_t allocator_
Definition hxmemory_manager.h:120
constexpr hxalignment_t hxalignment
hxalignment - The default alignment.
Definition hxmemory_manager.h:67
A unique owning pointer.
Read-only range utilities for libhatchet.
#define hxattr_nodiscard
hxattr_nodiscard - Indicates the caller should not discard the return value.
Definition hxsettings.h:98
#define hxattr_flatten
hxattr_flatten - Inline every call inside a function's body into it, recursively, regardless of the c...
Definition hxsettings.h:90
constexpr T && hxforward(hxremove_reference_t< T > &&x_)
hxforward - Implements std::forward.
Definition hxutility.h:353
hxremove_cv_t< hxremove_reference_t< T > > hxremove_cvref_t
hxremove_cvref_t<T> - Returns T with const, volatile, and references removed.
Definition hxutility.h:103
T && hxdeclval(void) noexcept
hxdeclval - Implements std::declval.
constexpr hxforward_like_t_< like_t_, value_t_ > hxforward_like(value_t_ &value_)
hxforward_like<T> - Returns value with the constness and value category of T.
Definition hxutility.h:387
Provides core macros, memory management and feature detection.
constexpr hxhash_t hxhash_bits
hxhash_bits - Number of bits in hxhash_t.
Definition libhatchet.h:285
#define hxnull
hxnull - The null pointer value for a given pointer type represented by the numeric literal 0.
Definition libhatchet.h:280
uint32_t hxhash_t
hxhash_t - Unsigned 32-bit hash value. Expect collisions.
Definition libhatchet.h:210
ptrdiff_t hxsize_t
hxsize_t - A signed size type, same as ssize_t or ptrdiff_t.
Definition libhatchet.h:207