lsquic_hash.c revision a74702c6
1/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * lsquic_hash.c
4 */
5
6#include <assert.h>
7#include <stdint.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/queue.h>
11#ifdef WIN32
12#include <vc_compat.h>
13#endif
14
15#include "lsquic_hash.h"
16#include "lsquic_xxhash.h"
17
18TAILQ_HEAD(hels_head, lsquic_hash_elem);
19
20#define N_BUCKETS(n_bits) (1U << (n_bits))
21#define BUCKNO(n_bits, hash) ((hash) & (N_BUCKETS(n_bits) - 1))
22
23struct lsquic_hash
24{
25    struct hels_head        *qh_buckets,
26                             qh_all;
27    struct lsquic_hash_elem *qh_iter_next;
28    int                    (*qh_cmp)(const void *, const void *, size_t);
29    unsigned               (*qh_hash)(const void *, size_t, unsigned seed);
30    unsigned                 qh_count;
31    unsigned                 qh_nbits;
32};
33
34
35struct lsquic_hash *
36lsquic_hash_create_ext (int (*cmp)(const void *, const void *, size_t),
37                    unsigned (*hashf)(const void *, size_t, unsigned seed))
38{
39    struct hels_head *buckets;
40    struct lsquic_hash *hash;
41    unsigned nbits = 2;
42    unsigned i;
43
44    buckets = malloc(sizeof(buckets[0]) * N_BUCKETS(nbits));
45    if (!buckets)
46        return NULL;
47
48    hash = malloc(sizeof(*hash));
49    if (!hash)
50    {
51        free(buckets);
52        return NULL;
53    }
54
55    for (i = 0; i < N_BUCKETS(nbits); ++i)
56        TAILQ_INIT(&buckets[i]);
57
58    TAILQ_INIT(&hash->qh_all);
59    hash->qh_cmp       = cmp;
60    hash->qh_hash      = hashf;
61    hash->qh_buckets   = buckets;
62    hash->qh_nbits     = nbits;
63    hash->qh_iter_next = NULL;
64    hash->qh_count     = 0;
65    return hash;
66}
67
68
69struct lsquic_hash *
70lsquic_hash_create (void)
71{
72    return lsquic_hash_create_ext(memcmp, XXH32);
73}
74
75
76void
77lsquic_hash_destroy (struct lsquic_hash *hash)
78{
79    free(hash->qh_buckets);
80    free(hash);
81}
82
83
84static int
85lsquic_hash_grow (struct lsquic_hash *hash)
86{
87    struct hels_head *new_buckets, *new[2];
88    struct lsquic_hash_elem *el;
89    unsigned n, old_nbits;
90    int idx;
91
92    old_nbits = hash->qh_nbits;
93    new_buckets = malloc(sizeof(hash->qh_buckets[0])
94                                                * N_BUCKETS(old_nbits + 1));
95    if (!new_buckets)
96        return -1;
97
98    for (n = 0; n < N_BUCKETS(old_nbits); ++n)
99    {
100        new[0] = &new_buckets[n];
101        new[1] = &new_buckets[n + N_BUCKETS(old_nbits)];
102        TAILQ_INIT(new[0]);
103        TAILQ_INIT(new[1]);
104        while ((el = TAILQ_FIRST(&hash->qh_buckets[n])))
105        {
106            TAILQ_REMOVE(&hash->qh_buckets[n], el, qhe_next_bucket);
107            idx = (BUCKNO(old_nbits + 1, el->qhe_hash_val) >> old_nbits) & 1;
108            TAILQ_INSERT_TAIL(new[idx], el, qhe_next_bucket);
109        }
110    }
111    free(hash->qh_buckets);
112    hash->qh_nbits   = old_nbits + 1;
113    hash->qh_buckets = new_buckets;
114    return 0;
115}
116
117
118struct lsquic_hash_elem *
119lsquic_hash_insert (struct lsquic_hash *hash, const void *key,
120                    unsigned key_sz, void *value, struct lsquic_hash_elem *el)
121{
122    unsigned buckno, hash_val;
123
124    if (el->qhe_flags & QHE_HASHED)
125        return NULL;
126
127    if (hash->qh_count >= N_BUCKETS(hash->qh_nbits) / 2 &&
128                                            0 != lsquic_hash_grow(hash))
129        return NULL;
130
131    hash_val = hash->qh_hash(key, key_sz, (uintptr_t) hash);
132    buckno = BUCKNO(hash->qh_nbits, hash_val);
133    TAILQ_INSERT_TAIL(&hash->qh_all, el, qhe_next_all);
134    TAILQ_INSERT_TAIL(&hash->qh_buckets[buckno], el, qhe_next_bucket);
135    el->qhe_key_data = key;
136    el->qhe_key_len  = key_sz;
137    el->qhe_value    = value;
138    el->qhe_hash_val = hash_val;
139    el->qhe_flags |= QHE_HASHED;
140    ++hash->qh_count;
141    return el;
142}
143
144
145struct lsquic_hash_elem *
146lsquic_hash_find (struct lsquic_hash *hash, const void *key, unsigned key_sz)
147{
148    unsigned buckno, hash_val;
149    struct lsquic_hash_elem *el;
150
151    hash_val = hash->qh_hash(key, key_sz, (uintptr_t) hash);
152    buckno = BUCKNO(hash->qh_nbits, hash_val);
153    TAILQ_FOREACH(el, &hash->qh_buckets[buckno], qhe_next_bucket)
154        if (hash_val == el->qhe_hash_val &&
155            key_sz   == el->qhe_key_len &&
156            0 == hash->qh_cmp(key, el->qhe_key_data, key_sz))
157        {
158            return el;
159        }
160
161    return NULL;
162}
163
164
165void
166lsquic_hash_erase (struct lsquic_hash *hash, struct lsquic_hash_elem *el)
167{
168    unsigned buckno;
169
170    assert(el->qhe_flags & QHE_HASHED);
171    buckno = BUCKNO(hash->qh_nbits, el->qhe_hash_val);
172    if (hash->qh_iter_next == el)
173        hash->qh_iter_next = TAILQ_NEXT(el, qhe_next_all);
174    TAILQ_REMOVE(&hash->qh_buckets[buckno], el, qhe_next_bucket);
175    TAILQ_REMOVE(&hash->qh_all, el, qhe_next_all);
176    el->qhe_flags &= ~QHE_HASHED;
177    --hash->qh_count;
178}
179
180
181void
182lsquic_hash_reset_iter (struct lsquic_hash *hash)
183{
184    hash->qh_iter_next = TAILQ_FIRST(&hash->qh_all);
185}
186
187
188struct lsquic_hash_elem *
189lsquic_hash_first (struct lsquic_hash *hash)
190{
191    lsquic_hash_reset_iter(hash);
192    return lsquic_hash_next(hash);
193}
194
195
196struct lsquic_hash_elem *
197lsquic_hash_next (struct lsquic_hash *hash)
198{
199    struct lsquic_hash_elem *el;
200    el = hash->qh_iter_next;
201    if (el)
202        hash->qh_iter_next = TAILQ_NEXT(el, qhe_next_all);
203    return el;
204}
205
206
207unsigned
208lsquic_hash_count (struct lsquic_hash *hash)
209{
210    return hash->qh_count;
211}
212
213
214size_t
215lsquic_hash_mem_used (const struct lsquic_hash *hash)
216{
217    return sizeof(*hash)
218         + N_BUCKETS(hash->qh_nbits) * sizeof(hash->qh_buckets[0]);
219}
220