Skip to content

Commit

Permalink
hash table BUGFIX invalid length when resizing dict
Browse files Browse the repository at this point in the history
Refs #1045
Refs #1093
  • Loading branch information
michalvasko committed Oct 6, 2020
1 parent 8ce5135 commit 5bcc33b
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 7 deletions.
45 changes: 42 additions & 3 deletions src/hash_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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 */

Expand Down Expand Up @@ -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);
}
20 changes: 18 additions & 2 deletions src/hash_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */
4 changes: 2 additions & 2 deletions tests/utests/test_hash_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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 */
Expand Down

0 comments on commit 5bcc33b

Please sign in to comment.