1 /*
2  * Copyright (c) 2022 Meta
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifndef ZEPHYR_INCLUDE_SYS_HASHMAP_API_H_
8 #define ZEPHYR_INCLUDE_SYS_HASHMAP_API_H_
9 
10 #include <stdbool.h>
11 #include <stddef.h>
12 #include <stdint.h>
13 
14 #include <zephyr/sys/hash_function.h>
15 #include <zephyr/sys/util.h>
16 
17 #ifdef __cplusplus
18 extern "C" {
19 #endif
20 
21 /**
22  * @file
23  * @defgroup hashmap_apis Hashmap
24  * @ingroup datastructure_apis
25  *
26  * @brief Hashmap (Hash Table) API
27  *
28  * Hashmaps (a.k.a Hash Tables) sacrifice space for speed. All operations
29  * on a Hashmap (insert, delete, search) are O(1) complexity (on average).
30  *
31  * @defgroup hashmap_implementations Hashmap Implementations
32  * @ingroup hashmap_apis
33  *
34  * @addtogroup hashmap_apis
35  * @{
36  */
37 
38 /**
39  * @brief Generic Hashmap iterator interface
40  *
41  * @note @a next should not be used without first checking
42  * @ref sys_hashmap_iterator_has_next
43  */
44 struct sys_hashmap_iterator {
45 	/** Pointer to the associated Hashmap */
46 	const struct sys_hashmap *map;
47 	/** Modify the iterator in-place to point to the next Hashmap entry */
48 	void (*next)(struct sys_hashmap_iterator *it);
49 	/** Implementation-specific iterator state */
50 	void *state;
51 	/** Key associated with the current entry */
52 	uint64_t key;
53 	/** Value associated with the current entry */
54 	uint64_t value;
55 	/** Number of entries in the map */
56 	const size_t size;
57 	/** Number of entries already iterated */
58 	size_t pos;
59 };
60 
61 /**
62  * @brief Check if a Hashmap iterator has a next entry
63  *
64  * @param it Hashmap iterator
65  * @return true if there is a next entry
66  * @return false if there is no next entry
67  */
sys_hashmap_iterator_has_next(const struct sys_hashmap_iterator * it)68 static inline bool sys_hashmap_iterator_has_next(const struct sys_hashmap_iterator *it)
69 {
70 	return it->pos < it->size;
71 }
72 
73 /**
74  * @brief Allocator interface for @ref sys_hashmap
75  *
76  * The Hashmap allocator can be any allocator that behaves similarly to `realloc()` with the
77  * additional specification that the allocator behaves like `free()` when @p new_size is zero.
78  *
79  * @param ptr Previously allocated memory region or `NULL` to make a new vallocation.
80  * @param new_size the new size of the allocation, in bytes.
81  *
82  * @see <a href="https://en.cppreference.com/w/c/memory/realloc">realloc</a>
83  */
84 typedef void *(*sys_hashmap_allocator_t)(void *ptr, size_t new_size);
85 
86 /**
87  * @brief In-place iterator constructor for @ref sys_hashmap
88  *
89  * Construct an iterator, @p it, for @p map.
90  *
91  * @param map Hashmap to iterate over.
92  * @param it Iterator to initialize.
93  */
94 typedef void (*sys_hashmap_iterator_t)(const struct sys_hashmap *map,
95 				       struct sys_hashmap_iterator *it);
96 
97 /**
98  * @brief Callback interface for @ref sys_hashmap
99  *
100  * This callback is used by some Hashmap methods.
101  *
102  * @param key Key corresponding to @p value
103  * @param value Value corresponding to @p key
104  * @param cookie User-specified variable
105  */
106 typedef void (*sys_hashmap_callback_t)(uint64_t key, uint64_t value, void *cookie);
107 
108 /**
109  * @brief Clear all entries contained in a @ref sys_hashmap
110  *
111  * @note If the values in a particular Hashmap are
112  *
113  * @param map Hashmap to clear
114  * @param cb Callback to call for each entry
115  * @param cookie User-specified variable
116  */
117 typedef void (*sys_hashmap_clear_t)(struct sys_hashmap *map, sys_hashmap_callback_t cb,
118 				    void *cookie);
119 
120 /**
121  * @brief Insert a new entry into a @ref sys_hashmap
122  *
123  * Insert a new @p key - @p value pair into @p map.
124  *
125  * @param map Hashmap to insert into
126  * @param key Key to associate with @p value
127  * @param value Value to associate with @p key
128  * @param old_value Location to store the value previously associated with @p key or `NULL`
129  * @retval 0 if @p value was inserted for an existing key, in which case @p old_value will contain
130  * the previous value
131  * @retval 1 if a new entry was inserted for the @p key - @p value pair
132  * @retval -ENOMEM if memory allocation failed
133  */
134 typedef int (*sys_hashmap_insert_t)(struct sys_hashmap *map, uint64_t key, uint64_t value,
135 				    uint64_t *old_value);
136 
137 /**
138  * @brief Remove an entry from a @ref sys_hashmap
139  *
140  * Erase the entry associated with key @p key, if one exists.
141  *
142  * @param map Hashmap to remove from
143  * @param key Key to remove from @p map
144  * @param value Location to store a potential value associated with @p key or `NULL`
145  *
146  * @retval true if @p map was modified as a result of this operation.
147  * @retval false if @p map does not contain a value associated with @p key.
148  */
149 typedef bool (*sys_hashmap_remove_t)(struct sys_hashmap *map, uint64_t key, uint64_t *value);
150 
151 /**
152  * @brief Get a value from a @ref sys_hashmap
153  *
154  * Look-up the @ref uint64_t associated with @p key, if one exists.
155  *
156  * @param map Hashmap to search through
157  * @param key Key with which to search @p map
158  * @param value Location to store a potential value associated with @p key or `NULL`
159  *
160  * @retval true if @p map contains a value associated with @p key.
161  * @retval false if @p map does not contain a value associated with @p key.
162  */
163 typedef bool (*sys_hashmap_get_t)(const struct sys_hashmap *map, uint64_t key, uint64_t *value);
164 
165 /**
166  * @brief Generic Hashmap API
167  */
168 struct sys_hashmap_api {
169 	/** Iterator constructor (in-place) */
170 	sys_hashmap_iterator_t iter;
171 	/** Clear the hash table, freeing all resources */
172 	sys_hashmap_clear_t clear;
173 	/** Insert a key-value pair into the Hashmap */
174 	sys_hashmap_insert_t insert;
175 	/** Remove a key-value pair from the Hashmap */
176 	sys_hashmap_remove_t remove;
177 	/** Retrieve the value associated with a given key from the Hashmap */
178 	sys_hashmap_get_t get;
179 };
180 
181 /**
182  * @brief Generic Hashmap configuration
183  *
184  * When there is a known limit imposed on the number of entries in the Hashmap,
185  * users should specify that via @a max_size. When the Hashmap should have
186  * no artificial limitation in size (and be bounded only by the provided
187  * allocator), users should specify `SIZE_MAX` here.
188  *
189  * The @a load_factor is defined as the size of the Hashmap divided by the
190  * number of buckets. In this case, the size of the Hashmap is defined as
191  * the number of valid entries plus the number of invalidated entries.
192  *
193  * The @a initial_n_buckets is defined as the number of buckets to allocate
194  * when moving from size 0 to size 1 such that the maximum @a load_factor
195  * property is preserved.
196  */
197 struct sys_hashmap_config {
198 	/** Maximum number of entries */
199 	size_t max_size;
200 	/** Maximum load factor expressed in hundredths */
201 	uint8_t load_factor;
202 	/** Initial number of buckets to allocate */
203 	uint8_t initial_n_buckets;
204 };
205 
206 /**
207  * @brief Initializer for @p sys_hashmap_config
208  *
209  * This macro helps to initialize a structure of type @p sys_hashmap_config.
210  *
211  * @param _max_size Maximum number of entries
212  * @param _load_factor Maximum load factor of expressed in hundredths
213  */
214 #define SYS_HASHMAP_CONFIG(_max_size, _load_factor)                                                \
215 	{                                                                                          \
216 		.max_size = (size_t)_max_size, .load_factor = (uint8_t)_load_factor,               \
217 		.initial_n_buckets = NHPOT(DIV_ROUND_UP(100, _load_factor)),                   \
218 	}
219 
220 /**
221  * @brief Generic Hashmap data
222  *
223  * @note When @a size is zero, @a buckets should be `NULL`.
224  */
225 struct sys_hashmap_data {
226 	/** Pointer for implementation-specific Hashmap storage */
227 	void *buckets;
228 	/** The number of buckets currently allocated */
229 	size_t n_buckets;
230 	/** The number of entries currently in the Hashmap */
231 	size_t size;
232 };
233 
234 /** @} */
235 
236 #ifdef __cplusplus
237 }
238 #endif
239 
240 #endif /* ZEPHYR_INCLUDE_SYS_HASHMAP_API_H_ */
241