hash: lazy-alloc the table in Curl_hash_add()

This makes Curl_hash_init() infallible which saves error paths.

Closes #8132
This commit is contained in:
Daniel Stenberg 2021-12-10 12:54:17 +01:00
parent e43ad4b474
commit 254f7bd78a
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
10 changed files with 78 additions and 83 deletions

View file

@ -53,32 +53,25 @@ hash_element_dtor(void *user, void *element)
* @unittest: 1602
* @unittest: 1603
*/
int
void
Curl_hash_init(struct Curl_hash *h,
int slots,
hash_function hfunc,
comp_function comparator,
Curl_hash_dtor dtor)
{
if(!slots || !hfunc || !comparator ||!dtor) {
return 1; /* failure */
}
DEBUGASSERT(h);
DEBUGASSERT(slots);
DEBUGASSERT(hfunc);
DEBUGASSERT(comparator);
DEBUGASSERT(dtor);
h->table = NULL;
h->hash_func = hfunc;
h->comp_func = comparator;
h->dtor = dtor;
h->size = 0;
h->slots = slots;
h->table = malloc(slots * sizeof(struct Curl_llist));
if(h->table) {
int i;
for(i = 0; i < slots; ++i)
Curl_llist_init(&h->table[i], (Curl_llist_dtor) hash_element_dtor);
return 0; /* fine */
}
h->slots = 0;
return 1; /* failure */
}
static struct Curl_hash_element *
@ -98,8 +91,9 @@ mk_hash_element(const void *key, size_t key_len, const void *p)
#define FETCH_LIST(x,y,z) &x->table[x->hash_func(y, z, x->slots)]
/* Insert the data in the hash. If there already was a match in the hash,
* that data is replaced.
/* Insert the data in the hash. If there already was a match in the hash, that
* data is replaced. This function also "lazily" allocates the table if
* needed, as it isn't done in the _init function (anymore).
*
* @unittest: 1305
* @unittest: 1602
@ -111,7 +105,18 @@ Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p)
struct Curl_hash_element *he;
struct Curl_llist_element *le;
struct Curl_llist *l;
DEBUGASSERT(h);
DEBUGASSERT(h->slots);
if(!h->table) {
int i;
h->table = malloc(h->slots * sizeof(struct Curl_llist));
if(!h->table)
return NULL; /* OOM */
for(i = 0; i < h->slots; ++i)
Curl_llist_init(&h->table[i], hash_element_dtor);
}
l = FETCH_LIST(h, key, key_len);
for(le = l->head; le; le = le->next) {
@ -142,15 +147,19 @@ int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len)
{
struct Curl_llist_element *le;
struct Curl_llist *l;
DEBUGASSERT(h->slots);
l = FETCH_LIST(h, key, key_len);
for(le = l->head; le; le = le->next) {
struct Curl_hash_element *he = le->ptr;
if(h->comp_func(he->key, he->key_len, key, key_len)) {
Curl_llist_remove(l, le, (void *) h);
--h->size;
return 0;
DEBUGASSERT(h);
DEBUGASSERT(h->slots);
if(h->table) {
l = FETCH_LIST(h, key, key_len);
for(le = l->head; le; le = le->next) {
struct Curl_hash_element *he = le->ptr;
if(h->comp_func(he->key, he->key_len, key, key_len)) {
Curl_llist_remove(l, le, (void *) h);
--h->size;
return 0;
}
}
}
return 1;
@ -166,7 +175,8 @@ Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len)
struct Curl_llist_element *le;
struct Curl_llist *l;
if(h) {
DEBUGASSERT(h);
if(h->table) {
DEBUGASSERT(h->slots);
l = FETCH_LIST(h, key, key_len);
for(le = l->head; le; le = le->next) {
@ -209,13 +219,13 @@ Curl_hash_apply(Curl_hash *h, void *user,
void
Curl_hash_destroy(struct Curl_hash *h)
{
int i;
for(i = 0; i < h->slots; ++i) {
Curl_llist_destroy(&h->table[i], (void *) h);
if(h->table) {
int i;
for(i = 0; i < h->slots; ++i) {
Curl_llist_destroy(&h->table[i], (void *) h);
}
Curl_safefree(h->table);
}
Curl_safefree(h->table);
h->size = 0;
h->slots = 0;
}
@ -240,7 +250,7 @@ Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user,
struct Curl_llist *list;
int i;
if(!h)
if(!h || !h->table)
return;
for(i = 0; i < h->slots; ++i) {
@ -295,6 +305,9 @@ Curl_hash_next_element(struct Curl_hash_iterator *iter)
{
struct Curl_hash *h = iter->hash;
if(!h->table)
return NULL; /* empty hash, nothing to return */
/* Get the next element in the current list, if any */
if(iter->current_element)
iter->current_element = iter->current_element->next;