summaryrefslogtreecommitdiffstats
path: root/src/hash_table.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/hash_table.c770
1 files changed, 212 insertions, 558 deletions
diff --git a/src/hash_table.c b/src/hash_table.c
index 4f9dec3..9655bd6 100644
--- a/src/hash_table.c
+++ b/src/hash_table.c
@@ -1,9 +1,10 @@
/**
* @file hash_table.c
* @author Radek Krejci <rkrejci@cesnet.cz>
- * @brief libyang dictionary for storing strings and generic hash table
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang generic hash table implementation
*
- * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -25,80 +26,8 @@
#include "dict.h"
#include "log.h"
-#define LYDICT_MIN_SIZE 1024
-
-/**
- * @brief Comparison callback for dictionary's hash table
- *
- * Implementation of ::lyht_value_equal_cb.
- */
-static ly_bool
-lydict_val_eq(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *cb_data)
-{
- LY_CHECK_ARG_RET(NULL, val1_p, val2_p, cb_data, 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 (strncmp(str1, str2, *(size_t *)cb_data) == 0) {
- return 1;
- }
-
- return 0;
-}
-
-void
-lydict_init(struct dict_table *dict)
-{
- LY_CHECK_ARG_RET(NULL, dict, );
-
- dict->hash_tab = lyht_new(LYDICT_MIN_SIZE, sizeof(struct dict_rec), lydict_val_eq, NULL, 1);
- LY_CHECK_ERR_RET(!dict->hash_tab, LOGINT(NULL), );
- pthread_mutex_init(&dict->lock, NULL);
-}
-
-void
-lydict_clean(struct dict_table *dict)
-{
- struct dict_rec *dict_rec = NULL;
- struct ht_rec *rec = NULL;
-
- LY_CHECK_ARG_RET(NULL, dict, );
-
- for (uint32_t i = 0; i < dict->hash_tab->size; i++) {
- /* get ith record */
- rec = (struct ht_rec *)&dict->hash_tab->recs[i * dict->hash_tab->rec_size];
- if (rec->hits == 1) {
- /*
- * this should not happen, all records inserted into
- * dictionary are supposed to be removed using lydict_remove()
- * before calling lydict_clean()
- */
- dict_rec = (struct dict_rec *)rec->val;
- LOGWRN(NULL, "String \"%s\" not freed from the dictionary, refcount %d", dict_rec->value, dict_rec->refcount);
- /* if record wasn't removed before free string allocated for that record */
-#ifdef NDEBUG
- free(dict_rec->value);
-#endif
- }
- }
-
- /* free table and destroy mutex */
- lyht_free(dict->hash_tab);
- pthread_mutex_destroy(&dict->lock);
-}
-
-/*
- * Usage:
- * - init hash to 0
- * - repeatedly call dict_hash_multi(), provide hash from the last call
- * - call dict_hash_multi() with key_part = NULL to finish the hash
- */
-uint32_t
-dict_hash_multi(uint32_t hash, const char *key_part, size_t len)
+LIBYANG_API_DEF uint32_t
+lyht_hash_multi(uint32_t hash, const char *key_part, size_t len)
{
uint32_t i;
@@ -117,203 +46,47 @@ dict_hash_multi(uint32_t hash, const char *key_part, size_t len)
return hash;
}
-/*
- * Bob Jenkin's one-at-a-time hash
- * http://www.burtleburtle.net/bob/hash/doobs.html
- *
- * Spooky hash is faster, but it works only for little endian architectures.
- */
-uint32_t
-dict_hash(const char *key, size_t len)
+LIBYANG_API_DEF uint32_t
+lyht_hash(const char *key, size_t len)
{
uint32_t hash;
- hash = dict_hash_multi(0, key, len);
- return dict_hash_multi(hash, NULL, len);
+ hash = lyht_hash_multi(0, key, len);
+ return lyht_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;
-}
-
-LIBYANG_API_DEF LY_ERR
-lydict_remove(const struct ly_ctx *ctx, const char *value)
-{
- LY_ERR ret = LY_SUCCESS;
- size_t len;
- uint32_t hash;
- struct dict_rec rec, *match = NULL;
- char *val_p;
-
- if (!ctx || !value) {
- return LY_SUCCESS;
- }
-
- LOGDBG(LY_LDGDICT, "removing \"%s\"", value);
-
- len = strlen(value);
- hash = dict_hash(value, len);
-
- /* create record for lyht_find call */
- rec.value = (char *)value;
- rec.refcount = 0;
-
- pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
- /* set len as data for compare callback */
- lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
- /* check if value is already inserted */
- ret = lyht_find(ctx->dict.hash_tab, &rec, hash, (void **)&match);
-
- if (ret == LY_SUCCESS) {
- LY_CHECK_ERR_GOTO(!match, LOGINT(ctx), finish);
-
- /* if value is already in dictionary, decrement reference counter */
- match->refcount--;
- if (match->refcount == 0) {
- /*
- * remove record
- * save pointer to stored string before lyht_remove to
- * free it after it is removed from hash table
- */
- val_p = match->value;
- 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);
- }
- } else if (ret == LY_ENOTFOUND) {
- LOGERR(ctx, LY_ENOTFOUND, "Value \"%s\" was not found in the dictionary.", value);
- } else {
- LOGINT(ctx);
- }
-
-finish:
- pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
- return ret;
-}
-
-LY_ERR
-dict_insert(const struct ly_ctx *ctx, char *value, size_t len, ly_bool zerocopy, const char **str_p)
+static LY_ERR
+lyht_init_hlists_and_records(struct ly_ht *ht)
{
- LY_ERR ret = LY_SUCCESS;
- struct dict_rec *match = NULL, rec;
- uint32_t hash;
-
- LOGDBG(LY_LDGDICT, "inserting \"%.*s\"", (int)len, value);
-
- hash = dict_hash(value, len);
- /* set len as data for compare callback */
- lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
- /* create record for lyht_insert */
- rec.value = value;
- rec.refcount = 1;
+ struct ly_ht_rec *rec;
+ uint32_t i;
- 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) {
- free(value);
- }
- ret = LY_SUCCESS;
- } else if (ret == LY_SUCCESS) {
- if (!zerocopy) {
- /*
- * allocate string for new record
- * record is already inserted in hash table
- */
- match->value = malloc(sizeof *match->value * (len + 1));
- LY_CHECK_ERR_RET(!match->value, LOGMEM(ctx), LY_EMEM);
- if (len) {
- memcpy(match->value, value, len);
- }
- match->value[len] = '\0';
- }
- } else {
- /* lyht_insert returned error */
- if (zerocopy) {
- free(value);
+ ht->recs = calloc(ht->size, ht->rec_size);
+ LY_CHECK_ERR_RET(!ht->recs, LOGMEM(NULL), LY_EMEM);
+ for (i = 0; i < ht->size; i++) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ if (i != ht->size) {
+ rec->next = i + 1;
+ } else {
+ rec->next = LYHT_NO_RECORD;
}
- return ret;
- }
-
- if (str_p) {
- *str_p = match->value;
- }
-
- return ret;
-}
-
-LIBYANG_API_DEF LY_ERR
-lydict_insert(const struct ly_ctx *ctx, const char *value, size_t len, const char **str_p)
-{
- LY_ERR result;
-
- LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
-
- if (!value) {
- *str_p = NULL;
- return LY_SUCCESS;
- }
-
- if (!len) {
- len = strlen(value);
}
- pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
- result = dict_insert(ctx, (char *)value, len, 0, str_p);
- pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
-
- return result;
-}
-
-LIBYANG_API_DEF LY_ERR
-lydict_insert_zc(const struct ly_ctx *ctx, char *value, const char **str_p)
-{
- LY_ERR result;
-
- LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
-
- if (!value) {
- *str_p = NULL;
- return LY_SUCCESS;
+ ht->hlists = malloc(sizeof(ht->hlists[0]) * ht->size);
+ LY_CHECK_ERR_RET(!ht->hlists, free(ht->recs); LOGMEM(NULL), LY_EMEM);
+ for (i = 0; i < ht->size; i++) {
+ ht->hlists[i].first = LYHT_NO_RECORD;
+ ht->hlists[i].last = LYHT_NO_RECORD;
}
+ ht->first_free_rec = 0;
- pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
- result = dict_insert(ctx, value, strlen(value), 1, str_p);
- pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
-
- return result;
-}
-
-struct ht_rec *
-lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx)
-{
- return (struct ht_rec *)&recs[idx * rec_size];
+ return LY_SUCCESS;
}
-struct hash_table *
+LIBYANG_API_DEF struct ly_ht *
lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *cb_data, uint16_t resize)
{
- struct hash_table *ht;
+ struct ly_ht *ht;
/* check that 2^x == size (power of 2) */
assert(size && !(size & (size - 1)));
@@ -329,21 +102,21 @@ lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *
ht->used = 0;
ht->size = size;
- ht->invalid = 0;
ht->val_equal = val_equal;
ht->cb_data = cb_data;
ht->resize = resize;
- ht->rec_size = (sizeof(struct ht_rec) - 1) + val_size;
- /* allocate the records correctly */
- ht->recs = calloc(size, ht->rec_size);
- LY_CHECK_ERR_RET(!ht->recs, free(ht); LOGMEM(NULL), NULL);
+ ht->rec_size = SIZEOF_LY_HT_REC + val_size;
+ if (lyht_init_hlists_and_records(ht) != LY_SUCCESS) {
+ free(ht);
+ return NULL;
+ }
return ht;
}
-lyht_value_equal_cb
-lyht_set_cb(struct hash_table *ht, lyht_value_equal_cb new_val_equal)
+LIBYANG_API_DEF lyht_value_equal_cb
+lyht_set_cb(struct ly_ht *ht, lyht_value_equal_cb new_val_equal)
{
lyht_value_equal_cb prev;
@@ -352,8 +125,8 @@ lyht_set_cb(struct hash_table *ht, lyht_value_equal_cb new_val_equal)
return prev;
}
-void *
-lyht_set_cb_data(struct hash_table *ht, void *new_cb_data)
+LIBYANG_API_DEF void *
+lyht_set_cb_data(struct ly_ht *ht, void *new_cb_data)
{
void *prev;
@@ -362,31 +135,43 @@ lyht_set_cb_data(struct hash_table *ht, void *new_cb_data)
return prev;
}
-struct hash_table *
-lyht_dup(const struct hash_table *orig)
+LIBYANG_API_DEF struct ly_ht *
+lyht_dup(const struct ly_ht *orig)
{
- struct hash_table *ht;
+ struct ly_ht *ht;
LY_CHECK_ARG_RET(NULL, orig, NULL);
- ht = lyht_new(orig->size, orig->rec_size - (sizeof(struct ht_rec) - 1), orig->val_equal, orig->cb_data, orig->resize ? 1 : 0);
+ ht = lyht_new(orig->size, orig->rec_size - SIZEOF_LY_HT_REC, orig->val_equal, orig->cb_data, orig->resize ? 1 : 0);
if (!ht) {
return NULL;
}
- memcpy(ht->recs, orig->recs, (size_t)orig->used * (size_t)orig->rec_size);
+ memcpy(ht->hlists, orig->hlists, sizeof(ht->hlists[0]) * orig->size);
+ memcpy(ht->recs, orig->recs, (size_t)orig->size * orig->rec_size);
ht->used = orig->used;
- ht->invalid = orig->invalid;
return ht;
}
-void
-lyht_free(struct hash_table *ht)
+LIBYANG_API_DEF void
+lyht_free(struct ly_ht *ht, void (*val_free)(void *val_p))
{
- if (ht) {
- free(ht->recs);
- free(ht);
+ struct ly_ht_rec *rec;
+ uint32_t hlist_idx;
+ uint32_t rec_idx;
+
+ if (!ht) {
+ return;
+ }
+
+ if (val_free) {
+ LYHT_ITER_ALL_RECS(ht, hlist_idx, rec_idx, rec) {
+ val_free(&rec->val);
+ }
}
+ free(ht->hlists);
+ free(ht->recs);
+ free(ht);
}
/**
@@ -397,14 +182,19 @@ lyht_free(struct hash_table *ht)
* @return LY_ERR value.
*/
static LY_ERR
-lyht_resize(struct hash_table *ht, int operation)
+lyht_resize(struct ly_ht *ht, int operation, int check)
{
- struct ht_rec *rec;
+ struct ly_ht_rec *rec;
+ struct ly_ht_hlist *old_hlists;
unsigned char *old_recs;
+ uint32_t old_first_free_rec;
uint32_t i, old_size;
+ uint32_t rec_idx;
+ old_hlists = ht->hlists;
old_recs = ht->recs;
old_size = ht->size;
+ old_first_free_rec = ht->first_free_rec;
if (operation > 0) {
/* double the size */
@@ -414,18 +204,29 @@ lyht_resize(struct hash_table *ht, int operation)
ht->size >>= 1;
}
- ht->recs = calloc(ht->size, ht->rec_size);
- LY_CHECK_ERR_RET(!ht->recs, LOGMEM(NULL); ht->recs = old_recs; ht->size = old_size, LY_EMEM);
+ if (lyht_init_hlists_and_records(ht) != LY_SUCCESS) {
+ ht->hlists = old_hlists;
+ ht->recs = old_recs;
+ ht->size = old_size;
+ ht->first_free_rec = old_first_free_rec;
+ return LY_EMEM;
+ }
- /* reset used and invalid, it will increase again */
+ /* reset used, it will increase again */
ht->used = 0;
- ht->invalid = 0;
/* add all the old records into the new records array */
- for (i = 0; i < old_size; ++i) {
- rec = lyht_get_rec(old_recs, ht->rec_size, i);
- if (rec->hits > 0) {
- LY_ERR ret = lyht_insert(ht, rec->val, rec->hash, NULL);
+ for (i = 0; i < old_size; i++) {
+ for (rec_idx = old_hlists[i].first, rec = lyht_get_rec(old_recs, ht->rec_size, rec_idx);
+ rec_idx != LYHT_NO_RECORD;
+ rec_idx = rec->next, rec = lyht_get_rec(old_recs, ht->rec_size, rec_idx)) {
+ LY_ERR ret;
+
+ if (check) {
+ ret = lyht_insert(ht, rec->val, rec->hash, NULL);
+ } else {
+ ret = lyht_insert_no_check(ht, rec->val, rec->hash, NULL);
+ }
assert(!ret);
(void)ret;
@@ -434,117 +235,18 @@ lyht_resize(struct hash_table *ht, int operation)
/* final touches */
free(old_recs);
+ free(old_hlists);
return LY_SUCCESS;
}
/**
- * @brief Search for the first match.
- *
- * @param[in] ht Hash table to search in.
- * @param[in] hash Hash to find.
- * @param[out] rec_p Optional found record.
- * @return LY_SUCCESS hash found, returned its record,
- * @return LY_ENOTFOUND hash not found, returned the record where it would be inserted.
- */
-static LY_ERR
-lyht_find_first(struct hash_table *ht, uint32_t hash, struct ht_rec **rec_p)
-{
- struct ht_rec *rec;
- uint32_t i, idx;
-
- if (rec_p) {
- *rec_p = NULL;
- }
-
- idx = i = hash & (ht->size - 1);
- rec = lyht_get_rec(ht->recs, ht->rec_size, idx);
-
- /* skip through overflow and deleted records */
- while ((rec->hits != 0) && ((rec->hits == -1) || ((rec->hash & (ht->size - 1)) != idx))) {
- if ((rec->hits == -1) && rec_p && !(*rec_p)) {
- /* remember this record for return */
- *rec_p = rec;
- }
- i = (i + 1) % ht->size;
- if (i == idx) {
- /* we went through all the records (very unlikely, but possible when many records are invalid),
- * just return not found */
- assert(!rec_p || *rec_p);
- return LY_ENOTFOUND;
- }
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- }
- if (rec->hits == 0) {
- /* we could not find the value */
- if (rec_p && !*rec_p) {
- *rec_p = rec;
- }
- return LY_ENOTFOUND;
- }
-
- /* we have found a record with equal (shortened) hash */
- if (rec_p) {
- *rec_p = rec;
- }
- return LY_SUCCESS;
-}
-
-/**
- * @brief Search for the next collision.
- *
- * @param[in] ht Hash table to search in.
- * @param[in,out] last Last returned collision record.
- * @param[in] first First collision record (hits > 1).
- * @return LY_SUCCESS when hash collision found, \p last points to this next collision,
- * @return LY_ENOTFOUND when hash collision not found, \p last points to the record where it would be inserted.
- */
-static LY_ERR
-lyht_find_collision(struct hash_table *ht, struct ht_rec **last, struct ht_rec *first)
-{
- struct ht_rec *empty = NULL;
- uint32_t i, idx;
-
- assert(last && *last);
-
- idx = (*last)->hash & (ht->size - 1);
- i = (((unsigned char *)*last) - ht->recs) / ht->rec_size;
-
- do {
- i = (i + 1) % ht->size;
- *last = lyht_get_rec(ht->recs, ht->rec_size, i);
- if (*last == first) {
- /* we went through all the records (very unlikely, but possible when many records are invalid),
- * just return an invalid record */
- assert(empty);
- *last = empty;
- return LY_ENOTFOUND;
- }
-
- if (((*last)->hits == -1) && !empty) {
- empty = *last;
- }
- } while (((*last)->hits != 0) && (((*last)->hits == -1) || (((*last)->hash & (ht->size - 1)) != idx)));
-
- if ((*last)->hits > 0) {
- /* we found a collision */
- assert((*last)->hits == 1);
- return LY_SUCCESS;
- }
-
- /* no next collision found, return the record where it would be inserted */
- if (empty) {
- *last = empty;
- } /* else (*last)->hits == 0, it is already correct */
- return LY_ENOTFOUND;
-}
-
-/**
* @brief Search for a record with specific value and hash.
*
* @param[in] ht Hash table to search in.
* @param[in] val_p Pointer to the value to find.
* @param[in] hash Hash to find.
* @param[in] mod Whether the operation modifies the hash table (insert or remove) or not (find).
+ * @param[in] val_equal Callback for checking value equivalence.
* @param[out] crec_p Optional found first record.
* @param[out] col Optional collision number of @p rec_p, 0 for no collision.
* @param[out] rec_p Found exact matching record, may be a collision of @p crec_p.
@@ -552,12 +254,12 @@ lyht_find_collision(struct hash_table *ht, struct ht_rec **last, struct ht_rec *
* @return LY_SUCCESS if record was found.
*/
static LY_ERR
-lyht_find_rec(struct hash_table *ht, void *val_p, uint32_t hash, ly_bool mod, struct ht_rec **crec_p, uint32_t *col,
- struct ht_rec **rec_p)
+lyht_find_rec(const struct ly_ht *ht, void *val_p, uint32_t hash, ly_bool mod, lyht_value_equal_cb val_equal,
+ struct ly_ht_rec **crec_p, uint32_t *col, struct ly_ht_rec **rec_p)
{
- struct ht_rec *rec, *crec;
- uint32_t i, c;
- LY_ERR r;
+ uint32_t hlist_idx = hash & (ht->size - 1);
+ struct ly_ht_rec *rec;
+ uint32_t rec_idx;
if (crec_p) {
*crec_p = NULL;
@@ -567,53 +269,43 @@ lyht_find_rec(struct hash_table *ht, void *val_p, uint32_t hash, ly_bool mod, st
}
*rec_p = NULL;
- if (lyht_find_first(ht, hash, &rec)) {
- /* not found */
- return LY_ENOTFOUND;
- }
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, mod, ht->cb_data)) {
- /* even the value matches */
- if (crec_p) {
- *crec_p = rec;
- }
- if (col) {
- *col = 0;
- }
- *rec_p = rec;
- return LY_SUCCESS;
- }
-
- /* some collisions, we need to go through them, too */
- crec = rec;
- c = crec->hits;
- for (i = 1; i < c; ++i) {
- r = lyht_find_collision(ht, &rec, crec);
- assert(!r);
- (void)r;
-
- /* compare values */
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, mod, ht->cb_data)) {
+ LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec) {
+ if ((rec->hash == hash) && val_equal(val_p, &rec->val, mod, ht->cb_data)) {
if (crec_p) {
- *crec_p = crec;
- }
- if (col) {
- *col = i;
+ *crec_p = rec;
}
*rec_p = rec;
return LY_SUCCESS;
}
+
+ if (col) {
+ *col = *col + 1;
+ }
}
/* not found even in collisions */
return LY_ENOTFOUND;
}
-LY_ERR
-lyht_find(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
+LIBYANG_API_DEF LY_ERR
+lyht_find(const struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
+{
+ struct ly_ht_rec *rec;
+
+ lyht_find_rec(ht, val_p, hash, 0, ht->val_equal, NULL, NULL, &rec);
+
+ if (rec && match_p) {
+ *match_p = rec->val;
+ }
+ return rec ? LY_SUCCESS : LY_ENOTFOUND;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyht_find_with_val_cb(const struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb val_equal, void **match_p)
{
- struct ht_rec *rec;
+ struct ly_ht_rec *rec;
- lyht_find_rec(ht, val_p, hash, 0, NULL, NULL, &rec);
+ lyht_find_rec(ht, val_p, hash, 0, val_equal ? val_equal : ht->val_equal, NULL, NULL, &rec);
if (rec && match_p) {
*match_p = rec->val;
@@ -621,26 +313,23 @@ lyht_find(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
return rec ? LY_SUCCESS : LY_ENOTFOUND;
}
-LY_ERR
-lyht_find_next_with_collision_cb(struct hash_table *ht, void *val_p, uint32_t hash,
+LIBYANG_API_DEF LY_ERR
+lyht_find_next_with_collision_cb(const struct ly_ht *ht, void *val_p, uint32_t hash,
lyht_value_equal_cb collision_val_equal, void **match_p)
{
- struct ht_rec *rec, *crec;
- uint32_t i, c;
- LY_ERR r;
+ struct ly_ht_rec *rec, *crec;
+ uint32_t rec_idx;
+ uint32_t i;
/* find the record of the previously found value */
- if (lyht_find_rec(ht, val_p, hash, 1, &crec, &i, &rec)) {
+ if (lyht_find_rec(ht, val_p, hash, 1, ht->val_equal, &crec, &i, &rec)) {
/* not found, cannot happen */
LOGINT_RET(NULL);
}
- /* go through collisions and find the next one after the previous one */
- c = crec->hits;
- for (++i; i < c; ++i) {
- r = lyht_find_collision(ht, &rec, crec);
- assert(!r);
- (void)r;
+ for (rec_idx = rec->next, rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx);
+ rec_idx != LYHT_NO_RECORD;
+ rec_idx = rec->next, rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx)) {
if (rec->hash != hash) {
continue;
@@ -667,71 +356,51 @@ lyht_find_next_with_collision_cb(struct hash_table *ht, void *val_p, uint32_t ha
return LY_ENOTFOUND;
}
-LY_ERR
-lyht_find_next(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
+LIBYANG_API_DEF LY_ERR
+lyht_find_next(const struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
{
return lyht_find_next_with_collision_cb(ht, val_p, hash, NULL, match_p);
}
-LY_ERR
-lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
- void **match_p)
+static LY_ERR
+_lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
+ void **match_p, int check)
{
+ uint32_t hlist_idx = hash & (ht->size - 1);
LY_ERR r, ret = LY_SUCCESS;
- struct ht_rec *rec, *crec = NULL;
- int32_t i;
+ struct ly_ht_rec *rec, *prev_rec;
lyht_value_equal_cb old_val_equal = NULL;
+ uint32_t rec_idx;
- if (!lyht_find_first(ht, hash, &rec)) {
- /* we found matching shortened hash */
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
- /* even the value matches */
- if (match_p) {
- *match_p = (void *)&rec->val;
+ if (check) {
+ if (lyht_find_rec(ht, val_p, hash, 1, ht->val_equal, NULL, NULL, &rec) == LY_SUCCESS) {
+ if (rec && match_p) {
+ *match_p = rec->val;
}
return LY_EEXIST;
}
+ }
- /* some collisions, we need to go through them, too */
- crec = rec;
- for (i = 1; i < crec->hits; ++i) {
- r = lyht_find_collision(ht, &rec, crec);
- assert(!r);
-
- /* compare values */
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
- if (match_p) {
- *match_p = (void *)&rec->val;
- }
- return LY_EEXIST;
- }
- }
+ rec_idx = ht->first_free_rec;
+ assert(rec_idx < ht->size);
+ rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx);
+ ht->first_free_rec = rec->next;
- /* value not found, get the record where it will be inserted */
- r = lyht_find_collision(ht, &rec, crec);
- assert(r);
+ if (ht->hlists[hlist_idx].first == LYHT_NO_RECORD) {
+ ht->hlists[hlist_idx].first = rec_idx;
+ } else {
+ prev_rec = lyht_get_rec(ht->recs, ht->rec_size, ht->hlists[hlist_idx].last);
+ prev_rec->next = rec_idx;
}
+ rec->next = LYHT_NO_RECORD;
+ ht->hlists[hlist_idx].last = rec_idx;
- /* insert it into the returned record */
- assert(rec->hits < 1);
- if (rec->hits < 0) {
- --ht->invalid;
- }
rec->hash = hash;
- rec->hits = 1;
- memcpy(&rec->val, val_p, ht->rec_size - (sizeof(struct ht_rec) - 1));
+ memcpy(&rec->val, val_p, ht->rec_size - SIZEOF_LY_HT_REC);
if (match_p) {
*match_p = (void *)&rec->val;
}
- if (crec) {
- /* there was a collision, increase hits */
- if (crec->hits == INT32_MAX) {
- LOGINT(NULL);
- }
- ++crec->hits;
- }
-
/* check size & enlarge if needed */
++ht->used;
if (ht->resize) {
@@ -746,10 +415,11 @@ lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, ly
}
/* enlarge */
- ret = lyht_resize(ht, 1);
+ ret = lyht_resize(ht, 1, check);
/* if hash_table was resized, we need to find new matching value */
if ((ret == LY_SUCCESS) && match_p) {
- lyht_find(ht, val_p, hash, match_p);
+ ret = lyht_find(ht, val_p, hash, match_p);
+ assert(!ret);
}
if (resize_val_equal) {
@@ -760,61 +430,66 @@ lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, ly
return ret;
}
-LY_ERR
-lyht_insert(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
+LIBYANG_API_DEF LY_ERR
+lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
+ void **match_p)
{
- return lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p);
+ return _lyht_insert_with_resize_cb(ht, val_p, hash, resize_val_equal, match_p, 1);
}
-LY_ERR
-lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal)
+LIBYANG_API_DEF LY_ERR
+lyht_insert(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
{
- struct ht_rec *rec, *crec;
- int32_t i;
- ly_bool first_matched = 0;
- LY_ERR r, ret = LY_SUCCESS;
- lyht_value_equal_cb old_val_equal;
+ return _lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p, 1);
+}
- LY_CHECK_ERR_RET(lyht_find_first(ht, hash, &rec), LOGARG(NULL, hash), LY_ENOTFOUND); /* hash not found */
+LIBYANG_API_DEF LY_ERR
+lyht_insert_no_check(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
+{
+ return _lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p, 0);
+}
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
- /* even the value matches */
- first_matched = 1;
- }
+LIBYANG_API_DEF LY_ERR
+lyht_remove_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal)
+{
+ struct ly_ht_rec *found_rec, *prev_rec, *rec;
+ uint32_t hlist_idx = hash & (ht->size - 1);
+ LY_ERR r, ret = LY_SUCCESS;
+ lyht_value_equal_cb old_val_equal = NULL;
+ uint32_t prev_rec_idx;
+ uint32_t rec_idx;
- /* we always need to go through collisions */
- crec = rec;
- for (i = 1; i < crec->hits; ++i) {
- r = lyht_find_collision(ht, &rec, crec);
- assert(!r);
+ if (lyht_find_rec(ht, val_p, hash, 1, ht->val_equal, NULL, NULL, &found_rec)) {
+ LOGARG(NULL, hash);
+ return LY_ENOTFOUND;
+ }
- /* compare values */
- if (!first_matched && (rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
+ prev_rec_idx = LYHT_NO_RECORD;
+ LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec) {
+ if (rec == found_rec) {
break;
}
+ prev_rec_idx = rec_idx;
}
- if (i < crec->hits) {
- /* one of collisions matched, reduce collision count, remove the record */
- assert(!first_matched);
- --crec->hits;
- rec->hits = -1;
- } else if (first_matched) {
- /* the first record matches */
- if (crec != rec) {
- /* ... so put the last collision in its place */
- rec->hits = crec->hits - 1;
- memcpy(crec, rec, ht->rec_size);
+ if (prev_rec_idx == LYHT_NO_RECORD) {
+ ht->hlists[hlist_idx].first = rec->next;
+ if (rec->next == LYHT_NO_RECORD) {
+ ht->hlists[hlist_idx].last = LYHT_NO_RECORD;
}
- rec->hits = -1;
} else {
- /* value not found even in collisions */
- return LY_ENOTFOUND;
+ prev_rec = lyht_get_rec(ht->recs, ht->rec_size, prev_rec_idx);
+ prev_rec->next = rec->next;
+ if (rec->next == LYHT_NO_RECORD) {
+ ht->hlists[hlist_idx].last = prev_rec_idx;
+ }
}
+ rec->next = ht->first_free_rec;
+ ht->first_free_rec = rec_idx;
+
/* check size & shrink if needed */
--ht->used;
- ++ht->invalid;
if (ht->resize == 2) {
r = (ht->used * LYHT_HUNDRED_PERCENTAGE) / ht->size;
if ((r < LYHT_SHRINK_PERCENTAGE) && (ht->size > LYHT_MIN_SIZE)) {
@@ -823,7 +498,7 @@ lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, ly
}
/* shrink */
- ret = lyht_resize(ht, -1);
+ ret = lyht_resize(ht, -1, 1);
if (resize_val_equal) {
lyht_set_cb(ht, old_val_equal);
@@ -831,50 +506,29 @@ lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, ly
}
}
- /* rehash all records if needed */
- r = ((ht->size - ht->used - ht->invalid) * 100) / ht->size;
- if (r < LYHT_REHASH_PERCENTAGE) {
- if (resize_val_equal) {
- old_val_equal = lyht_set_cb(ht, resize_val_equal);
- }
-
- /* rehash */
- 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)
+LIBYANG_API_DEF LY_ERR
+lyht_remove(struct ly_ht *ht, void *val_p, uint32_t hash)
{
return lyht_remove_with_resize_cb(ht, val_p, hash, NULL);
}
-uint32_t
+LIBYANG_API_DEF uint32_t
lyht_get_fixed_size(uint32_t item_count)
{
- uint32_t i, size = 0;
-
- /* detect number of upper zero bits in the items' counter value ... */
- for (i = (sizeof item_count * CHAR_BIT) - 1; i > 0; i--) {
- size = item_count << i;
- size = size >> i;
- if (size == item_count) {
- break;
- }
+ if (item_count == 0) {
+ return 1;
}
- assert(i);
-
- /* ... and then we convert it to the position of the highest non-zero bit ... */
- i = (sizeof item_count * CHAR_BIT) - i;
- /* ... and by using it to shift 1 to the left we get the closest sufficient hash table size */
- size = 1 << i;
+ /* return next power of 2 (greater or equal) */
+ item_count--;
+ item_count |= item_count >> 1;
+ item_count |= item_count >> 2;
+ item_count |= item_count >> 4;
+ item_count |= item_count >> 8;
+ item_count |= item_count >> 16;
- return size;
+ return item_count + 1;
}