1 /*
2 * Copyright (c) 2022 Meta
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @file
9 * @addtogroup hashmap_apis
10 * @{
11 */
12
13 #ifndef ZEPHYR_INCLUDE_SYS_HASH_MAP_H_
14 #define ZEPHYR_INCLUDE_SYS_HASH_MAP_H_
15
16 #include <stdbool.h>
17 #include <stddef.h>
18 #include <stdint.h>
19
20 #include <zephyr/kernel.h>
21 #include <zephyr/sys/hash_map_api.h>
22 #include <zephyr/sys/hash_map_cxx.h>
23 #include <zephyr/sys/hash_map_oa_lp.h>
24 #include <zephyr/sys/hash_map_sc.h>
25
26 #ifdef __cplusplus
27 extern "C" {
28 #endif
29
30 /**
31 * @brief Declare a Hashmap (advanced)
32 *
33 * Declare a Hashmap with control over advanced parameters.
34 *
35 * @note The allocator @p _alloc is used for allocating internal Hashmap
36 * entries and does not interact with any user-provided keys or values.
37 *
38 * @param _name Name of the Hashmap.
39 * @param _api API pointer of type @ref sys_hashmap_api.
40 * @param _config_type Variant of @ref sys_hashmap_config.
41 * @param _data_type Variant of @ref sys_hashmap_data.
42 * @param _hash_func Hash function pointer of type @ref sys_hash_func32_t.
43 * @param _alloc_func Allocator function pointer of type @ref sys_hashmap_allocator_t.
44 * @param ... Variant-specific details for @p _config_type.
45 */
46 #define SYS_HASHMAP_DEFINE_ADVANCED(_name, _api, _config_type, _data_type, _hash_func, \
47 _alloc_func, ...) \
48 const struct _config_type _name##_config = __VA_ARGS__; \
49 struct _data_type _name##_data; \
50 struct sys_hashmap _name = { \
51 .api = (const struct sys_hashmap_api *)(_api), \
52 .config = (const struct sys_hashmap_config *)&_name##_config, \
53 .data = (struct sys_hashmap_data *)&_name##_data, \
54 .hash_func = (_hash_func), \
55 .alloc_func = (_alloc_func), \
56 }
57
58 /**
59 * @brief Declare a Hashmap statically (advanced)
60 *
61 * Declare a Hashmap statically with control over advanced parameters.
62 *
63 * @note The allocator @p _alloc is used for allocating internal Hashmap
64 * entries and does not interact with any user-provided keys or values.
65 *
66 * @param _name Name of the Hashmap.
67 * @param _api API pointer of type @ref sys_hashmap_api.
68 * @param _config_type Variant of @ref sys_hashmap_config.
69 * @param _data_type Variant of @ref sys_hashmap_data.
70 * @param _hash_func Hash function pointer of type @ref sys_hash_func32_t.
71 * @param _alloc_func Allocator function pointer of type @ref sys_hashmap_allocator_t.
72 * @param ... Variant-specific details for @p _config_type.
73 */
74 #define SYS_HASHMAP_DEFINE_STATIC_ADVANCED(_name, _api, _config_type, _data_type, _hash_func, \
75 _alloc_func, ...) \
76 static const struct _config_type _name##_config = __VA_ARGS__; \
77 static struct _data_type _name##_data; \
78 static struct sys_hashmap _name = { \
79 .api = (const struct sys_hashmap_api *)(_api), \
80 .config = (const struct sys_hashmap_config *)&_name##_config, \
81 .data = (struct sys_hashmap_data *)&_name##_data, \
82 .hash_func = (_hash_func), \
83 .alloc_func = (_alloc_func), \
84 }
85
86 /**
87 * @brief Declare a Hashmap
88 *
89 * Declare a Hashmap with default parameters.
90 *
91 * @param _name Name of the Hashmap.
92 */
93 #define SYS_HASHMAP_DEFINE(_name) SYS_HASHMAP_DEFAULT_DEFINE(_name)
94
95 /**
96 * @brief Declare a Hashmap statically
97 *
98 * Declare a Hashmap statically with default parameters.
99 *
100 * @param _name Name of the Hashmap.
101 */
102 #define SYS_HASHMAP_DEFINE_STATIC(_name) SYS_HASHMAP_DEFAULT_DEFINE_STATIC(_name)
103
104 /*
105 * A safe wrapper for realloc(), invariant of which libc provides it.
106 */
sys_hashmap_default_allocator(void * ptr,size_t size)107 static inline void *sys_hashmap_default_allocator(void *ptr, size_t size)
108 {
109 if (size == 0) {
110 free(ptr);
111 return NULL;
112 }
113
114 return realloc(ptr, size);
115 }
116
117 /** @brief The default Hashmap allocator */
118 #define SYS_HASHMAP_DEFAULT_ALLOCATOR sys_hashmap_default_allocator
119
120 /** @brief The default Hashmap load factor (in hundredths) */
121 #define SYS_HASHMAP_DEFAULT_LOAD_FACTOR 75
122
123 /** @brief Generic Hashmap */
124 struct sys_hashmap {
125 /** Hashmap API */
126 const struct sys_hashmap_api *api;
127 /** Hashmap configuration */
128 const struct sys_hashmap_config *config;
129 /** Hashmap data */
130 struct sys_hashmap_data *data;
131 /** Hash function */
132 sys_hash_func32_t hash_func;
133 /** Allocator */
134 sys_hashmap_allocator_t alloc_func;
135 };
136
137 /**
138 * @brief Iterate over all values contained in a @ref sys_hashmap
139 *
140 * @param map Hashmap to iterate over
141 * @param cb Callback to call for each entry
142 * @param cookie User-specified variable
143 */
sys_hashmap_foreach(const struct sys_hashmap * map,sys_hashmap_callback_t cb,void * cookie)144 static inline void sys_hashmap_foreach(const struct sys_hashmap *map, sys_hashmap_callback_t cb,
145 void *cookie)
146 {
147 struct sys_hashmap_iterator it = {0};
148
149 for (map->api->iter(map, &it); sys_hashmap_iterator_has_next(&it);) {
150 it.next(&it);
151 cb(it.key, it.value, cookie);
152 }
153 }
154
155 /**
156 * @brief Clear all entries contained in a @ref sys_hashmap
157 *
158 * @note If the values in a particular Hashmap are
159 *
160 * @param map Hashmap to clear
161 * @param cb Callback to call for each entry
162 * @param cookie User-specified variable
163 */
sys_hashmap_clear(struct sys_hashmap * map,sys_hashmap_callback_t cb,void * cookie)164 static inline void sys_hashmap_clear(struct sys_hashmap *map, sys_hashmap_callback_t cb,
165 void *cookie)
166 {
167 map->api->clear(map, cb, cookie);
168 }
169
170 /**
171 * @brief Insert a new entry into a @ref sys_hashmap
172 *
173 * Insert a new @p key - @p value pair into @p map.
174 *
175 * @param map Hashmap to insert into
176 * @param key Key to associate with @p value
177 * @param value Value to associate with @p key
178 * @param old_value Location to store the value previously associated with @p key or `NULL`
179 * @retval 0 if @p value was inserted for an existing key, in which case @p old_value will contain
180 * the previous value
181 * @retval 1 if a new entry was inserted for the @p key - @p value pair
182 * @retval -ENOMEM if memory allocation failed
183 * @retval -ENOSPC if the size limit has been reached
184 */
sys_hashmap_insert(struct sys_hashmap * map,uint64_t key,uint64_t value,uint64_t * old_value)185 static inline int sys_hashmap_insert(struct sys_hashmap *map, uint64_t key, uint64_t value,
186 uint64_t *old_value)
187 {
188 return map->api->insert(map, key, value, old_value);
189 }
190
191 /**
192 * @brief Remove an entry from a @ref sys_hashmap
193 *
194 * Erase the entry associated with key @p key, if one exists.
195 *
196 * @param map Hashmap to remove from
197 * @param key Key to remove from @p map
198 * @param value Location to store a potential value associated with @p key or `NULL`
199 *
200 * @retval true if @p map was modified as a result of this operation.
201 * @retval false if @p map does not contain a value associated with @p key.
202 */
sys_hashmap_remove(struct sys_hashmap * map,uint64_t key,uint64_t * value)203 static inline bool sys_hashmap_remove(struct sys_hashmap *map, uint64_t key, uint64_t *value)
204 {
205 return map->api->remove(map, key, value);
206 }
207
208 /**
209 * @brief Get a value from a @ref sys_hashmap
210 *
211 * Look-up the @ref uint64_t associated with @p key, if one exists.
212 *
213 * @param map Hashmap to search through
214 * @param key Key with which to search @p map
215 * @param value Location to store a potential value associated with @p key or `NULL`
216 *
217 * @retval true if @p map contains a value associated with @p key.
218 * @retval false if @p map does not contain a value associated with @p key.
219 */
sys_hashmap_get(const struct sys_hashmap * map,uint64_t key,uint64_t * value)220 static inline bool sys_hashmap_get(const struct sys_hashmap *map, uint64_t key, uint64_t *value)
221 {
222 return map->api->get(map, key, value);
223 }
224
225 /**
226 * @brief Check if @p map contains a value associated with @p key
227 *
228 * @param map Hashmap to search through
229 * @param key Key with which to search @p map
230 *
231 * @retval true if @p map contains a value associated with @p key.
232 * @retval false if @p map does not contain a value associated with @p key.
233 */
sys_hashmap_contains_key(const struct sys_hashmap * map,uint64_t key)234 static inline bool sys_hashmap_contains_key(const struct sys_hashmap *map, uint64_t key)
235 {
236 return sys_hashmap_get(map, key, NULL);
237 }
238
239 /**
240 * @brief Query the number of entries contained within @p map
241 *
242 * @param map Hashmap to search through
243 *
244 * @return the number of entries contained within @p map.
245 */
sys_hashmap_size(const struct sys_hashmap * map)246 static inline size_t sys_hashmap_size(const struct sys_hashmap *map)
247 {
248 return map->data->size;
249 }
250
251 /**
252 * @brief Check if @p map is empty
253 *
254 * @param map Hashmap to query
255 *
256 * @retval true if @p map is empty.
257 * @retval false if @p map is not empty.
258 */
sys_hashmap_is_empty(const struct sys_hashmap * map)259 static inline bool sys_hashmap_is_empty(const struct sys_hashmap *map)
260 {
261 return map->data->size == 0;
262 }
263
264 /**
265 * @brief Query the load factor of @p map
266 *
267 * @note To convert the load factor to a floating-point value use
268 * `sys_hash_load_factor(map) / 100.0f`.
269 *
270 * @param map Hashmap to query
271 *
272 * @return Load factor of @p map expressed in hundredths.
273 */
sys_hashmap_load_factor(const struct sys_hashmap * map)274 static inline uint8_t sys_hashmap_load_factor(const struct sys_hashmap *map)
275 {
276 if (map->data->n_buckets == 0) {
277 return 0;
278 }
279
280 return (map->data->size * 100) / map->data->n_buckets;
281 }
282
283 /**
284 * @brief Query the number of buckets used in @p map
285 *
286 * @param map Hashmap to query
287 * @return Number of buckets used in @p map
288 */
sys_hashmap_num_buckets(const struct sys_hashmap * map)289 static inline size_t sys_hashmap_num_buckets(const struct sys_hashmap *map)
290 {
291 return map->data->n_buckets;
292 }
293
294 /**
295 * @brief Decide whether the Hashmap should be resized
296 *
297 * This is a simple opportunistic method that implementations
298 * can choose to use. It will grow and shrink the Hashmap by a factor
299 * of 2 when insertion / removal would exceed / fall into the specified
300 * load factor.
301 *
302 * @note Users should call this prior to inserting a new key-value pair and after removing a
303 * key-value pair.
304 *
305 * @note The number of reserved entries is implementation-defined, but it is only considered
306 * as part of the load factor when growing the hash table.
307 *
308 * @param map Hashmap to examine
309 * @param grow true if an entry is to be added. false if an entry has been removed
310 * @param num_reserved the number of reserved entries
311 * @param[out] new_num_buckets variable Hashmap size
312 * @return true if the Hashmap should be rehashed
313 * @return false if the Hashmap should not be rehashed
314 */
sys_hashmap_should_rehash(const struct sys_hashmap * map,bool grow,size_t num_reserved,size_t * new_num_buckets)315 static inline bool sys_hashmap_should_rehash(const struct sys_hashmap *map, bool grow,
316 size_t num_reserved, size_t *new_num_buckets)
317 {
318 size_t size;
319 bool should_grow;
320 size_t n_buckets;
321 bool should_shrink;
322 const bool shrink = !grow;
323 struct sys_hashmap_oa_lp_data *const data = (struct sys_hashmap_oa_lp_data *)map->data;
324 const struct sys_hashmap_config *const config = map->config;
325
326 /* All branchless calculations, so very cache-friendly */
327
328 /* calculate new size */
329 size = data->size;
330 size += grow;
331 /* maximum size imposed by the implementation */
332 __ASSERT_NO_MSG(size < SIZE_MAX / 100);
333
334 /* calculate new number of buckets */
335 n_buckets = data->n_buckets;
336 /* initial number of buckets */
337 n_buckets += grow * (size == 1) * config->initial_n_buckets;
338 /* grow at a rate of 2x */
339 n_buckets <<= grow * (size != 1);
340 /* shrink at a rate of 2x */
341 n_buckets >>= shrink;
342
343 /* shrink to zero if empty */
344 n_buckets *= (size != 0);
345
346 __ASSERT_NO_MSG(new_num_buckets != NULL);
347 __ASSERT_NO_MSG(new_num_buckets != &data->n_buckets);
348 *new_num_buckets = n_buckets;
349
350 should_grow =
351 grow && (data->n_buckets == 0 ||
352 (size + num_reserved) * 100 / data->n_buckets > map->config->load_factor);
353 should_shrink =
354 shrink && (n_buckets == 0 || (size * 100) / n_buckets <= map->config->load_factor);
355
356 return should_grow || should_shrink;
357 }
358
359 /** @} */
360
361 #ifdef __cplusplus
362 }
363 #endif
364
365 #endif /* ZEPHYR_INCLUDE_SYS_HASH_MAP_H_ */
366