162306a36Sopenharmony_ci/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Generic non-thread safe hash map implementation.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2019 Facebook
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#ifndef __LIBBPF_HASHMAP_H
962306a36Sopenharmony_ci#define __LIBBPF_HASHMAP_H
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <stdbool.h>
1262306a36Sopenharmony_ci#include <stddef.h>
1362306a36Sopenharmony_ci#include <limits.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic inline size_t hash_bits(size_t h, int bits)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	/* shuffle bits and return requested number of upper bits */
1862306a36Sopenharmony_ci	if (bits == 0)
1962306a36Sopenharmony_ci		return 0;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#if (__SIZEOF_SIZE_T__ == __SIZEOF_LONG_LONG__)
2262306a36Sopenharmony_ci	/* LP64 case */
2362306a36Sopenharmony_ci	return (h * 11400714819323198485llu) >> (__SIZEOF_LONG_LONG__ * 8 - bits);
2462306a36Sopenharmony_ci#elif (__SIZEOF_SIZE_T__ <= __SIZEOF_LONG__)
2562306a36Sopenharmony_ci	return (h * 2654435769lu) >> (__SIZEOF_LONG__ * 8 - bits);
2662306a36Sopenharmony_ci#else
2762306a36Sopenharmony_ci#	error "Unsupported size_t size"
2862306a36Sopenharmony_ci#endif
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* generic C-string hashing function */
3262306a36Sopenharmony_cistatic inline size_t str_hash(const char *s)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	size_t h = 0;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	while (*s) {
3762306a36Sopenharmony_ci		h = h * 31 + *s;
3862306a36Sopenharmony_ci		s++;
3962306a36Sopenharmony_ci	}
4062306a36Sopenharmony_ci	return h;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_citypedef size_t (*hashmap_hash_fn)(long key, void *ctx);
4462306a36Sopenharmony_citypedef bool (*hashmap_equal_fn)(long key1, long key2, void *ctx);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/*
4762306a36Sopenharmony_ci * Hashmap interface is polymorphic, keys and values could be either
4862306a36Sopenharmony_ci * long-sized integers or pointers, this is achieved as follows:
4962306a36Sopenharmony_ci * - interface functions that operate on keys and values are hidden
5062306a36Sopenharmony_ci *   behind auxiliary macros, e.g. hashmap_insert <-> hashmap__insert;
5162306a36Sopenharmony_ci * - these auxiliary macros cast the key and value parameters as
5262306a36Sopenharmony_ci *   long or long *, so the user does not have to specify the casts explicitly;
5362306a36Sopenharmony_ci * - for pointer parameters (e.g. old_key) the size of the pointed
5462306a36Sopenharmony_ci *   type is verified by hashmap_cast_ptr using _Static_assert;
5562306a36Sopenharmony_ci * - when iterating using hashmap__for_each_* forms
5662306a36Sopenharmony_ci *   hasmap_entry->key should be used for integer keys and
5762306a36Sopenharmony_ci *   hasmap_entry->pkey should be used for pointer keys,
5862306a36Sopenharmony_ci *   same goes for values.
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_cistruct hashmap_entry {
6162306a36Sopenharmony_ci	union {
6262306a36Sopenharmony_ci		long key;
6362306a36Sopenharmony_ci		const void *pkey;
6462306a36Sopenharmony_ci	};
6562306a36Sopenharmony_ci	union {
6662306a36Sopenharmony_ci		long value;
6762306a36Sopenharmony_ci		void *pvalue;
6862306a36Sopenharmony_ci	};
6962306a36Sopenharmony_ci	struct hashmap_entry *next;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistruct hashmap {
7362306a36Sopenharmony_ci	hashmap_hash_fn hash_fn;
7462306a36Sopenharmony_ci	hashmap_equal_fn equal_fn;
7562306a36Sopenharmony_ci	void *ctx;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	struct hashmap_entry **buckets;
7862306a36Sopenharmony_ci	size_t cap;
7962306a36Sopenharmony_ci	size_t cap_bits;
8062306a36Sopenharmony_ci	size_t sz;
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_civoid hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
8462306a36Sopenharmony_ci		   hashmap_equal_fn equal_fn, void *ctx);
8562306a36Sopenharmony_cistruct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
8662306a36Sopenharmony_ci			     hashmap_equal_fn equal_fn,
8762306a36Sopenharmony_ci			     void *ctx);
8862306a36Sopenharmony_civoid hashmap__clear(struct hashmap *map);
8962306a36Sopenharmony_civoid hashmap__free(struct hashmap *map);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cisize_t hashmap__size(const struct hashmap *map);
9262306a36Sopenharmony_cisize_t hashmap__capacity(const struct hashmap *map);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/*
9562306a36Sopenharmony_ci * Hashmap insertion strategy:
9662306a36Sopenharmony_ci * - HASHMAP_ADD - only add key/value if key doesn't exist yet;
9762306a36Sopenharmony_ci * - HASHMAP_SET - add key/value pair if key doesn't exist yet; otherwise,
9862306a36Sopenharmony_ci *   update value;
9962306a36Sopenharmony_ci * - HASHMAP_UPDATE - update value, if key already exists; otherwise, do
10062306a36Sopenharmony_ci *   nothing and return -ENOENT;
10162306a36Sopenharmony_ci * - HASHMAP_APPEND - always add key/value pair, even if key already exists.
10262306a36Sopenharmony_ci *   This turns hashmap into a multimap by allowing multiple values to be
10362306a36Sopenharmony_ci *   associated with the same key. Most useful read API for such hashmap is
10462306a36Sopenharmony_ci *   hashmap__for_each_key_entry() iteration. If hashmap__find() is still
10562306a36Sopenharmony_ci *   used, it will return last inserted key/value entry (first in a bucket
10662306a36Sopenharmony_ci *   chain).
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_cienum hashmap_insert_strategy {
10962306a36Sopenharmony_ci	HASHMAP_ADD,
11062306a36Sopenharmony_ci	HASHMAP_SET,
11162306a36Sopenharmony_ci	HASHMAP_UPDATE,
11262306a36Sopenharmony_ci	HASHMAP_APPEND,
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci#define hashmap_cast_ptr(p) ({								\
11662306a36Sopenharmony_ci	_Static_assert((__builtin_constant_p((p)) ? (p) == NULL : 0) ||			\
11762306a36Sopenharmony_ci				sizeof(*(p)) == sizeof(long),				\
11862306a36Sopenharmony_ci		       #p " pointee should be a long-sized integer or a pointer");	\
11962306a36Sopenharmony_ci	(long *)(p);									\
12062306a36Sopenharmony_ci})
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/*
12362306a36Sopenharmony_ci * hashmap__insert() adds key/value entry w/ various semantics, depending on
12462306a36Sopenharmony_ci * provided strategy value. If a given key/value pair replaced already
12562306a36Sopenharmony_ci * existing key/value pair, both old key and old value will be returned
12662306a36Sopenharmony_ci * through old_key and old_value to allow calling code do proper memory
12762306a36Sopenharmony_ci * management.
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_ciint hashmap_insert(struct hashmap *map, long key, long value,
13062306a36Sopenharmony_ci		   enum hashmap_insert_strategy strategy,
13162306a36Sopenharmony_ci		   long *old_key, long *old_value);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#define hashmap__insert(map, key, value, strategy, old_key, old_value) \
13462306a36Sopenharmony_ci	hashmap_insert((map), (long)(key), (long)(value), (strategy),  \
13562306a36Sopenharmony_ci		       hashmap_cast_ptr(old_key),		       \
13662306a36Sopenharmony_ci		       hashmap_cast_ptr(old_value))
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci#define hashmap__add(map, key, value) \
13962306a36Sopenharmony_ci	hashmap__insert((map), (key), (value), HASHMAP_ADD, NULL, NULL)
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#define hashmap__set(map, key, value, old_key, old_value) \
14262306a36Sopenharmony_ci	hashmap__insert((map), (key), (value), HASHMAP_SET, (old_key), (old_value))
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci#define hashmap__update(map, key, value, old_key, old_value) \
14562306a36Sopenharmony_ci	hashmap__insert((map), (key), (value), HASHMAP_UPDATE, (old_key), (old_value))
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci#define hashmap__append(map, key, value) \
14862306a36Sopenharmony_ci	hashmap__insert((map), (key), (value), HASHMAP_APPEND, NULL, NULL)
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cibool hashmap_delete(struct hashmap *map, long key, long *old_key, long *old_value);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci#define hashmap__delete(map, key, old_key, old_value)		       \
15362306a36Sopenharmony_ci	hashmap_delete((map), (long)(key),			       \
15462306a36Sopenharmony_ci		       hashmap_cast_ptr(old_key),		       \
15562306a36Sopenharmony_ci		       hashmap_cast_ptr(old_value))
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cibool hashmap_find(const struct hashmap *map, long key, long *value);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci#define hashmap__find(map, key, value) \
16062306a36Sopenharmony_ci	hashmap_find((map), (long)(key), hashmap_cast_ptr(value))
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/*
16362306a36Sopenharmony_ci * hashmap__for_each_entry - iterate over all entries in hashmap
16462306a36Sopenharmony_ci * @map: hashmap to iterate
16562306a36Sopenharmony_ci * @cur: struct hashmap_entry * used as a loop cursor
16662306a36Sopenharmony_ci * @bkt: integer used as a bucket loop cursor
16762306a36Sopenharmony_ci */
16862306a36Sopenharmony_ci#define hashmap__for_each_entry(map, cur, bkt)				    \
16962306a36Sopenharmony_ci	for (bkt = 0; bkt < map->cap; bkt++)				    \
17062306a36Sopenharmony_ci		for (cur = map->buckets[bkt]; cur; cur = cur->next)
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci/*
17362306a36Sopenharmony_ci * hashmap__for_each_entry_safe - iterate over all entries in hashmap, safe
17462306a36Sopenharmony_ci * against removals
17562306a36Sopenharmony_ci * @map: hashmap to iterate
17662306a36Sopenharmony_ci * @cur: struct hashmap_entry * used as a loop cursor
17762306a36Sopenharmony_ci * @tmp: struct hashmap_entry * used as a temporary next cursor storage
17862306a36Sopenharmony_ci * @bkt: integer used as a bucket loop cursor
17962306a36Sopenharmony_ci */
18062306a36Sopenharmony_ci#define hashmap__for_each_entry_safe(map, cur, tmp, bkt)		    \
18162306a36Sopenharmony_ci	for (bkt = 0; bkt < map->cap; bkt++)				    \
18262306a36Sopenharmony_ci		for (cur = map->buckets[bkt];				    \
18362306a36Sopenharmony_ci		     cur && ({tmp = cur->next; true; });		    \
18462306a36Sopenharmony_ci		     cur = tmp)
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/*
18762306a36Sopenharmony_ci * hashmap__for_each_key_entry - iterate over entries associated with given key
18862306a36Sopenharmony_ci * @map: hashmap to iterate
18962306a36Sopenharmony_ci * @cur: struct hashmap_entry * used as a loop cursor
19062306a36Sopenharmony_ci * @key: key to iterate entries for
19162306a36Sopenharmony_ci */
19262306a36Sopenharmony_ci#define hashmap__for_each_key_entry(map, cur, _key)			    \
19362306a36Sopenharmony_ci	for (cur = map->buckets						    \
19462306a36Sopenharmony_ci		     ? map->buckets[hash_bits(map->hash_fn((_key), map->ctx), map->cap_bits)] \
19562306a36Sopenharmony_ci		     : NULL;						    \
19662306a36Sopenharmony_ci	     cur;							    \
19762306a36Sopenharmony_ci	     cur = cur->next)						    \
19862306a36Sopenharmony_ci		if (map->equal_fn(cur->key, (_key), map->ctx))
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci#define hashmap__for_each_key_entry_safe(map, cur, tmp, _key)		    \
20162306a36Sopenharmony_ci	for (cur = map->buckets						    \
20262306a36Sopenharmony_ci		     ? map->buckets[hash_bits(map->hash_fn((_key), map->ctx), map->cap_bits)] \
20362306a36Sopenharmony_ci		     : NULL;						    \
20462306a36Sopenharmony_ci	     cur && ({ tmp = cur->next; true; });			    \
20562306a36Sopenharmony_ci	     cur = tmp)							    \
20662306a36Sopenharmony_ci		if (map->equal_fn(cur->key, (_key), map->ctx))
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci#endif /* __LIBBPF_HASHMAP_H */
209