util/IntrusiveHashSet: replace template parameters Hash/Equal with a single one

Preparing to add a key extraction function.  Without this "Operators"
template parameter, we'd have even more template parameters, and that
parameter list would grow too complex.  Better wrap it in one single
template that contains all operators.

This is an API change which all callers need to adjust to, but it will
be worth it.
This commit is contained in:
Max Kellermann 2023-08-02 22:11:57 +02:00 committed by Max Kellermann
parent dcd7c6337c
commit 2cd5f4cd3e
4 changed files with 39 additions and 28 deletions

View File

@ -100,11 +100,10 @@ class RemoteTagCache final {
*/
ItemList invoke_list;
using KeyMap = IntrusiveHashSet<Item, 127, Item::Hash, Item::Equal,
IntrusiveHashSetBaseHookTraits<Item>,
true>;
KeyMap map;
IntrusiveHashSet<Item, 127,
IntrusiveHashSetOperators<Item::Hash, Item::Equal>,
IntrusiveHashSetBaseHookTraits<Item>,
true> map;
public:
RemoteTagCache(EventLoop &event_loop,

View File

@ -48,10 +48,8 @@ class InputCacheManager {
IntrusiveList<InputCacheItem> items_by_time;
using UriMap =
IntrusiveHashSet<InputCacheItem, 127, ItemHash, ItemEqual>;
UriMap items_by_uri;
IntrusiveHashSet<InputCacheItem, 127,
IntrusiveHashSetOperators<ItemHash, ItemEqual>> items_by_uri;
public:
explicit InputCacheManager(const InputCacheConfig &config) noexcept;

View File

@ -75,13 +75,28 @@ struct IntrusiveHashSetMemberHookTraits {
}
};
template<typename Hash, typename Equal>
struct IntrusiveHashSetOperators {
using hasher = Hash;
using key_equal = Equal;
[[no_unique_address]]
Hash hash;
[[no_unique_address]]
Equal equal;
};
/**
* A hash table implementation which stores pointers to items which
* have an embedded #IntrusiveHashSetHook. The actual table is
* embedded with a compile-time fixed size in this object.
*
* @param Operators a class which contains functions `hash` and
* `equal`
*/
template<typename T, std::size_t table_size,
typename Hash=typename T::Hash, typename Equal=typename T::Equal,
typename Operators,
typename HookTraits=IntrusiveHashSetBaseHookTraits<T>,
bool constant_time_size=false>
class IntrusiveHashSet {
@ -89,10 +104,7 @@ class IntrusiveHashSet {
OptionalCounter<constant_time_size> counter;
[[no_unique_address]]
Hash hash;
[[no_unique_address]]
Equal equal;
Operators ops;
struct BucketHookTraits {
template<typename U>
@ -129,20 +141,20 @@ public:
using const_pointer = const T *;
using size_type = std::size_t;
using hasher = Hash;
using key_equal = Equal;
using hasher = typename Operators::hasher;
using key_equal = typename Operators::key_equal;
[[nodiscard]]
IntrusiveHashSet() noexcept = default;
[[nodiscard]]
constexpr const hasher &hash_function() const noexcept {
return hash;
return ops.hash;
}
[[nodiscard]]
constexpr const key_equal &key_eq() const noexcept {
return equal;
return ops.equal;
}
[[nodiscard]]
@ -192,7 +204,7 @@ public:
Disposer<value_type> auto disposer) noexcept {
auto &bucket = GetBucket(key);
counter -= bucket.remove_and_dispose_if([this, &key](const auto &item){
return equal(key, item);
return ops.equal(key, item);
}, disposer);
}
@ -201,7 +213,7 @@ public:
Disposer<value_type> auto disposer) noexcept {
auto &bucket = GetBucket(key);
counter -= bucket.remove_and_dispose_if([this, &key, &pred](const auto &item){
return equal(key, item) && pred(item);
return ops.equal(key, item) && pred(item);
}, disposer);
}
@ -221,7 +233,7 @@ public:
constexpr std::pair<bucket_iterator, bool> insert_check(const auto &key) noexcept {
auto &bucket = GetBucket(key);
for (auto &i : bucket)
if (equal(key, i))
if (ops.equal(key, i))
return {bucket.iterator_to(i), false};
/* bucket.end() is a pointer to the bucket's list
@ -269,7 +281,7 @@ public:
constexpr bucket_iterator find(const auto &key) noexcept {
auto &bucket = GetBucket(key);
for (auto &i : bucket)
if (equal(key, i))
if (ops.equal(key, i))
return bucket.iterator_to(i);
return end();
@ -279,7 +291,7 @@ public:
constexpr const_bucket_iterator find(const auto &key) const noexcept {
auto &bucket = GetBucket(key);
for (auto &i : bucket)
if (equal(key, i))
if (ops.equal(key, i))
return bucket.iterator_to(i);
return end();
@ -296,7 +308,7 @@ public:
std::predicate<const_reference> auto pred) noexcept {
auto &bucket = GetBucket(key);
for (auto &i : bucket)
if (equal(key, i) && pred(i))
if (ops.equal(key, i) && pred(i))
return bucket.iterator_to(i);
return end();
@ -323,7 +335,7 @@ public:
auto &bucket = GetBucket(key);
for (auto i = bucket.begin(), e = bucket.end(); i != e;) {
if (!equal(key, *i))
if (!ops.equal(key, *i))
++i;
else if (expired_pred(*i))
i = erase_and_dispose(i, disposer);
@ -361,7 +373,7 @@ private:
[[gnu::pure]]
[[nodiscard]]
constexpr auto &GetBucket(K &&key) noexcept {
const auto h = hash(std::forward<K>(key));
const auto h = ops.hash(std::forward<K>(key));
return table[h % table_size];
}
@ -369,7 +381,7 @@ private:
[[gnu::pure]]
[[nodiscard]]
constexpr const auto &GetBucket(K &&key) const noexcept {
const auto h = hash(std::forward<K>(key));
const auto h = ops.hash(std::forward<K>(key));
return table[h % table_size];
}
};

View File

@ -38,7 +38,9 @@ TEST(IntrusiveHashSet, Basic)
{
IntItem a{1}, b{2}, c{3}, d{4}, e{5}, f{1};
IntrusiveHashSet<IntItem, 3> set;
IntrusiveHashSet<IntItem, 3,
IntrusiveHashSetOperators<IntItem::Hash,
IntItem::Equal>> set;
{
auto [position, inserted] = set.insert_check(2);