1 1.12 joe /* $NetBSD: hash.c,v 1.12 2025/01/07 14:21:11 joe Exp $ */ 2 1.1 thorpej 3 1.1 thorpej /* 4 1.1 thorpej * Copyright (c) 1992, 1993 5 1.1 thorpej * The Regents of the University of California. All rights reserved. 6 1.1 thorpej * 7 1.1 thorpej * This software was developed by the Computer Systems Engineering group 8 1.1 thorpej * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 9 1.1 thorpej * contributed to Berkeley. 10 1.1 thorpej * 11 1.1 thorpej * All advertising materials mentioning features or use of this software 12 1.1 thorpej * must display the following acknowledgement: 13 1.1 thorpej * This product includes software developed by the University of 14 1.1 thorpej * California, Lawrence Berkeley Laboratories. 15 1.1 thorpej * 16 1.1 thorpej * Redistribution and use in source and binary forms, with or without 17 1.1 thorpej * modification, are permitted provided that the following conditions 18 1.1 thorpej * are met: 19 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 20 1.1 thorpej * notice, this list of conditions and the following disclaimer. 21 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 22 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 23 1.1 thorpej * documentation and/or other materials provided with the distribution. 24 1.1 thorpej * 3. Neither the name of the University nor the names of its contributors 25 1.1 thorpej * may be used to endorse or promote products derived from this software 26 1.1 thorpej * without specific prior written permission. 27 1.1 thorpej * 28 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 1.1 thorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 1.1 thorpej * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 1.1 thorpej * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 1.1 thorpej * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 1.1 thorpej * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 1.1 thorpej * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 1.1 thorpej * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 1.1 thorpej * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 1.1 thorpej * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 1.1 thorpej * SUCH DAMAGE. 39 1.1 thorpej * 40 1.1 thorpej * from: @(#)hash.c 8.1 (Berkeley) 6/6/93 41 1.1 thorpej */ 42 1.1 thorpej 43 1.1 thorpej #if HAVE_NBTOOL_CONFIG_H 44 1.1 thorpej #include "nbtool_config.h" 45 1.1 thorpej #endif 46 1.1 thorpej 47 1.11 christos #include <sys/cdefs.h> 48 1.12 joe __RCSID("$NetBSD: hash.c,v 1.12 2025/01/07 14:21:11 joe Exp $"); 49 1.11 christos 50 1.1 thorpej #include <sys/param.h> 51 1.4 christos #include <assert.h> 52 1.1 thorpej #include <stdlib.h> 53 1.1 thorpej #include <string.h> 54 1.2 christos #include <util.h> 55 1.1 thorpej #include "defs.h" 56 1.1 thorpej 57 1.1 thorpej /* 58 1.1 thorpej * Interned strings are kept in a hash table. By making each string 59 1.1 thorpej * unique, the program can compare strings by comparing pointers. 60 1.1 thorpej */ 61 1.1 thorpej struct hashent { 62 1.1 thorpej // XXXLUKEM: a SIMPLEQ might be more appropriate 63 1.1 thorpej TAILQ_ENTRY(hashent) h_next; 64 1.9 uebayasi const char *h_names[2]; /* the string */ 65 1.9 uebayasi #define h_name1 h_names[0] 66 1.9 uebayasi #define h_name2 h_names[1] 67 1.9 uebayasi #define h_name h_name1 68 1.1 thorpej u_int h_hash; /* its hash value */ 69 1.1 thorpej void *h_value; /* other values (for name=value) */ 70 1.1 thorpej }; 71 1.1 thorpej struct hashtab { 72 1.1 thorpej size_t ht_size; /* size (power of 2) */ 73 1.11 christos size_t ht_mask; /* == ht_size - 1 */ 74 1.11 christos size_t ht_used; /* number of entries used */ 75 1.11 christos size_t ht_lim; /* when to expand */ 76 1.1 thorpej TAILQ_HEAD(hashenthead, hashent) *ht_tab; 77 1.1 thorpej }; 78 1.1 thorpej 79 1.1 thorpej static struct hashtab strings; 80 1.1 thorpej 81 1.1 thorpej /* 82 1.1 thorpej * HASHFRACTION controls ht_lim, which in turn controls the average chain 83 1.1 thorpej * length. We allow a few entries, on average, as comparing them is usually 84 1.1 thorpej * cheap (the h_hash values prevent a strcmp). 85 1.1 thorpej */ 86 1.1 thorpej #define HASHFRACTION(sz) ((sz) * 3 / 2) 87 1.1 thorpej 88 1.1 thorpej static void ht_expand(struct hashtab *); 89 1.1 thorpej static void ht_init(struct hashtab *, size_t); 90 1.9 uebayasi static inline u_int hash(u_int, const char *); 91 1.9 uebayasi static inline u_int hash2(u_int, const char *, const char *); 92 1.1 thorpej static inline struct hashent *newhashent(const char *, u_int); 93 1.1 thorpej 94 1.1 thorpej /* 95 1.1 thorpej * Initialize a new hash table. The size must be a power of 2. 96 1.1 thorpej */ 97 1.1 thorpej static void 98 1.1 thorpej ht_init(struct hashtab *ht, size_t sz) 99 1.1 thorpej { 100 1.1 thorpej u_int n; 101 1.1 thorpej 102 1.1 thorpej ht->ht_tab = emalloc(sz * sizeof (ht->ht_tab[0])); 103 1.1 thorpej ht->ht_size = sz; 104 1.1 thorpej ht->ht_mask = sz - 1; 105 1.1 thorpej for (n = 0; n < sz; n++) 106 1.1 thorpej TAILQ_INIT(&ht->ht_tab[n]); 107 1.1 thorpej ht->ht_used = 0; 108 1.1 thorpej ht->ht_lim = HASHFRACTION(sz); 109 1.1 thorpej } 110 1.1 thorpej 111 1.1 thorpej /* 112 1.1 thorpej * Expand an existing hash table. 113 1.1 thorpej */ 114 1.1 thorpej static void 115 1.1 thorpej ht_expand(struct hashtab *ht) 116 1.1 thorpej { 117 1.1 thorpej struct hashenthead *h, *oldh; 118 1.1 thorpej struct hashent *p; 119 1.11 christos size_t n, i; 120 1.1 thorpej 121 1.1 thorpej n = ht->ht_size * 2; 122 1.1 thorpej h = emalloc(n * sizeof *h); 123 1.1 thorpej for (i = 0; i < n; i++) 124 1.1 thorpej TAILQ_INIT(&h[i]); 125 1.1 thorpej oldh = ht->ht_tab; 126 1.1 thorpej n--; 127 1.1 thorpej for (i = 0; i < ht->ht_size; i++) { 128 1.1 thorpej while ((p = TAILQ_FIRST(&oldh[i])) != NULL) { 129 1.1 thorpej TAILQ_REMOVE(&oldh[i], p, h_next); 130 1.1 thorpej // XXXLUKEM: really should be TAILQ_INSERT_TAIL 131 1.1 thorpej TAILQ_INSERT_HEAD(&h[p->h_hash & n], p, h_next); 132 1.1 thorpej } 133 1.1 thorpej } 134 1.1 thorpej free(ht->ht_tab); 135 1.1 thorpej ht->ht_tab = h; 136 1.1 thorpej ht->ht_mask = n; 137 1.1 thorpej ht->ht_size = ++n; 138 1.1 thorpej ht->ht_lim = HASHFRACTION(n); 139 1.1 thorpej } 140 1.1 thorpej 141 1.1 thorpej /* 142 1.1 thorpej * Make a new hash entry, setting its h_next to NULL. 143 1.1 thorpej * If the free list is not empty, use the first entry from there, 144 1.1 thorpej * otherwise allocate a new entry. 145 1.1 thorpej */ 146 1.1 thorpej static inline struct hashent * 147 1.9 uebayasi newhashent2(const char *name1, const char *name2, u_int h) 148 1.1 thorpej { 149 1.1 thorpej struct hashent *hp; 150 1.1 thorpej 151 1.3 dsl hp = ecalloc(1, sizeof(*hp)); 152 1.1 thorpej 153 1.9 uebayasi hp->h_name1 = name1; 154 1.9 uebayasi hp->h_name2 = name2; 155 1.1 thorpej hp->h_hash = h; 156 1.1 thorpej return (hp); 157 1.1 thorpej } 158 1.1 thorpej 159 1.9 uebayasi static inline struct hashent * 160 1.9 uebayasi newhashent(const char *name, u_int h) 161 1.9 uebayasi { 162 1.9 uebayasi return newhashent2(name, NULL, h); 163 1.9 uebayasi } 164 1.9 uebayasi 165 1.9 uebayasi static inline u_int 166 1.9 uebayasi hv(u_int h, char c) 167 1.9 uebayasi { 168 1.11 christos return (h << 5) + h + (unsigned char)c; 169 1.9 uebayasi } 170 1.9 uebayasi 171 1.1 thorpej /* 172 1.1 thorpej * Hash a string. 173 1.1 thorpej */ 174 1.1 thorpej static inline u_int 175 1.9 uebayasi hash(u_int h, const char *str) 176 1.9 uebayasi { 177 1.9 uebayasi 178 1.9 uebayasi while (str && *str) 179 1.9 uebayasi h = hv(h, *str++); 180 1.9 uebayasi return (h); 181 1.9 uebayasi } 182 1.9 uebayasi 183 1.9 uebayasi #define HASH2DELIM ' ' 184 1.9 uebayasi 185 1.9 uebayasi static inline u_int 186 1.9 uebayasi hash2(u_int h, const char *str1, const char *str2) 187 1.1 thorpej { 188 1.1 thorpej 189 1.9 uebayasi h = hash(h, str1); 190 1.9 uebayasi h = hv(h, HASH2DELIM); 191 1.9 uebayasi h = hash(h, str2); 192 1.1 thorpej return (h); 193 1.1 thorpej } 194 1.1 thorpej 195 1.1 thorpej void 196 1.1 thorpej initintern(void) 197 1.1 thorpej { 198 1.1 thorpej 199 1.1 thorpej ht_init(&strings, 128); 200 1.1 thorpej } 201 1.1 thorpej 202 1.1 thorpej /* 203 1.1 thorpej * Generate a single unique copy of the given string. We expect this 204 1.1 thorpej * function to be used frequently, so it should be fast. 205 1.1 thorpej */ 206 1.1 thorpej const char * 207 1.1 thorpej intern(const char *s) 208 1.1 thorpej { 209 1.1 thorpej struct hashtab *ht; 210 1.1 thorpej struct hashent *hp; 211 1.1 thorpej struct hashenthead *hpp; 212 1.1 thorpej u_int h; 213 1.1 thorpej char *p; 214 1.1 thorpej 215 1.1 thorpej ht = &strings; 216 1.9 uebayasi h = hash2(0, s, NULL); 217 1.1 thorpej hpp = &ht->ht_tab[h & ht->ht_mask]; 218 1.1 thorpej TAILQ_FOREACH(hp, hpp, h_next) { 219 1.1 thorpej if (hp->h_hash == h && strcmp(hp->h_name, s) == 0) 220 1.1 thorpej return (hp->h_name); 221 1.1 thorpej } 222 1.1 thorpej p = estrdup(s); 223 1.1 thorpej hp = newhashent(p, h); 224 1.1 thorpej TAILQ_INSERT_TAIL(hpp, hp, h_next); 225 1.1 thorpej if (++ht->ht_used > ht->ht_lim) 226 1.1 thorpej ht_expand(ht); 227 1.1 thorpej return (p); 228 1.1 thorpej } 229 1.1 thorpej 230 1.1 thorpej struct hashtab * 231 1.1 thorpej ht_new(void) 232 1.1 thorpej { 233 1.1 thorpej struct hashtab *ht; 234 1.1 thorpej 235 1.1 thorpej ht = ecalloc(1, sizeof *ht); 236 1.1 thorpej ht_init(ht, 8); 237 1.1 thorpej return (ht); 238 1.1 thorpej } 239 1.1 thorpej 240 1.4 christos void 241 1.4 christos ht_free(struct hashtab *ht) 242 1.4 christos { 243 1.6 lukem size_t i; 244 1.4 christos struct hashent *hp; 245 1.4 christos struct hashenthead *hpp; 246 1.4 christos 247 1.5 alc for (i = 0; i < ht->ht_size; i++) { 248 1.4 christos hpp = &ht->ht_tab[i]; 249 1.4 christos while ((hp = TAILQ_FIRST(hpp)) != NULL) { 250 1.4 christos TAILQ_REMOVE(hpp, hp, h_next); 251 1.4 christos free(hp); 252 1.4 christos ht->ht_used--; 253 1.4 christos } 254 1.4 christos } 255 1.4 christos 256 1.4 christos assert(ht->ht_used == 0); 257 1.4 christos free(ht->ht_tab); 258 1.4 christos free(ht); 259 1.4 christos } 260 1.4 christos 261 1.1 thorpej /* 262 1.1 thorpej * Insert and/or replace. 263 1.1 thorpej */ 264 1.1 thorpej int 265 1.9 uebayasi ht_insrep2(struct hashtab *ht, const char *nam1, const char *nam2, void *val, int replace) 266 1.1 thorpej { 267 1.1 thorpej struct hashent *hp; 268 1.1 thorpej struct hashenthead *hpp; 269 1.1 thorpej u_int h; 270 1.1 thorpej 271 1.10 uebayasi h = hash2(0, nam1, nam2); 272 1.1 thorpej hpp = &ht->ht_tab[h & ht->ht_mask]; 273 1.1 thorpej TAILQ_FOREACH(hp, hpp, h_next) { 274 1.9 uebayasi if (hp->h_name1 == nam1 && 275 1.9 uebayasi hp->h_name2 == nam2) { 276 1.1 thorpej if (replace) 277 1.1 thorpej hp->h_value = val; 278 1.1 thorpej return (1); 279 1.1 thorpej } 280 1.1 thorpej } 281 1.9 uebayasi hp = newhashent2(nam1, nam2, h); 282 1.1 thorpej TAILQ_INSERT_TAIL(hpp, hp, h_next); 283 1.1 thorpej hp->h_value = val; 284 1.1 thorpej if (++ht->ht_used > ht->ht_lim) 285 1.1 thorpej ht_expand(ht); 286 1.1 thorpej return (0); 287 1.1 thorpej } 288 1.1 thorpej 289 1.9 uebayasi int 290 1.9 uebayasi ht_insrep(struct hashtab *ht, const char *nam, void *val, int replace) 291 1.9 uebayasi { 292 1.9 uebayasi return ht_insrep2(ht, nam, NULL, val, replace); 293 1.9 uebayasi } 294 1.9 uebayasi 295 1.1 thorpej /* 296 1.1 thorpej * Remove. 297 1.1 thorpej */ 298 1.1 thorpej int 299 1.9 uebayasi ht_remove2(struct hashtab *ht, const char *name1, const char *name2) 300 1.1 thorpej { 301 1.1 thorpej struct hashent *hp; 302 1.1 thorpej struct hashenthead *hpp; 303 1.1 thorpej u_int h; 304 1.1 thorpej 305 1.9 uebayasi h = hash2(0, name1, name2); 306 1.1 thorpej hpp = &ht->ht_tab[h & ht->ht_mask]; 307 1.1 thorpej 308 1.1 thorpej TAILQ_FOREACH(hp, hpp, h_next) { 309 1.9 uebayasi if (hp->h_name1 != name1 || hp->h_name2 != name2) 310 1.1 thorpej continue; 311 1.1 thorpej TAILQ_REMOVE(hpp, hp, h_next); 312 1.1 thorpej 313 1.3 dsl free(hp); 314 1.1 thorpej ht->ht_used--; 315 1.1 thorpej return (0); 316 1.1 thorpej } 317 1.1 thorpej return (1); 318 1.1 thorpej } 319 1.1 thorpej 320 1.9 uebayasi int 321 1.9 uebayasi ht_remove(struct hashtab *ht, const char *name) 322 1.9 uebayasi { 323 1.9 uebayasi return ht_remove2(ht, name, NULL); 324 1.9 uebayasi } 325 1.9 uebayasi 326 1.1 thorpej void * 327 1.9 uebayasi ht_lookup2(struct hashtab *ht, const char *nam1, const char *nam2) 328 1.1 thorpej { 329 1.1 thorpej struct hashent *hp; 330 1.1 thorpej struct hashenthead *hpp; 331 1.1 thorpej u_int h; 332 1.1 thorpej 333 1.10 uebayasi h = hash2(0, nam1, nam2); 334 1.1 thorpej hpp = &ht->ht_tab[h & ht->ht_mask]; 335 1.1 thorpej TAILQ_FOREACH(hp, hpp, h_next) 336 1.9 uebayasi if (hp->h_name == nam1) 337 1.1 thorpej return (hp->h_value); 338 1.1 thorpej return (NULL); 339 1.1 thorpej } 340 1.1 thorpej 341 1.9 uebayasi void * 342 1.9 uebayasi ht_lookup(struct hashtab *ht, const char *nam) 343 1.9 uebayasi { 344 1.9 uebayasi return ht_lookup2(ht, nam, NULL); 345 1.9 uebayasi } 346 1.9 uebayasi 347 1.1 thorpej /* 348 1.1 thorpej * first parameter to callback is the entry name from the hash table 349 1.1 thorpej * second parameter is the value from the hash table 350 1.1 thorpej * third argument is passed through from the "arg" parameter to ht_enumerate() 351 1.1 thorpej */ 352 1.1 thorpej 353 1.1 thorpej int 354 1.9 uebayasi ht_enumerate2(struct hashtab *ht, ht_callback2 cbfunc2, void *arg) 355 1.9 uebayasi { 356 1.9 uebayasi struct hashent *hp; 357 1.9 uebayasi struct hashenthead *hpp; 358 1.9 uebayasi size_t i; 359 1.9 uebayasi int rval = 0; 360 1.12 joe 361 1.9 uebayasi for (i = 0; i < ht->ht_size; i++) { 362 1.9 uebayasi hpp = &ht->ht_tab[i]; 363 1.9 uebayasi TAILQ_FOREACH(hp, hpp, h_next) 364 1.9 uebayasi rval += (*cbfunc2)(hp->h_name1, hp->h_name2, hp->h_value, arg); 365 1.9 uebayasi } 366 1.9 uebayasi return rval; 367 1.9 uebayasi } 368 1.9 uebayasi 369 1.9 uebayasi int 370 1.1 thorpej ht_enumerate(struct hashtab *ht, ht_callback cbfunc, void *arg) 371 1.1 thorpej { 372 1.1 thorpej struct hashent *hp; 373 1.1 thorpej struct hashenthead *hpp; 374 1.6 lukem size_t i; 375 1.1 thorpej int rval = 0; 376 1.12 joe 377 1.1 thorpej for (i = 0; i < ht->ht_size; i++) { 378 1.1 thorpej hpp = &ht->ht_tab[i]; 379 1.1 thorpej TAILQ_FOREACH(hp, hpp, h_next) 380 1.1 thorpej rval += (*cbfunc)(hp->h_name, hp->h_value, arg); 381 1.1 thorpej } 382 1.1 thorpej return rval; 383 1.1 thorpej } 384 1.7 dholland 385 1.7 dholland /************************************************************/ 386 1.7 dholland 387 1.7 dholland /* 388 1.7 dholland * Type-safe wrappers. 389 1.7 dholland */ 390 1.7 dholland 391 1.7 dholland #define DEFHASH(HT, VT) \ 392 1.7 dholland struct HT { \ 393 1.7 dholland struct hashtab imp; \ 394 1.7 dholland }; \ 395 1.7 dholland \ 396 1.7 dholland struct HT * \ 397 1.7 dholland HT##_create(void) \ 398 1.7 dholland { \ 399 1.7 dholland struct HT *tbl; \ 400 1.7 dholland \ 401 1.7 dholland tbl = ecalloc(1, sizeof(*tbl)); \ 402 1.7 dholland ht_init(&tbl->imp, 8); \ 403 1.7 dholland return tbl; \ 404 1.7 dholland } \ 405 1.7 dholland \ 406 1.7 dholland int \ 407 1.7 dholland HT##_insert(struct HT *tbl, const char *name, struct VT *val) \ 408 1.7 dholland { \ 409 1.7 dholland return ht_insert(&tbl->imp, name, val); \ 410 1.7 dholland } \ 411 1.7 dholland \ 412 1.7 dholland int \ 413 1.7 dholland HT##_replace(struct HT *tbl, const char *name, struct VT *val) \ 414 1.7 dholland { \ 415 1.7 dholland return ht_replace(&tbl->imp, name, val); \ 416 1.7 dholland } \ 417 1.7 dholland \ 418 1.7 dholland int \ 419 1.7 dholland HT##_remove(struct HT *tbl, const char *name) \ 420 1.7 dholland { \ 421 1.7 dholland return ht_remove(&tbl->imp, name); \ 422 1.7 dholland } \ 423 1.7 dholland \ 424 1.7 dholland struct VT * \ 425 1.7 dholland HT##_lookup(struct HT *tbl, const char *name) \ 426 1.7 dholland { \ 427 1.7 dholland return ht_lookup(&tbl->imp, name); \ 428 1.7 dholland } \ 429 1.7 dholland \ 430 1.7 dholland struct HT##_enumcontext { \ 431 1.7 dholland int (*func)(const char *, struct VT *, void *); \ 432 1.7 dholland void *userctx; \ 433 1.7 dholland }; \ 434 1.7 dholland \ 435 1.7 dholland static int \ 436 1.7 dholland HT##_enumerate_thunk(const char *name, void *value, void *voidctx) \ 437 1.7 dholland { \ 438 1.7 dholland struct HT##_enumcontext *ctx = voidctx; \ 439 1.7 dholland \ 440 1.7 dholland return ctx->func(name, value, ctx->userctx); \ 441 1.7 dholland } \ 442 1.7 dholland \ 443 1.7 dholland int \ 444 1.7 dholland HT##_enumerate(struct HT *tbl, \ 445 1.7 dholland int (*func)(const char *, struct VT *, void *), \ 446 1.7 dholland void *userctx) \ 447 1.7 dholland { \ 448 1.7 dholland struct HT##_enumcontext ctx; \ 449 1.7 dholland \ 450 1.7 dholland ctx.func = func; \ 451 1.7 dholland ctx.userctx = userctx; \ 452 1.7 dholland return ht_enumerate(&tbl->imp, HT##_enumerate_thunk, &ctx); \ 453 1.7 dholland } 454 1.7 dholland 455 1.7 dholland DEFHASH(nvhash, nvlist); 456 1.8 dholland DEFHASH(dlhash, defoptlist); 457