lsquic_hash.c revision 5392f7a3
1/* Copyright (c) 2017 - 2019 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 unsigned qh_count; 29 unsigned qh_nbits; 30}; 31 32 33struct lsquic_hash * 34lsquic_hash_create (void) 35{ 36 struct hels_head *buckets; 37 struct lsquic_hash *hash; 38 unsigned nbits = 2; 39 unsigned i; 40 41 buckets = malloc(sizeof(buckets[0]) * N_BUCKETS(nbits)); 42 if (!buckets) 43 return NULL; 44 45 hash = malloc(sizeof(*hash)); 46 if (!hash) 47 { 48 free(buckets); 49 return NULL; 50 } 51 52 for (i = 0; i < N_BUCKETS(nbits); ++i) 53 TAILQ_INIT(&buckets[i]); 54 55 TAILQ_INIT(&hash->qh_all); 56 hash->qh_buckets = buckets; 57 hash->qh_nbits = nbits; 58 hash->qh_iter_next = NULL; 59 hash->qh_count = 0; 60 return hash; 61} 62 63 64void 65lsquic_hash_destroy (struct lsquic_hash *hash) 66{ 67 free(hash->qh_buckets); 68 free(hash); 69} 70 71 72static int 73lsquic_hash_grow (struct lsquic_hash *hash) 74{ 75 struct hels_head *new_buckets, *new[2]; 76 struct lsquic_hash_elem *el; 77 unsigned n, old_nbits; 78 int idx; 79 80 old_nbits = hash->qh_nbits; 81 new_buckets = malloc(sizeof(hash->qh_buckets[0]) 82 * N_BUCKETS(old_nbits + 1)); 83 if (!new_buckets) 84 return -1; 85 86 for (n = 0; n < N_BUCKETS(old_nbits); ++n) 87 { 88 new[0] = &new_buckets[n]; 89 new[1] = &new_buckets[n + N_BUCKETS(old_nbits)]; 90 TAILQ_INIT(new[0]); 91 TAILQ_INIT(new[1]); 92 while ((el = TAILQ_FIRST(&hash->qh_buckets[n]))) 93 { 94 TAILQ_REMOVE(&hash->qh_buckets[n], el, qhe_next_bucket); 95 idx = (BUCKNO(old_nbits + 1, el->qhe_hash_val) >> old_nbits) & 1; 96 TAILQ_INSERT_TAIL(new[idx], el, qhe_next_bucket); 97 } 98 } 99 free(hash->qh_buckets); 100 hash->qh_nbits = old_nbits + 1; 101 hash->qh_buckets = new_buckets; 102 return 0; 103} 104 105 106struct lsquic_hash_elem * 107lsquic_hash_insert (struct lsquic_hash *hash, const void *key, 108 unsigned key_sz, void *value, struct lsquic_hash_elem *el) 109{ 110 unsigned buckno, hash_val; 111 112 if (el->qhe_flags & QHE_HASHED) 113 return NULL; 114 115 if (hash->qh_count >= N_BUCKETS(hash->qh_nbits) / 2 && 116 0 != lsquic_hash_grow(hash)) 117 return NULL; 118 119 hash_val = XXH64(key, key_sz, (uintptr_t) hash); 120 buckno = BUCKNO(hash->qh_nbits, hash_val); 121 TAILQ_INSERT_TAIL(&hash->qh_all, el, qhe_next_all); 122 TAILQ_INSERT_TAIL(&hash->qh_buckets[buckno], el, qhe_next_bucket); 123 el->qhe_key_data = key; 124 el->qhe_key_len = key_sz; 125 el->qhe_value = value; 126 el->qhe_hash_val = hash_val; 127 el->qhe_flags |= QHE_HASHED; 128 ++hash->qh_count; 129 return el; 130} 131 132 133struct lsquic_hash_elem * 134lsquic_hash_find (struct lsquic_hash *hash, const void *key, unsigned key_sz) 135{ 136 unsigned buckno, hash_val; 137 struct lsquic_hash_elem *el; 138 139 hash_val = XXH64(key, key_sz, (uintptr_t) hash); 140 buckno = BUCKNO(hash->qh_nbits, hash_val); 141 TAILQ_FOREACH(el, &hash->qh_buckets[buckno], qhe_next_bucket) 142 if (hash_val == el->qhe_hash_val && 143 key_sz == el->qhe_key_len && 144 0 == memcmp(key, el->qhe_key_data, key_sz)) 145 { 146 return el; 147 } 148 149 return NULL; 150} 151 152 153void 154lsquic_hash_erase (struct lsquic_hash *hash, struct lsquic_hash_elem *el) 155{ 156 unsigned buckno; 157 158 assert(el->qhe_flags & QHE_HASHED); 159 buckno = BUCKNO(hash->qh_nbits, el->qhe_hash_val); 160 if (hash->qh_iter_next == el) 161 hash->qh_iter_next = TAILQ_NEXT(el, qhe_next_all); 162 TAILQ_REMOVE(&hash->qh_buckets[buckno], el, qhe_next_bucket); 163 TAILQ_REMOVE(&hash->qh_all, el, qhe_next_all); 164 el->qhe_flags &= ~QHE_HASHED; 165 --hash->qh_count; 166} 167 168 169void 170lsquic_hash_reset_iter (struct lsquic_hash *hash) 171{ 172 hash->qh_iter_next = TAILQ_FIRST(&hash->qh_all); 173} 174 175 176struct lsquic_hash_elem * 177lsquic_hash_first (struct lsquic_hash *hash) 178{ 179 lsquic_hash_reset_iter(hash); 180 return lsquic_hash_next(hash); 181} 182 183 184struct lsquic_hash_elem * 185lsquic_hash_next (struct lsquic_hash *hash) 186{ 187 struct lsquic_hash_elem *el; 188 el = hash->qh_iter_next; 189 if (el) 190 hash->qh_iter_next = TAILQ_NEXT(el, qhe_next_all); 191 return el; 192} 193 194 195unsigned 196lsquic_hash_count (struct lsquic_hash *hash) 197{ 198 return hash->qh_count; 199} 200 201 202size_t 203lsquic_hash_mem_used (const struct lsquic_hash *hash) 204{ 205 return sizeof(*hash) 206 + N_BUCKETS(hash->qh_nbits) * sizeof(hash->qh_buckets[0]); 207} 208