hashmap: honor the value destructor in set_ensure_consume()

Sets store their element in the key slot but use a value destructor
(DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR). On the reject/duplicate path
set_ensure_consume() only checked free_key and otherwise called free().

Follow-up for fcc1d0315d
This commit is contained in:
Luca Boccassi
2026-07-06 13:42:21 +01:00
parent 2f72db10d9
commit 87e57c52c1
2 changed files with 45 additions and 0 deletions
+3
View File
@@ -1330,6 +1330,9 @@ int set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key) {
if (r <= 0) {
if (hash_ops && hash_ops->free_key)
hash_ops->free_key(key);
else if (hash_ops && hash_ops->free_value)
/* Sets store their element in the key slot but may carry a value destructor. */
hash_ops->free_value(key);
else
free(key);
}
+42
View File
@@ -2,8 +2,10 @@
#include <unistd.h>
#include "alloc-util.h"
#include "random-util.h"
#include "set.h"
#include "siphash24.h"
#include "strv.h"
#include "tests.h"
@@ -51,6 +53,46 @@ TEST(set_free_with_hash_ops) {
assert_se(items[3].seen == 0);
}
static unsigned set_ensure_consume_freed = 0;
typedef struct ValItem {
int key;
} ValItem;
static void val_item_free(ValItem *i) {
set_ensure_consume_freed++;
free(i);
}
static void val_item_hash_func(const ValItem *i, struct siphash *state) {
siphash24_compress_typesafe(i->key, state);
}
static int val_item_compare_func(const ValItem *a, const ValItem *b) {
return CMP(a->key, b->key);
}
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
val_item_hash_ops, ValItem, val_item_hash_func, val_item_compare_func,
ValItem, val_item_free);
TEST(set_ensure_consume_value_destructor) {
_cleanup_set_free_ Set *s = NULL;
ValItem *a, *b;
ASSERT_NOT_NULL(a = new0(ValItem, 1));
a->key = 1;
ASSERT_OK_POSITIVE(set_ensure_consume(&s, &val_item_hash_ops, a));
ASSERT_NOT_NULL(b = new0(ValItem, 1));
b->key = 1; /* same key, different pointer -> duplicate */
set_ensure_consume_freed = 0;
/* The rejected duplicate must be released via the value destructor, not plain free(). */
ASSERT_OK_ZERO(set_ensure_consume(&s, &val_item_hash_ops, b));
ASSERT_EQ(set_ensure_consume_freed, 1u);
}
TEST(set_put) {
_cleanup_set_free_ Set *m = NULL;