diff --git a/src/hash_table.c b/src/hash_table.c index 5f1af016e..cc6fbd03c 100644 --- a/src/hash_table.c +++ b/src/hash_table.c @@ -130,6 +130,30 @@ dict_hash(const char *key, size_t len) return dict_hash_multi(hash, NULL, len); } +static ly_bool +lydict_resize_val_eq(void *val1_p, void *val2_p, ly_bool mod, void *cb_data) +{ + LY_CHECK_ARG_RET(NULL, val1_p, val2_p, 0); + + const char *str1 = ((struct dict_rec *)val1_p)->value; + const char *str2 = ((struct dict_rec *)val2_p)->value; + + LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0); + LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0); + + if (mod) { + /* used when inserting new values */ + if (strcmp(str1, str2) == 0) { + return 1; + } + } else { + /* used when finding the original value again in the resized table */ + return lydict_val_eq(val1_p, val2_p, mod, cb_data); + } + + return 0; +} + API void lydict_remove(const struct ly_ctx *ctx, const char *value) { @@ -168,7 +192,7 @@ lydict_remove(const struct ly_ctx *ctx, const char *value) * free it after it is removed from hash table */ val_p = match->value; - ret = lyht_remove(ctx->dict.hash_tab, &rec, hash); + ret = lyht_remove_with_resize_cb(ctx->dict.hash_tab, &rec, hash, lydict_resize_val_eq); free(val_p); LY_CHECK_ERR_GOTO(ret, LOGINT(ctx), finish); } @@ -193,7 +217,7 @@ dict_insert(const struct ly_ctx *ctx, char *value, size_t len, ly_bool zerocopy, rec.refcount = 1; LOGDBG(LY_LDGDICT, "inserting \"%s\"", rec.value); - ret = lyht_insert(ctx->dict.hash_tab, (void *)&rec, hash, (void **)&match); + ret = lyht_insert_with_resize_cb(ctx->dict.hash_tab, (void *)&rec, hash, lydict_resize_val_eq, (void **)&match); if (ret == LY_EEXIST) { match->refcount++; if (zerocopy) { @@ -688,12 +712,13 @@ lyht_insert(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p) } LY_ERR -lyht_remove(struct hash_table *ht, void *val_p, uint32_t hash) +lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, values_equal_cb resize_val_equal) { struct ht_rec *rec, *crec; int32_t i; ly_bool first_matched = 0; LY_ERR r, ret = LY_SUCCESS; + values_equal_cb old_val_equal; LY_CHECK_ERR_RET(lyht_find_first(ht, hash, &rec), LOGARG(NULL, hash), LY_ENOTFOUND); /* hash not found */ @@ -737,10 +762,24 @@ lyht_remove(struct hash_table *ht, void *val_p, uint32_t hash) if (ht->resize == 2) { r = (ht->used * 100) / ht->size; if ((r < LYHT_SHRINK_PERCENTAGE) && (ht->size > LYHT_MIN_SIZE)) { + if (resize_val_equal) { + old_val_equal = lyht_set_cb(ht, resize_val_equal); + } + /* shrink */ ret = lyht_resize(ht, 0); + + if (resize_val_equal) { + lyht_set_cb(ht, old_val_equal); + } } } return ret; } + +LY_ERR +lyht_remove(struct hash_table *ht, void *val_p, uint32_t hash) +{ + return lyht_remove_with_resize_cb(ht, val_p, hash, NULL); +} diff --git a/src/hash_table.h b/src/hash_table.h index 91c3da20d..575bc9f93 100644 --- a/src/hash_table.h +++ b/src/hash_table.h @@ -223,12 +223,28 @@ LY_ERR lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t h * @brief Remove a value from a hash table. * * @param[in] ht Hash table to remove from. - * @param[in] value_p Pointer to value to be removed. Be careful, if the values stored in the hash table - * are pointers, \p value_p must be a pointer to a pointer. + * @param[in] val_p Pointer to value to be removed. Be careful, if the values stored in the hash table + * are pointers, \p val_p must be a pointer to a pointer. * @param[in] hash Hash of the stored value. * @return LY_SUCCESS on success, * @return LY_ENOTFOUND if value was not found. */ LY_ERR lyht_remove(struct hash_table *ht, void *val_p, uint32_t hash); +/** + * @brief Remove a value from a hash table. Same functionality as lyht_remove() + * but allows to specify a temporary val equal callback to be used in case the hash table + * will be resized after successful removal. + * + * @param[in] ht Hash table to remove from. + * @param[in] val_p Pointer to value to be removed. Be careful, if the values stored in the hash table + * are pointers, \p val_p must be a pointer to a pointer. + * @param[in] hash Hash of the stored value. + * @param[in] resize_val_equal Val equal callback to use for resizing. + * @return LY_SUCCESS on success, + * @return LY_ENOTFOUND if value was not found. + */ +LY_ERR lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, values_equal_cb resize_val_equal); + + #endif /* LY_HASH_TABLE_H_ */ diff --git a/tests/utests/test_hash_table.c b/tests/utests/test_hash_table.c index bf40371a7..57e0de098 100644 --- a/tests/utests/test_hash_table.c +++ b/tests/utests/test_hash_table.c @@ -150,7 +150,7 @@ test_ht_basic(void **state) assert_int_equal(LY_SUCCESS, lyht_remove(ht, &i, i)); assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL)); assert_int_equal(LY_ENOTFOUND, lyht_remove(ht, &i, i)); - logbuf_assert("Invalid argument hash (lyht_remove())."); + logbuf_assert("Invalid argument hash (lyht_remove_with_resize_cb())."); lyht_free(ht); } @@ -192,7 +192,7 @@ test_ht_resize(void **state) for (i = 0; i < 2; ++i) { logbuf_clean(); assert_int_equal(LY_ENOTFOUND, lyht_remove(ht, &i, i)); - logbuf_assert("Invalid argument hash (lyht_remove())."); + logbuf_assert("Invalid argument hash (lyht_remove_with_resize_cb())."); } /* removing present data, resize should happened * when we are below 25% of the table filled, so with 3 records left */