blob: 4dbfdd18b8c35402676653996d84e4ca73ea9b7c [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
Michal Vasko69730152020-10-09 16:30:07 +020024#include "compat.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020025#include "dict.h"
Radek Krejci857189e2020-09-01 13:26:36 +020026#include "log.h"
Michal Vasko8f702ee2024-02-20 15:44:24 +010027#include "ly_common.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);
Olivier Matzc0feb682023-09-21 08:53:59 +020068 if (i != ht->size) {
Olivier Matz75c00192023-09-21 14:35:12 +020069 rec->next = i + 1;
Olivier Matzc0feb682023-09-21 08:53:59 +020070 } else {
Olivier Matz75c00192023-09-21 14:35:12 +020071 rec->next = LYHT_NO_RECORD;
Olivier Matzc0feb682023-09-21 08:53:59 +020072 }
Olivier Matz75c00192023-09-21 14:35:12 +020073 }
74
75 ht->hlists = malloc(sizeof(ht->hlists[0]) * ht->size);
76 LY_CHECK_ERR_RET(!ht->hlists, free(ht->recs); LOGMEM(NULL), LY_EMEM);
Olivier Matz6a669c12023-09-28 12:07:12 +020077 for (i = 0; i < ht->size; i++) {
78 ht->hlists[i].first = LYHT_NO_RECORD;
79 ht->hlists[i].last = LYHT_NO_RECORD;
80 }
Olivier Matz75c00192023-09-21 14:35:12 +020081 ht->first_free_rec = 0;
82
83 return LY_SUCCESS;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020084}
85
Michal Vaskoae130f52023-04-20 14:25:16 +020086LIBYANG_API_DEF struct ly_ht *
Michal Vasko62524a92021-02-26 10:08:50 +010087lyht_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 +020088{
Michal Vasko8efac242023-03-30 08:24:56 +020089 struct ly_ht *ht;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020090
91 /* check that 2^x == size (power of 2) */
92 assert(size && !(size & (size - 1)));
93 assert(val_equal && val_size);
94 assert(resize == 0 || resize == 1);
95
96 if (size < LYHT_MIN_SIZE) {
97 size = LYHT_MIN_SIZE;
98 }
99
100 ht = malloc(sizeof *ht);
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200101 LY_CHECK_ERR_RET(!ht, LOGMEM(NULL), NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200102
103 ht->used = 0;
104 ht->size = size;
105 ht->val_equal = val_equal;
106 ht->cb_data = cb_data;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200107 ht->resize = resize;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200108
Olivier Matz75c00192023-09-21 14:35:12 +0200109 ht->rec_size = SIZEOF_LY_HT_REC + val_size;
110 if (lyht_init_hlists_and_records(ht) != LY_SUCCESS) {
111 free(ht);
112 return NULL;
113 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200114
115 return ht;
116}
117
Michal Vaskoae130f52023-04-20 14:25:16 +0200118LIBYANG_API_DEF lyht_value_equal_cb
Michal Vasko8efac242023-03-30 08:24:56 +0200119lyht_set_cb(struct ly_ht *ht, lyht_value_equal_cb new_val_equal)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200120{
Michal Vasko62524a92021-02-26 10:08:50 +0100121 lyht_value_equal_cb prev;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200122
123 prev = ht->val_equal;
124 ht->val_equal = new_val_equal;
125 return prev;
126}
127
Michal Vaskoae130f52023-04-20 14:25:16 +0200128LIBYANG_API_DEF void *
Michal Vasko8efac242023-03-30 08:24:56 +0200129lyht_set_cb_data(struct ly_ht *ht, void *new_cb_data)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200130{
131 void *prev;
132
133 prev = ht->cb_data;
134 ht->cb_data = new_cb_data;
135 return prev;
136}
137
Michal Vaskoae130f52023-04-20 14:25:16 +0200138LIBYANG_API_DEF struct ly_ht *
Michal Vasko8efac242023-03-30 08:24:56 +0200139lyht_dup(const struct ly_ht *orig)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200140{
Michal Vasko8efac242023-03-30 08:24:56 +0200141 struct ly_ht *ht;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200142
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200143 LY_CHECK_ARG_RET(NULL, orig, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200144
Olivier Matz75c00192023-09-21 14:35:12 +0200145 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 +0200146 if (!ht) {
147 return NULL;
148 }
149
Olivier Matz75c00192023-09-21 14:35:12 +0200150 memcpy(ht->hlists, orig->hlists, sizeof(ht->hlists[0]) * orig->size);
151 memcpy(ht->recs, orig->recs, (size_t)orig->size * orig->rec_size);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200152 ht->used = orig->used;
153 return ht;
154}
155
Michal Vaskoae130f52023-04-20 14:25:16 +0200156LIBYANG_API_DEF void
Michal Vasko8efac242023-03-30 08:24:56 +0200157lyht_free(struct ly_ht *ht, void (*val_free)(void *val_p))
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200158{
Michal Vasko8efac242023-03-30 08:24:56 +0200159 struct ly_ht_rec *rec;
Olivier Matz75c00192023-09-21 14:35:12 +0200160 uint32_t hlist_idx;
161 uint32_t rec_idx;
Michal Vasko77b7f90a2023-01-31 15:42:41 +0100162
163 if (!ht) {
164 return;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200165 }
Michal Vasko77b7f90a2023-01-31 15:42:41 +0100166
167 if (val_free) {
Olivier Matzc0feb682023-09-21 08:53:59 +0200168 LYHT_ITER_ALL_RECS(ht, hlist_idx, rec_idx, rec) {
Olivier Matz75c00192023-09-21 14:35:12 +0200169 val_free(&rec->val);
Olivier Matzc0feb682023-09-21 08:53:59 +0200170 }
Michal Vasko77b7f90a2023-01-31 15:42:41 +0100171 }
Olivier Matz75c00192023-09-21 14:35:12 +0200172 free(ht->hlists);
Michal Vasko77b7f90a2023-01-31 15:42:41 +0100173 free(ht->recs);
174 free(ht);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200175}
176
Michal Vaskodc95d9c2021-04-12 15:11:48 +0200177/**
178 * @brief Resize a hash table.
179 *
180 * @param[in] ht Hash table to resize.
181 * @param[in] operation Operation to perform. 1 to enlarge, -1 to shrink, 0 to only rehash all records.
Ondrej Kusnirik79e721c2024-08-19 10:51:39 +0200182 * @param[in] check Whether to check if the value has already been inserted or not.
Michal Vaskodc95d9c2021-04-12 15:11:48 +0200183 * @return LY_ERR value.
184 */
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200185static LY_ERR
Olivier Matz6ad93d92023-09-28 12:13:36 +0200186lyht_resize(struct ly_ht *ht, int operation, int check)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200187{
Michal Vasko8efac242023-03-30 08:24:56 +0200188 struct ly_ht_rec *rec;
Olivier Matz6a669c12023-09-28 12:07:12 +0200189 struct ly_ht_hlist *old_hlists;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200190 unsigned char *old_recs;
Olivier Matz75c00192023-09-21 14:35:12 +0200191 uint32_t old_first_free_rec;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200192 uint32_t i, old_size;
Olivier Matz75c00192023-09-21 14:35:12 +0200193 uint32_t rec_idx;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200194
Olivier Matz75c00192023-09-21 14:35:12 +0200195 old_hlists = ht->hlists;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200196 old_recs = ht->recs;
197 old_size = ht->size;
Olivier Matz75c00192023-09-21 14:35:12 +0200198 old_first_free_rec = ht->first_free_rec;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200199
Michal Vaskodc95d9c2021-04-12 15:11:48 +0200200 if (operation > 0) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200201 /* double the size */
202 ht->size <<= 1;
Michal Vaskodc95d9c2021-04-12 15:11:48 +0200203 } else if (operation < 0) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200204 /* half the size */
205 ht->size >>= 1;
206 }
207
Olivier Matz75c00192023-09-21 14:35:12 +0200208 if (lyht_init_hlists_and_records(ht) != LY_SUCCESS) {
209 ht->hlists = old_hlists;
210 ht->recs = old_recs;
211 ht->size = old_size;
212 ht->first_free_rec = old_first_free_rec;
213 return LY_EMEM;
214 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200215
Olivier Matz75c00192023-09-21 14:35:12 +0200216 /* reset used, it will increase again */
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200217 ht->used = 0;
218
219 /* add all the old records into the new records array */
Olivier Matz75c00192023-09-21 14:35:12 +0200220 for (i = 0; i < old_size; i++) {
Olivier Matz6a669c12023-09-28 12:07:12 +0200221 for (rec_idx = old_hlists[i].first, rec = lyht_get_rec(old_recs, ht->rec_size, rec_idx);
Olivier Matzc0feb682023-09-21 08:53:59 +0200222 rec_idx != LYHT_NO_RECORD;
223 rec_idx = rec->next, rec = lyht_get_rec(old_recs, ht->rec_size, rec_idx)) {
Olivier Matz75c00192023-09-21 14:35:12 +0200224 LY_ERR ret;
225
Olivier Matzc0feb682023-09-21 08:53:59 +0200226 if (check) {
Olivier Matz6ad93d92023-09-28 12:13:36 +0200227 ret = lyht_insert(ht, rec->val, rec->hash, NULL);
Olivier Matzc0feb682023-09-21 08:53:59 +0200228 } else {
Olivier Matz6ad93d92023-09-28 12:13:36 +0200229 ret = lyht_insert_no_check(ht, rec->val, rec->hash, NULL);
Olivier Matzc0feb682023-09-21 08:53:59 +0200230 }
Michal Vasko26bbb272022-08-02 14:54:33 +0200231
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200232 assert(!ret);
233 (void)ret;
234 }
235 }
236
237 /* final touches */
238 free(old_recs);
Olivier Matz75c00192023-09-21 14:35:12 +0200239 free(old_hlists);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200240 return LY_SUCCESS;
241}
242
Michal Vaskoda859032020-07-14 12:20:14 +0200243/**
Michal Vaskoda859032020-07-14 12:20:14 +0200244 * @brief Search for a record with specific value and hash.
245 *
246 * @param[in] ht Hash table to search in.
247 * @param[in] val_p Pointer to the value to find.
248 * @param[in] hash Hash to find.
Radek Krejci857189e2020-09-01 13:26:36 +0200249 * @param[in] mod Whether the operation modifies the hash table (insert or remove) or not (find).
Michal Vaskof1e09412023-10-23 10:03:21 +0200250 * @param[in] val_equal Callback for checking value equivalence.
Michal Vaskoda859032020-07-14 12:20:14 +0200251 * @param[out] col Optional collision number of @p rec_p, 0 for no collision.
252 * @param[out] rec_p Found exact matching record, may be a collision of @p crec_p.
253 * @return LY_ENOTFOUND if no record found,
254 * @return LY_SUCCESS if record was found.
255 */
256static LY_ERR
Michal Vaskof1e09412023-10-23 10:03:21 +0200257lyht_find_rec(const struct ly_ht *ht, void *val_p, uint32_t hash, ly_bool mod, lyht_value_equal_cb val_equal,
Ondrej Kusnirikc2f76fe2024-08-15 09:09:13 +0200258 uint32_t *col, struct ly_ht_rec **rec_p)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200259{
Olivier Matz75c00192023-09-21 14:35:12 +0200260 uint32_t hlist_idx = hash & (ht->size - 1);
261 struct ly_ht_rec *rec;
262 uint32_t rec_idx;
Michal Vaskoda859032020-07-14 12:20:14 +0200263
Michal Vaskoda859032020-07-14 12:20:14 +0200264 if (col) {
265 *col = 0;
266 }
267 *rec_p = NULL;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200268
Olivier Matz75c00192023-09-21 14:35:12 +0200269 LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec) {
Michal Vaskof1e09412023-10-23 10:03:21 +0200270 if ((rec->hash == hash) && val_equal(val_p, &rec->val, mod, ht->cb_data)) {
Michal Vaskoda859032020-07-14 12:20:14 +0200271 *rec_p = rec;
272 return LY_SUCCESS;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200273 }
Olivier Matz75c00192023-09-21 14:35:12 +0200274
275 if (col) {
276 *col = *col + 1;
277 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200278 }
279
280 /* not found even in collisions */
Michal Vaskoda859032020-07-14 12:20:14 +0200281 return LY_ENOTFOUND;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200282}
283
Michal Vaskoae130f52023-04-20 14:25:16 +0200284LIBYANG_API_DEF LY_ERR
Michal Vaskof1e09412023-10-23 10:03:21 +0200285lyht_find(const struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
Michal Vaskoda859032020-07-14 12:20:14 +0200286{
Michal Vasko8efac242023-03-30 08:24:56 +0200287 struct ly_ht_rec *rec;
Michal Vaskoda859032020-07-14 12:20:14 +0200288
Michal Vasko3a2d4612024-08-16 15:55:15 +0200289 if (match_p) {
290 *match_p = NULL;
291 }
292
Ondrej Kusnirikc2f76fe2024-08-15 09:09:13 +0200293 lyht_find_rec(ht, val_p, hash, 0, ht->val_equal, NULL, &rec);
Michal Vaskoda859032020-07-14 12:20:14 +0200294
295 if (rec && match_p) {
296 *match_p = rec->val;
297 }
298 return rec ? LY_SUCCESS : LY_ENOTFOUND;
299}
300
Michal Vaskoae130f52023-04-20 14:25:16 +0200301LIBYANG_API_DEF LY_ERR
Michal Vaskof1e09412023-10-23 10:03:21 +0200302lyht_find_with_val_cb(const struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb val_equal, void **match_p)
303{
304 struct ly_ht_rec *rec;
305
Ondrej Kusnirikc2f76fe2024-08-15 09:09:13 +0200306 lyht_find_rec(ht, val_p, hash, 0, val_equal ? val_equal : ht->val_equal, NULL, &rec);
Michal Vaskof1e09412023-10-23 10:03:21 +0200307
308 if (rec && match_p) {
309 *match_p = rec->val;
310 }
311 return rec ? LY_SUCCESS : LY_ENOTFOUND;
312}
313
314LIBYANG_API_DEF LY_ERR
315lyht_find_next_with_collision_cb(const struct ly_ht *ht, void *val_p, uint32_t hash,
Michal Vasko6374de22022-09-05 15:48:48 +0200316 lyht_value_equal_cb collision_val_equal, void **match_p)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200317{
Ondrej Kusnirikc2f76fe2024-08-15 09:09:13 +0200318 struct ly_ht_rec *rec;
Olivier Matz75c00192023-09-21 14:35:12 +0200319 uint32_t rec_idx;
320 uint32_t i;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200321
Michal Vasko6374de22022-09-05 15:48:48 +0200322 /* find the record of the previously found value */
Ondrej Kusnirikc2f76fe2024-08-15 09:09:13 +0200323 if (lyht_find_rec(ht, val_p, hash, 1, ht->val_equal, &i, &rec)) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200324 /* not found, cannot happen */
Michal Vaskoda859032020-07-14 12:20:14 +0200325 LOGINT_RET(NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200326 }
327
Olivier Matz75c00192023-09-21 14:35:12 +0200328 for (rec_idx = rec->next, rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx);
Olivier Matzc0feb682023-09-21 08:53:59 +0200329 rec_idx != LYHT_NO_RECORD;
330 rec_idx = rec->next, rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx)) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200331
Michal Vasko6374de22022-09-05 15:48:48 +0200332 if (rec->hash != hash) {
333 continue;
334 }
335
336 if (collision_val_equal) {
337 if (collision_val_equal(val_p, &rec->val, 0, ht->cb_data)) {
338 /* even the value matches */
339 if (match_p) {
340 *match_p = rec->val;
341 }
342 return LY_SUCCESS;
343 }
344 } else if (ht->val_equal(val_p, &rec->val, 0, ht->cb_data)) {
Michal Vaskoda859032020-07-14 12:20:14 +0200345 /* even the value matches */
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200346 if (match_p) {
347 *match_p = rec->val;
348 }
Michal Vaskoda859032020-07-14 12:20:14 +0200349 return LY_SUCCESS;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200350 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200351 }
352
353 /* the last equal value was already returned */
Michal Vaskoda859032020-07-14 12:20:14 +0200354 return LY_ENOTFOUND;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200355}
356
Michal Vaskoae130f52023-04-20 14:25:16 +0200357LIBYANG_API_DEF LY_ERR
Michal Vaskof1e09412023-10-23 10:03:21 +0200358lyht_find_next(const struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
Michal Vasko6374de22022-09-05 15:48:48 +0200359{
360 return lyht_find_next_with_collision_cb(ht, val_p, hash, NULL, match_p);
361}
362
Olivier Matz75c00192023-09-21 14:35:12 +0200363static LY_ERR
Michal Vaskof1e09412023-10-23 10:03:21 +0200364_lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
Olivier Matz6ad93d92023-09-28 12:13:36 +0200365 void **match_p, int check)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200366{
Olivier Matz75c00192023-09-21 14:35:12 +0200367 uint32_t hlist_idx = hash & (ht->size - 1);
Radek Krejci1deb5be2020-08-26 16:43:36 +0200368 LY_ERR r, ret = LY_SUCCESS;
Olivier Matz6a669c12023-09-28 12:07:12 +0200369 struct ly_ht_rec *rec, *prev_rec;
Michal Vasko62524a92021-02-26 10:08:50 +0100370 lyht_value_equal_cb old_val_equal = NULL;
Olivier Matz75c00192023-09-21 14:35:12 +0200371 uint32_t rec_idx;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200372
Olivier Matz6ad93d92023-09-28 12:13:36 +0200373 if (check) {
Ondrej Kusnirikc2f76fe2024-08-15 09:09:13 +0200374 if (lyht_find_rec(ht, val_p, hash, 1, ht->val_equal, NULL, &rec) == LY_SUCCESS) {
Olivier Matz6ad93d92023-09-28 12:13:36 +0200375 if (rec && match_p) {
376 *match_p = rec->val;
377 }
378 return LY_EEXIST;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200379 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200380 }
381
Olivier Matz75c00192023-09-21 14:35:12 +0200382 rec_idx = ht->first_free_rec;
383 assert(rec_idx < ht->size);
384 rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx);
385 ht->first_free_rec = rec->next;
Olivier Matz6a669c12023-09-28 12:07:12 +0200386
387 if (ht->hlists[hlist_idx].first == LYHT_NO_RECORD) {
388 ht->hlists[hlist_idx].first = rec_idx;
389 } else {
390 prev_rec = lyht_get_rec(ht->recs, ht->rec_size, ht->hlists[hlist_idx].last);
391 prev_rec->next = rec_idx;
392 }
393 rec->next = LYHT_NO_RECORD;
394 ht->hlists[hlist_idx].last = rec_idx;
Olivier Matz75c00192023-09-21 14:35:12 +0200395
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200396 rec->hash = hash;
Olivier Matz75c00192023-09-21 14:35:12 +0200397 memcpy(&rec->val, val_p, ht->rec_size - SIZEOF_LY_HT_REC);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200398 if (match_p) {
399 *match_p = (void *)&rec->val;
400 }
401
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200402 /* check size & enlarge if needed */
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200403 ++ht->used;
404 if (ht->resize) {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100405 r = (ht->used * LYHT_HUNDRED_PERCENTAGE) / ht->size;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200406 if ((ht->resize == 1) && (r >= LYHT_FIRST_SHRINK_PERCENTAGE)) {
407 /* enable shrinking */
408 ht->resize = 2;
409 }
410 if ((ht->resize == 2) && (r >= LYHT_ENLARGE_PERCENTAGE)) {
411 if (resize_val_equal) {
412 old_val_equal = lyht_set_cb(ht, resize_val_equal);
413 }
414
415 /* enlarge */
Olivier Matz6ad93d92023-09-28 12:13:36 +0200416 ret = lyht_resize(ht, 1, check);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200417 /* if hash_table was resized, we need to find new matching value */
Michal Vasko69730152020-10-09 16:30:07 +0200418 if ((ret == LY_SUCCESS) && match_p) {
Michal Vasko59118802023-10-04 16:00:26 +0200419 ret = lyht_find(ht, val_p, hash, match_p);
420 assert(!ret);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200421 }
422
423 if (resize_val_equal) {
424 lyht_set_cb(ht, old_val_equal);
425 }
426 }
427 }
428 return ret;
429}
430
Michal Vaskoae130f52023-04-20 14:25:16 +0200431LIBYANG_API_DEF LY_ERR
Olivier Matz75c00192023-09-21 14:35:12 +0200432lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
433 void **match_p)
434{
Michal Vaskof1e09412023-10-23 10:03:21 +0200435 return _lyht_insert_with_resize_cb(ht, val_p, hash, resize_val_equal, match_p, 1);
Olivier Matz75c00192023-09-21 14:35:12 +0200436}
437
438LIBYANG_API_DEF LY_ERR
Michal Vasko8efac242023-03-30 08:24:56 +0200439lyht_insert(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200440{
Michal Vaskof1e09412023-10-23 10:03:21 +0200441 return _lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p, 1);
Olivier Matz6ad93d92023-09-28 12:13:36 +0200442}
443
444LIBYANG_API_DEF LY_ERR
445lyht_insert_no_check(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
446{
Michal Vaskof1e09412023-10-23 10:03:21 +0200447 return _lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p, 0);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200448}
449
Michal Vaskoae130f52023-04-20 14:25:16 +0200450LIBYANG_API_DEF LY_ERR
Michal Vasko8efac242023-03-30 08:24:56 +0200451lyht_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 +0200452{
Olivier Matz75c00192023-09-21 14:35:12 +0200453 struct ly_ht_rec *found_rec, *prev_rec, *rec;
454 uint32_t hlist_idx = hash & (ht->size - 1);
Radek Krejci1deb5be2020-08-26 16:43:36 +0200455 LY_ERR r, ret = LY_SUCCESS;
stewegd8e2fc92023-05-31 09:52:56 +0200456 lyht_value_equal_cb old_val_equal = NULL;
Olivier Matz75c00192023-09-21 14:35:12 +0200457 uint32_t prev_rec_idx;
458 uint32_t rec_idx;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200459
Ondrej Kusnirikc2f76fe2024-08-15 09:09:13 +0200460 if (lyht_find_rec(ht, val_p, hash, 1, ht->val_equal, NULL, &found_rec)) {
Michal Vaskoc70cf032023-10-23 10:04:29 +0200461 LOGARG(NULL, hash);
462 return LY_ENOTFOUND;
463 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200464
Olivier Matz75c00192023-09-21 14:35:12 +0200465 prev_rec_idx = LYHT_NO_RECORD;
466 LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec) {
Olivier Matzc0feb682023-09-21 08:53:59 +0200467 if (rec == found_rec) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200468 break;
Olivier Matzc0feb682023-09-21 08:53:59 +0200469 }
Olivier Matz75c00192023-09-21 14:35:12 +0200470 prev_rec_idx = rec_idx;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200471 }
472
Olivier Matz75c00192023-09-21 14:35:12 +0200473 if (prev_rec_idx == LYHT_NO_RECORD) {
Olivier Matz6a669c12023-09-28 12:07:12 +0200474 ht->hlists[hlist_idx].first = rec->next;
Olivier Matzc0feb682023-09-21 08:53:59 +0200475 if (rec->next == LYHT_NO_RECORD) {
Olivier Matz6a669c12023-09-28 12:07:12 +0200476 ht->hlists[hlist_idx].last = LYHT_NO_RECORD;
Olivier Matzc0feb682023-09-21 08:53:59 +0200477 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200478 } else {
Olivier Matz75c00192023-09-21 14:35:12 +0200479 prev_rec = lyht_get_rec(ht->recs, ht->rec_size, prev_rec_idx);
480 prev_rec->next = rec->next;
Olivier Matzc0feb682023-09-21 08:53:59 +0200481 if (rec->next == LYHT_NO_RECORD) {
Olivier Matz6a669c12023-09-28 12:07:12 +0200482 ht->hlists[hlist_idx].last = prev_rec_idx;
Olivier Matzc0feb682023-09-21 08:53:59 +0200483 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200484 }
485
Olivier Matz75c00192023-09-21 14:35:12 +0200486 rec->next = ht->first_free_rec;
487 ht->first_free_rec = rec_idx;
488
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200489 /* check size & shrink if needed */
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200490 --ht->used;
491 if (ht->resize == 2) {
Radek Krejcif13b87b2020-12-01 22:02:17 +0100492 r = (ht->used * LYHT_HUNDRED_PERCENTAGE) / ht->size;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200493 if ((r < LYHT_SHRINK_PERCENTAGE) && (ht->size > LYHT_MIN_SIZE)) {
Michal Vasko5bcc33b2020-10-06 15:33:44 +0200494 if (resize_val_equal) {
495 old_val_equal = lyht_set_cb(ht, resize_val_equal);
496 }
497
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200498 /* shrink */
Olivier Matz6ad93d92023-09-28 12:13:36 +0200499 ret = lyht_resize(ht, -1, 1);
Michal Vasko5bcc33b2020-10-06 15:33:44 +0200500
501 if (resize_val_equal) {
502 lyht_set_cb(ht, old_val_equal);
503 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200504 }
505 }
506
507 return ret;
508}
Michal Vasko5bcc33b2020-10-06 15:33:44 +0200509
Michal Vaskoae130f52023-04-20 14:25:16 +0200510LIBYANG_API_DEF LY_ERR
Michal Vasko8efac242023-03-30 08:24:56 +0200511lyht_remove(struct ly_ht *ht, void *val_p, uint32_t hash)
Michal Vasko5bcc33b2020-10-06 15:33:44 +0200512{
513 return lyht_remove_with_resize_cb(ht, val_p, hash, NULL);
514}
Michal Vasko626196f2022-08-05 12:49:52 +0200515
Michal Vaskoae130f52023-04-20 14:25:16 +0200516LIBYANG_API_DEF uint32_t
Michal Vasko626196f2022-08-05 12:49:52 +0200517lyht_get_fixed_size(uint32_t item_count)
518{
Olivier Matzc0feb682023-09-21 08:53:59 +0200519 if (item_count == 0) {
Olivier Matz75c00192023-09-21 14:35:12 +0200520 return 1;
Olivier Matzc0feb682023-09-21 08:53:59 +0200521 }
Michal Vasko626196f2022-08-05 12:49:52 +0200522
Olivier Matz75c00192023-09-21 14:35:12 +0200523 /* return next power of 2 (greater or equal) */
524 item_count--;
525 item_count |= item_count >> 1;
526 item_count |= item_count >> 2;
527 item_count |= item_count >> 4;
528 item_count |= item_count >> 8;
529 item_count |= item_count >> 16;
Michal Vasko626196f2022-08-05 12:49:52 +0200530
Olivier Matz75c00192023-09-21 14:35:12 +0200531 return item_count + 1;
Michal Vasko626196f2022-08-05 12:49:52 +0200532}