1a74702c6SGeorge Wang/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
250aadb33SDmitri Tikhonov/*
350aadb33SDmitri Tikhonov * lsquic_hash.c
450aadb33SDmitri Tikhonov */
550aadb33SDmitri Tikhonov
650aadb33SDmitri Tikhonov#include <assert.h>
750aadb33SDmitri Tikhonov#include <stdint.h>
850aadb33SDmitri Tikhonov#include <stdlib.h>
950aadb33SDmitri Tikhonov#include <string.h>
1050aadb33SDmitri Tikhonov#include <sys/queue.h>
11461e84d8SAmol Deshpande#ifdef WIN32
12461e84d8SAmol Deshpande#include <vc_compat.h>
13461e84d8SAmol Deshpande#endif
1450aadb33SDmitri Tikhonov
1550aadb33SDmitri Tikhonov#include "lsquic_hash.h"
1650aadb33SDmitri Tikhonov#include "lsquic_xxhash.h"
1750aadb33SDmitri Tikhonov
1850aadb33SDmitri TikhonovTAILQ_HEAD(hels_head, lsquic_hash_elem);
1950aadb33SDmitri Tikhonov
2050aadb33SDmitri Tikhonov#define N_BUCKETS(n_bits) (1U << (n_bits))
2150aadb33SDmitri Tikhonov#define BUCKNO(n_bits, hash) ((hash) & (N_BUCKETS(n_bits) - 1))
2250aadb33SDmitri Tikhonov
2350aadb33SDmitri Tikhonovstruct lsquic_hash
2450aadb33SDmitri Tikhonov{
2550aadb33SDmitri Tikhonov    struct hels_head        *qh_buckets,
2650aadb33SDmitri Tikhonov                             qh_all;
2750aadb33SDmitri Tikhonov    struct lsquic_hash_elem *qh_iter_next;
2892f6e17bSDmitri Tikhonov    int                    (*qh_cmp)(const void *, const void *, size_t);
2992f6e17bSDmitri Tikhonov    unsigned               (*qh_hash)(const void *, size_t, unsigned seed);
3050aadb33SDmitri Tikhonov    unsigned                 qh_count;
3150aadb33SDmitri Tikhonov    unsigned                 qh_nbits;
3250aadb33SDmitri Tikhonov};
3350aadb33SDmitri Tikhonov
3450aadb33SDmitri Tikhonov
3550aadb33SDmitri Tikhonovstruct lsquic_hash *
3692f6e17bSDmitri Tikhonovlsquic_hash_create_ext (int (*cmp)(const void *, const void *, size_t),
3792f6e17bSDmitri Tikhonov                    unsigned (*hashf)(const void *, size_t, unsigned seed))
3850aadb33SDmitri Tikhonov{
3950aadb33SDmitri Tikhonov    struct hels_head *buckets;
4050aadb33SDmitri Tikhonov    struct lsquic_hash *hash;
4150aadb33SDmitri Tikhonov    unsigned nbits = 2;
4250aadb33SDmitri Tikhonov    unsigned i;
4350aadb33SDmitri Tikhonov
4450aadb33SDmitri Tikhonov    buckets = malloc(sizeof(buckets[0]) * N_BUCKETS(nbits));
4550aadb33SDmitri Tikhonov    if (!buckets)
4650aadb33SDmitri Tikhonov        return NULL;
4750aadb33SDmitri Tikhonov
4850aadb33SDmitri Tikhonov    hash = malloc(sizeof(*hash));
4950aadb33SDmitri Tikhonov    if (!hash)
5050aadb33SDmitri Tikhonov    {
5150aadb33SDmitri Tikhonov        free(buckets);
5250aadb33SDmitri Tikhonov        return NULL;
5350aadb33SDmitri Tikhonov    }
5450aadb33SDmitri Tikhonov
5550aadb33SDmitri Tikhonov    for (i = 0; i < N_BUCKETS(nbits); ++i)
5650aadb33SDmitri Tikhonov        TAILQ_INIT(&buckets[i]);
5750aadb33SDmitri Tikhonov
5850aadb33SDmitri Tikhonov    TAILQ_INIT(&hash->qh_all);
5992f6e17bSDmitri Tikhonov    hash->qh_cmp       = cmp;
6092f6e17bSDmitri Tikhonov    hash->qh_hash      = hashf;
6150aadb33SDmitri Tikhonov    hash->qh_buckets   = buckets;
6250aadb33SDmitri Tikhonov    hash->qh_nbits     = nbits;
6350aadb33SDmitri Tikhonov    hash->qh_iter_next = NULL;
6450aadb33SDmitri Tikhonov    hash->qh_count     = 0;
6550aadb33SDmitri Tikhonov    return hash;
6650aadb33SDmitri Tikhonov}
6750aadb33SDmitri Tikhonov
6850aadb33SDmitri Tikhonov
6992f6e17bSDmitri Tikhonovstruct lsquic_hash *
7092f6e17bSDmitri Tikhonovlsquic_hash_create (void)
7192f6e17bSDmitri Tikhonov{
7292f6e17bSDmitri Tikhonov    return lsquic_hash_create_ext(memcmp, XXH32);
7392f6e17bSDmitri Tikhonov}
7492f6e17bSDmitri Tikhonov
7592f6e17bSDmitri Tikhonov
7650aadb33SDmitri Tikhonovvoid
7750aadb33SDmitri Tikhonovlsquic_hash_destroy (struct lsquic_hash *hash)
7850aadb33SDmitri Tikhonov{
7950aadb33SDmitri Tikhonov    free(hash->qh_buckets);
8050aadb33SDmitri Tikhonov    free(hash);
8150aadb33SDmitri Tikhonov}
8250aadb33SDmitri Tikhonov
8350aadb33SDmitri Tikhonov
8450aadb33SDmitri Tikhonovstatic int
8550aadb33SDmitri Tikhonovlsquic_hash_grow (struct lsquic_hash *hash)
8650aadb33SDmitri Tikhonov{
8750aadb33SDmitri Tikhonov    struct hels_head *new_buckets, *new[2];
8850aadb33SDmitri Tikhonov    struct lsquic_hash_elem *el;
8950aadb33SDmitri Tikhonov    unsigned n, old_nbits;
9050aadb33SDmitri Tikhonov    int idx;
9150aadb33SDmitri Tikhonov
9250aadb33SDmitri Tikhonov    old_nbits = hash->qh_nbits;
9350aadb33SDmitri Tikhonov    new_buckets = malloc(sizeof(hash->qh_buckets[0])
9450aadb33SDmitri Tikhonov                                                * N_BUCKETS(old_nbits + 1));
9550aadb33SDmitri Tikhonov    if (!new_buckets)
9650aadb33SDmitri Tikhonov        return -1;
9750aadb33SDmitri Tikhonov
9850aadb33SDmitri Tikhonov    for (n = 0; n < N_BUCKETS(old_nbits); ++n)
9950aadb33SDmitri Tikhonov    {
10050aadb33SDmitri Tikhonov        new[0] = &new_buckets[n];
10150aadb33SDmitri Tikhonov        new[1] = &new_buckets[n + N_BUCKETS(old_nbits)];
10250aadb33SDmitri Tikhonov        TAILQ_INIT(new[0]);
10350aadb33SDmitri Tikhonov        TAILQ_INIT(new[1]);
10450aadb33SDmitri Tikhonov        while ((el = TAILQ_FIRST(&hash->qh_buckets[n])))
10550aadb33SDmitri Tikhonov        {
10650aadb33SDmitri Tikhonov            TAILQ_REMOVE(&hash->qh_buckets[n], el, qhe_next_bucket);
10750aadb33SDmitri Tikhonov            idx = (BUCKNO(old_nbits + 1, el->qhe_hash_val) >> old_nbits) & 1;
10850aadb33SDmitri Tikhonov            TAILQ_INSERT_TAIL(new[idx], el, qhe_next_bucket);
10950aadb33SDmitri Tikhonov        }
11050aadb33SDmitri Tikhonov    }
11150aadb33SDmitri Tikhonov    free(hash->qh_buckets);
11250aadb33SDmitri Tikhonov    hash->qh_nbits   = old_nbits + 1;
11350aadb33SDmitri Tikhonov    hash->qh_buckets = new_buckets;
11450aadb33SDmitri Tikhonov    return 0;
11550aadb33SDmitri Tikhonov}
11650aadb33SDmitri Tikhonov
11750aadb33SDmitri Tikhonov
11850aadb33SDmitri Tikhonovstruct lsquic_hash_elem *
11950aadb33SDmitri Tikhonovlsquic_hash_insert (struct lsquic_hash *hash, const void *key,
1205392f7a3SLiteSpeed Tech                    unsigned key_sz, void *value, struct lsquic_hash_elem *el)
12150aadb33SDmitri Tikhonov{
12250aadb33SDmitri Tikhonov    unsigned buckno, hash_val;
12350aadb33SDmitri Tikhonov
1245392f7a3SLiteSpeed Tech    if (el->qhe_flags & QHE_HASHED)
12550aadb33SDmitri Tikhonov        return NULL;
12650aadb33SDmitri Tikhonov
12750aadb33SDmitri Tikhonov    if (hash->qh_count >= N_BUCKETS(hash->qh_nbits) / 2 &&
12850aadb33SDmitri Tikhonov                                            0 != lsquic_hash_grow(hash))
12950aadb33SDmitri Tikhonov        return NULL;
13050aadb33SDmitri Tikhonov
13192f6e17bSDmitri Tikhonov    hash_val = hash->qh_hash(key, key_sz, (uintptr_t) hash);
13250aadb33SDmitri Tikhonov    buckno = BUCKNO(hash->qh_nbits, hash_val);
13350aadb33SDmitri Tikhonov    TAILQ_INSERT_TAIL(&hash->qh_all, el, qhe_next_all);
13450aadb33SDmitri Tikhonov    TAILQ_INSERT_TAIL(&hash->qh_buckets[buckno], el, qhe_next_bucket);
13550aadb33SDmitri Tikhonov    el->qhe_key_data = key;
13650aadb33SDmitri Tikhonov    el->qhe_key_len  = key_sz;
1375392f7a3SLiteSpeed Tech    el->qhe_value    = value;
13850aadb33SDmitri Tikhonov    el->qhe_hash_val = hash_val;
1395392f7a3SLiteSpeed Tech    el->qhe_flags |= QHE_HASHED;
14050aadb33SDmitri Tikhonov    ++hash->qh_count;
14150aadb33SDmitri Tikhonov    return el;
14250aadb33SDmitri Tikhonov}
14350aadb33SDmitri Tikhonov
14450aadb33SDmitri Tikhonov
14550aadb33SDmitri Tikhonovstruct lsquic_hash_elem *
14650aadb33SDmitri Tikhonovlsquic_hash_find (struct lsquic_hash *hash, const void *key, unsigned key_sz)
14750aadb33SDmitri Tikhonov{
14850aadb33SDmitri Tikhonov    unsigned buckno, hash_val;
14950aadb33SDmitri Tikhonov    struct lsquic_hash_elem *el;
15050aadb33SDmitri Tikhonov
15192f6e17bSDmitri Tikhonov    hash_val = hash->qh_hash(key, key_sz, (uintptr_t) hash);
15250aadb33SDmitri Tikhonov    buckno = BUCKNO(hash->qh_nbits, hash_val);
15350aadb33SDmitri Tikhonov    TAILQ_FOREACH(el, &hash->qh_buckets[buckno], qhe_next_bucket)
15450aadb33SDmitri Tikhonov        if (hash_val == el->qhe_hash_val &&
15550aadb33SDmitri Tikhonov            key_sz   == el->qhe_key_len &&
15692f6e17bSDmitri Tikhonov            0 == hash->qh_cmp(key, el->qhe_key_data, key_sz))
15750aadb33SDmitri Tikhonov        {
15850aadb33SDmitri Tikhonov            return el;
15950aadb33SDmitri Tikhonov        }
16050aadb33SDmitri Tikhonov
16150aadb33SDmitri Tikhonov    return NULL;
16250aadb33SDmitri Tikhonov}
16350aadb33SDmitri Tikhonov
16450aadb33SDmitri Tikhonov
16550aadb33SDmitri Tikhonovvoid
16650aadb33SDmitri Tikhonovlsquic_hash_erase (struct lsquic_hash *hash, struct lsquic_hash_elem *el)
16750aadb33SDmitri Tikhonov{
16850aadb33SDmitri Tikhonov    unsigned buckno;
16950aadb33SDmitri Tikhonov
1705392f7a3SLiteSpeed Tech    assert(el->qhe_flags & QHE_HASHED);
17150aadb33SDmitri Tikhonov    buckno = BUCKNO(hash->qh_nbits, el->qhe_hash_val);
1725392f7a3SLiteSpeed Tech    if (hash->qh_iter_next == el)
1735392f7a3SLiteSpeed Tech        hash->qh_iter_next = TAILQ_NEXT(el, qhe_next_all);
17450aadb33SDmitri Tikhonov    TAILQ_REMOVE(&hash->qh_buckets[buckno], el, qhe_next_bucket);
17550aadb33SDmitri Tikhonov    TAILQ_REMOVE(&hash->qh_all, el, qhe_next_all);
1765392f7a3SLiteSpeed Tech    el->qhe_flags &= ~QHE_HASHED;
17750aadb33SDmitri Tikhonov    --hash->qh_count;
17850aadb33SDmitri Tikhonov}
17950aadb33SDmitri Tikhonov
18050aadb33SDmitri Tikhonov
18150aadb33SDmitri Tikhonovvoid
18250aadb33SDmitri Tikhonovlsquic_hash_reset_iter (struct lsquic_hash *hash)
18350aadb33SDmitri Tikhonov{
18450aadb33SDmitri Tikhonov    hash->qh_iter_next = TAILQ_FIRST(&hash->qh_all);
18550aadb33SDmitri Tikhonov}
18650aadb33SDmitri Tikhonov
18750aadb33SDmitri Tikhonov
18850aadb33SDmitri Tikhonovstruct lsquic_hash_elem *
18950aadb33SDmitri Tikhonovlsquic_hash_first (struct lsquic_hash *hash)
19050aadb33SDmitri Tikhonov{
19150aadb33SDmitri Tikhonov    lsquic_hash_reset_iter(hash);
19250aadb33SDmitri Tikhonov    return lsquic_hash_next(hash);
19350aadb33SDmitri Tikhonov}
19450aadb33SDmitri Tikhonov
19550aadb33SDmitri Tikhonov
19650aadb33SDmitri Tikhonovstruct lsquic_hash_elem *
19750aadb33SDmitri Tikhonovlsquic_hash_next (struct lsquic_hash *hash)
19850aadb33SDmitri Tikhonov{
19950aadb33SDmitri Tikhonov    struct lsquic_hash_elem *el;
20050aadb33SDmitri Tikhonov    el = hash->qh_iter_next;
20150aadb33SDmitri Tikhonov    if (el)
20250aadb33SDmitri Tikhonov        hash->qh_iter_next = TAILQ_NEXT(el, qhe_next_all);
20350aadb33SDmitri Tikhonov    return el;
20450aadb33SDmitri Tikhonov}
20550aadb33SDmitri Tikhonov
20650aadb33SDmitri Tikhonov
20750aadb33SDmitri Tikhonovunsigned
20850aadb33SDmitri Tikhonovlsquic_hash_count (struct lsquic_hash *hash)
20950aadb33SDmitri Tikhonov{
21050aadb33SDmitri Tikhonov    return hash->qh_count;
21150aadb33SDmitri Tikhonov}
212c51ce338SDmitri Tikhonov
213c51ce338SDmitri Tikhonov
214c51ce338SDmitri Tikhonovsize_t
215c51ce338SDmitri Tikhonovlsquic_hash_mem_used (const struct lsquic_hash *hash)
216c51ce338SDmitri Tikhonov{
217c51ce338SDmitri Tikhonov    return sizeof(*hash)
2185392f7a3SLiteSpeed Tech         + N_BUCKETS(hash->qh_nbits) * sizeof(hash->qh_buckets[0]);
219c51ce338SDmitri Tikhonov}
220