Skip to content

Commit e48e5f3

Browse files
author
Julian LALU
committed
Improve hashset comments
1 parent 378cdc7 commit e48e5f3

1 file changed

Lines changed: 225 additions & 22 deletions

File tree

interface/core/containers/hashset.h

Lines changed: 225 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -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

35633746
namespace 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

Comments
 (0)