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