lsquic_hash.c revision 10c492f0
1/* Copyright (c) 2017 - 2018 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_malo.h"
16#include "lsquic_hash.h"
17#include "lsquic_xxhash.h"
18
19struct lsquic_hash_elem
20{
21    TAILQ_ENTRY(lsquic_hash_elem)
22                    qhe_next_bucket,
23                    qhe_next_all;
24    const void     *qhe_key_data;
25    unsigned        qhe_key_len;
26    void           *qhe_value;
27    unsigned        qhe_hash_val;
28};
29
30TAILQ_HEAD(hels_head, lsquic_hash_elem);
31
32#define N_BUCKETS(n_bits) (1U << (n_bits))
33#define BUCKNO(n_bits, hash) ((hash) & (N_BUCKETS(n_bits) - 1))
34
35struct lsquic_hash
36{
37    struct hels_head        *qh_buckets,
38                             qh_all;
39    struct malo             *qh_malo_els;
40    struct lsquic_hash_elem *qh_iter_next;
41    unsigned                 qh_count;
42    unsigned                 qh_nbits;
43};
44
45
46struct lsquic_hash *
47lsquic_hash_create (void)
48{
49    struct hels_head *buckets;
50    struct lsquic_hash *hash;
51    struct malo *malo;
52    unsigned nbits = 2;
53    unsigned i;
54
55    buckets = malloc(sizeof(buckets[0]) * N_BUCKETS(nbits));
56    if (!buckets)
57        return NULL;
58
59    hash = malloc(sizeof(*hash));
60    if (!hash)
61    {
62        free(buckets);
63        return NULL;
64    }
65
66    malo = lsquic_malo_create(sizeof(struct lsquic_hash_elem));
67    if (!malo)
68    {
69        free(hash);
70        free(buckets);
71        return NULL;
72    }
73
74    for (i = 0; i < N_BUCKETS(nbits); ++i)
75        TAILQ_INIT(&buckets[i]);
76
77    TAILQ_INIT(&hash->qh_all);
78    hash->qh_buckets   = buckets;
79    hash->qh_nbits     = nbits;
80    hash->qh_malo_els  = malo;
81    hash->qh_iter_next = NULL;
82    hash->qh_count     = 0;
83    return hash;
84}
85
86
87void
88lsquic_hash_destroy (struct lsquic_hash *hash)
89{
90    lsquic_malo_destroy(hash->qh_malo_els);
91    free(hash->qh_buckets);
92    free(hash);
93}
94
95
96static int
97lsquic_hash_grow (struct lsquic_hash *hash)
98{
99    struct hels_head *new_buckets, *new[2];
100    struct lsquic_hash_elem *el;
101    unsigned n, old_nbits;
102    int idx;
103
104    old_nbits = hash->qh_nbits;
105    new_buckets = malloc(sizeof(hash->qh_buckets[0])
106                                                * N_BUCKETS(old_nbits + 1));
107    if (!new_buckets)
108        return -1;
109
110    for (n = 0; n < N_BUCKETS(old_nbits); ++n)
111    {
112        new[0] = &new_buckets[n];
113        new[1] = &new_buckets[n + N_BUCKETS(old_nbits)];
114        TAILQ_INIT(new[0]);
115        TAILQ_INIT(new[1]);
116        while ((el = TAILQ_FIRST(&hash->qh_buckets[n])))
117        {
118            TAILQ_REMOVE(&hash->qh_buckets[n], el, qhe_next_bucket);
119            idx = (BUCKNO(old_nbits + 1, el->qhe_hash_val) >> old_nbits) & 1;
120            TAILQ_INSERT_TAIL(new[idx], el, qhe_next_bucket);
121        }
122    }
123    free(hash->qh_buckets);
124    hash->qh_nbits   = old_nbits + 1;
125    hash->qh_buckets = new_buckets;
126    return 0;
127}
128
129
130struct lsquic_hash_elem *
131lsquic_hash_insert (struct lsquic_hash *hash, const void *key,
132                                            unsigned key_sz, void *data)
133{
134    unsigned buckno, hash_val;
135    struct lsquic_hash_elem *el;
136
137    el = lsquic_malo_get(hash->qh_malo_els);
138    if (!el)
139        return NULL;
140
141    if (hash->qh_count >= N_BUCKETS(hash->qh_nbits) / 2 &&
142                                            0 != lsquic_hash_grow(hash))
143    {
144        lsquic_malo_put(el);
145        return NULL;
146    }
147
148    hash_val = XXH64(key, key_sz, (uintptr_t) hash);
149    buckno = BUCKNO(hash->qh_nbits, hash_val);
150    TAILQ_INSERT_TAIL(&hash->qh_all, el, qhe_next_all);
151    TAILQ_INSERT_TAIL(&hash->qh_buckets[buckno], el, qhe_next_bucket);
152    el->qhe_key_data = key;
153    el->qhe_key_len  = key_sz;
154    el->qhe_value    = data;
155    el->qhe_hash_val = hash_val;
156    ++hash->qh_count;
157    return el;
158}
159
160
161struct lsquic_hash_elem *
162lsquic_hash_find (struct lsquic_hash *hash, const void *key, unsigned key_sz)
163{
164    unsigned buckno, hash_val;
165    struct lsquic_hash_elem *el;
166
167    hash_val = XXH64(key, key_sz, (uintptr_t) hash);
168    buckno = BUCKNO(hash->qh_nbits, hash_val);
169    TAILQ_FOREACH(el, &hash->qh_buckets[buckno], qhe_next_bucket)
170        if (hash_val == el->qhe_hash_val &&
171            key_sz   == el->qhe_key_len &&
172            0 == memcmp(key, el->qhe_key_data, key_sz))
173        {
174            return el;
175        }
176
177    return NULL;
178}
179
180
181void *
182lsquic_hashelem_getdata (const struct lsquic_hash_elem *el)
183{
184    return el->qhe_value;
185}
186
187
188void
189lsquic_hash_erase (struct lsquic_hash *hash, struct lsquic_hash_elem *el)
190{
191    unsigned buckno;
192
193    buckno = BUCKNO(hash->qh_nbits, el->qhe_hash_val);
194    TAILQ_REMOVE(&hash->qh_buckets[buckno], el, qhe_next_bucket);
195    TAILQ_REMOVE(&hash->qh_all, el, qhe_next_all);
196    lsquic_malo_put(el);
197    --hash->qh_count;
198}
199
200
201void
202lsquic_hash_reset_iter (struct lsquic_hash *hash)
203{
204    hash->qh_iter_next = TAILQ_FIRST(&hash->qh_all);
205}
206
207
208struct lsquic_hash_elem *
209lsquic_hash_first (struct lsquic_hash *hash)
210{
211    lsquic_hash_reset_iter(hash);
212    return lsquic_hash_next(hash);
213}
214
215
216struct lsquic_hash_elem *
217lsquic_hash_next (struct lsquic_hash *hash)
218{
219    struct lsquic_hash_elem *el;
220    el = hash->qh_iter_next;
221    if (el)
222        hash->qh_iter_next = TAILQ_NEXT(el, qhe_next_all);
223    return el;
224}
225
226
227unsigned
228lsquic_hash_count (struct lsquic_hash *hash)
229{
230    return hash->qh_count;
231}
232
233
234size_t
235lsquic_hash_mem_used (const struct lsquic_hash *hash)
236{
237    return sizeof(*hash)
238         + N_BUCKETS(hash->qh_nbits) * sizeof(hash->qh_buckets[0])
239         + lsquic_malo_mem_used(hash->qh_malo_els);
240}
241