113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#include <curl/curl.h>
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci#include "hash.h"
3013498266Sopenharmony_ci#include "llist.h"
3113498266Sopenharmony_ci#include "curl_memory.h"
3213498266Sopenharmony_ci
3313498266Sopenharmony_ci/* The last #include file should be: */
3413498266Sopenharmony_ci#include "memdebug.h"
3513498266Sopenharmony_ci
3613498266Sopenharmony_cistatic void
3713498266Sopenharmony_cihash_element_dtor(void *user, void *element)
3813498266Sopenharmony_ci{
3913498266Sopenharmony_ci  struct Curl_hash *h = (struct Curl_hash *) user;
4013498266Sopenharmony_ci  struct Curl_hash_element *e = (struct Curl_hash_element *) element;
4113498266Sopenharmony_ci
4213498266Sopenharmony_ci  if(e->ptr) {
4313498266Sopenharmony_ci    h->dtor(e->ptr);
4413498266Sopenharmony_ci    e->ptr = NULL;
4513498266Sopenharmony_ci  }
4613498266Sopenharmony_ci
4713498266Sopenharmony_ci  e->key_len = 0;
4813498266Sopenharmony_ci
4913498266Sopenharmony_ci  free(e);
5013498266Sopenharmony_ci}
5113498266Sopenharmony_ci
5213498266Sopenharmony_ci/* Initializes a hash structure.
5313498266Sopenharmony_ci * Return 1 on error, 0 is fine.
5413498266Sopenharmony_ci *
5513498266Sopenharmony_ci * @unittest: 1602
5613498266Sopenharmony_ci * @unittest: 1603
5713498266Sopenharmony_ci */
5813498266Sopenharmony_civoid
5913498266Sopenharmony_ciCurl_hash_init(struct Curl_hash *h,
6013498266Sopenharmony_ci               int slots,
6113498266Sopenharmony_ci               hash_function hfunc,
6213498266Sopenharmony_ci               comp_function comparator,
6313498266Sopenharmony_ci               Curl_hash_dtor dtor)
6413498266Sopenharmony_ci{
6513498266Sopenharmony_ci  DEBUGASSERT(h);
6613498266Sopenharmony_ci  DEBUGASSERT(slots);
6713498266Sopenharmony_ci  DEBUGASSERT(hfunc);
6813498266Sopenharmony_ci  DEBUGASSERT(comparator);
6913498266Sopenharmony_ci  DEBUGASSERT(dtor);
7013498266Sopenharmony_ci
7113498266Sopenharmony_ci  h->table = NULL;
7213498266Sopenharmony_ci  h->hash_func = hfunc;
7313498266Sopenharmony_ci  h->comp_func = comparator;
7413498266Sopenharmony_ci  h->dtor = dtor;
7513498266Sopenharmony_ci  h->size = 0;
7613498266Sopenharmony_ci  h->slots = slots;
7713498266Sopenharmony_ci}
7813498266Sopenharmony_ci
7913498266Sopenharmony_cistatic struct Curl_hash_element *
8013498266Sopenharmony_cimk_hash_element(const void *key, size_t key_len, const void *p)
8113498266Sopenharmony_ci{
8213498266Sopenharmony_ci  /* allocate the struct plus memory after it to store the key */
8313498266Sopenharmony_ci  struct Curl_hash_element *he = malloc(sizeof(struct Curl_hash_element) +
8413498266Sopenharmony_ci                                        key_len);
8513498266Sopenharmony_ci  if(he) {
8613498266Sopenharmony_ci    /* copy the key */
8713498266Sopenharmony_ci    memcpy(he->key, key, key_len);
8813498266Sopenharmony_ci    he->key_len = key_len;
8913498266Sopenharmony_ci    he->ptr = (void *) p;
9013498266Sopenharmony_ci  }
9113498266Sopenharmony_ci  return he;
9213498266Sopenharmony_ci}
9313498266Sopenharmony_ci
9413498266Sopenharmony_ci#define FETCH_LIST(x,y,z) &x->table[x->hash_func(y, z, x->slots)]
9513498266Sopenharmony_ci
9613498266Sopenharmony_ci/* Insert the data in the hash. If there already was a match in the hash, that
9713498266Sopenharmony_ci * data is replaced. This function also "lazily" allocates the table if
9813498266Sopenharmony_ci * needed, as it isn't done in the _init function (anymore).
9913498266Sopenharmony_ci *
10013498266Sopenharmony_ci * @unittest: 1305
10113498266Sopenharmony_ci * @unittest: 1602
10213498266Sopenharmony_ci * @unittest: 1603
10313498266Sopenharmony_ci */
10413498266Sopenharmony_civoid *
10513498266Sopenharmony_ciCurl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p)
10613498266Sopenharmony_ci{
10713498266Sopenharmony_ci  struct Curl_hash_element  *he;
10813498266Sopenharmony_ci  struct Curl_llist_element *le;
10913498266Sopenharmony_ci  struct Curl_llist *l;
11013498266Sopenharmony_ci
11113498266Sopenharmony_ci  DEBUGASSERT(h);
11213498266Sopenharmony_ci  DEBUGASSERT(h->slots);
11313498266Sopenharmony_ci  if(!h->table) {
11413498266Sopenharmony_ci    int i;
11513498266Sopenharmony_ci    h->table = malloc(h->slots * sizeof(struct Curl_llist));
11613498266Sopenharmony_ci    if(!h->table)
11713498266Sopenharmony_ci      return NULL; /* OOM */
11813498266Sopenharmony_ci    for(i = 0; i < h->slots; ++i)
11913498266Sopenharmony_ci      Curl_llist_init(&h->table[i], hash_element_dtor);
12013498266Sopenharmony_ci  }
12113498266Sopenharmony_ci
12213498266Sopenharmony_ci  l = FETCH_LIST(h, key, key_len);
12313498266Sopenharmony_ci
12413498266Sopenharmony_ci  for(le = l->head; le; le = le->next) {
12513498266Sopenharmony_ci    he = (struct Curl_hash_element *) le->ptr;
12613498266Sopenharmony_ci    if(h->comp_func(he->key, he->key_len, key, key_len)) {
12713498266Sopenharmony_ci      Curl_llist_remove(l, le, (void *)h);
12813498266Sopenharmony_ci      --h->size;
12913498266Sopenharmony_ci      break;
13013498266Sopenharmony_ci    }
13113498266Sopenharmony_ci  }
13213498266Sopenharmony_ci
13313498266Sopenharmony_ci  he = mk_hash_element(key, key_len, p);
13413498266Sopenharmony_ci  if(he) {
13513498266Sopenharmony_ci    Curl_llist_insert_next(l, l->tail, he, &he->list);
13613498266Sopenharmony_ci    ++h->size;
13713498266Sopenharmony_ci    return p; /* return the new entry */
13813498266Sopenharmony_ci  }
13913498266Sopenharmony_ci
14013498266Sopenharmony_ci  return NULL; /* failure */
14113498266Sopenharmony_ci}
14213498266Sopenharmony_ci
14313498266Sopenharmony_ci/* Remove the identified hash entry.
14413498266Sopenharmony_ci * Returns non-zero on failure.
14513498266Sopenharmony_ci *
14613498266Sopenharmony_ci * @unittest: 1603
14713498266Sopenharmony_ci */
14813498266Sopenharmony_ciint Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len)
14913498266Sopenharmony_ci{
15013498266Sopenharmony_ci  struct Curl_llist_element *le;
15113498266Sopenharmony_ci  struct Curl_llist *l;
15213498266Sopenharmony_ci
15313498266Sopenharmony_ci  DEBUGASSERT(h);
15413498266Sopenharmony_ci  DEBUGASSERT(h->slots);
15513498266Sopenharmony_ci  if(h->table) {
15613498266Sopenharmony_ci    l = FETCH_LIST(h, key, key_len);
15713498266Sopenharmony_ci
15813498266Sopenharmony_ci    for(le = l->head; le; le = le->next) {
15913498266Sopenharmony_ci      struct Curl_hash_element *he = le->ptr;
16013498266Sopenharmony_ci      if(h->comp_func(he->key, he->key_len, key, key_len)) {
16113498266Sopenharmony_ci        Curl_llist_remove(l, le, (void *) h);
16213498266Sopenharmony_ci        --h->size;
16313498266Sopenharmony_ci        return 0;
16413498266Sopenharmony_ci      }
16513498266Sopenharmony_ci    }
16613498266Sopenharmony_ci  }
16713498266Sopenharmony_ci  return 1;
16813498266Sopenharmony_ci}
16913498266Sopenharmony_ci
17013498266Sopenharmony_ci/* Retrieves a hash element.
17113498266Sopenharmony_ci *
17213498266Sopenharmony_ci * @unittest: 1603
17313498266Sopenharmony_ci */
17413498266Sopenharmony_civoid *
17513498266Sopenharmony_ciCurl_hash_pick(struct Curl_hash *h, void *key, size_t key_len)
17613498266Sopenharmony_ci{
17713498266Sopenharmony_ci  struct Curl_llist_element *le;
17813498266Sopenharmony_ci  struct Curl_llist *l;
17913498266Sopenharmony_ci
18013498266Sopenharmony_ci  DEBUGASSERT(h);
18113498266Sopenharmony_ci  if(h->table) {
18213498266Sopenharmony_ci    DEBUGASSERT(h->slots);
18313498266Sopenharmony_ci    l = FETCH_LIST(h, key, key_len);
18413498266Sopenharmony_ci    for(le = l->head; le; le = le->next) {
18513498266Sopenharmony_ci      struct Curl_hash_element *he = le->ptr;
18613498266Sopenharmony_ci      if(h->comp_func(he->key, he->key_len, key, key_len)) {
18713498266Sopenharmony_ci        return he->ptr;
18813498266Sopenharmony_ci      }
18913498266Sopenharmony_ci    }
19013498266Sopenharmony_ci  }
19113498266Sopenharmony_ci
19213498266Sopenharmony_ci  return NULL;
19313498266Sopenharmony_ci}
19413498266Sopenharmony_ci
19513498266Sopenharmony_ci#if defined(DEBUGBUILD) && defined(AGGRESSIVE_TEST)
19613498266Sopenharmony_civoid
19713498266Sopenharmony_ciCurl_hash_apply(Curl_hash *h, void *user,
19813498266Sopenharmony_ci                void (*cb)(void *user, void *ptr))
19913498266Sopenharmony_ci{
20013498266Sopenharmony_ci  struct Curl_llist_element  *le;
20113498266Sopenharmony_ci  int                  i;
20213498266Sopenharmony_ci
20313498266Sopenharmony_ci  for(i = 0; i < h->slots; ++i) {
20413498266Sopenharmony_ci    for(le = (h->table[i])->head;
20513498266Sopenharmony_ci        le;
20613498266Sopenharmony_ci        le = le->next) {
20713498266Sopenharmony_ci      Curl_hash_element *el = le->ptr;
20813498266Sopenharmony_ci      cb(user, el->ptr);
20913498266Sopenharmony_ci    }
21013498266Sopenharmony_ci  }
21113498266Sopenharmony_ci}
21213498266Sopenharmony_ci#endif
21313498266Sopenharmony_ci
21413498266Sopenharmony_ci/* Destroys all the entries in the given hash and resets its attributes,
21513498266Sopenharmony_ci * prepping the given hash for [static|dynamic] deallocation.
21613498266Sopenharmony_ci *
21713498266Sopenharmony_ci * @unittest: 1305
21813498266Sopenharmony_ci * @unittest: 1602
21913498266Sopenharmony_ci * @unittest: 1603
22013498266Sopenharmony_ci */
22113498266Sopenharmony_civoid
22213498266Sopenharmony_ciCurl_hash_destroy(struct Curl_hash *h)
22313498266Sopenharmony_ci{
22413498266Sopenharmony_ci  if(h->table) {
22513498266Sopenharmony_ci    int i;
22613498266Sopenharmony_ci    for(i = 0; i < h->slots; ++i) {
22713498266Sopenharmony_ci      Curl_llist_destroy(&h->table[i], (void *) h);
22813498266Sopenharmony_ci    }
22913498266Sopenharmony_ci    Curl_safefree(h->table);
23013498266Sopenharmony_ci  }
23113498266Sopenharmony_ci  h->size = 0;
23213498266Sopenharmony_ci  h->slots = 0;
23313498266Sopenharmony_ci}
23413498266Sopenharmony_ci
23513498266Sopenharmony_ci/* Removes all the entries in the given hash.
23613498266Sopenharmony_ci *
23713498266Sopenharmony_ci * @unittest: 1602
23813498266Sopenharmony_ci */
23913498266Sopenharmony_civoid
24013498266Sopenharmony_ciCurl_hash_clean(struct Curl_hash *h)
24113498266Sopenharmony_ci{
24213498266Sopenharmony_ci  Curl_hash_clean_with_criterium(h, NULL, NULL);
24313498266Sopenharmony_ci}
24413498266Sopenharmony_ci
24513498266Sopenharmony_ci/* Cleans all entries that pass the comp function criteria. */
24613498266Sopenharmony_civoid
24713498266Sopenharmony_ciCurl_hash_clean_with_criterium(struct Curl_hash *h, void *user,
24813498266Sopenharmony_ci                               int (*comp)(void *, void *))
24913498266Sopenharmony_ci{
25013498266Sopenharmony_ci  struct Curl_llist_element *le;
25113498266Sopenharmony_ci  struct Curl_llist_element *lnext;
25213498266Sopenharmony_ci  struct Curl_llist *list;
25313498266Sopenharmony_ci  int i;
25413498266Sopenharmony_ci
25513498266Sopenharmony_ci  if(!h || !h->table)
25613498266Sopenharmony_ci    return;
25713498266Sopenharmony_ci
25813498266Sopenharmony_ci  for(i = 0; i < h->slots; ++i) {
25913498266Sopenharmony_ci    list = &h->table[i];
26013498266Sopenharmony_ci    le = list->head; /* get first list entry */
26113498266Sopenharmony_ci    while(le) {
26213498266Sopenharmony_ci      struct Curl_hash_element *he = le->ptr;
26313498266Sopenharmony_ci      lnext = le->next;
26413498266Sopenharmony_ci      /* ask the callback function if we shall remove this entry or not */
26513498266Sopenharmony_ci      if(!comp || comp(user, he->ptr)) {
26613498266Sopenharmony_ci        Curl_llist_remove(list, le, (void *) h);
26713498266Sopenharmony_ci        --h->size; /* one less entry in the hash now */
26813498266Sopenharmony_ci      }
26913498266Sopenharmony_ci      le = lnext;
27013498266Sopenharmony_ci    }
27113498266Sopenharmony_ci  }
27213498266Sopenharmony_ci}
27313498266Sopenharmony_ci
27413498266Sopenharmony_cisize_t Curl_hash_str(void *key, size_t key_length, size_t slots_num)
27513498266Sopenharmony_ci{
27613498266Sopenharmony_ci  const char *key_str = (const char *) key;
27713498266Sopenharmony_ci  const char *end = key_str + key_length;
27813498266Sopenharmony_ci  size_t h = 5381;
27913498266Sopenharmony_ci
28013498266Sopenharmony_ci  while(key_str < end) {
28113498266Sopenharmony_ci    h += h << 5;
28213498266Sopenharmony_ci    h ^= *key_str++;
28313498266Sopenharmony_ci  }
28413498266Sopenharmony_ci
28513498266Sopenharmony_ci  return (h % slots_num);
28613498266Sopenharmony_ci}
28713498266Sopenharmony_ci
28813498266Sopenharmony_cisize_t Curl_str_key_compare(void *k1, size_t key1_len,
28913498266Sopenharmony_ci                            void *k2, size_t key2_len)
29013498266Sopenharmony_ci{
29113498266Sopenharmony_ci  if((key1_len == key2_len) && !memcmp(k1, k2, key1_len))
29213498266Sopenharmony_ci    return 1;
29313498266Sopenharmony_ci
29413498266Sopenharmony_ci  return 0;
29513498266Sopenharmony_ci}
29613498266Sopenharmony_ci
29713498266Sopenharmony_civoid Curl_hash_start_iterate(struct Curl_hash *hash,
29813498266Sopenharmony_ci                             struct Curl_hash_iterator *iter)
29913498266Sopenharmony_ci{
30013498266Sopenharmony_ci  iter->hash = hash;
30113498266Sopenharmony_ci  iter->slot_index = 0;
30213498266Sopenharmony_ci  iter->current_element = NULL;
30313498266Sopenharmony_ci}
30413498266Sopenharmony_ci
30513498266Sopenharmony_cistruct Curl_hash_element *
30613498266Sopenharmony_ciCurl_hash_next_element(struct Curl_hash_iterator *iter)
30713498266Sopenharmony_ci{
30813498266Sopenharmony_ci  struct Curl_hash *h = iter->hash;
30913498266Sopenharmony_ci
31013498266Sopenharmony_ci  if(!h->table)
31113498266Sopenharmony_ci    return NULL; /* empty hash, nothing to return */
31213498266Sopenharmony_ci
31313498266Sopenharmony_ci  /* Get the next element in the current list, if any */
31413498266Sopenharmony_ci  if(iter->current_element)
31513498266Sopenharmony_ci    iter->current_element = iter->current_element->next;
31613498266Sopenharmony_ci
31713498266Sopenharmony_ci  /* If we have reached the end of the list, find the next one */
31813498266Sopenharmony_ci  if(!iter->current_element) {
31913498266Sopenharmony_ci    int i;
32013498266Sopenharmony_ci    for(i = iter->slot_index; i < h->slots; i++) {
32113498266Sopenharmony_ci      if(h->table[i].head) {
32213498266Sopenharmony_ci        iter->current_element = h->table[i].head;
32313498266Sopenharmony_ci        iter->slot_index = i + 1;
32413498266Sopenharmony_ci        break;
32513498266Sopenharmony_ci      }
32613498266Sopenharmony_ci    }
32713498266Sopenharmony_ci  }
32813498266Sopenharmony_ci
32913498266Sopenharmony_ci  if(iter->current_element) {
33013498266Sopenharmony_ci    struct Curl_hash_element *he = iter->current_element->ptr;
33113498266Sopenharmony_ci    return he;
33213498266Sopenharmony_ci  }
33313498266Sopenharmony_ci  return NULL;
33413498266Sopenharmony_ci}
33513498266Sopenharmony_ci
33613498266Sopenharmony_ci#if 0 /* useful function for debugging hashes and their contents */
33713498266Sopenharmony_civoid Curl_hash_print(struct Curl_hash *h,
33813498266Sopenharmony_ci                     void (*func)(void *))
33913498266Sopenharmony_ci{
34013498266Sopenharmony_ci  struct Curl_hash_iterator iter;
34113498266Sopenharmony_ci  struct Curl_hash_element *he;
34213498266Sopenharmony_ci  int last_index = -1;
34313498266Sopenharmony_ci
34413498266Sopenharmony_ci  if(!h)
34513498266Sopenharmony_ci    return;
34613498266Sopenharmony_ci
34713498266Sopenharmony_ci  fprintf(stderr, "=Hash dump=\n");
34813498266Sopenharmony_ci
34913498266Sopenharmony_ci  Curl_hash_start_iterate(h, &iter);
35013498266Sopenharmony_ci
35113498266Sopenharmony_ci  he = Curl_hash_next_element(&iter);
35213498266Sopenharmony_ci  while(he) {
35313498266Sopenharmony_ci    if(iter.slot_index != last_index) {
35413498266Sopenharmony_ci      fprintf(stderr, "index %d:", iter.slot_index);
35513498266Sopenharmony_ci      if(last_index >= 0) {
35613498266Sopenharmony_ci        fprintf(stderr, "\n");
35713498266Sopenharmony_ci      }
35813498266Sopenharmony_ci      last_index = iter.slot_index;
35913498266Sopenharmony_ci    }
36013498266Sopenharmony_ci
36113498266Sopenharmony_ci    if(func)
36213498266Sopenharmony_ci      func(he->ptr);
36313498266Sopenharmony_ci    else
36413498266Sopenharmony_ci      fprintf(stderr, " [%p]", (void *)he->ptr);
36513498266Sopenharmony_ci
36613498266Sopenharmony_ci    he = Curl_hash_next_element(&iter);
36713498266Sopenharmony_ci  }
36813498266Sopenharmony_ci  fprintf(stderr, "\n");
36913498266Sopenharmony_ci}
37013498266Sopenharmony_ci#endif
371