From: Ian Rogers <irogers@google.com>
To: Peter Zijlstra <peterz@infradead.org>,
Ingo Molnar <mingo@redhat.com>,
Arnaldo Carvalho de Melo <acme@kernel.org>,
Namhyung Kim <namhyung@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>, Ian Rogers <irogers@google.com>,
Adrian Hunter <adrian.hunter@intel.com>,
James Clark <james.clark@linaro.org>,
linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v1 08/26] perf hashmap: Introduce generic types and macros
Date: Fri, 13 Nov 2026 13:48:32 -0800 [thread overview]
Message-ID: <20261113214850.3329699-9-irogers@google.com> (raw)
In-Reply-To: <20261113214850.3329699-1-irogers@google.com>
Add DECLARE_HASHMAP(name, key_type, value_type) and type-checked
hashmap__* wrapper macros around struct __hashmap while keeping legacy
struct hashmap compatible so callers can be migrated one hashmap at a
time:
- Encode key_type and value_type via phantom pointer members in a union
with struct __hashmap so wrapper structs preserve identical size and
alignment across 32-bit and 64-bit targets, with _Static_assert checks
that key_type and value_type fit in a long.
- Generate typed <name>__new() and NULL-safe <name>__free() helpers,
plus pointer-taking hashmap__init(), hashmap__clear(), hashmap__free(),
hashmap__size(), hashmap__capacity(), hashmap__insert(),
hashmap__add(), hashmap__set(), hashmap__update(), hashmap__append(),
hashmap__find(), and hashmap__delete() macros.
- Marshal output parameters (value_ptr, old_key, old_value) through
long-sized temporaries and assign via the declared types so sub-long
types (such as bool, int, or pid_t) are safe from stack buffer
overflows, endianness bugs, and strict-aliasing violations, while
allowing NULL output pointers and const-qualified lookup keys.
- Implement hashmap__for_each{,_safe}() and
hashmap__for_each_key{,_safe}() using single-loop __hashmap_iter and
__hashmap_key_iter helpers so 'break' exits the entire iteration,
bucket cursors are internal, and element deletion during iteration is
safe.
- Add a 'Generic hashmap' perf test suite exercising integer, string,
and sub-long bool/int32 maps, NULL frees, const lookups, early loop
breaks, and in-loop deletions.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/Build | 1 +
tools/perf/tests/builtin-test.c | 1 +
tools/perf/tests/hashmap.c | 192 +++++++++
tools/perf/tests/tests.h | 1 +
tools/perf/util/hashmap.c | 30 +-
tools/perf/util/hashmap.h | 675 ++++++++++++++++++++++++++------
6 files changed, 773 insertions(+), 127 deletions(-)
create mode 100644 tools/perf/tests/hashmap.c
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 5eebb2ec5dde..2c2560613323 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -74,6 +74,7 @@ perf-test-y += hwmon_pmu.o
perf-test-y += tool_pmu.o
perf-test-y += subcmd-help.o
perf-test-y += kallsyms-split.o
+perf-test-y += hashmap.o
ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc riscv))
perf-test-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 327483f96401..24e7f8443e94 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -192,6 +192,7 @@ static struct test_suite *generic_tests[] = {
&suite__util,
&suite__subcmd_help,
&suite__kallsyms_split,
+ &suite__hashmap,
NULL,
};
diff --git a/tools/perf/tests/hashmap.c b/tools/perf/tests/hashmap.c
new file mode 100644
index 000000000000..5a4f39eb8c17
--- /dev/null
+++ b/tools/perf/tests/hashmap.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "util/hashmap.h"
+#include "tests.h"
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+DECLARE_HASHMAP(int_map, long, long);
+DECLARE_HASHMAP(bool_map, char *, bool);
+DECLARE_HASHMAP(int32_map, int, const char *);
+
+static size_t int_hash(long key, void *ctx __maybe_unused)
+{
+ return (size_t)key;
+}
+
+static bool int_equal(long key1, long key2, void *ctx __maybe_unused)
+{
+ return key1 == key2;
+}
+
+static size_t str_hash_fn(long key, void *ctx __maybe_unused)
+{
+ return str_hash((const char *)key);
+}
+
+static bool str_equal_fn(long key1, long key2, void *ctx __maybe_unused)
+{
+ return strcmp((const char *)key1, (const char *)key2) == 0;
+}
+
+static int test_int_map(void)
+{
+ struct int_map imap;
+ long val = -1, old_k = -1, old_v = -1;
+ long k, v, sum = 0;
+
+ hashmap__init(&imap, int_hash, int_equal, NULL);
+ TEST_ASSERT_EQUAL("initial size", (int)hashmap__size(&imap), 0);
+
+ TEST_ASSERT_EQUAL("add 1", hashmap__add(&imap, 1L, 10L), 0);
+ TEST_ASSERT_EQUAL("add 2", hashmap__add(&imap, 2L, 20L), 0);
+ TEST_ASSERT_EQUAL("add dup", hashmap__add(&imap, 1L, 99L), -EEXIST);
+ TEST_ASSERT_EQUAL("size after add", (int)hashmap__size(&imap), 2);
+ TEST_ASSERT_VAL("capacity > 0", hashmap__capacity(&imap) >= 2);
+
+ TEST_ASSERT_VAL("find 1", hashmap__find(&imap, 1L, &val));
+ TEST_ASSERT_EQUAL("find 1 val", (int)val, 10);
+ TEST_ASSERT_VAL("find 2 NULL out", hashmap__find(&imap, 2L, NULL));
+ TEST_ASSERT_VAL("find 3 missing", !hashmap__find(&imap, 3L, &val));
+
+ /* Update missing vs existing */
+ TEST_ASSERT_EQUAL("update missing",
+ hashmap__update(&imap, 3L, 30L, &old_k, &old_v), -ENOENT);
+ TEST_ASSERT_EQUAL("update existing",
+ hashmap__update(&imap, 1L, 11L, &old_k, &old_v), 0);
+ TEST_ASSERT_EQUAL("update old_k", (int)old_k, 1);
+ TEST_ASSERT_EQUAL("update old_v", (int)old_v, 10);
+
+ /* Set new vs existing */
+ TEST_ASSERT_EQUAL("set new",
+ hashmap__set(&imap, 3L, 30L, &old_k, &old_v), 0);
+ TEST_ASSERT_EQUAL("set new old_k", (int)old_k, 0);
+ TEST_ASSERT_EQUAL("set new old_v", (int)old_v, 0);
+
+ /* Multimap append + for_each_key */
+ TEST_ASSERT_EQUAL("append 2", hashmap__append(&imap, 2L, 200L), 0);
+ sum = 0;
+ hashmap__for_each_key(&imap, 2L, v)
+ sum += v;
+ TEST_ASSERT_EQUAL("for_each_key sum", (int)sum, 220);
+
+ /* Delete one of the key=2 entries; verify missing delete zeroes outputs */
+ TEST_ASSERT_VAL("delete 2", hashmap__delete(&imap, 2L, &old_k, &old_v));
+ TEST_ASSERT_EQUAL("delete old_k", (int)old_k, 2);
+ TEST_ASSERT_VAL("delete missing", !hashmap__delete(&imap, 999L, &old_k, &old_v));
+ TEST_ASSERT_EQUAL("delete missing old_k zeroed", (int)old_k, 0);
+ TEST_ASSERT_EQUAL("delete missing old_v zeroed", (int)old_v, 0);
+
+ sum = 0;
+ hashmap__for_each(&imap, k, v)
+ sum += v;
+ TEST_ASSERT_EQUAL("for_each sum", (int)sum, 11 + 20 + 30);
+
+ hashmap__clear(&imap);
+ TEST_ASSERT_EQUAL("cleared size", (int)hashmap__size(&imap), 0);
+ return 0;
+}
+
+static int test_bool_and_str_map(void)
+{
+ struct bool_map *bmap;
+ char *k1, *k2, *k2_dup, *old_key = NULL;
+ char *iter_key;
+ const char *const_lookup = "alpha";
+ /* Guard bytes around bool to catch any >1-byte write overflow. */
+ struct {
+ unsigned char pre[8];
+ bool val;
+ unsigned char post[8];
+ } guard;
+ bool old_val = true;
+
+ /* Freeing NULL must be a safe no-op (including under UBSAN). */
+ bool_map__free(NULL);
+
+ bmap = bool_map__new(str_hash_fn, str_equal_fn, NULL);
+ TEST_ASSERT_VAL("bool_map__new", bmap != NULL);
+
+ k1 = strdup("alpha");
+ k2 = strdup("beta");
+ k2_dup = strdup("beta");
+ TEST_ASSERT_VAL("strdup", k1 && k2 && k2_dup);
+
+ TEST_ASSERT_EQUAL("add alpha", hashmap__add(bmap, k1, true), 0);
+ TEST_ASSERT_EQUAL("add beta", hashmap__add(bmap, k2, false), 0);
+
+ memset(&guard, 0x5a, sizeof(guard));
+ TEST_ASSERT_VAL("find const key", hashmap__find(bmap, const_lookup, &guard.val));
+ TEST_ASSERT_VAL("alpha is true", guard.val == true);
+ for (size_t i = 0; i < sizeof(guard.pre); i++) {
+ TEST_ASSERT_EQUAL("pre guard intact", guard.pre[i], 0x5a);
+ TEST_ASSERT_EQUAL("post guard intact", guard.post[i], 0x5a);
+ }
+
+ TEST_ASSERT_VAL("find beta", hashmap__find(bmap, "beta", &guard.val));
+ TEST_ASSERT_VAL("beta is false", guard.val == false);
+
+ TEST_ASSERT_EQUAL("set beta",
+ hashmap__set(bmap, k2_dup, true, &old_key, &old_val), 0);
+ TEST_ASSERT_VAL("old_key == k2", old_key == k2);
+ TEST_ASSERT_VAL("old_val == false", old_val == false);
+ free(old_key);
+
+ hashmap__for_each(bmap, iter_key, old_val)
+ free(iter_key);
+ bool_map__free(bmap);
+ return 0;
+}
+
+static int test_iter_break_and_safe_delete(void)
+{
+ struct int32_map map;
+ const struct int32_map *cmap = ↦
+ int k, count = 0;
+ const char *v;
+
+ hashmap__init(&map, int_hash, int_equal, NULL);
+
+ /* Insert enough entries (including negative keys) to span multiple buckets. */
+ for (int i = -10; i < 20; i++)
+ TEST_ASSERT_EQUAL("add int32", hashmap__add(&map, i, "entry"), 0);
+
+ TEST_ASSERT_VAL("find negative key on const map",
+ hashmap__find(cmap, -7, &v) && strcmp(v, "entry") == 0);
+
+ /* Verify 'break' exits the entire hashmap__for_each loop, not just one bucket. */
+ hashmap__for_each(cmap, k, v) {
+ count++;
+ if (count == 3)
+ break;
+ }
+ TEST_ASSERT_EQUAL("break stops outer iteration", count, 3);
+
+ /* Verify safe deletion during iteration. */
+ hashmap__for_each_safe(&map, k, v) {
+ if (k < 0)
+ TEST_ASSERT_VAL("delete negative", hashmap__delete(&map, k, NULL, NULL));
+ }
+ TEST_ASSERT_EQUAL("remaining non-negative", (int)hashmap__size(&map), 20);
+
+ hashmap__clear(&map);
+ return 0;
+}
+
+static int test__hashmap(struct test_suite *t __maybe_unused, int subtest __maybe_unused)
+{
+ int err;
+
+ err = test_int_map();
+ if (err)
+ return err;
+
+ err = test_bool_and_str_map();
+ if (err)
+ return err;
+
+ return test_iter_break_and_safe_delete();
+}
+
+DEFINE_SUITE("Generic hashmap", hashmap);
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index af822885890d..e9cbc0dcd778 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -203,6 +203,7 @@ DECLARE_SUITE(util);
DECLARE_SUITE(uncore_event_sorting);
DECLARE_SUITE(subcmd_help);
DECLARE_SUITE(kallsyms_split);
+DECLARE_SUITE(hashmap);
/*
* PowerPC and S390 do not support creation of instruction breakpoints using the
diff --git a/tools/perf/util/hashmap.c b/tools/perf/util/hashmap.c
index f2d8beeac27a..f6bfaeaee9d7 100644
--- a/tools/perf/util/hashmap.c
+++ b/tools/perf/util/hashmap.c
@@ -35,7 +35,7 @@ static void __hashmap__del_entry(struct hashmap_entry **pprev,
entry->next = NULL;
}
-void __hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
+void __hashmap__init(struct __hashmap *map, hashmap_hash_fn hash_fn,
hashmap_equal_fn equal_fn, void *ctx)
{
map->hash_fn = hash_fn;
@@ -48,11 +48,11 @@ void __hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
map->sz = 0;
}
-struct hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn,
- void *ctx)
+struct __hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn,
+ void *ctx)
{
- struct hashmap *map = malloc(sizeof(struct hashmap));
+ struct __hashmap *map = malloc(sizeof(*map));
if (!map) {
errno = ENOMEM;
@@ -62,7 +62,7 @@ struct hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
return map;
}
-void __hashmap__clear(struct hashmap *map)
+void __hashmap__clear(struct __hashmap *map)
{
struct hashmap_entry *cur, *tmp;
size_t bkt;
@@ -75,7 +75,7 @@ void __hashmap__clear(struct hashmap *map)
map->cap = map->cap_bits = map->sz = 0;
}
-void __hashmap__free(struct hashmap *map)
+void __hashmap__free(struct __hashmap *map)
{
if (!map)
return;
@@ -84,23 +84,23 @@ void __hashmap__free(struct hashmap *map)
free(map);
}
-size_t __hashmap__size(const struct hashmap *map)
+size_t __hashmap__size(const struct __hashmap *map)
{
return map->sz;
}
-size_t __hashmap__capacity(const struct hashmap *map)
+size_t __hashmap__capacity(const struct __hashmap *map)
{
return map->cap;
}
-static bool __hashmap__needs_to_grow(struct hashmap *map)
+static bool __hashmap__needs_to_grow(struct __hashmap *map)
{
/* grow if empty or more than 75% filled */
return (map->cap == 0) || ((map->sz + 1) * 4 / 3 > map->cap);
}
-static int __hashmap__grow(struct hashmap *map)
+static int __hashmap__grow(struct __hashmap *map)
{
struct hashmap_entry **new_buckets;
struct hashmap_entry *cur, *tmp;
@@ -129,7 +129,7 @@ static int __hashmap__grow(struct hashmap *map)
return 0;
}
-static bool __hashmap__find_entry(const struct hashmap *map,
+static bool __hashmap__find_entry(const struct __hashmap *map,
const long key, size_t hash,
struct hashmap_entry ***pprev,
struct hashmap_entry **entry)
@@ -153,7 +153,7 @@ static bool __hashmap__find_entry(const struct hashmap *map,
return false;
}
-int __hashmap__insert(struct hashmap *map, long key, long value,
+int __hashmap__insert(struct __hashmap *map, long key, long value,
enum hashmap_insert_strategy strategy,
void *old_key, void *old_value)
{
@@ -205,7 +205,7 @@ int __hashmap__insert(struct hashmap *map, long key, long value,
return 0;
}
-bool __hashmap__find(const struct hashmap *map, long key, void *value)
+bool __hashmap__find(const struct __hashmap *map, long key, void *value)
{
struct hashmap_entry *entry;
size_t h;
@@ -219,7 +219,7 @@ bool __hashmap__find(const struct hashmap *map, long key, void *value)
return true;
}
-bool __hashmap__delete(struct hashmap *map, long key,
+bool __hashmap__delete(struct __hashmap *map, long key,
void *old_key, void *old_value)
{
struct hashmap_entry **pprev, *entry;
diff --git a/tools/perf/util/hashmap.h b/tools/perf/util/hashmap.h
index 4dcc2cf3ef1a..bb85a65486be 100644
--- a/tools/perf/util/hashmap.h
+++ b/tools/perf/util/hashmap.h
@@ -8,10 +8,20 @@
#ifndef __PERF_UTIL_HASHMAP_H
#define __PERF_UTIL_HASHMAP_H
+#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
+#include <stdlib.h>
#include <limits.h>
+#include <linux/compiler.h>
+/**
+ * hash_bits() - Shuffle hash value and return requested number of upper bits.
+ * @h: Raw hash value.
+ * @bits: Number of bits to return (0 returns 0).
+ *
+ * Return: Multiplicative hash truncated to @bits bits.
+ */
static inline size_t hash_bits(size_t h, int bits)
{
/* shuffle bits and return requested number of upper bits */
@@ -28,7 +38,12 @@ static inline size_t hash_bits(size_t h, int bits)
#endif
}
-/* generic C-string hashing function */
+/**
+ * str_hash() - Generic C-string hashing function.
+ * @s: NUL-terminated string to hash.
+ *
+ * Return: Unshuffled hash value suitable for hash_bits().
+ */
static inline size_t str_hash(const char *s)
{
size_t h = 0;
@@ -40,22 +55,42 @@ static inline size_t str_hash(const char *s)
return h;
}
+/**
+ * typedef hashmap_hash_fn - Hash callback computing a key's hash value.
+ * @key: Key value cast to long.
+ * @ctx: Optional user context pointer passed at hashmap initialization.
+ *
+ * Return: Hash value for @key (shuffled internally via hash_bits()).
+ */
typedef size_t (*hashmap_hash_fn)(long key, void *ctx);
+
+/**
+ * typedef hashmap_equal_fn - Equality callback comparing two keys.
+ * @key1: First key value cast to long.
+ * @key2: Second key value cast to long.
+ * @ctx: Optional user context pointer passed at hashmap initialization.
+ *
+ * Return: true if @key1 and @key2 are equal, false otherwise.
+ */
typedef bool (*hashmap_equal_fn)(long key1, long key2, void *ctx);
/*
- * Hashmap interface is polymorphic, keys and values could be either
- * long-sized integers or pointers, this is achieved as follows:
- * - interface functions that operate on keys and values are hidden
- * behind auxiliary macros, e.g. hashmap_insert <-> hashmap__insert;
- * - these auxiliary macros cast the key and value parameters as
- * long or long *, so the user does not have to specify the casts explicitly;
- * - for pointer parameters (e.g. old_key) the size of the pointed
- * type is verified by hashmap_cast_ptr using _Static_assert;
- * - when iterating using hashmap__for_each_* forms
- * hasmap_entry->key should be used for integer keys and
- * hasmap_entry->pkey should be used for pointer keys,
- * same goes for values.
+ * Typed hashmap interface:
+ *
+ * Hashmaps are declared with DECLARE_HASHMAP(name, key_type, value_type),
+ * where key_type and value_type may be any integer, enum, bool, or pointer
+ * type with sizeof <= sizeof(long):
+ * - DECLARE_HASHMAP() defines 'struct <name>' along with '<name>__new()' and
+ * '<name>__free()' allocation helpers;
+ * - phantom pointer members (__key, __value) in a union with the backing
+ * struct __hashmap encode the declared key and value types at zero
+ * runtime space cost and without altering struct size or alignment;
+ * - hashmap__* macros type-check keys, values, output pointers, and iteration
+ * cursor variables against the declared types at compile time;
+ * - output parameters (value_ptr, old_key, old_value) are marshaled through
+ * long temporaries and assigned via the declared types so sub-long types
+ * (e.g. bool, int, pid_t) are safe from buffer overflows, endianness bugs,
+ * and strict-aliasing violations.
*/
struct hashmap_entry {
union {
@@ -69,7 +104,7 @@ struct hashmap_entry {
struct hashmap_entry *next;
};
-struct hashmap {
+struct __hashmap {
hashmap_hash_fn hash_fn;
hashmap_equal_fn equal_fn;
void *ctx;
@@ -80,148 +115,564 @@ struct hashmap {
size_t sz;
};
-void __hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn, void *ctx);
-struct hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn,
- void *ctx);
-void __hashmap__clear(struct hashmap *map);
-void __hashmap__free(struct hashmap *map);
+/* Legacy untyped hashmap, kept while migrating callers to DECLARE_HASHMAP. */
+struct hashmap {
+ union {
+ struct __hashmap map;
+ struct {
+ hashmap_hash_fn hash_fn;
+ hashmap_equal_fn equal_fn;
+ void *ctx;
+
+ struct hashmap_entry **buckets;
+ size_t cap;
+ size_t cap_bits;
+ size_t sz;
+ };
+ long *__key;
+ long *__value;
+ char __untyped;
+ };
+};
-size_t __hashmap__size(const struct hashmap *map);
-size_t __hashmap__capacity(const struct hashmap *map);
+void __hashmap__init(struct __hashmap *map, hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn, void *ctx);
+struct __hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn,
+ void *ctx);
+void __hashmap__clear(struct __hashmap *map);
+void __hashmap__free(struct __hashmap *map);
-static inline void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn, void *ctx)
-{
- __hashmap__init(map, hash_fn, equal_fn, ctx);
-}
+size_t __hashmap__size(const struct __hashmap *map);
+size_t __hashmap__capacity(const struct __hashmap *map);
static inline struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
hashmap_equal_fn equal_fn,
void *ctx)
{
- return __hashmap__new(hash_fn, equal_fn, ctx);
+ struct hashmap *map = malloc(sizeof(*map));
+
+ if (!map) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ __hashmap__init(&map->map, hash_fn, equal_fn, ctx);
+ return map;
}
-static inline void hashmap__clear(struct hashmap *map)
+/**
+ * enum hashmap_insert_strategy - Strategy for hashmap__insert().
+ * @HASHMAP_ADD: Only add key/value if key doesn't exist yet; returns -EEXIST
+ * and optionally fills old_key/old_value if already present.
+ * @HASHMAP_SET: Add key/value pair if key doesn't exist yet; otherwise update
+ * key/value and optionally return previous key/value.
+ * @HASHMAP_UPDATE: Update key/value if key already exists; otherwise do
+ * nothing and return -ENOENT.
+ * @HASHMAP_APPEND: Always add key/value pair, even if key already exists.
+ * Turns the hashmap into a multimap allowing multiple values
+ * associated with the same key. Read via
+ * hashmap__for_each_key(); hashmap__find() returns the most
+ * recently inserted entry for the key.
+ */
+enum hashmap_insert_strategy {
+ HASHMAP_ADD,
+ HASHMAP_SET,
+ HASHMAP_UPDATE,
+ HASHMAP_APPEND,
+};
+
+int __hashmap__insert(struct __hashmap *map, long key, long value,
+ enum hashmap_insert_strategy strategy,
+ void *old_key, void *old_value);
+bool __hashmap__delete(struct __hashmap *map, long key,
+ void *old_key, void *old_value);
+bool __hashmap__find(const struct __hashmap *map, long key, void *value);
+
+/*
+ * Single-loop iterators used by hashmap__for_each* so 'break' exits the entire
+ * iteration and element deletion during iteration is safe.
+ */
+struct __hashmap_iter {
+ const struct __hashmap *map;
+ size_t bkt;
+ struct hashmap_entry *cur;
+ struct hashmap_entry *next;
+};
+
+static inline struct __hashmap_iter
+__hashmap_iter__init(const struct __hashmap *map)
{
- __hashmap__clear(map);
+ return (struct __hashmap_iter){
+ .map = map,
+ .bkt = 0,
+ .cur = NULL,
+ .next = (map && map->buckets && map->cap > 0) ? map->buckets[0] : NULL,
+ };
}
-static inline void hashmap__free(struct hashmap *map)
+static inline struct hashmap_entry *
+__hashmap_iter__advance(struct __hashmap_iter *it)
{
- __hashmap__free(map);
+ if (!it->map || !it->map->buckets)
+ return NULL;
+
+ while (!it->next) {
+ it->bkt++;
+ if (it->bkt >= it->map->cap) {
+ it->cur = NULL;
+ return NULL;
+ }
+ it->next = it->map->buckets[it->bkt];
+ }
+ it->cur = it->next;
+ it->next = it->cur->next;
+ return it->cur;
}
-static inline size_t hashmap__size(const struct hashmap *map)
+struct __hashmap_key_iter {
+ const struct __hashmap *map;
+ long key;
+ struct hashmap_entry *cur;
+ struct hashmap_entry *next;
+};
+
+static inline struct __hashmap_key_iter
+__hashmap_key_iter__init(const struct __hashmap *map, long key)
{
- return __hashmap__size(map);
+ struct hashmap_entry *first = NULL;
+
+ if (map && map->buckets) {
+ size_t h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
+
+ first = map->buckets[h];
+ }
+ return (struct __hashmap_key_iter){
+ .map = map,
+ .key = key,
+ .cur = NULL,
+ .next = first,
+ };
}
-static inline size_t hashmap__capacity(const struct hashmap *map)
+static inline struct hashmap_entry *
+__hashmap_key_iter__advance(struct __hashmap_key_iter *it)
{
- return __hashmap__capacity(map);
+ while (it->next) {
+ struct hashmap_entry *entry = it->next;
+
+ it->next = entry->next;
+ if (it->map->equal_fn(entry->key, it->key, it->map->ctx)) {
+ it->cur = entry;
+ return entry;
+ }
+ }
+ it->cur = NULL;
+ return NULL;
}
-/*
- * Hashmap insertion strategy:
- * - HASHMAP_ADD - only add key/value if key doesn't exist yet;
- * - HASHMAP_SET - add key/value pair if key doesn't exist yet; otherwise,
- * update value;
- * - HASHMAP_UPDATE - update value, if key already exists; otherwise, do
- * nothing and return -ENOENT;
- * - HASHMAP_APPEND - always add key/value pair, even if key already exists.
- * This turns hashmap into a multimap by allowing multiple values to be
- * associated with the same key. Most useful read API for such hashmap is
- * hashmap__for_each_key_entry() iteration. If hashmap__find() is still
- * used, it will return last inserted key/value entry (first in a bucket
- * chain).
+/**
+ * DECLARE_HASHMAP() - Declare a type-safe hashmap struct and alloc helpers.
+ * @name: Struct tag to define (creates 'struct @name', '@name__new()', and
+ * '@name__free()').
+ * @key_type: Type of keys stored in the map (sizeof(@key_type) <= sizeof(long)).
+ * @value_type: Type of values stored in the map
+ * (sizeof(@value_type) <= sizeof(long)).
+ *
+ * '@name__new(hash_fn, equal_fn, ctx)' allocates and initializes a new
+ * 'struct @name *', returning NULL with errno = ENOMEM on allocation failure.
+ * '@name__free(hmap)' clears all bucket storage and frees @hmap (NULL-safe;
+ * does not free caller-owned pointees referenced by keys or values).
*/
-enum hashmap_insert_strategy {
- HASHMAP_ADD,
- HASHMAP_SET,
- HASHMAP_UPDATE,
- HASHMAP_APPEND,
-};
-
-#define hashmap_cast_ptr(p) ({ \
- _Static_assert((__builtin_constant_p((p)) ? (p) == NULL : 0) || \
- sizeof(*(p)) == sizeof(long), \
- #p " pointee should be a long-sized integer or a pointer"); \
- (void *)(p); \
-})
+#define DECLARE_HASHMAP(name, key_type, value_type) \
+struct name { \
+ union { \
+ struct __hashmap map; \
+ key_type *__key; \
+ value_type *__value; \
+ int __untyped; \
+ }; \
+ _Static_assert(sizeof(key_type) <= sizeof(long), \
+ #name " key_type exceeds sizeof(long)"); \
+ _Static_assert(sizeof(value_type) <= sizeof(long), \
+ #name " value_type exceeds sizeof(long)"); \
+}; \
+static inline __maybe_unused struct name * \
+name##__new(hashmap_hash_fn hash_fn, hashmap_equal_fn equal_fn, void *ctx) \
+{ \
+ struct name *hmap = malloc(sizeof(*hmap)); \
+ \
+ if (!hmap) { \
+ errno = ENOMEM; \
+ return NULL; \
+ } \
+ __hashmap__init(&hmap->map, hash_fn, equal_fn, ctx); \
+ return hmap; \
+} \
+static inline __maybe_unused void name##__free(struct name *hmap) \
+{ \
+ if (!hmap) \
+ return; \
+ __hashmap__clear(&hmap->map); \
+ free(hmap); \
+}
/*
- * hashmap__insert() adds key/value entry w/ various semantics, depending on
- * provided strategy value. If a given key/value pair replaced already
- * existing key/value pair, both old key and old value will be returned
- * through old_key and old_value to allow calling code do proper memory
- * management.
+ * For pointer key types (T *), allow const-qualified lookup keys (const T *);
+ * for non-pointer key types, preserve the exact declared key_type.
*/
-int __hashmap__insert(struct hashmap *map, long key, long value,
- enum hashmap_insert_strategy strategy,
- void *old_key, void *old_value);
-
-#define hashmap__insert(map, key, value, strategy, old_key, old_value) \
- __hashmap__insert((map), (long)(key), (long)(value), (strategy), \
- hashmap_cast_ptr(old_key), \
- hashmap_cast_ptr(old_value))
-
-#define hashmap__add(map, key, value) \
- hashmap__insert((map), (key), (value), HASHMAP_ADD, NULL, NULL)
-
-#define hashmap__set(map, key, value, old_key, old_value) \
- hashmap__insert((map), (key), (value), HASHMAP_SET, (old_key), (old_value))
+#define ___hashmap__lookup_key_type(hm) \
+ typeof(__builtin_choose_expr( \
+ __builtin_classify_type(*(hm)->__key) == 5, \
+ (const typeof(*__builtin_choose_expr( \
+ __builtin_classify_type(*(hm)->__key) == 5, \
+ *(hm)->__key, (char *)NULL)) *)NULL, \
+ *(hm)->__key))
+
+#define ___hashmap__choose(hm, untyped_expr, typed_expr) \
+ __builtin_choose_expr(sizeof((hm)->__untyped) == 1, \
+ (untyped_expr), (typed_expr))
+
+#define ___hashmap__in_val(hm, expr) \
+ ___hashmap__choose(hm, (long)(expr), (expr))
+
+#define ___hashmap__check_untyped_ptr(hm, ptr) \
+ _Static_assert(sizeof((hm)->__untyped) != 1 || \
+ (__builtin_constant_p((ptr)) ? (ptr) == NULL : 0) || \
+ sizeof(*(ptr)) == sizeof(long), \
+ #ptr " pointee should be a long-sized integer or a pointer")
+
+/**
+ * hashmap__init() - Initialize an embedded/stack-allocated typed hashmap.
+ * @hmap: Pointer to a hashmap struct declared with DECLARE_HASHMAP().
+ * @hash_fn: Hash callback function.
+ * @equal_fn: Key equality callback function.
+ * @ctx: Optional user context pointer passed to @hash_fn and @equal_fn.
+ */
+#define hashmap__init(hmap, hash_fn, equal_fn, ctx) \
+ __hashmap__init(&(hmap)->map, (hash_fn), (equal_fn), (ctx))
-#define hashmap__update(map, key, value, old_key, old_value) \
- hashmap__insert((map), (key), (value), HASHMAP_UPDATE, (old_key), (old_value))
+/**
+ * hashmap__clear() - Remove all entries from a typed hashmap and free buckets.
+ * @hmap: Pointer to a typed hashmap.
+ *
+ * Does not free @hmap itself or caller-owned memory referenced by keys/values.
+ */
+#define hashmap__clear(hmap) \
+ __hashmap__clear(&(hmap)->map)
+
+#define ___hashmap__free(hmap, hm) \
+do { \
+ typeof(hmap) hm = (hmap); \
+ if (hm) { \
+ __hashmap__clear(&(hm)->map); \
+ free(hm); \
+ } \
+} while (0)
+
+/**
+ * hashmap__free() - Clear and free a heap-allocated typed hashmap.
+ * @hmap: Pointer to a typed hashmap allocated with <name>__new() (NULL-safe).
+ *
+ * Does not free caller-owned memory referenced by keys or values.
+ */
+#define hashmap__free(hmap) \
+ ___hashmap__free(hmap, __PASTE(__hm_, __COUNTER__))
-#define hashmap__append(map, key, value) \
- hashmap__insert((map), (key), (value), HASHMAP_APPEND, NULL, NULL)
+/**
+ * hashmap__size() - Return the number of entries currently in a hashmap.
+ * @hmap: Pointer to a typed hashmap (may be const).
+ *
+ * Return: Number of stored key/value entries.
+ */
+#define hashmap__size(hmap) \
+ __hashmap__size(&(hmap)->map)
-bool __hashmap__delete(struct hashmap *map, long key, void *old_key, void *old_value);
+/**
+ * hashmap__capacity() - Return the bucket count of a hashmap.
+ * @hmap: Pointer to a typed hashmap (may be const).
+ *
+ * Return: Number of allocated hash buckets.
+ */
+#define hashmap__capacity(hmap) \
+ __hashmap__capacity(&(hmap)->map)
+
+#define ___hashmap__insert(hmap, key, value, strategy, old_key, old_value, \
+ hm, k, v, okp, ovp, raw_ok, raw_ov, ok, ov, err) \
+({ \
+ typeof(hmap) hm = (hmap); \
+ typeof(*(hm)->__key) k = ___hashmap__in_val(hm, key); \
+ typeof(*(hm)->__value) v = ___hashmap__in_val(hm, value); \
+ long ok = 0, ov = 0; \
+ typeof((hm)->__key) okp = \
+ ___hashmap__choose(hm, (long *)NULL, (old_key)); \
+ typeof((hm)->__value) ovp = \
+ ___hashmap__choose(hm, (long *)NULL, (old_value)); \
+ void *raw_ok = ___hashmap__choose(hm, (void *)(old_key), \
+ (void *)(okp ? &ok : NULL)); \
+ void *raw_ov = ___hashmap__choose(hm, (void *)(old_value), \
+ (void *)(ovp ? &ov : NULL)); \
+ int err; \
+ ___hashmap__check_untyped_ptr(hm, old_key); \
+ ___hashmap__check_untyped_ptr(hm, old_value); \
+ err = __hashmap__insert(&(hm)->map, (long)k, (long)v, (strategy), \
+ raw_ok, raw_ov); \
+ if (okp) \
+ *okp = (typeof(*(hm)->__key))ok; \
+ if (ovp) \
+ *ovp = (typeof(*(hm)->__value))ov; \
+ err; \
+})
-#define hashmap__delete(map, key, old_key, old_value) \
- __hashmap__delete((map), (long)(key), \
- hashmap_cast_ptr(old_key), \
- hashmap_cast_ptr(old_value))
+#define _hashmap__insert(hmap, key, value, strategy, old_key, old_value, u) \
+ ___hashmap__insert(hmap, key, value, strategy, old_key, old_value, \
+ __PASTE(__hm_, u), __PASTE(__k_, u), __PASTE(__v_, u), \
+ __PASTE(__okp_, u), __PASTE(__ovp_, u), \
+ __PASTE(__rok_, u), __PASTE(__rov_, u), \
+ __PASTE(__ok_, u), __PASTE(__ov_, u), __PASTE(__err_, u))
+
+/**
+ * hashmap__insert() - Insert a key/value entry using the specified strategy.
+ * @hmap: Pointer to a typed hashmap.
+ * @key: Key to insert (must be compatible with declared key_type).
+ * @value: Value to insert (must be compatible with declared value_type).
+ * @strategy: Insertion strategy (enum hashmap_insert_strategy).
+ * @old_key: Optional pointer to key_type receiving previous key on match (or
+ * zeroed if no existing entry matched); may be NULL.
+ * @old_value: Optional pointer to value_type receiving previous value on match
+ * (or zeroed if no existing entry matched); may be NULL.
+ *
+ * If a given key/value pair replaced or matched an existing entry, both old
+ * key and old value are returned through @old_key and @old_value so calling
+ * code can perform proper memory management.
+ *
+ * Return: 0 on success, -EEXIST if @strategy is HASHMAP_ADD and @key exists,
+ * -ENOENT if @strategy is HASHMAP_UPDATE and @key is absent, or -ENOMEM on
+ * allocation failure.
+ */
+#define hashmap__insert(hmap, key, value, strategy, old_key, old_value) \
+ _hashmap__insert(hmap, key, value, strategy, old_key, old_value, __COUNTER__)
+
+/**
+ * hashmap__add() - Add a new key/value entry only if the key does not exist.
+ * @hmap: Pointer to a typed hashmap.
+ * @key: Key to insert.
+ * @value: Value to insert.
+ *
+ * Return: 0 on success, -EEXIST if @key already exists, or -ENOMEM.
+ */
+#define hashmap__add(hmap, key, value) \
+ hashmap__insert(hmap, key, value, HASHMAP_ADD, NULL, NULL)
+
+/**
+ * hashmap__set() - Add a key/value entry or replace an existing one.
+ * @hmap: Pointer to a typed hashmap.
+ * @key: Key to insert or replace.
+ * @value: Value to insert or replace.
+ * @old_key: Optional pointer to key_type receiving replaced key (or zeroed if
+ * newly inserted); may be NULL.
+ * @old_value: Optional pointer to value_type receiving replaced value (or
+ * zeroed if newly inserted); may be NULL.
+ *
+ * Return: 0 on success, or -ENOMEM on allocation failure.
+ */
+#define hashmap__set(hmap, key, value, old_key, old_value) \
+ hashmap__insert(hmap, key, value, HASHMAP_SET, old_key, old_value)
+
+/**
+ * hashmap__update() - Update value for an existing key only.
+ * @hmap: Pointer to a typed hashmap.
+ * @key: Key to look up and replace.
+ * @value: New value to store.
+ * @old_key: Optional pointer to key_type receiving replaced key; may be NULL.
+ * @old_value: Optional pointer to value_type receiving replaced value; may be
+ * NULL.
+ *
+ * Return: 0 on success, or -ENOENT if @key does not exist.
+ */
+#define hashmap__update(hmap, key, value, old_key, old_value) \
+ hashmap__insert(hmap, key, value, HASHMAP_UPDATE, old_key, old_value)
+
+/**
+ * hashmap__append() - Unconditionally append a key/value pair (multimap).
+ * @hmap: Pointer to a typed hashmap.
+ * @key: Key to append.
+ * @value: Value to append.
+ *
+ * Return: 0 on success, or -ENOMEM on allocation failure.
+ */
+#define hashmap__append(hmap, key, value) \
+ hashmap__insert(hmap, key, value, HASHMAP_APPEND, NULL, NULL)
+
+#define ___hashmap__find(hmap, key, value_ptr, hm, k, vp, raw_vp, v, found) \
+({ \
+ typeof(hmap) hm = (hmap); \
+ ___hashmap__lookup_key_type(hm) k = ___hashmap__in_val(hm, key); \
+ long v = 0; \
+ typeof((hm)->__value) vp = \
+ ___hashmap__choose(hm, (long *)NULL, (value_ptr)); \
+ void *raw_vp = ___hashmap__choose(hm, (void *)(value_ptr), \
+ (void *)(vp ? &v : NULL)); \
+ bool found; \
+ ___hashmap__check_untyped_ptr(hm, value_ptr); \
+ found = __hashmap__find(&(hm)->map, (long)k, raw_vp); \
+ if (found && vp) \
+ *vp = (typeof(*(hm)->__value))v; \
+ found; \
+})
-bool __hashmap__find(const struct hashmap *map, long key, void *value);
+#define _hashmap__find(hmap, key, value_ptr, u) \
+ ___hashmap__find(hmap, key, value_ptr, \
+ __PASTE(__hm_, u), __PASTE(__k_, u), __PASTE(__vp_, u), \
+ __PASTE(__rvp_, u), __PASTE(__v_, u), __PASTE(__found_, u))
+
+/**
+ * hashmap__find() - Look up a key in a typed hashmap.
+ * @hmap: Pointer to a typed hashmap (may be const).
+ * @key: Key to search for (const-qualified pointer keys are accepted).
+ * @value_ptr: Optional pointer to value_type receiving the found value; may be
+ * NULL to test membership only. Left unmodified if not found.
+ *
+ * Return: true if @key was found, false otherwise.
+ */
+#define hashmap__find(hmap, key, value_ptr) \
+ _hashmap__find(hmap, key, value_ptr, __COUNTER__)
+
+#define ___hashmap__delete(hmap, key, old_key, old_value, \
+ hm, k, okp, ovp, raw_ok, raw_ov, ok, ov, rem) \
+({ \
+ typeof(hmap) hm = (hmap); \
+ ___hashmap__lookup_key_type(hm) k = ___hashmap__in_val(hm, key); \
+ long ok = 0, ov = 0; \
+ typeof((hm)->__key) okp = \
+ ___hashmap__choose(hm, (long *)NULL, (old_key)); \
+ typeof((hm)->__value) ovp = \
+ ___hashmap__choose(hm, (long *)NULL, (old_value)); \
+ void *raw_ok = ___hashmap__choose(hm, (void *)(old_key), \
+ (void *)(okp ? &ok : NULL)); \
+ void *raw_ov = ___hashmap__choose(hm, (void *)(old_value), \
+ (void *)(ovp ? &ov : NULL)); \
+ bool rem; \
+ ___hashmap__check_untyped_ptr(hm, old_key); \
+ ___hashmap__check_untyped_ptr(hm, old_value); \
+ rem = __hashmap__delete(&(hm)->map, (long)k, raw_ok, raw_ov); \
+ if (okp) \
+ *okp = (typeof(*(hm)->__key))ok; \
+ if (ovp) \
+ *ovp = (typeof(*(hm)->__value))ov; \
+ rem; \
+})
-#define hashmap__find(map, key, value) \
- __hashmap__find((map), (long)(key), hashmap_cast_ptr(value))
+#define _hashmap__delete(hmap, key, old_key, old_value, u) \
+ ___hashmap__delete(hmap, key, old_key, old_value, \
+ __PASTE(__hm_, u), __PASTE(__k_, u), \
+ __PASTE(__okp_, u), __PASTE(__ovp_, u), \
+ __PASTE(__rok_, u), __PASTE(__rov_, u), \
+ __PASTE(__ok_, u), __PASTE(__ov_, u), __PASTE(__rem_, u))
+
+/**
+ * hashmap__delete() - Remove an entry matching @key from a typed hashmap.
+ * @hmap: Pointer to a typed hashmap.
+ * @key: Key to delete (const-qualified pointer keys are accepted).
+ * @old_key: Optional pointer to key_type receiving removed key (or zeroed if
+ * not found); may be NULL.
+ * @old_value: Optional pointer to value_type receiving removed value (or
+ * zeroed if not found); may be NULL.
+ *
+ * Return: true if an entry was removed, false if @key was not found.
+ */
+#define hashmap__delete(hmap, key, old_key, old_value) \
+ _hashmap__delete(hmap, key, old_key, old_value, __COUNTER__)
+
+#define ___hashmap__for_each(hmap, key_var, val_var, it, hm) \
+ for (struct __hashmap_iter it = ({ \
+ typeof(hmap) hm = (hmap); \
+ __hashmap_iter__init(hm ? &(hm)->map : NULL); \
+ }); \
+ __hashmap_iter__advance(&it) && ({ \
+ _Static_assert(sizeof(key_var) >= sizeof(*(hmap)->__key), \
+ "key_var narrower than key_type"); \
+ _Static_assert(sizeof(val_var) >= sizeof(*(hmap)->__value), \
+ "val_var narrower than value_type"); \
+ (key_var) = (typeof(*(hmap)->__key))it.cur->key; \
+ (val_var) = (typeof(*(hmap)->__value))it.cur->value; \
+ true; \
+ });)
+
+#define _hashmap__for_each(hmap, key_var, val_var, u) \
+ ___hashmap__for_each(hmap, key_var, val_var, \
+ __PASTE(__it_, u), __PASTE(__hm_, u))
+
+/**
+ * hashmap__for_each() - Iterate over all entries in a typed hashmap.
+ * @hmap: Pointer to a typed hashmap (may be const or NULL).
+ * @key_var: Loop cursor variable assigned each entry's key.
+ * @val_var: Loop cursor variable assigned each entry's value.
+ *
+ * Safe against deleting the current entry during iteration, and 'break' exits
+ * the entire loop across all buckets.
+ */
+#define hashmap__for_each(hmap, key_var, val_var) \
+ _hashmap__for_each(hmap, key_var, val_var, __COUNTER__)
+
+/**
+ * hashmap__for_each_safe() - Iterate over all entries in a typed hashmap, safe
+ * against removals.
+ * @hmap: Pointer to a typed hashmap (may be const or NULL).
+ * @key_var: Loop cursor variable assigned each entry's key.
+ * @val_var: Loop cursor variable assigned each entry's value.
+ */
+#define hashmap__for_each_safe(hmap, key_var, val_var) \
+ hashmap__for_each(hmap, key_var, val_var)
+
+#define ___hashmap__for_each_key(hmap, key, val_var, kit, hm, k) \
+ for (struct __hashmap_key_iter kit = ({ \
+ typeof(hmap) hm = (hmap); \
+ ___hashmap__lookup_key_type(hm) k = (key); \
+ __hashmap_key_iter__init(hm ? &(hm)->map : NULL, (long)k); \
+ }); \
+ __hashmap_key_iter__advance(&kit) && ({ \
+ _Static_assert(sizeof(val_var) >= sizeof(*(hmap)->__value), \
+ "val_var narrower than value_type"); \
+ (val_var) = (typeof(*(hmap)->__value))kit.cur->value; \
+ true; \
+ });)
+
+#define _hashmap__for_each_key(hmap, key, val_var, u) \
+ ___hashmap__for_each_key(hmap, key, val_var, __PASTE(__kit_, u), \
+ __PASTE(__hm_, u), __PASTE(__k_, u))
+
+/**
+ * hashmap__for_each_key() - Iterate over all values associated with @key.
+ * @hmap: Pointer to a typed hashmap (may be const or NULL).
+ * @key: Key to iterate entries for (const-qualified pointer keys accepted).
+ * @val_var: Loop cursor variable assigned each matching entry's value.
+ */
+#define hashmap__for_each_key(hmap, key, val_var) \
+ _hashmap__for_each_key(hmap, key, val_var, __COUNTER__)
+
+/**
+ * hashmap__for_each_key_safe() - Iterate over all values associated with @key,
+ * safe against removals.
+ * @hmap: Pointer to a typed hashmap (may be const or NULL).
+ * @key: Key to iterate entries for (const-qualified pointer keys accepted).
+ * @val_var: Loop cursor variable assigned each matching entry's value.
+ */
+#define hashmap__for_each_key_safe(hmap, key, val_var) \
+ hashmap__for_each_key(hmap, key, val_var)
/*
- * hashmap__for_each_entry - iterate over all entries in hashmap
- * @map: hashmap to iterate
- * @cur: struct hashmap_entry * used as a loop cursor
- * @bkt: integer used as a bucket loop cursor
+ * Legacy entry iterators for unconverted struct hashmap callers.
*/
#define hashmap__for_each_entry(map, cur, bkt) \
for (bkt = 0; bkt < (map)->cap; bkt++) \
for (cur = (map)->buckets[bkt]; cur; cur = cur->next)
-/*
- * hashmap__for_each_entry_safe - iterate over all entries in hashmap, safe
- * against removals
- * @map: hashmap to iterate
- * @cur: struct hashmap_entry * used as a loop cursor
- * @tmp: struct hashmap_entry * used as a temporary next cursor storage
- * @bkt: integer used as a bucket loop cursor
- */
#define hashmap__for_each_entry_safe(map, cur, tmp, bkt) \
for (bkt = 0; bkt < (map)->cap; bkt++) \
for (cur = (map)->buckets[bkt]; \
cur && ({tmp = cur->next; true; }); \
cur = tmp)
-/*
- * hashmap__for_each_key_entry - iterate over entries associated with given key
- * @map: hashmap to iterate
- * @cur: struct hashmap_entry * used as a loop cursor
- * @key: key to iterate entries for
- */
#define hashmap__for_each_key_entry(map, cur, _key) \
for (cur = (map)->buckets \
? (map)->buckets[hash_bits((map)->hash_fn((_key), (map)->ctx), (map)->cap_bits)] \
--
2.56.1.703.gf848ed807c-goog
next prev parent reply other threads:[~2026-11-13 21:49 UTC|newest]
Thread overview: 53+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
2026-11-13 21:48 ` [PATCH v1 01/26] perf threads: Fix uninitialized pointer put when thread removal misses Ian Rogers
2026-11-13 21:56 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 02/26] perf annotate: Fix sym_hist_entry memory leak when zeroing histograms Ian Rogers
2026-11-13 21:59 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 03/26] perf stat: Fix per_pkg_mask key leak on hashmap__add failure Ian Rogers
2026-11-13 21:55 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 04/26] perf build: Don't check difference of perf and libbpf hashmap Ian Rogers
2026-11-13 21:54 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 05/26] perf hashmap: Always build perf's hashmap and avoid libbpf symbol conflict Ian Rogers
2026-11-13 21:55 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 06/26] perf hashmap: Fix strict aliasing violations in hashmap Ian Rogers
2026-11-13 21:56 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 07/26] perf hashmap: Remove errptr usage from hashmap Ian Rogers
2026-11-13 22:01 ` sashiko-bot
2026-11-13 21:48 ` Ian Rogers [this message]
2026-11-13 22:01 ` [PATCH v1 08/26] perf hashmap: Introduce generic types and macros sashiko-bot
2026-11-13 21:48 ` [PATCH v1 09/26] perf fncache: Use generic hashmap for fncache Ian Rogers
2026-11-13 21:56 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 10/26] perf ftrace: Use generic hashmap for profile_hash Ian Rogers
2026-11-13 21:57 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 11/26] perf trace: Use generic hashmap for syscall_stats Ian Rogers
2026-11-13 22:04 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 12/26] perf trace: Use generic hashmap for bpf-trace-summary Ian Rogers
2026-11-13 21:58 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 13/26] perf lock: Use generic hashmap for slab_hash Ian Rogers
2026-11-13 21:57 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 14/26] perf stat: Use generic hashmap for per_pkg_mask Ian Rogers
2026-11-13 21:57 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 15/26] perf expr: Use generic hashmap for expr_ids Ian Rogers
2026-11-13 22:02 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 16/26] perf pmu: Use generic hashmap for pmu aliases Ian Rogers
2026-11-13 22:04 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 17/26] perf hwmon_pmu: Use generic hashmap for hwmon events Ian Rogers
2026-11-13 21:58 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 18/26] perf s390: Use generic hashmap for counter name cache Ian Rogers
2026-11-13 21:59 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 19/26] perf threads: Use generic hashmap for thread shards Ian Rogers
2026-11-13 22:01 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 20/26] perf annotate: Use generic hashmap for sym_samples Ian Rogers
2026-11-13 22:08 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 21/26] perf annotate: Use generic hashmap for browser type_hash Ian Rogers
2026-11-13 21:59 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 22/26] perf annotate-data: Use generic hashmap for type_hist samples Ian Rogers
2026-11-13 22:00 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 23/26] perf inject: Use generic hashmap for aslr remap_addresses Ian Rogers
2026-11-13 21:59 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 24/26] perf inject: Use generic hashmap for aslr top_addresses Ian Rogers
2026-11-13 21:59 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 25/26] perf inject: Use generic hashmap for aslr evsel_orig_attrs Ian Rogers
2026-11-13 22:00 ` sashiko-bot
2026-11-13 21:48 ` [PATCH v1 26/26] perf hashmap: Remove untyped struct hashmap API Ian Rogers
2026-11-13 22:01 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261113214850.3329699-9-irogers@google.com \
--to=irogers@google.com \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.