@@ -1898,25 +1898,25 @@ namespace hud
18981898 }
18991899
19001900 /* * Retrieves the allocator. */
1901- [[nodiscard]] HD_FORCEINLINE constexpr const allocator_type &allocator () const noexcept
1901+ [[nodiscard]] constexpr const allocator_type &allocator () const noexcept
19021902 {
19031903 return hud::get<0 >(compressed_);
19041904 }
19051905
19061906 /* * Returns the number of free slots before a rehash is needed. */
1907- [[nodiscard]] HD_FORCEINLINE constexpr usize slack () const noexcept
1907+ [[nodiscard]] constexpr usize slack () const noexcept
19081908 {
19091909 return free_slot_before_grow ();
19101910 }
19111911
19121912 /* * Checks whether the container is empty. */
1913- [[nodiscard]] HD_FORCEINLINE constexpr bool is_empty () const noexcept
1913+ [[nodiscard]] constexpr bool is_empty () const noexcept
19141914 {
19151915 return count_ == 0 ;
19161916 }
19171917
19181918 /* * Returns the number of elements currently in the container. */
1919- [[nodiscard]] HD_FORCEINLINE constexpr usize count () const noexcept
1919+ [[nodiscard]] constexpr usize count () const noexcept
19201920 {
19211921 return count_;
19221922 }
@@ -1929,7 +1929,7 @@ namespace hud
19291929 * depends on the load factor, which is 7/8 by default. This means a resize
19301930 * will occur when 7/8 of the slots are occupied.
19311931 */
1932- [[nodiscard]] HD_FORCEINLINE constexpr usize max_count () const noexcept
1932+ [[nodiscard]] constexpr usize max_count () const noexcept
19331933 {
19341934 return max_slot_count_;
19351935 }
@@ -3072,8 +3072,7 @@ namespace hud
30723072 */
30733073 [[nodiscard]] constexpr usize max_slot_before_grow (usize capacity) const noexcept
30743074 {
3075- return SLOT_PER_GROUP () == 8 && capacity == 7 ? 6 :
3076- capacity - capacity / 8 ;
3075+ return (SLOT_PER_GROUP () == 8 && capacity == 7 ) ? 6 : capacity - capacity / 8 ;
30773076 }
30783077
30793078 /* *
@@ -3399,6 +3398,77 @@ namespace hud
33993398
34003399 } // namespace details::hashset
34013400
3401+ /* *
3402+ * A high-performance hash set similar to Abseil's flat_hash_set.
3403+ *
3404+ * Template parameters:
3405+ * --------------------
3406+ * - `element_t` : The type of the element stored in the set.
3407+ * - `hasher_t` : Type providing a 64-bit hash function for `element_t`. Default is `hud::hash_64<element_t>`.
3408+ * - `key_equal_t`: Type providing key equality comparisons. Default is `hud::equal<element_t>`.
3409+ * - `allocator_t`: Allocator type for memory management. Default is `hud::heap_allocator`.
3410+ *
3411+ * Features:
3412+ * ---------
3413+ * 1. Open-addressing hash set with control bytes to track empty, deleted, and full slots.
3414+ * 2. Supports constant-evaluated contexts (`consteval`) and runtime.
3415+ * 3. Supports heterogeneous lookup: you can find, insert, or remove elements using types
3416+ * other than the stored `element_t` as long as `hasher_t` and `key_equal_t` are specialized
3417+ * to handle the alternative type `K`.
3418+ * 4. Supports piecewise construction for complex keys. Piecewise construction allows in-place initialization of complex keys.
3419+ * 5. Automatic resizing according to a 7/8 load factor.
3420+ *
3421+ * Heterogeneous lookup with alternative key types `K`:
3422+ * -----------------------------------------------------
3423+ * You can hash and compare keys directly from their type `K`. To enable this for a user-defined type:
3424+ * - Specialize the hashset's `hasher_type` for your type `K` by implementing `operator()(const K&)`
3425+ * to compute a 64-bit hash. You can also provide overloads for alternative key representations
3426+ * (like an ID or a tuple) to avoid constructing the full key.
3427+ * - Specialize the hashset's `key_equal_type` to provide comparisons between the stored key type
3428+ * and the type `K` (or alternative key representations). Implement `operator()(const key_type&, const K&)`.
3429+ *
3430+ * Memory management and performance:
3431+ * ----------------------------------
3432+ * - `reserve(count)` allocates memory for at least `count` elements, avoiding unnecessary rehashes.
3433+ * - `clear()` destroys all elements without releasing memory.
3434+ * - `clear_shrink()` destroys all elements and frees memory.
3435+ * - `rehash(max_count)` resizes the underlying table; if `max_count` is less than current capacity, nothing happens.
3436+ * - `shrink_to_fit()` reduces the table to fit the current number of elements exactly.
3437+ * - `free_slot_before_grow()` returns the number of free slots before the next resizing.
3438+ *
3439+ * Iterators:
3440+ * ----------
3441+ * Supports forward iteration over elements:
3442+ * - `begin()` / `end()` for mutable access
3443+ * - `begin() const` / `end() const` for const access
3444+ *
3445+ * Example:
3446+ * ```cpp
3447+ * struct MyType { int id; };
3448+ *
3449+ * // Hasher specialization
3450+ * template<> struct hud::hash_64<MyType> {
3451+ * constexpr u64 operator()(const MyType& v) const noexcept { return hud::hash_64<int>{}(v.id); }
3452+ * constexpr u64 operator()(const int id) const noexcept { return hud::hash_64<int>{}(id); } // heterogeneous support
3453+ * };
3454+ *
3455+ * // Equality specialization
3456+ * template<> struct hud::equal<MyType> {
3457+ * constexpr bool operator()(const MyType& lhs, const MyType& rhs) const noexcept { return lhs.id == rhs.id; }
3458+ * constexpr bool operator()(const MyType& lhs, const int& rhs) const noexcept { return lhs.id == rhs; }
3459+ * };
3460+ *
3461+ * hud::hashset<MyType> set;
3462+ * set.add(MyType{42}); // insert using MyType
3463+ * set.add(56); // insert using alternative key (int)
3464+ * bool has42 = set.contains(MyType{42});
3465+ * bool has56 = set.contains(56);
3466+ * ```
3467+ *
3468+ * Notes:
3469+ * ------
3470+ * - Piecewise construction allows in-place initialization of complex keys.
3471+ */
34023472 template <
34033473 typename element_t ,
34043474 typename hasher_t = hud::hash_64<element_t >,
@@ -3431,13 +3501,30 @@ namespace hud
34313501 using super::super;
34323502 using super::operator =;
34333503
3504+ /* *
3505+ * Default constructor.
3506+ * Constructs an empty hashset with default-constructed allocator and other components.
3507+ */
34343508 explicit constexpr hashset () noexcept = default;
34353509
3510+ /* *
3511+ * Constructs an empty hashset using the provided allocator.
3512+ * @param allocator The allocator instance to use for internal memory management.
3513+ */
34363514 constexpr explicit hashset (const allocator_type &allocator) noexcept
34373515 : super {allocator}
34383516 {
34393517 }
34403518
3519+ /* *
3520+ * Constructs a hashset and inserts elements from the given initializer list.
3521+ * Each element must be constructible into the internal storage type (`storage_type`).
3522+ * The hashset reserves memory for all elements beforehand to avoid unnecessary rehashing.
3523+ *
3524+ * @tparam u_element_t Type of elements in the initializer list.
3525+ * @param list The initializer list containing elements to insert.
3526+ * @param allocator Optional allocator instance (defaults to `allocator_type()`).
3527+ */
34413528 template <typename u_element_t >
34423529 requires (hud::is_constructible_v<storage_type, u_element_t >)
34433530 constexpr hashset (std::initializer_list<u_element_t > list, const allocator_type &allocator = allocator_type()) noexcept
@@ -3451,14 +3538,15 @@ namespace hud
34513538 }
34523539
34533540 /* *
3454- * Constructor that initializes the hash map with a list of key-value pairs and additional capacity.
3541+ * Constructor that initializes the hash set with a list of keys and additional capacity.
3542+ *
3543+ * This constructor allows preallocating extra space beyond the initializer list size to reduce future reallocations.
3544+ *
34553545 * @tparam u_key_t The type of the keys in the initializer list.
3456- * @tparam u_value_t The type of the values in the initializer list.
3457- * @param list The initializer list of key-value pairs.
3458- * @param extra_element_count Additional capacity to reserve.
3546+ * @param list The initializer list of keys.
3547+ * @param extra_element_count Additional capacity to reserve beyond the list size.
34593548 * @param allocator The allocator to use for memory management.
34603549 */
3461-
34623550 template <typename u_key_t = key_type>
34633551 requires (hud::is_copy_constructible_v<key_type, u_key_t >)
34643552 constexpr hashset (std::initializer_list<u_key_t > list, const usize extra_element_count, const allocator_type &allocator = allocator_type()) noexcept
@@ -3474,8 +3562,13 @@ namespace hud
34743562
34753563 /* *
34763564 * Insert a key in the hashset.
3477- * @param key The key
3478- * @return Iterator to the storage containing the `key`
3565+ *
3566+ * This function forwards the key to the underlying storage and constructs it in-place
3567+ * if necessary. If the key already exists, returns an iterator to the existing element.
3568+ *
3569+ * @tparam u_key_t The type of the key being inserted.
3570+ * @param key The key to insert.
3571+ * @return Iterator pointing to the storage containing the inserted `key`.
34793572 */
34803573 template <typename u_key_t = key_type>
34813574 constexpr iterator add (u_key_t &&key) noexcept
@@ -3485,22 +3578,43 @@ namespace hud
34853578 }
34863579
34873580 /* *
3488- * Adds a new element to the container using piecewise construction of the key and value.
3581+ * Adds a new element to the hashset using piecewise construction of the key.
3582+ *
3583+ * Piecewise construction allows constructing complex keys directly in-place
3584+ * from a tuple of arguments, avoiding the need to first create a temporary key object.
34893585 *
34903586 * If an element with the given key already exists, returns an iterator to it.
3491- * Otherwise, constructs a new element in-place using the provided key and value tuples .
3587+ * Otherwise, constructs a new element in-place using the provided key tuple .
34923588 *
34933589 * The key can be provided either as a fully constructed `key_type` or as a tuple of arguments
34943590 * used to construct the key in-place. If the key tuple can't be used directly (e.g., it's not
34953591 * hashable or comparable), it must be convertible into a valid `key_type`.
34963592 *
3497- * To enable custom key lookup using a tuple of arguments, you can specialize the `hud::equal<key_type>`
3498- * and `hud::hash<key_type>` functors to support comparisons and hashes against a forwarding tuple
3499- * (i.e., `hud::tuple<Args&&...>&&`).
3593+ * To enable custom key lookup using a tuple of arguments, you can specialize the
3594+ * `hud::equal<key_type>` and `hud::hash<key_type>` functors to support comparisons and
3595+ * hashes against a forwarding tuple (i.e., `hud::tuple<Args&&...>&&`).
3596+ *
3597+ * Example:
3598+ * ```cpp
3599+ * struct MyType { int id; };
3600+ * template<> struct hud::equal<MyType> {
3601+ * [[nodiscard]] constexpr bool operator()(const MyType &lhs, const MyType &rhs) const noexcept { return lhs.id == rhs.id; }
3602+ * [[nodiscard]] constexpr bool operator()(const MyType &lhs, const i32 &rhs) const noexcept { return lhs.id == rhs; }
3603+ * template<typename First, typename Second> requires hud::convertible_to<First, i32>
3604+ * [[nodiscard]] constexpr bool operator()(const MyType &lhs, hud::tuple<First, Second> const &rhs) const noexcept { return lhs.id == hud::get<0>(rhs); }
3605+ * };
3606+ * template<> struct hud::hash_64<MyType> {
3607+ * [[nodiscard]] constexpr u64 operator()(const MyType &v) const noexcept { return hud::hash_64<i32>{}(v.id); }
3608+ * [[nodiscard]] constexpr u64 operator()(const i32 id) const noexcept { return hud::hash_64<i32> {}(id); }
3609+ * template<typename First, typename Second> requires hud::convertible_to<First, i32>
3610+ * [[nodiscard]] constexpr u64 operator()(hud::tuple<First, Second> const &rhs) const noexcept { return hud::hash_64<i32> {}(hud::get<0>(rhs)); }
3611+ * };
35003612 *
3501- * @param key_tuple Tuple of arguments used to identify or construct the key.
3502- * @param value_tuple Tuple of arguments used to construct the associated value.
3503- * @return An iterator to the existing or newly inserted element.
3613+ * hud::hashset<MyType> set;
3614+ * auto it = set.add(hud::tag_piecewise_construct, hud::tuple<int> {42});
3615+ * ```
3616+ * @param key_tuple Tuple of arguments used to identify or construct the key.
3617+ * @return Iterator to the existing or newly inserted element.
35043618 */
35053619 template <typename key_tuple_t >
35063620 constexpr iterator add (hud::tag_piecewise_construct_t , key_tuple_t &&key_tuple) noexcept
@@ -3509,25 +3623,94 @@ namespace hud
35093623 }
35103624 };
35113625
3626+ /* *
3627+ * Provides a `hud::tuple_size` specialization for `hashset_storage`.
3628+ * This allows `hashset_storage` to be treated as a tuple of size 1,
3629+ * primarily for structured bindings or tuple-like interface compatibility.
3630+ *
3631+ * Example:
3632+ * ```cpp
3633+ * hud::hashset<int> s;
3634+ * s.add(42);
3635+ * for (auto &elem : s) {
3636+ * auto [key] = elem; // structured binding works because tuple_size = 1
3637+ * hud::println(key); // prints 42
3638+ * }
3639+ * ```
3640+ */
35123641 template <typename key_t >
35133642 struct tuple_size <details::hashset::hashset_storage<key_t >>
35143643 : hud::integral_constant<usize, 1 >
35153644 {
35163645 };
35173646
3647+ /* *
3648+ * Provides a `hud::tuple_element` specialization for `hashset_storage`.
3649+ * Enables accessing the contained key type using `std::tuple_element`.
3650+ *
3651+ * @tparam idx_to_reach Index to access. Only 0 is valid.
3652+ * @tparam key_t The key type stored in the hashset_storage.
3653+ *
3654+ * Example:
3655+ * ```cpp
3656+ * using storage = details::hashset::hashset_storage<int>;
3657+ * using key_type = std::tuple_element<0, storage>::type; // key_type = const int
3658+ * ```
3659+ */
35183660 template <usize idx_to_reach, typename key_t >
35193661 struct tuple_element <idx_to_reach, details::hashset::hashset_storage<key_t >>
35203662 {
35213663 static_assert (idx_to_reach < 1 , " hashset hashset_storage index out of bounds" );
35223664 using type = const typename details::hashset::hashset_storage<key_t >::key_type;
35233665 };
35243666
3667+ /* *
3668+ * Swaps the contents of two hashsets.
3669+ *
3670+ * @tparam key_t The type of the keys.
3671+ * @tparam hasher_t The type of the hasher.
3672+ * @tparam key_equal_t The type of the key equality comparator.
3673+ * @tparam allocator_t The allocator type.
3674+ * @param first First hashset to swap.
3675+ * @param second Second hashset to swap.
3676+ *
3677+ * Example:
3678+ * ```cpp
3679+ * hud::hashset<int> a{{1,2,3}};
3680+ * hud::hashset<int> b{{4,5}};
3681+ * hud::swap(a, b); // a now has 4,5 ; b has 1,2,3
3682+ * ```
3683+ */
35253684 template <typename key_t , typename hasher_t , typename key_equal_t , typename allocator_t >
35263685 constexpr void swap (hashset<key_t , hasher_t , key_equal_t , allocator_t > &first, hashset<key_t , hasher_t , key_equal_t , allocator_t > &second) noexcept
35273686 {
35283687 first.swap (second);
35293688 }
35303689
3690+ /* *
3691+ * Compares two hashsets for equality.
3692+ *
3693+ * Two hashsets are considered equal if:
3694+ * 1. They contain the same number of elements.
3695+ * 2. Every element in the larger hashset exists in the smaller hashset.
3696+ *
3697+ * This comparison uses the key equality comparator of the hashsets.
3698+ *
3699+ * @tparam key_t The type of keys stored.
3700+ * @tparam hasher_t The hasher type.
3701+ * @tparam key_equal_t The key equality comparator type.
3702+ * @tparam allocator_t The allocator type.
3703+ * @param left First hashset.
3704+ * @param right Second hashset.
3705+ * @return `true` if the hashsets contain the same elements; `false` otherwise.
3706+ *
3707+ * Example:
3708+ * ```cpp
3709+ * hud::hashset<int> a{{1,2,3}};
3710+ * hud::hashset<int> b{{3,2,1}};
3711+ * bool are_equal = (a == b); // true
3712+ * ```
3713+ */
35313714 template <typename key_t , typename hasher_t , typename key_equal_t , typename allocator_t >
35323715 [[nodiscard]] constexpr bool operator ==(const hashset<key_t , hasher_t , key_equal_t , allocator_t > &left, const hashset<key_t , hasher_t , key_equal_t , allocator_t > &right) noexcept
35333716 {
@@ -3562,12 +3745,32 @@ namespace hud
35623745
35633746namespace std
35643747{
3748+ /* *
3749+ * Specialization of std::tuple_size for hud::details::hashset::hashset_storage.
3750+ *
3751+ * Enables structured bindings with hashset_storage, allowing syntax like:
3752+ * ```cpp
3753+ * hud::details::hashset::hashset_storage<int> storage;
3754+ * auto [key] = storage; // Structured binding works
3755+ * ```
3756+ */
35653757 template <typename key_t >
35663758 struct tuple_size <hud::details::hashset::hashset_storage<key_t >>
35673759 : hud::tuple_size<hud::details::hashset::hashset_storage<key_t >>
35683760 {
35693761 };
35703762
3763+ /* *
3764+ * Specialization of std::tuple_element for hud::details::hashset::hashset_storage.
3765+ * Allows access to the key type by index in structured bindings.
3766+ *
3767+ * Example:
3768+ * ```cpp
3769+ * hud::details::hashset::hashset_storage<int> storage{1};
3770+ * using key_type = std::tuple_element<0, decltype(storage)>::type;
3771+ * static_assert(std::is_same_v<key_type, const int>);
3772+ * ```
3773+ */
35713774 template <std::size_t idx_to_reach, typename key_t >
35723775 struct tuple_element <idx_to_reach, hud::details::hashset::hashset_storage<key_t >>
35733776 : hud::tuple_element<idx_to_reach, hud::details::hashset::hashset_storage<key_t >>
0 commit comments