hash table BUGFIX invalid length when resizing dict
Refs #1045
Refs #1093
diff --git a/src/hash_table.c b/src/hash_table.c
index 5f1af01..cc6fbd0 100644
--- a/src/hash_table.c
+++ b/src/hash_table.c
@@ -130,6 +130,30 @@
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 @@
* 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 @@
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 @@
}
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 @@
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 91c3da2..575bc9f 100644
--- a/src/hash_table.h
+++ b/src/hash_table.h
@@ -223,12 +223,28 @@
* @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 bf40371..57e0de0 100644
--- a/tests/utests/test_hash_table.c
+++ b/tests/utests/test_hash_table.c
@@ -150,7 +150,7 @@
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 @@
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 */