11cb0ef41Sopenharmony_ci/* MIT License
21cb0ef41Sopenharmony_ci *
31cb0ef41Sopenharmony_ci * Copyright (c) 2023 Brad House
41cb0ef41Sopenharmony_ci *
51cb0ef41Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
61cb0ef41Sopenharmony_ci * of this software and associated documentation files (the "Software"), to deal
71cb0ef41Sopenharmony_ci * in the Software without restriction, including without limitation the rights
81cb0ef41Sopenharmony_ci * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
91cb0ef41Sopenharmony_ci * copies of the Software, and to permit persons to whom the Software is
101cb0ef41Sopenharmony_ci * furnished to do so, subject to the following conditions:
111cb0ef41Sopenharmony_ci *
121cb0ef41Sopenharmony_ci * The above copyright notice and this permission notice (including the next
131cb0ef41Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
141cb0ef41Sopenharmony_ci * Software.
151cb0ef41Sopenharmony_ci *
161cb0ef41Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
171cb0ef41Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
181cb0ef41Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
191cb0ef41Sopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
201cb0ef41Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
211cb0ef41Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
221cb0ef41Sopenharmony_ci * SOFTWARE.
231cb0ef41Sopenharmony_ci *
241cb0ef41Sopenharmony_ci * SPDX-License-Identifier: MIT
251cb0ef41Sopenharmony_ci */
261cb0ef41Sopenharmony_ci#include "ares_setup.h"
271cb0ef41Sopenharmony_ci#include "ares.h"
281cb0ef41Sopenharmony_ci#include "ares_private.h"
291cb0ef41Sopenharmony_ci#include "ares__llist.h"
301cb0ef41Sopenharmony_ci#include "ares__htable.h"
311cb0ef41Sopenharmony_ci
321cb0ef41Sopenharmony_ci#define ARES__HTABLE_MAX_BUCKETS    (1U << 24)
331cb0ef41Sopenharmony_ci#define ARES__HTABLE_MIN_BUCKETS    (1U << 4)
341cb0ef41Sopenharmony_ci#define ARES__HTABLE_EXPAND_PERCENT 75
351cb0ef41Sopenharmony_ci
361cb0ef41Sopenharmony_cistruct ares__htable {
371cb0ef41Sopenharmony_ci  ares__htable_hashfunc_t    hash;
381cb0ef41Sopenharmony_ci  ares__htable_bucket_key_t  bucket_key;
391cb0ef41Sopenharmony_ci  ares__htable_bucket_free_t bucket_free;
401cb0ef41Sopenharmony_ci  ares__htable_key_eq_t      key_eq;
411cb0ef41Sopenharmony_ci  unsigned int               seed;
421cb0ef41Sopenharmony_ci  unsigned int               size;
431cb0ef41Sopenharmony_ci  size_t                     num_keys;
441cb0ef41Sopenharmony_ci  size_t                     num_collisions;
451cb0ef41Sopenharmony_ci  /* NOTE: if we converted buckets into ares__slist_t we could guarantee on
461cb0ef41Sopenharmony_ci   *       hash collisions we would have O(log n) worst case insert and search
471cb0ef41Sopenharmony_ci   *       performance.  (We'd also need to make key_eq into a key_cmp to
481cb0ef41Sopenharmony_ci   *       support sort).  That said, risk with a random hash seed is near zero,
491cb0ef41Sopenharmony_ci   *       and ares__slist_t is heavier weight, so I think using ares__llist_t
501cb0ef41Sopenharmony_ci   *       is an overall win. */
511cb0ef41Sopenharmony_ci  ares__llist_t            **buckets;
521cb0ef41Sopenharmony_ci};
531cb0ef41Sopenharmony_ci
541cb0ef41Sopenharmony_cistatic unsigned int ares__htable_generate_seed(ares__htable_t *htable)
551cb0ef41Sopenharmony_ci{
561cb0ef41Sopenharmony_ci  unsigned int seed = 0;
571cb0ef41Sopenharmony_ci  time_t       t    = time(NULL);
581cb0ef41Sopenharmony_ci
591cb0ef41Sopenharmony_ci  /* Mix stack address, heap address, and time to generate a random seed, it
601cb0ef41Sopenharmony_ci   * doesn't have to be super secure, just quick.  Likelihood of a hash
611cb0ef41Sopenharmony_ci   * collision attack is very low with a small amount of effort */
621cb0ef41Sopenharmony_ci  seed |= (unsigned int)((size_t)htable & 0xFFFFFFFF);
631cb0ef41Sopenharmony_ci  seed |= (unsigned int)((size_t)&seed & 0xFFFFFFFF);
641cb0ef41Sopenharmony_ci  seed |= (unsigned int)(t & 0xFFFFFFFF);
651cb0ef41Sopenharmony_ci  return seed;
661cb0ef41Sopenharmony_ci}
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_cistatic void ares__htable_buckets_destroy(ares__llist_t **buckets,
691cb0ef41Sopenharmony_ci                                         unsigned int    size,
701cb0ef41Sopenharmony_ci                                         ares_bool_t     destroy_vals)
711cb0ef41Sopenharmony_ci{
721cb0ef41Sopenharmony_ci  unsigned int i;
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_ci  if (buckets == NULL) {
751cb0ef41Sopenharmony_ci    return;
761cb0ef41Sopenharmony_ci  }
771cb0ef41Sopenharmony_ci
781cb0ef41Sopenharmony_ci  for (i = 0; i < size; i++) {
791cb0ef41Sopenharmony_ci    if (buckets[i] == NULL) {
801cb0ef41Sopenharmony_ci      continue;
811cb0ef41Sopenharmony_ci    }
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ci    if (!destroy_vals) {
841cb0ef41Sopenharmony_ci      ares__llist_replace_destructor(buckets[i], NULL);
851cb0ef41Sopenharmony_ci    }
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci    ares__llist_destroy(buckets[i]);
881cb0ef41Sopenharmony_ci  }
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_ci  ares_free(buckets);
911cb0ef41Sopenharmony_ci}
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_civoid ares__htable_destroy(ares__htable_t *htable)
941cb0ef41Sopenharmony_ci{
951cb0ef41Sopenharmony_ci  if (htable == NULL) {
961cb0ef41Sopenharmony_ci    return;
971cb0ef41Sopenharmony_ci  }
981cb0ef41Sopenharmony_ci  ares__htable_buckets_destroy(htable->buckets, htable->size, ARES_TRUE);
991cb0ef41Sopenharmony_ci  ares_free(htable);
1001cb0ef41Sopenharmony_ci}
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ciares__htable_t *ares__htable_create(ares__htable_hashfunc_t    hash_func,
1031cb0ef41Sopenharmony_ci                                    ares__htable_bucket_key_t  bucket_key,
1041cb0ef41Sopenharmony_ci                                    ares__htable_bucket_free_t bucket_free,
1051cb0ef41Sopenharmony_ci                                    ares__htable_key_eq_t      key_eq)
1061cb0ef41Sopenharmony_ci{
1071cb0ef41Sopenharmony_ci  ares__htable_t *htable = NULL;
1081cb0ef41Sopenharmony_ci
1091cb0ef41Sopenharmony_ci  if (hash_func == NULL || bucket_key == NULL || bucket_free == NULL ||
1101cb0ef41Sopenharmony_ci      key_eq == NULL) {
1111cb0ef41Sopenharmony_ci    goto fail;
1121cb0ef41Sopenharmony_ci  }
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ci  htable = ares_malloc_zero(sizeof(*htable));
1151cb0ef41Sopenharmony_ci  if (htable == NULL) {
1161cb0ef41Sopenharmony_ci    goto fail;
1171cb0ef41Sopenharmony_ci  }
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_ci  htable->hash        = hash_func;
1201cb0ef41Sopenharmony_ci  htable->bucket_key  = bucket_key;
1211cb0ef41Sopenharmony_ci  htable->bucket_free = bucket_free;
1221cb0ef41Sopenharmony_ci  htable->key_eq      = key_eq;
1231cb0ef41Sopenharmony_ci  htable->seed        = ares__htable_generate_seed(htable);
1241cb0ef41Sopenharmony_ci  htable->size        = ARES__HTABLE_MIN_BUCKETS;
1251cb0ef41Sopenharmony_ci  htable->buckets = ares_malloc_zero(sizeof(*htable->buckets) * htable->size);
1261cb0ef41Sopenharmony_ci
1271cb0ef41Sopenharmony_ci  if (htable->buckets == NULL) {
1281cb0ef41Sopenharmony_ci    goto fail;
1291cb0ef41Sopenharmony_ci  }
1301cb0ef41Sopenharmony_ci
1311cb0ef41Sopenharmony_ci  return htable;
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_cifail:
1341cb0ef41Sopenharmony_ci  ares__htable_destroy(htable);
1351cb0ef41Sopenharmony_ci  return NULL;
1361cb0ef41Sopenharmony_ci}
1371cb0ef41Sopenharmony_ci
1381cb0ef41Sopenharmony_ciconst void **ares__htable_all_buckets(const ares__htable_t *htable, size_t *num)
1391cb0ef41Sopenharmony_ci{
1401cb0ef41Sopenharmony_ci  const void **out = NULL;
1411cb0ef41Sopenharmony_ci  size_t       cnt = 0;
1421cb0ef41Sopenharmony_ci  size_t       i;
1431cb0ef41Sopenharmony_ci
1441cb0ef41Sopenharmony_ci  if (htable == NULL || num == NULL) {
1451cb0ef41Sopenharmony_ci    return NULL;
1461cb0ef41Sopenharmony_ci  }
1471cb0ef41Sopenharmony_ci
1481cb0ef41Sopenharmony_ci  *num = 0;
1491cb0ef41Sopenharmony_ci
1501cb0ef41Sopenharmony_ci  out = ares_malloc_zero(sizeof(*out) * htable->num_keys);
1511cb0ef41Sopenharmony_ci  if (out == NULL) {
1521cb0ef41Sopenharmony_ci    return NULL;
1531cb0ef41Sopenharmony_ci  }
1541cb0ef41Sopenharmony_ci
1551cb0ef41Sopenharmony_ci  for (i = 0; i < htable->size; i++) {
1561cb0ef41Sopenharmony_ci    ares__llist_node_t *node;
1571cb0ef41Sopenharmony_ci    for (node = ares__llist_node_first(htable->buckets[i]); node != NULL;
1581cb0ef41Sopenharmony_ci         node = ares__llist_node_next(node)) {
1591cb0ef41Sopenharmony_ci      out[cnt++] = ares__llist_node_val(node);
1601cb0ef41Sopenharmony_ci    }
1611cb0ef41Sopenharmony_ci  }
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_ci  *num = cnt;
1641cb0ef41Sopenharmony_ci  return out;
1651cb0ef41Sopenharmony_ci}
1661cb0ef41Sopenharmony_ci
1671cb0ef41Sopenharmony_ci/*! Grabs the Hashtable index from the key and length.  The h index is
1681cb0ef41Sopenharmony_ci *  the hash of the function reduced to the size of the bucket list.
1691cb0ef41Sopenharmony_ci *  We are doing "hash & (size - 1)" since we are guaranteeing a power of
1701cb0ef41Sopenharmony_ci *  2 for size. This is equivalent to "hash % size", but should be more
1711cb0ef41Sopenharmony_ci * efficient */
1721cb0ef41Sopenharmony_ci#define HASH_IDX(h, key) h->hash(key, h->seed) & (h->size - 1)
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_cistatic ares__llist_node_t *ares__htable_find(const ares__htable_t *htable,
1751cb0ef41Sopenharmony_ci                                             unsigned int idx, const void *key)
1761cb0ef41Sopenharmony_ci{
1771cb0ef41Sopenharmony_ci  ares__llist_node_t *node = NULL;
1781cb0ef41Sopenharmony_ci
1791cb0ef41Sopenharmony_ci  for (node = ares__llist_node_first(htable->buckets[idx]); node != NULL;
1801cb0ef41Sopenharmony_ci       node = ares__llist_node_next(node)) {
1811cb0ef41Sopenharmony_ci    if (htable->key_eq(key, htable->bucket_key(ares__llist_node_val(node)))) {
1821cb0ef41Sopenharmony_ci      break;
1831cb0ef41Sopenharmony_ci    }
1841cb0ef41Sopenharmony_ci  }
1851cb0ef41Sopenharmony_ci
1861cb0ef41Sopenharmony_ci  return node;
1871cb0ef41Sopenharmony_ci}
1881cb0ef41Sopenharmony_ci
1891cb0ef41Sopenharmony_cistatic ares_bool_t ares__htable_expand(ares__htable_t *htable)
1901cb0ef41Sopenharmony_ci{
1911cb0ef41Sopenharmony_ci  ares__llist_t **buckets  = NULL;
1921cb0ef41Sopenharmony_ci  unsigned int    old_size = htable->size;
1931cb0ef41Sopenharmony_ci  size_t          i;
1941cb0ef41Sopenharmony_ci  ares__llist_t **prealloc_llist     = NULL;
1951cb0ef41Sopenharmony_ci  size_t          prealloc_llist_len = 0;
1961cb0ef41Sopenharmony_ci  ares_bool_t     rv                 = ARES_FALSE;
1971cb0ef41Sopenharmony_ci
1981cb0ef41Sopenharmony_ci  /* Not a failure, just won't expand */
1991cb0ef41Sopenharmony_ci  if (old_size == ARES__HTABLE_MAX_BUCKETS) {
2001cb0ef41Sopenharmony_ci    return ARES_TRUE;
2011cb0ef41Sopenharmony_ci  }
2021cb0ef41Sopenharmony_ci
2031cb0ef41Sopenharmony_ci  htable->size <<= 1;
2041cb0ef41Sopenharmony_ci
2051cb0ef41Sopenharmony_ci  /* We must pre-allocate all memory we'll need before moving entries to the
2061cb0ef41Sopenharmony_ci   * new hash array.  Otherwise if there's a memory allocation failure in the
2071cb0ef41Sopenharmony_ci   * middle, we wouldn't be able to recover. */
2081cb0ef41Sopenharmony_ci  buckets = ares_malloc_zero(sizeof(*buckets) * htable->size);
2091cb0ef41Sopenharmony_ci  if (buckets == NULL) {
2101cb0ef41Sopenharmony_ci    goto done;
2111cb0ef41Sopenharmony_ci  }
2121cb0ef41Sopenharmony_ci
2131cb0ef41Sopenharmony_ci  /* The maximum number of new llists we'll need is the number of collisions
2141cb0ef41Sopenharmony_ci   * that were recorded */
2151cb0ef41Sopenharmony_ci  prealloc_llist_len = htable->num_collisions;
2161cb0ef41Sopenharmony_ci  if (prealloc_llist_len) {
2171cb0ef41Sopenharmony_ci    prealloc_llist =
2181cb0ef41Sopenharmony_ci      ares_malloc_zero(sizeof(*prealloc_llist) * prealloc_llist_len);
2191cb0ef41Sopenharmony_ci    if (prealloc_llist == NULL) {
2201cb0ef41Sopenharmony_ci      goto done;
2211cb0ef41Sopenharmony_ci    }
2221cb0ef41Sopenharmony_ci  }
2231cb0ef41Sopenharmony_ci  for (i = 0; i < prealloc_llist_len; i++) {
2241cb0ef41Sopenharmony_ci    prealloc_llist[i] = ares__llist_create(htable->bucket_free);
2251cb0ef41Sopenharmony_ci    if (prealloc_llist[i] == NULL) {
2261cb0ef41Sopenharmony_ci      goto done;
2271cb0ef41Sopenharmony_ci    }
2281cb0ef41Sopenharmony_ci  }
2291cb0ef41Sopenharmony_ci
2301cb0ef41Sopenharmony_ci  /* Iterate across all buckets and move the entries to the new buckets */
2311cb0ef41Sopenharmony_ci  htable->num_collisions = 0;
2321cb0ef41Sopenharmony_ci  for (i = 0; i < old_size; i++) {
2331cb0ef41Sopenharmony_ci    ares__llist_node_t *node;
2341cb0ef41Sopenharmony_ci
2351cb0ef41Sopenharmony_ci    /* Nothing in this bucket */
2361cb0ef41Sopenharmony_ci    if (htable->buckets[i] == NULL) {
2371cb0ef41Sopenharmony_ci      continue;
2381cb0ef41Sopenharmony_ci    }
2391cb0ef41Sopenharmony_ci
2401cb0ef41Sopenharmony_ci    /* Fast path optimization (most likely case), there is likely only a single
2411cb0ef41Sopenharmony_ci     * entry in both the source and destination, check for this to confirm and
2421cb0ef41Sopenharmony_ci     * if so, just move the bucket over */
2431cb0ef41Sopenharmony_ci    if (ares__llist_len(htable->buckets[i]) == 1) {
2441cb0ef41Sopenharmony_ci      const void *val = ares__llist_first_val(htable->buckets[i]);
2451cb0ef41Sopenharmony_ci      size_t      idx = HASH_IDX(htable, htable->bucket_key(val));
2461cb0ef41Sopenharmony_ci
2471cb0ef41Sopenharmony_ci      if (buckets[idx] == NULL) {
2481cb0ef41Sopenharmony_ci        /* Swap! */
2491cb0ef41Sopenharmony_ci        buckets[idx]       = htable->buckets[i];
2501cb0ef41Sopenharmony_ci        htable->buckets[i] = NULL;
2511cb0ef41Sopenharmony_ci        continue;
2521cb0ef41Sopenharmony_ci      }
2531cb0ef41Sopenharmony_ci    }
2541cb0ef41Sopenharmony_ci
2551cb0ef41Sopenharmony_ci    /* Slow path, collisions */
2561cb0ef41Sopenharmony_ci    while ((node = ares__llist_node_first(htable->buckets[i])) != NULL) {
2571cb0ef41Sopenharmony_ci      const void *val = ares__llist_node_val(node);
2581cb0ef41Sopenharmony_ci      size_t      idx = HASH_IDX(htable, htable->bucket_key(val));
2591cb0ef41Sopenharmony_ci
2601cb0ef41Sopenharmony_ci      /* Try fast path again as maybe we popped one collision off and the
2611cb0ef41Sopenharmony_ci       * next we can reuse the llist parent */
2621cb0ef41Sopenharmony_ci      if (buckets[idx] == NULL && ares__llist_len(htable->buckets[i]) == 1) {
2631cb0ef41Sopenharmony_ci        /* Swap! */
2641cb0ef41Sopenharmony_ci        buckets[idx]       = htable->buckets[i];
2651cb0ef41Sopenharmony_ci        htable->buckets[i] = NULL;
2661cb0ef41Sopenharmony_ci        break;
2671cb0ef41Sopenharmony_ci      }
2681cb0ef41Sopenharmony_ci
2691cb0ef41Sopenharmony_ci      /* Grab one off our preallocated list */
2701cb0ef41Sopenharmony_ci      if (buckets[idx] == NULL) {
2711cb0ef41Sopenharmony_ci        /* Silence static analysis, this isn't possible but it doesn't know */
2721cb0ef41Sopenharmony_ci        if (prealloc_llist == NULL || prealloc_llist_len == 0) {
2731cb0ef41Sopenharmony_ci          goto done;
2741cb0ef41Sopenharmony_ci        }
2751cb0ef41Sopenharmony_ci        buckets[idx] = prealloc_llist[prealloc_llist_len - 1];
2761cb0ef41Sopenharmony_ci        prealloc_llist_len--;
2771cb0ef41Sopenharmony_ci      } else {
2781cb0ef41Sopenharmony_ci        /* Collision occurred since the bucket wasn't empty */
2791cb0ef41Sopenharmony_ci        htable->num_collisions++;
2801cb0ef41Sopenharmony_ci      }
2811cb0ef41Sopenharmony_ci
2821cb0ef41Sopenharmony_ci      ares__llist_node_move_parent_first(node, buckets[idx]);
2831cb0ef41Sopenharmony_ci    }
2841cb0ef41Sopenharmony_ci
2851cb0ef41Sopenharmony_ci    /* Abandoned bucket, destroy */
2861cb0ef41Sopenharmony_ci    if (htable->buckets[i] != NULL) {
2871cb0ef41Sopenharmony_ci      ares__llist_destroy(htable->buckets[i]);
2881cb0ef41Sopenharmony_ci      htable->buckets[i] = NULL;
2891cb0ef41Sopenharmony_ci    }
2901cb0ef41Sopenharmony_ci  }
2911cb0ef41Sopenharmony_ci
2921cb0ef41Sopenharmony_ci  /* We have guaranteed all the buckets have either been moved or destroyed,
2931cb0ef41Sopenharmony_ci   * so we just call ares_free() on the array and swap out the pointer */
2941cb0ef41Sopenharmony_ci  ares_free(htable->buckets);
2951cb0ef41Sopenharmony_ci  htable->buckets = buckets;
2961cb0ef41Sopenharmony_ci  buckets         = NULL;
2971cb0ef41Sopenharmony_ci  rv              = ARES_TRUE;
2981cb0ef41Sopenharmony_ci
2991cb0ef41Sopenharmony_cidone:
3001cb0ef41Sopenharmony_ci  ares_free(buckets);
3011cb0ef41Sopenharmony_ci  /* destroy any unused preallocated buckets */
3021cb0ef41Sopenharmony_ci  ares__htable_buckets_destroy(prealloc_llist, (unsigned int)prealloc_llist_len,
3031cb0ef41Sopenharmony_ci                               ARES_FALSE);
3041cb0ef41Sopenharmony_ci
3051cb0ef41Sopenharmony_ci  /* On failure, we need to restore the htable size */
3061cb0ef41Sopenharmony_ci  if (rv != ARES_TRUE) {
3071cb0ef41Sopenharmony_ci    htable->size = old_size;
3081cb0ef41Sopenharmony_ci  }
3091cb0ef41Sopenharmony_ci
3101cb0ef41Sopenharmony_ci  return rv;
3111cb0ef41Sopenharmony_ci}
3121cb0ef41Sopenharmony_ci
3131cb0ef41Sopenharmony_ciares_bool_t ares__htable_insert(ares__htable_t *htable, void *bucket)
3141cb0ef41Sopenharmony_ci{
3151cb0ef41Sopenharmony_ci  unsigned int        idx  = 0;
3161cb0ef41Sopenharmony_ci  ares__llist_node_t *node = NULL;
3171cb0ef41Sopenharmony_ci  const void         *key  = NULL;
3181cb0ef41Sopenharmony_ci
3191cb0ef41Sopenharmony_ci  if (htable == NULL || bucket == NULL) {
3201cb0ef41Sopenharmony_ci    return ARES_FALSE;
3211cb0ef41Sopenharmony_ci  }
3221cb0ef41Sopenharmony_ci
3231cb0ef41Sopenharmony_ci
3241cb0ef41Sopenharmony_ci  key = htable->bucket_key(bucket);
3251cb0ef41Sopenharmony_ci  idx = HASH_IDX(htable, key);
3261cb0ef41Sopenharmony_ci
3271cb0ef41Sopenharmony_ci  /* See if we have a matching bucket already, if so, replace it */
3281cb0ef41Sopenharmony_ci  node = ares__htable_find(htable, idx, key);
3291cb0ef41Sopenharmony_ci  if (node != NULL) {
3301cb0ef41Sopenharmony_ci    ares__llist_node_replace(node, bucket);
3311cb0ef41Sopenharmony_ci    return ARES_TRUE;
3321cb0ef41Sopenharmony_ci  }
3331cb0ef41Sopenharmony_ci
3341cb0ef41Sopenharmony_ci  /* Check to see if we should rehash because likelihood of collisions has
3351cb0ef41Sopenharmony_ci   * increased beyond our threshold */
3361cb0ef41Sopenharmony_ci  if (htable->num_keys + 1 >
3371cb0ef41Sopenharmony_ci      (htable->size * ARES__HTABLE_EXPAND_PERCENT) / 100) {
3381cb0ef41Sopenharmony_ci    if (!ares__htable_expand(htable)) {
3391cb0ef41Sopenharmony_ci      return ARES_FALSE;
3401cb0ef41Sopenharmony_ci    }
3411cb0ef41Sopenharmony_ci    /* If we expanded, need to calculate a new index */
3421cb0ef41Sopenharmony_ci    idx = HASH_IDX(htable, key);
3431cb0ef41Sopenharmony_ci  }
3441cb0ef41Sopenharmony_ci
3451cb0ef41Sopenharmony_ci  /* We lazily allocate the linked list */
3461cb0ef41Sopenharmony_ci  if (htable->buckets[idx] == NULL) {
3471cb0ef41Sopenharmony_ci    htable->buckets[idx] = ares__llist_create(htable->bucket_free);
3481cb0ef41Sopenharmony_ci    if (htable->buckets[idx] == NULL) {
3491cb0ef41Sopenharmony_ci      return ARES_FALSE;
3501cb0ef41Sopenharmony_ci    }
3511cb0ef41Sopenharmony_ci  }
3521cb0ef41Sopenharmony_ci
3531cb0ef41Sopenharmony_ci  node = ares__llist_insert_first(htable->buckets[idx], bucket);
3541cb0ef41Sopenharmony_ci  if (node == NULL) {
3551cb0ef41Sopenharmony_ci    return ARES_FALSE;
3561cb0ef41Sopenharmony_ci  }
3571cb0ef41Sopenharmony_ci
3581cb0ef41Sopenharmony_ci  /* Track collisions for rehash stability */
3591cb0ef41Sopenharmony_ci  if (ares__llist_len(htable->buckets[idx]) > 1) {
3601cb0ef41Sopenharmony_ci    htable->num_collisions++;
3611cb0ef41Sopenharmony_ci  }
3621cb0ef41Sopenharmony_ci
3631cb0ef41Sopenharmony_ci  htable->num_keys++;
3641cb0ef41Sopenharmony_ci
3651cb0ef41Sopenharmony_ci  return ARES_TRUE;
3661cb0ef41Sopenharmony_ci}
3671cb0ef41Sopenharmony_ci
3681cb0ef41Sopenharmony_civoid *ares__htable_get(const ares__htable_t *htable, const void *key)
3691cb0ef41Sopenharmony_ci{
3701cb0ef41Sopenharmony_ci  unsigned int idx;
3711cb0ef41Sopenharmony_ci
3721cb0ef41Sopenharmony_ci  if (htable == NULL || key == NULL) {
3731cb0ef41Sopenharmony_ci    return NULL;
3741cb0ef41Sopenharmony_ci  }
3751cb0ef41Sopenharmony_ci
3761cb0ef41Sopenharmony_ci  idx = HASH_IDX(htable, key);
3771cb0ef41Sopenharmony_ci
3781cb0ef41Sopenharmony_ci  return ares__llist_node_val(ares__htable_find(htable, idx, key));
3791cb0ef41Sopenharmony_ci}
3801cb0ef41Sopenharmony_ci
3811cb0ef41Sopenharmony_ciares_bool_t ares__htable_remove(ares__htable_t *htable, const void *key)
3821cb0ef41Sopenharmony_ci{
3831cb0ef41Sopenharmony_ci  ares__llist_node_t *node;
3841cb0ef41Sopenharmony_ci  unsigned int        idx;
3851cb0ef41Sopenharmony_ci
3861cb0ef41Sopenharmony_ci  if (htable == NULL || key == NULL) {
3871cb0ef41Sopenharmony_ci    return ARES_FALSE;
3881cb0ef41Sopenharmony_ci  }
3891cb0ef41Sopenharmony_ci
3901cb0ef41Sopenharmony_ci  idx  = HASH_IDX(htable, key);
3911cb0ef41Sopenharmony_ci  node = ares__htable_find(htable, idx, key);
3921cb0ef41Sopenharmony_ci  if (node == NULL) {
3931cb0ef41Sopenharmony_ci    return ARES_FALSE;
3941cb0ef41Sopenharmony_ci  }
3951cb0ef41Sopenharmony_ci
3961cb0ef41Sopenharmony_ci  htable->num_keys--;
3971cb0ef41Sopenharmony_ci
3981cb0ef41Sopenharmony_ci  /* Reduce collisions */
3991cb0ef41Sopenharmony_ci  if (ares__llist_len(ares__llist_node_parent(node)) > 1) {
4001cb0ef41Sopenharmony_ci    htable->num_collisions--;
4011cb0ef41Sopenharmony_ci  }
4021cb0ef41Sopenharmony_ci
4031cb0ef41Sopenharmony_ci  ares__llist_node_destroy(node);
4041cb0ef41Sopenharmony_ci  return ARES_TRUE;
4051cb0ef41Sopenharmony_ci}
4061cb0ef41Sopenharmony_ci
4071cb0ef41Sopenharmony_cisize_t ares__htable_num_keys(const ares__htable_t *htable)
4081cb0ef41Sopenharmony_ci{
4091cb0ef41Sopenharmony_ci  if (htable == NULL) {
4101cb0ef41Sopenharmony_ci    return 0;
4111cb0ef41Sopenharmony_ci  }
4121cb0ef41Sopenharmony_ci  return htable->num_keys;
4131cb0ef41Sopenharmony_ci}
4141cb0ef41Sopenharmony_ci
4151cb0ef41Sopenharmony_ciunsigned int ares__htable_hash_FNV1a(const unsigned char *key, size_t key_len,
4161cb0ef41Sopenharmony_ci                                     unsigned int seed)
4171cb0ef41Sopenharmony_ci{
4181cb0ef41Sopenharmony_ci  /* recommended seed is 2166136261U, but we don't want collisions */
4191cb0ef41Sopenharmony_ci  unsigned int hv = seed;
4201cb0ef41Sopenharmony_ci  size_t       i;
4211cb0ef41Sopenharmony_ci
4221cb0ef41Sopenharmony_ci  for (i = 0; i < key_len; i++) {
4231cb0ef41Sopenharmony_ci    hv ^= (unsigned int)key[i];
4241cb0ef41Sopenharmony_ci    /* hv *= 0x01000193 */
4251cb0ef41Sopenharmony_ci    hv += (hv << 1) + (hv << 4) + (hv << 7) + (hv << 8) + (hv << 24);
4261cb0ef41Sopenharmony_ci  }
4271cb0ef41Sopenharmony_ci
4281cb0ef41Sopenharmony_ci  return hv;
4291cb0ef41Sopenharmony_ci}
4301cb0ef41Sopenharmony_ci
4311cb0ef41Sopenharmony_ci/* Case insensitive version, meant for ASCII strings */
4321cb0ef41Sopenharmony_ciunsigned int ares__htable_hash_FNV1a_casecmp(const unsigned char *key,
4331cb0ef41Sopenharmony_ci                                             size_t key_len, unsigned int seed)
4341cb0ef41Sopenharmony_ci{
4351cb0ef41Sopenharmony_ci  /* recommended seed is 2166136261U, but we don't want collisions */
4361cb0ef41Sopenharmony_ci  unsigned int hv = seed;
4371cb0ef41Sopenharmony_ci  size_t       i;
4381cb0ef41Sopenharmony_ci
4391cb0ef41Sopenharmony_ci  for (i = 0; i < key_len; i++) {
4401cb0ef41Sopenharmony_ci    hv ^= (unsigned int)ares__tolower(key[i]);
4411cb0ef41Sopenharmony_ci    /* hv *= 0x01000193 */
4421cb0ef41Sopenharmony_ci    hv += (hv << 1) + (hv << 4) + (hv << 7) + (hv << 8) + (hv << 24);
4431cb0ef41Sopenharmony_ci  }
4441cb0ef41Sopenharmony_ci
4451cb0ef41Sopenharmony_ci  return hv;
4461cb0ef41Sopenharmony_ci}
447