blob: cdd2163cad628859764e75436aa81ad384f31a35 [file] [log] [blame]
Radek Krejci5aeea3a2018-09-05 13:29:36 +02001/**
2 * @file hash_table.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
Michal Vaskoae130f52023-04-20 14:25:16 +02004 * @author Michal Vasko <mvasko@cesnet.cz>
5 * @brief libyang generic hash table implementation
Radek Krejci5aeea3a2018-09-05 13:29:36 +02006 *
Michal Vaskoae130f52023-04-20 14:25:16 +02007 * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
Radek Krejci5aeea3a2018-09-05 13:29:36 +02008 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
Radek Krejci535ea9f2020-05-29 16:01:05 +020016#include "hash_table.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020017
Michal Vasko69730152020-10-09 16:30:07 +020018#include <assert.h>
19#include <pthread.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020020#include <stdint.h>
21#include <stdlib.h>
Michal Vasko69730152020-10-09 16:30:07 +020022#include <string.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020023
Radek Krejci535ea9f2020-05-29 16:01:05 +020024#include "common.h"
Michal Vasko69730152020-10-09 16:30:07 +020025#include "compat.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020026#include "dict.h"
Radek Krejci857189e2020-09-01 13:26:36 +020027#include "log.h"
Radek Krejci5aeea3a2018-09-05 13:29:36 +020028
Michal Vaskoae130f52023-04-20 14:25:16 +020029LIBYANG_API_DEF uint32_t
30lyht_hash_multi(uint32_t hash, const char *key_part, size_t len)
Radek Krejci5aeea3a2018-09-05 13:29:36 +020031{
32 uint32_t i;
33
aPiecek4f07c3e2021-06-11 10:53:07 +020034 if (key_part && len) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +020035 for (i = 0; i < len; ++i) {
36 hash += key_part[i];
37 hash += (hash << 10);
38 hash ^= (hash >> 6);
39 }
40 } else {
41 hash += (hash << 3);
42 hash ^= (hash >> 11);
43 hash += (hash << 15);
44 }
45
46 return hash;
47}
48
Michal Vaskoae130f52023-04-20 14:25:16 +020049LIBYANG_API_DEF uint32_t
50lyht_hash(const char *key, size_t len)
Radek Krejcif2dc4c52018-11-08 09:04:13 +010051{
52 uint32_t hash;
53
Michal Vaskoae130f52023-04-20 14:25:16 +020054 hash = lyht_hash_multi(0, key, len);
55 return lyht_hash_multi(hash, NULL, len);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020056}
57
Olivier Matz75c00192023-09-21 14:35:12 +020058static LY_ERR
59lyht_init_hlists_and_records(struct ly_ht *ht)
Radek Krejci5aeea3a2018-09-05 13:29:36 +020060{
Olivier Matz75c00192023-09-21 14:35:12 +020061 struct ly_ht_rec *rec;
62 uint32_t i;
63
64 ht->recs = calloc(ht->size, ht->rec_size);
65 LY_CHECK_ERR_RET(!ht->recs, LOGMEM(NULL), LY_EMEM);
66 for (i = 0; i < ht->size; i++) {
67 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
68 if (i != ht->size)
69 rec->next = i + 1;
70 else
71 rec->next = LYHT_NO_RECORD;
72 }
73
74 ht->hlists = malloc(sizeof(ht->hlists[0]) * ht->size);
75 LY_CHECK_ERR_RET(!ht->hlists, free(ht->recs); LOGMEM(NULL), LY_EMEM);
76 for (i = 0; i < ht->size; i++)
77 ht->hlists[i] = LYHT_NO_RECORD;
78 ht->first_free_rec = 0;
79
80 return LY_SUCCESS;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020081}
82
Michal Vaskoae130f52023-04-20 14:25:16 +020083LIBYANG_API_DEF struct ly_ht *
Michal Vasko62524a92021-02-26 10:08:50 +010084lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *cb_data, uint16_t resize)
Radek Krejci5aeea3a2018-09-05 13:29:36 +020085{
Michal Vasko8efac242023-03-30 08:24:56 +020086 struct ly_ht *ht;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020087
88 /* check that 2^x == size (power of 2) */
89 assert(size && !(size & (size - 1)));
90 assert(val_equal && val_size);
91 assert(resize == 0 || resize == 1);
92
93 if (size < LYHT_MIN_SIZE) {
94 size = LYHT_MIN_SIZE;
95 }
96
97 ht = malloc(sizeof *ht);
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020098 LY_CHECK_ERR_RET(!ht, LOGMEM(NULL), NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020099
100 ht->used = 0;
101 ht->size = size;
102 ht->val_equal = val_equal;
103 ht->cb_data = cb_data;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200104 ht->resize = resize;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200105
Olivier Matz75c00192023-09-21 14:35:12 +0200106 ht->rec_size = SIZEOF_LY_HT_REC + val_size;
107 if (lyht_init_hlists_and_records(ht) != LY_SUCCESS) {
108 free(ht);
109 return NULL;
110 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200111
112 return ht;
113}
114
Michal Vaskoae130f52023-04-20 14:25:16 +0200115LIBYANG_API_DEF lyht_value_equal_cb
Michal Vasko8efac242023-03-30 08:24:56 +0200116lyht_set_cb(struct ly_ht *ht, lyht_value_equal_cb new_val_equal)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200117{
Michal Vasko62524a92021-02-26 10:08:50 +0100118 lyht_value_equal_cb prev;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200119
120 prev = ht->val_equal;
121 ht->val_equal = new_val_equal;
122 return prev;
123}
124
Michal Vaskoae130f52023-04-20 14:25:16 +0200125LIBYANG_API_DEF void *
Michal Vasko8efac242023-03-30 08:24:56 +0200126lyht_set_cb_data(struct ly_ht *ht, void *new_cb_data)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200127{
128 void *prev;
129
130 prev = ht->cb_data;
131 ht->cb_data = new_cb_data;
132 return prev;
133}
134
Michal Vaskoae130f52023-04-20 14:25:16 +0200135LIBYANG_API_DEF struct ly_ht *
Michal Vasko8efac242023-03-30 08:24:56 +0200136lyht_dup(const struct ly_ht *orig)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200137{
Michal Vasko8efac242023-03-30 08:24:56 +0200138 struct ly_ht *ht;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200139
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200140 LY_CHECK_ARG_RET(NULL, orig, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200141
Olivier Matz75c00192023-09-21 14:35:12 +0200142 ht = lyht_new(orig->size, orig->rec_size - SIZEOF_LY_HT_REC, orig->val_equal, orig->cb_data, orig->resize ? 1 : 0);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200143 if (!ht) {
144 return NULL;
145 }
146
Olivier Matz75c00192023-09-21 14:35:12 +0200147 memcpy(ht->hlists, orig->hlists, sizeof(ht->hlists[0]) * orig->size);
148 memcpy(ht->recs, orig->recs, (size_t)orig->size * orig->rec_size);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200149 ht->used = orig->used;
150 return ht;
151}
152
Michal Vaskoae130f52023-04-20 14:25:16 +0200153LIBYANG_API_DEF void
Michal Vasko8efac242023-03-30 08:24:56 +0200154lyht_free(struct ly_ht *ht, void (*val_free)(void *val_p))
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200155{
Michal Vasko8efac242023-03-30 08:24:56 +0200156 struct ly_ht_rec *rec;
Olivier Matz75c00192023-09-21 14:35:12 +0200157 uint32_t hlist_idx;
158 uint32_t rec_idx;
Michal Vasko77b7f90a2023-01-31 15:42:41 +0100159
160 if (!ht) {
161 return;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200162 }
Michal Vasko77b7f90a2023-01-31 15:42:41 +0100163
164 if (val_free) {
Olivier Matz75c00192023-09-21 14:35:12 +0200165 LYHT_ITER_ALL_RECS(ht, hlist_idx, rec_idx, rec)
166 val_free(&rec->val);
Michal Vasko77b7f90a2023-01-31 15:42:41 +0100167 }
Olivier Matz75c00192023-09-21 14:35:12 +0200168 free(ht->hlists);
Michal Vasko77b7f90a2023-01-31 15:42:41 +0100169 free(ht->recs);
170 free(ht);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200171}
172
Michal Vaskodc95d9c2021-04-12 15:11:48 +0200173/**
174 * @brief Resize a hash table.
175 *
176 * @param[in] ht Hash table to resize.
177 * @param[in] operation Operation to perform. 1 to enlarge, -1 to shrink, 0 to only rehash all records.
178 * @return LY_ERR value.
179 */
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200180static LY_ERR
Michal Vasko8efac242023-03-30 08:24:56 +0200181lyht_resize(struct ly_ht *ht, int operation)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200182{
Michal Vasko8efac242023-03-30 08:24:56 +0200183 struct ly_ht_rec *rec;
Olivier Matz75c00192023-09-21 14:35:12 +0200184 uint32_t *old_hlists;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200185 unsigned char *old_recs;
Olivier Matz75c00192023-09-21 14:35:12 +0200186 uint32_t old_first_free_rec;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200187 uint32_t i, old_size;
Olivier Matz75c00192023-09-21 14:35:12 +0200188 uint32_t rec_idx;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200189
Olivier Matz75c00192023-09-21 14:35:12 +0200190 old_hlists = ht->hlists;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200191 old_recs = ht->recs;
192 old_size = ht->size;
Olivier Matz75c00192023-09-21 14:35:12 +0200193 old_first_free_rec = ht->first_free_rec;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200194
Michal Vaskodc95d9c2021-04-12 15:11:48 +0200195 if (operation > 0) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200196 /* double the size */
197 ht->size <<= 1;
Michal Vaskodc95d9c2021-04-12 15:11:48 +0200198 } else if (operation < 0) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200199 /* half the size */
200 ht->size >>= 1;
201 }
202
Olivier Matz75c00192023-09-21 14:35:12 +0200203 if (lyht_init_hlists_and_records(ht) != LY_SUCCESS) {
204 ht->hlists = old_hlists;
205 ht->recs = old_recs;
206 ht->size = old_size;
207 ht->first_free_rec = old_first_free_rec;
208 return LY_EMEM;
209 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200210
Olivier Matz75c00192023-09-21 14:35:12 +0200211 /* reset used, it will increase again */
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200212 ht->used = 0;
213
214 /* add all the old records into the new records array */
Olivier Matz75c00192023-09-21 14:35:12 +0200215 for (i = 0; i < old_size; i++) {
216 for (rec_idx = old_hlists[i], rec = lyht_get_rec(old_recs, ht->rec_size, rec_idx);
217 rec_idx != LYHT_NO_RECORD;
218 rec_idx = rec->next, rec = lyht_get_rec(old_recs, ht->rec_size, rec_idx)) {
219 LY_ERR ret;
220
221 ret = lyht_insert(ht, rec->val, rec->hash, NULL);
Michal Vasko26bbb272022-08-02 14:54:33 +0200222
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200223 assert(!ret);
224 (void)ret;
225 }
226 }
227
228 /* final touches */
229 free(old_recs);
Olivier Matz75c00192023-09-21 14:35:12 +0200230 free(old_hlists);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200231 return LY_SUCCESS;
232}
233
Michal Vaskoda859032020-07-14 12:20:14 +0200234/**
Michal Vaskoda859032020-07-14 12:20:14 +0200235 * @brief Search for a record with specific value and hash.
236 *
237 * @param[in] ht Hash table to search in.
238 * @param[in] val_p Pointer to the value to find.
239 * @param[in] hash Hash to find.
Radek Krejci857189e2020-09-01 13:26:36 +0200240 * @param[in] mod Whether the operation modifies the hash table (insert or remove) or not (find).
Michal Vaskoda859032020-07-14 12:20:14 +0200241 * @param[out] crec_p Optional found first record.
242 * @param[out] col Optional collision number of @p rec_p, 0 for no collision.
243 * @param[out] rec_p Found exact matching record, may be a collision of @p crec_p.
244 * @return LY_ENOTFOUND if no record found,
245 * @return LY_SUCCESS if record was found.
246 */
247static LY_ERR
Michal Vasko8efac242023-03-30 08:24:56 +0200248lyht_find_rec(struct ly_ht *ht, void *val_p, uint32_t hash, ly_bool mod, struct ly_ht_rec **crec_p, uint32_t *col,
249 struct ly_ht_rec **rec_p)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200250{
Olivier Matz75c00192023-09-21 14:35:12 +0200251 uint32_t hlist_idx = hash & (ht->size - 1);
252 struct ly_ht_rec *rec;
253 uint32_t rec_idx;
Michal Vaskoda859032020-07-14 12:20:14 +0200254
255 if (crec_p) {
256 *crec_p = NULL;
257 }
258 if (col) {
259 *col = 0;
260 }
261 *rec_p = NULL;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200262
Olivier Matz75c00192023-09-21 14:35:12 +0200263 LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec) {
Michal Vaskoda859032020-07-14 12:20:14 +0200264 if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, mod, ht->cb_data)) {
265 if (crec_p) {
Olivier Matz75c00192023-09-21 14:35:12 +0200266 *crec_p = rec;
Michal Vaskoda859032020-07-14 12:20:14 +0200267 }
268 *rec_p = rec;
269 return LY_SUCCESS;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200270 }
Olivier Matz75c00192023-09-21 14:35:12 +0200271
272 if (col) {
273 *col = *col + 1;
274 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200275 }
276
277 /* not found even in collisions */
Michal Vaskoda859032020-07-14 12:20:14 +0200278 return LY_ENOTFOUND;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200279}
280
Michal Vaskoae130f52023-04-20 14:25:16 +0200281LIBYANG_API_DEF LY_ERR
Michal Vasko8efac242023-03-30 08:24:56 +0200282lyht_find(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
Michal Vaskoda859032020-07-14 12:20:14 +0200283{
Michal Vasko8efac242023-03-30 08:24:56 +0200284 struct ly_ht_rec *rec;
Michal Vaskoda859032020-07-14 12:20:14 +0200285
286 lyht_find_rec(ht, val_p, hash, 0, NULL, NULL, &rec);
287
288 if (rec && match_p) {
289 *match_p = rec->val;
290 }
291 return rec ? LY_SUCCESS : LY_ENOTFOUND;
292}
293
Michal Vaskoae130f52023-04-20 14:25:16 +0200294LIBYANG_API_DEF LY_ERR
Michal Vasko8efac242023-03-30 08:24:56 +0200295lyht_find_next_with_collision_cb(struct ly_ht *ht, void *val_p, uint32_t hash,
Michal Vasko6374de22022-09-05 15:48:48 +0200296 lyht_value_equal_cb collision_val_equal, void **match_p)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200297{
Michal Vasko8efac242023-03-30 08:24:56 +0200298 struct ly_ht_rec *rec, *crec;
Olivier Matz75c00192023-09-21 14:35:12 +0200299 uint32_t rec_idx;
300 uint32_t i;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200301
Michal Vasko6374de22022-09-05 15:48:48 +0200302 /* find the record of the previously found value */
Michal Vaskoda859032020-07-14 12:20:14 +0200303 if (lyht_find_rec(ht, val_p, hash, 1, &crec, &i, &rec)) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200304 /* not found, cannot happen */
Michal Vaskoda859032020-07-14 12:20:14 +0200305 LOGINT_RET(NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200306 }
307
Olivier Matz75c00192023-09-21 14:35:12 +0200308 for (rec_idx = rec->next, rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx);
309 rec_idx != LYHT_NO_RECORD;
310 rec_idx = rec->next, rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx)) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200311
Michal Vasko6374de22022-09-05 15:48:48 +0200312 if (rec->hash != hash) {
313 continue;
314 }
315
316 if (collision_val_equal) {
317 if (collision_val_equal(val_p, &rec->val, 0, ht->cb_data)) {
318 /* even the value matches */
319 if (match_p) {
320 *match_p = rec->val;
321 }
322 return LY_SUCCESS;
323 }
324 } else if (ht->val_equal(val_p, &rec->val, 0, ht->cb_data)) {
Michal Vaskoda859032020-07-14 12:20:14 +0200325 /* even the value matches */
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200326 if (match_p) {
327 *match_p = rec->val;
328 }
Michal Vaskoda859032020-07-14 12:20:14 +0200329 return LY_SUCCESS;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200330 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200331 }
332
333 /* the last equal value was already returned */
Michal Vaskoda859032020-07-14 12:20:14 +0200334 return LY_ENOTFOUND;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200335}
336
Michal Vaskoae130f52023-04-20 14:25:16 +0200337LIBYANG_API_DEF LY_ERR
Michal Vasko8efac242023-03-30 08:24:56 +0200338lyht_find_next(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
Michal Vasko6374de22022-09-05 15:48:48 +0200339{
340 return lyht_find_next_with_collision_cb(ht, val_p, hash, NULL, match_p);
341}
342
Olivier Matz75c00192023-09-21 14:35:12 +0200343static LY_ERR
344__lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
Michal Vasko62524a92021-02-26 10:08:50 +0100345 void **match_p)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200346{
Olivier Matz75c00192023-09-21 14:35:12 +0200347 uint32_t hlist_idx = hash & (ht->size - 1);
Radek Krejci1deb5be2020-08-26 16:43:36 +0200348 LY_ERR r, ret = LY_SUCCESS;
Olivier Matz75c00192023-09-21 14:35:12 +0200349 struct ly_ht_rec *rec;
Michal Vasko62524a92021-02-26 10:08:50 +0100350 lyht_value_equal_cb old_val_equal = NULL;
Olivier Matz75c00192023-09-21 14:35:12 +0200351 uint32_t rec_idx;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200352
Olivier Matz75c00192023-09-21 14:35:12 +0200353 if (lyht_find_rec(ht, val_p, hash, 1, NULL, NULL, &rec) == LY_SUCCESS) {
354 if (rec && match_p) {
355 *match_p = rec->val;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200356 }
Olivier Matz75c00192023-09-21 14:35:12 +0200357 return LY_EEXIST;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200358 }
359
Olivier Matz75c00192023-09-21 14:35:12 +0200360 rec_idx = ht->first_free_rec;
361 assert(rec_idx < ht->size);
362 rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx);
363 ht->first_free_rec = rec->next;
364 rec->next = ht->hlists[hlist_idx];
365 ht->hlists[hlist_idx] = rec_idx;
366
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200367 rec->hash = hash;
Olivier Matz75c00192023-09-21 14:35:12 +0200368 memcpy(&rec->val, val_p, ht->rec_size - SIZEOF_LY_HT_REC);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200369 if (match_p) {
370 *match_p = (void *)&rec->val;
371 }
372
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200373 /* check size & enlarge if needed */
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200374 ++ht->used;
375 if (ht->resize) {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100376 r = (ht->used * LYHT_HUNDRED_PERCENTAGE) / ht->size;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200377 if ((ht->resize == 1) && (r >= LYHT_FIRST_SHRINK_PERCENTAGE)) {
378 /* enable shrinking */
379 ht->resize = 2;
380 }
381 if ((ht->resize == 2) && (r >= LYHT_ENLARGE_PERCENTAGE)) {
382 if (resize_val_equal) {
383 old_val_equal = lyht_set_cb(ht, resize_val_equal);
384 }
385
386 /* enlarge */
387 ret = lyht_resize(ht, 1);
388 /* if hash_table was resized, we need to find new matching value */
Michal Vasko69730152020-10-09 16:30:07 +0200389 if ((ret == LY_SUCCESS) && match_p) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200390 lyht_find(ht, val_p, hash, match_p);
391 }
392
393 if (resize_val_equal) {
394 lyht_set_cb(ht, old_val_equal);
395 }
396 }
397 }
398 return ret;
399}
400
Michal Vaskoae130f52023-04-20 14:25:16 +0200401LIBYANG_API_DEF LY_ERR
Olivier Matz75c00192023-09-21 14:35:12 +0200402lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
403 void **match_p)
404{
405 return __lyht_insert_with_resize_cb(ht, val_p, hash, resize_val_equal, match_p);
406}
407
408LIBYANG_API_DEF LY_ERR
Michal Vasko8efac242023-03-30 08:24:56 +0200409lyht_insert(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200410{
Olivier Matz75c00192023-09-21 14:35:12 +0200411 return __lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200412}
413
Michal Vaskoae130f52023-04-20 14:25:16 +0200414LIBYANG_API_DEF LY_ERR
Michal Vasko8efac242023-03-30 08:24:56 +0200415lyht_remove_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200416{
Olivier Matz75c00192023-09-21 14:35:12 +0200417 struct ly_ht_rec *found_rec, *prev_rec, *rec;
418 uint32_t hlist_idx = hash & (ht->size - 1);
Radek Krejci1deb5be2020-08-26 16:43:36 +0200419 LY_ERR r, ret = LY_SUCCESS;
stewegd8e2fc92023-05-31 09:52:56 +0200420 lyht_value_equal_cb old_val_equal = NULL;
Olivier Matz75c00192023-09-21 14:35:12 +0200421 uint32_t prev_rec_idx;
422 uint32_t rec_idx;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200423
Olivier Matz75c00192023-09-21 14:35:12 +0200424 LY_CHECK_ERR_RET(lyht_find_rec(ht, val_p, hash, 1, NULL, NULL, &found_rec),
425 LOGARG(NULL, hash), LY_ENOTFOUND); /* hash not found */
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200426
Olivier Matz75c00192023-09-21 14:35:12 +0200427 prev_rec_idx = LYHT_NO_RECORD;
428 LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec) {
429 if (rec == found_rec)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200430 break;
Olivier Matz75c00192023-09-21 14:35:12 +0200431 prev_rec_idx = rec_idx;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200432 }
433
Olivier Matz75c00192023-09-21 14:35:12 +0200434 if (prev_rec_idx == LYHT_NO_RECORD) {
435 ht->hlists[hlist_idx] = rec->next;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200436 } else {
Olivier Matz75c00192023-09-21 14:35:12 +0200437 prev_rec = lyht_get_rec(ht->recs, ht->rec_size, prev_rec_idx);
438 prev_rec->next = rec->next;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200439 }
440
Olivier Matz75c00192023-09-21 14:35:12 +0200441 rec->next = ht->first_free_rec;
442 ht->first_free_rec = rec_idx;
443
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200444 /* check size & shrink if needed */
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200445 --ht->used;
446 if (ht->resize == 2) {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100447 r = (ht->used * LYHT_HUNDRED_PERCENTAGE) / ht->size;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200448 if ((r < LYHT_SHRINK_PERCENTAGE) && (ht->size > LYHT_MIN_SIZE)) {
Michal Vasko5bcc33b2020-10-06 15:33:44 +0200449 if (resize_val_equal) {
450 old_val_equal = lyht_set_cb(ht, resize_val_equal);
451 }
452
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200453 /* shrink */
Michal Vaskodc95d9c2021-04-12 15:11:48 +0200454 ret = lyht_resize(ht, -1);
Michal Vasko5bcc33b2020-10-06 15:33:44 +0200455
456 if (resize_val_equal) {
457 lyht_set_cb(ht, old_val_equal);
458 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200459 }
460 }
461
462 return ret;
463}
Michal Vasko5bcc33b2020-10-06 15:33:44 +0200464
Michal Vaskoae130f52023-04-20 14:25:16 +0200465LIBYANG_API_DEF LY_ERR
Michal Vasko8efac242023-03-30 08:24:56 +0200466lyht_remove(struct ly_ht *ht, void *val_p, uint32_t hash)
Michal Vasko5bcc33b2020-10-06 15:33:44 +0200467{
468 return lyht_remove_with_resize_cb(ht, val_p, hash, NULL);
469}
Michal Vasko626196f2022-08-05 12:49:52 +0200470
Michal Vaskoae130f52023-04-20 14:25:16 +0200471LIBYANG_API_DEF uint32_t
Michal Vasko626196f2022-08-05 12:49:52 +0200472lyht_get_fixed_size(uint32_t item_count)
473{
Olivier Matz75c00192023-09-21 14:35:12 +0200474 if (item_count == 0)
475 return 1;
Michal Vasko626196f2022-08-05 12:49:52 +0200476
Olivier Matz75c00192023-09-21 14:35:12 +0200477 /* return next power of 2 (greater or equal) */
478 item_count--;
479 item_count |= item_count >> 1;
480 item_count |= item_count >> 2;
481 item_count |= item_count >> 4;
482 item_count |= item_count >> 8;
483 item_count |= item_count >> 16;
Michal Vasko626196f2022-08-05 12:49:52 +0200484
Olivier Matz75c00192023-09-21 14:35:12 +0200485 return item_count + 1;
Michal Vasko626196f2022-08-05 12:49:52 +0200486}