1*61d06d6bSBaptiste Daroussin #include "config.h" 2*61d06d6bSBaptiste Daroussin 3*61d06d6bSBaptiste Daroussin #if HAVE_OHASH 4*61d06d6bSBaptiste Daroussin 5*61d06d6bSBaptiste Daroussin int dummy; 6*61d06d6bSBaptiste Daroussin 7*61d06d6bSBaptiste Daroussin #else 8*61d06d6bSBaptiste Daroussin 9*61d06d6bSBaptiste Daroussin /* $OpenBSD: ohash.c,v 1.1 2014/06/02 18:52:03 deraadt Exp $ */ 10*61d06d6bSBaptiste Daroussin 11*61d06d6bSBaptiste Daroussin /* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> 12*61d06d6bSBaptiste Daroussin * 13*61d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 14*61d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 15*61d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 16*61d06d6bSBaptiste Daroussin * 17*61d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 18*61d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 19*61d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 20*61d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 21*61d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 22*61d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 23*61d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24*61d06d6bSBaptiste Daroussin */ 25*61d06d6bSBaptiste Daroussin 26*61d06d6bSBaptiste Daroussin #include <sys/types.h> 27*61d06d6bSBaptiste Daroussin 28*61d06d6bSBaptiste Daroussin #include <stddef.h> 29*61d06d6bSBaptiste Daroussin #include <stdint.h> 30*61d06d6bSBaptiste Daroussin #include <stdlib.h> 31*61d06d6bSBaptiste Daroussin #include <string.h> 32*61d06d6bSBaptiste Daroussin #include <limits.h> 33*61d06d6bSBaptiste Daroussin #include "compat_ohash.h" 34*61d06d6bSBaptiste Daroussin 35*61d06d6bSBaptiste Daroussin struct _ohash_record { 36*61d06d6bSBaptiste Daroussin uint32_t hv; 37*61d06d6bSBaptiste Daroussin const char *p; 38*61d06d6bSBaptiste Daroussin }; 39*61d06d6bSBaptiste Daroussin 40*61d06d6bSBaptiste Daroussin #define DELETED ((const char *)h) 41*61d06d6bSBaptiste Daroussin #define NONE (h->size) 42*61d06d6bSBaptiste Daroussin 43*61d06d6bSBaptiste Daroussin /* Don't bother changing the hash table if the change is small enough. */ 44*61d06d6bSBaptiste Daroussin #define MINSIZE (1UL << 4) 45*61d06d6bSBaptiste Daroussin #define MINDELETED 4 46*61d06d6bSBaptiste Daroussin 47*61d06d6bSBaptiste Daroussin static void ohash_resize(struct ohash *); 48*61d06d6bSBaptiste Daroussin 49*61d06d6bSBaptiste Daroussin 50*61d06d6bSBaptiste Daroussin /* This handles the common case of variable length keys, where the 51*61d06d6bSBaptiste Daroussin * key is stored at the end of the record. 52*61d06d6bSBaptiste Daroussin */ 53*61d06d6bSBaptiste Daroussin void * 54*61d06d6bSBaptiste Daroussin ohash_create_entry(struct ohash_info *i, const char *start, const char **end) 55*61d06d6bSBaptiste Daroussin { 56*61d06d6bSBaptiste Daroussin char *p; 57*61d06d6bSBaptiste Daroussin 58*61d06d6bSBaptiste Daroussin if (!*end) 59*61d06d6bSBaptiste Daroussin *end = start + strlen(start); 60*61d06d6bSBaptiste Daroussin p = (i->alloc)(i->key_offset + (*end - start) + 1, i->data); 61*61d06d6bSBaptiste Daroussin if (p) { 62*61d06d6bSBaptiste Daroussin memcpy(p+i->key_offset, start, *end-start); 63*61d06d6bSBaptiste Daroussin p[i->key_offset + (*end - start)] = '\0'; 64*61d06d6bSBaptiste Daroussin } 65*61d06d6bSBaptiste Daroussin return (void *)p; 66*61d06d6bSBaptiste Daroussin } 67*61d06d6bSBaptiste Daroussin 68*61d06d6bSBaptiste Daroussin /* hash_delete only frees the hash structure. Use hash_first/hash_next 69*61d06d6bSBaptiste Daroussin * to free entries as well. */ 70*61d06d6bSBaptiste Daroussin void 71*61d06d6bSBaptiste Daroussin ohash_delete(struct ohash *h) 72*61d06d6bSBaptiste Daroussin { 73*61d06d6bSBaptiste Daroussin (h->info.free)(h->t, h->info.data); 74*61d06d6bSBaptiste Daroussin #ifndef NDEBUG 75*61d06d6bSBaptiste Daroussin h->t = NULL; 76*61d06d6bSBaptiste Daroussin #endif 77*61d06d6bSBaptiste Daroussin } 78*61d06d6bSBaptiste Daroussin 79*61d06d6bSBaptiste Daroussin static void 80*61d06d6bSBaptiste Daroussin ohash_resize(struct ohash *h) 81*61d06d6bSBaptiste Daroussin { 82*61d06d6bSBaptiste Daroussin struct _ohash_record *n; 83*61d06d6bSBaptiste Daroussin size_t ns; 84*61d06d6bSBaptiste Daroussin unsigned int j; 85*61d06d6bSBaptiste Daroussin unsigned int i, incr; 86*61d06d6bSBaptiste Daroussin 87*61d06d6bSBaptiste Daroussin if (4 * h->deleted < h->total) { 88*61d06d6bSBaptiste Daroussin if (h->size >= (UINT_MAX >> 1U)) 89*61d06d6bSBaptiste Daroussin ns = UINT_MAX; 90*61d06d6bSBaptiste Daroussin else 91*61d06d6bSBaptiste Daroussin ns = h->size << 1U; 92*61d06d6bSBaptiste Daroussin } else if (3 * h->deleted > 2 * h->total) 93*61d06d6bSBaptiste Daroussin ns = h->size >> 1U; 94*61d06d6bSBaptiste Daroussin else 95*61d06d6bSBaptiste Daroussin ns = h->size; 96*61d06d6bSBaptiste Daroussin if (ns < MINSIZE) 97*61d06d6bSBaptiste Daroussin ns = MINSIZE; 98*61d06d6bSBaptiste Daroussin #ifdef STATS_HASH 99*61d06d6bSBaptiste Daroussin STAT_HASH_EXPAND++; 100*61d06d6bSBaptiste Daroussin STAT_HASH_SIZE += ns - h->size; 101*61d06d6bSBaptiste Daroussin #endif 102*61d06d6bSBaptiste Daroussin 103*61d06d6bSBaptiste Daroussin n = (h->info.calloc)(ns, sizeof(struct _ohash_record), h->info.data); 104*61d06d6bSBaptiste Daroussin if (!n) 105*61d06d6bSBaptiste Daroussin return; 106*61d06d6bSBaptiste Daroussin 107*61d06d6bSBaptiste Daroussin for (j = 0; j < h->size; j++) { 108*61d06d6bSBaptiste Daroussin if (h->t[j].p != NULL && h->t[j].p != DELETED) { 109*61d06d6bSBaptiste Daroussin i = h->t[j].hv % ns; 110*61d06d6bSBaptiste Daroussin incr = ((h->t[j].hv % (ns - 2)) & ~1) + 1; 111*61d06d6bSBaptiste Daroussin while (n[i].p != NULL) { 112*61d06d6bSBaptiste Daroussin i += incr; 113*61d06d6bSBaptiste Daroussin if (i >= ns) 114*61d06d6bSBaptiste Daroussin i -= ns; 115*61d06d6bSBaptiste Daroussin } 116*61d06d6bSBaptiste Daroussin n[i].hv = h->t[j].hv; 117*61d06d6bSBaptiste Daroussin n[i].p = h->t[j].p; 118*61d06d6bSBaptiste Daroussin } 119*61d06d6bSBaptiste Daroussin } 120*61d06d6bSBaptiste Daroussin (h->info.free)(h->t, h->info.data); 121*61d06d6bSBaptiste Daroussin h->t = n; 122*61d06d6bSBaptiste Daroussin h->size = ns; 123*61d06d6bSBaptiste Daroussin h->total -= h->deleted; 124*61d06d6bSBaptiste Daroussin h->deleted = 0; 125*61d06d6bSBaptiste Daroussin } 126*61d06d6bSBaptiste Daroussin 127*61d06d6bSBaptiste Daroussin void * 128*61d06d6bSBaptiste Daroussin ohash_remove(struct ohash *h, unsigned int i) 129*61d06d6bSBaptiste Daroussin { 130*61d06d6bSBaptiste Daroussin void *result = (void *)h->t[i].p; 131*61d06d6bSBaptiste Daroussin 132*61d06d6bSBaptiste Daroussin if (result == NULL || result == DELETED) 133*61d06d6bSBaptiste Daroussin return NULL; 134*61d06d6bSBaptiste Daroussin 135*61d06d6bSBaptiste Daroussin #ifdef STATS_HASH 136*61d06d6bSBaptiste Daroussin STAT_HASH_ENTRIES--; 137*61d06d6bSBaptiste Daroussin #endif 138*61d06d6bSBaptiste Daroussin h->t[i].p = DELETED; 139*61d06d6bSBaptiste Daroussin h->deleted++; 140*61d06d6bSBaptiste Daroussin if (h->deleted >= MINDELETED && 4 * h->deleted > h->total) 141*61d06d6bSBaptiste Daroussin ohash_resize(h); 142*61d06d6bSBaptiste Daroussin return result; 143*61d06d6bSBaptiste Daroussin } 144*61d06d6bSBaptiste Daroussin 145*61d06d6bSBaptiste Daroussin void * 146*61d06d6bSBaptiste Daroussin ohash_find(struct ohash *h, unsigned int i) 147*61d06d6bSBaptiste Daroussin { 148*61d06d6bSBaptiste Daroussin if (h->t[i].p == DELETED) 149*61d06d6bSBaptiste Daroussin return NULL; 150*61d06d6bSBaptiste Daroussin else 151*61d06d6bSBaptiste Daroussin return (void *)h->t[i].p; 152*61d06d6bSBaptiste Daroussin } 153*61d06d6bSBaptiste Daroussin 154*61d06d6bSBaptiste Daroussin void * 155*61d06d6bSBaptiste Daroussin ohash_insert(struct ohash *h, unsigned int i, void *p) 156*61d06d6bSBaptiste Daroussin { 157*61d06d6bSBaptiste Daroussin #ifdef STATS_HASH 158*61d06d6bSBaptiste Daroussin STAT_HASH_ENTRIES++; 159*61d06d6bSBaptiste Daroussin #endif 160*61d06d6bSBaptiste Daroussin if (h->t[i].p == DELETED) { 161*61d06d6bSBaptiste Daroussin h->deleted--; 162*61d06d6bSBaptiste Daroussin h->t[i].p = p; 163*61d06d6bSBaptiste Daroussin } else { 164*61d06d6bSBaptiste Daroussin h->t[i].p = p; 165*61d06d6bSBaptiste Daroussin /* Arbitrary resize boundary. Tweak if not efficient enough. */ 166*61d06d6bSBaptiste Daroussin if (++h->total * 4 > h->size * 3) 167*61d06d6bSBaptiste Daroussin ohash_resize(h); 168*61d06d6bSBaptiste Daroussin } 169*61d06d6bSBaptiste Daroussin return p; 170*61d06d6bSBaptiste Daroussin } 171*61d06d6bSBaptiste Daroussin 172*61d06d6bSBaptiste Daroussin unsigned int 173*61d06d6bSBaptiste Daroussin ohash_entries(struct ohash *h) 174*61d06d6bSBaptiste Daroussin { 175*61d06d6bSBaptiste Daroussin return h->total - h->deleted; 176*61d06d6bSBaptiste Daroussin } 177*61d06d6bSBaptiste Daroussin 178*61d06d6bSBaptiste Daroussin void * 179*61d06d6bSBaptiste Daroussin ohash_first(struct ohash *h, unsigned int *pos) 180*61d06d6bSBaptiste Daroussin { 181*61d06d6bSBaptiste Daroussin *pos = 0; 182*61d06d6bSBaptiste Daroussin return ohash_next(h, pos); 183*61d06d6bSBaptiste Daroussin } 184*61d06d6bSBaptiste Daroussin 185*61d06d6bSBaptiste Daroussin void * 186*61d06d6bSBaptiste Daroussin ohash_next(struct ohash *h, unsigned int *pos) 187*61d06d6bSBaptiste Daroussin { 188*61d06d6bSBaptiste Daroussin for (; *pos < h->size; (*pos)++) 189*61d06d6bSBaptiste Daroussin if (h->t[*pos].p != DELETED && h->t[*pos].p != NULL) 190*61d06d6bSBaptiste Daroussin return (void *)h->t[(*pos)++].p; 191*61d06d6bSBaptiste Daroussin return NULL; 192*61d06d6bSBaptiste Daroussin } 193*61d06d6bSBaptiste Daroussin 194*61d06d6bSBaptiste Daroussin void 195*61d06d6bSBaptiste Daroussin ohash_init(struct ohash *h, unsigned int size, struct ohash_info *info) 196*61d06d6bSBaptiste Daroussin { 197*61d06d6bSBaptiste Daroussin h->size = 1UL << size; 198*61d06d6bSBaptiste Daroussin if (h->size < MINSIZE) 199*61d06d6bSBaptiste Daroussin h->size = MINSIZE; 200*61d06d6bSBaptiste Daroussin #ifdef STATS_HASH 201*61d06d6bSBaptiste Daroussin STAT_HASH_CREATION++; 202*61d06d6bSBaptiste Daroussin STAT_HASH_SIZE += h->size; 203*61d06d6bSBaptiste Daroussin #endif 204*61d06d6bSBaptiste Daroussin /* Copy info so that caller may free it. */ 205*61d06d6bSBaptiste Daroussin h->info.key_offset = info->key_offset; 206*61d06d6bSBaptiste Daroussin h->info.calloc = info->calloc; 207*61d06d6bSBaptiste Daroussin h->info.free = info->free; 208*61d06d6bSBaptiste Daroussin h->info.alloc = info->alloc; 209*61d06d6bSBaptiste Daroussin h->info.data = info->data; 210*61d06d6bSBaptiste Daroussin h->t = (h->info.calloc)(h->size, sizeof(struct _ohash_record), 211*61d06d6bSBaptiste Daroussin h->info.data); 212*61d06d6bSBaptiste Daroussin h->total = h->deleted = 0; 213*61d06d6bSBaptiste Daroussin } 214*61d06d6bSBaptiste Daroussin 215*61d06d6bSBaptiste Daroussin uint32_t 216*61d06d6bSBaptiste Daroussin ohash_interval(const char *s, const char **e) 217*61d06d6bSBaptiste Daroussin { 218*61d06d6bSBaptiste Daroussin uint32_t k; 219*61d06d6bSBaptiste Daroussin 220*61d06d6bSBaptiste Daroussin if (!*e) 221*61d06d6bSBaptiste Daroussin *e = s + strlen(s); 222*61d06d6bSBaptiste Daroussin if (s == *e) 223*61d06d6bSBaptiste Daroussin k = 0; 224*61d06d6bSBaptiste Daroussin else 225*61d06d6bSBaptiste Daroussin k = *s++; 226*61d06d6bSBaptiste Daroussin while (s != *e) 227*61d06d6bSBaptiste Daroussin k = ((k << 2) | (k >> 30)) ^ *s++; 228*61d06d6bSBaptiste Daroussin return k; 229*61d06d6bSBaptiste Daroussin } 230*61d06d6bSBaptiste Daroussin 231*61d06d6bSBaptiste Daroussin unsigned int 232*61d06d6bSBaptiste Daroussin ohash_lookup_interval(struct ohash *h, const char *start, const char *end, 233*61d06d6bSBaptiste Daroussin uint32_t hv) 234*61d06d6bSBaptiste Daroussin { 235*61d06d6bSBaptiste Daroussin unsigned int i, incr; 236*61d06d6bSBaptiste Daroussin unsigned int empty; 237*61d06d6bSBaptiste Daroussin 238*61d06d6bSBaptiste Daroussin #ifdef STATS_HASH 239*61d06d6bSBaptiste Daroussin STAT_HASH_LOOKUP++; 240*61d06d6bSBaptiste Daroussin #endif 241*61d06d6bSBaptiste Daroussin empty = NONE; 242*61d06d6bSBaptiste Daroussin i = hv % h->size; 243*61d06d6bSBaptiste Daroussin incr = ((hv % (h->size-2)) & ~1) + 1; 244*61d06d6bSBaptiste Daroussin while (h->t[i].p != NULL) { 245*61d06d6bSBaptiste Daroussin #ifdef STATS_HASH 246*61d06d6bSBaptiste Daroussin STAT_HASH_LENGTH++; 247*61d06d6bSBaptiste Daroussin #endif 248*61d06d6bSBaptiste Daroussin if (h->t[i].p == DELETED) { 249*61d06d6bSBaptiste Daroussin if (empty == NONE) 250*61d06d6bSBaptiste Daroussin empty = i; 251*61d06d6bSBaptiste Daroussin } else if (h->t[i].hv == hv && 252*61d06d6bSBaptiste Daroussin strncmp(h->t[i].p+h->info.key_offset, start, 253*61d06d6bSBaptiste Daroussin end - start) == 0 && 254*61d06d6bSBaptiste Daroussin (h->t[i].p+h->info.key_offset)[end-start] == '\0') { 255*61d06d6bSBaptiste Daroussin if (empty != NONE) { 256*61d06d6bSBaptiste Daroussin h->t[empty].hv = hv; 257*61d06d6bSBaptiste Daroussin h->t[empty].p = h->t[i].p; 258*61d06d6bSBaptiste Daroussin h->t[i].p = DELETED; 259*61d06d6bSBaptiste Daroussin return empty; 260*61d06d6bSBaptiste Daroussin } else { 261*61d06d6bSBaptiste Daroussin #ifdef STATS_HASH 262*61d06d6bSBaptiste Daroussin STAT_HASH_POSITIVE++; 263*61d06d6bSBaptiste Daroussin #endif 264*61d06d6bSBaptiste Daroussin return i; 265*61d06d6bSBaptiste Daroussin } 266*61d06d6bSBaptiste Daroussin } 267*61d06d6bSBaptiste Daroussin i += incr; 268*61d06d6bSBaptiste Daroussin if (i >= h->size) 269*61d06d6bSBaptiste Daroussin i -= h->size; 270*61d06d6bSBaptiste Daroussin } 271*61d06d6bSBaptiste Daroussin 272*61d06d6bSBaptiste Daroussin /* Found an empty position. */ 273*61d06d6bSBaptiste Daroussin if (empty != NONE) 274*61d06d6bSBaptiste Daroussin i = empty; 275*61d06d6bSBaptiste Daroussin h->t[i].hv = hv; 276*61d06d6bSBaptiste Daroussin return i; 277*61d06d6bSBaptiste Daroussin } 278*61d06d6bSBaptiste Daroussin 279*61d06d6bSBaptiste Daroussin unsigned int 280*61d06d6bSBaptiste Daroussin ohash_lookup_memory(struct ohash *h, const char *k, size_t size, uint32_t hv) 281*61d06d6bSBaptiste Daroussin { 282*61d06d6bSBaptiste Daroussin unsigned int i, incr; 283*61d06d6bSBaptiste Daroussin unsigned int empty; 284*61d06d6bSBaptiste Daroussin 285*61d06d6bSBaptiste Daroussin #ifdef STATS_HASH 286*61d06d6bSBaptiste Daroussin STAT_HASH_LOOKUP++; 287*61d06d6bSBaptiste Daroussin #endif 288*61d06d6bSBaptiste Daroussin empty = NONE; 289*61d06d6bSBaptiste Daroussin i = hv % h->size; 290*61d06d6bSBaptiste Daroussin incr = ((hv % (h->size-2)) & ~1) + 1; 291*61d06d6bSBaptiste Daroussin while (h->t[i].p != NULL) { 292*61d06d6bSBaptiste Daroussin #ifdef STATS_HASH 293*61d06d6bSBaptiste Daroussin STAT_HASH_LENGTH++; 294*61d06d6bSBaptiste Daroussin #endif 295*61d06d6bSBaptiste Daroussin if (h->t[i].p == DELETED) { 296*61d06d6bSBaptiste Daroussin if (empty == NONE) 297*61d06d6bSBaptiste Daroussin empty = i; 298*61d06d6bSBaptiste Daroussin } else if (h->t[i].hv == hv && 299*61d06d6bSBaptiste Daroussin memcmp(h->t[i].p+h->info.key_offset, k, size) == 0) { 300*61d06d6bSBaptiste Daroussin if (empty != NONE) { 301*61d06d6bSBaptiste Daroussin h->t[empty].hv = hv; 302*61d06d6bSBaptiste Daroussin h->t[empty].p = h->t[i].p; 303*61d06d6bSBaptiste Daroussin h->t[i].p = DELETED; 304*61d06d6bSBaptiste Daroussin return empty; 305*61d06d6bSBaptiste Daroussin } else { 306*61d06d6bSBaptiste Daroussin #ifdef STATS_HASH 307*61d06d6bSBaptiste Daroussin STAT_HASH_POSITIVE++; 308*61d06d6bSBaptiste Daroussin #endif 309*61d06d6bSBaptiste Daroussin } return i; 310*61d06d6bSBaptiste Daroussin } 311*61d06d6bSBaptiste Daroussin i += incr; 312*61d06d6bSBaptiste Daroussin if (i >= h->size) 313*61d06d6bSBaptiste Daroussin i -= h->size; 314*61d06d6bSBaptiste Daroussin } 315*61d06d6bSBaptiste Daroussin 316*61d06d6bSBaptiste Daroussin /* Found an empty position. */ 317*61d06d6bSBaptiste Daroussin if (empty != NONE) 318*61d06d6bSBaptiste Daroussin i = empty; 319*61d06d6bSBaptiste Daroussin h->t[i].hv = hv; 320*61d06d6bSBaptiste Daroussin return i; 321*61d06d6bSBaptiste Daroussin } 322*61d06d6bSBaptiste Daroussin 323*61d06d6bSBaptiste Daroussin unsigned int 324*61d06d6bSBaptiste Daroussin ohash_qlookup(struct ohash *h, const char *s) 325*61d06d6bSBaptiste Daroussin { 326*61d06d6bSBaptiste Daroussin const char *e = NULL; 327*61d06d6bSBaptiste Daroussin return ohash_qlookupi(h, s, &e); 328*61d06d6bSBaptiste Daroussin } 329*61d06d6bSBaptiste Daroussin 330*61d06d6bSBaptiste Daroussin unsigned int 331*61d06d6bSBaptiste Daroussin ohash_qlookupi(struct ohash *h, const char *s, const char **e) 332*61d06d6bSBaptiste Daroussin { 333*61d06d6bSBaptiste Daroussin uint32_t hv; 334*61d06d6bSBaptiste Daroussin 335*61d06d6bSBaptiste Daroussin hv = ohash_interval(s, e); 336*61d06d6bSBaptiste Daroussin return ohash_lookup_interval(h, s, *e, hv); 337*61d06d6bSBaptiste Daroussin } 338*61d06d6bSBaptiste Daroussin 339*61d06d6bSBaptiste Daroussin #endif /*!HAVE_OHASH*/ 340