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