1 /* 2 * Copyright (c) 1993-2001 by Sun Microsystems, Inc. 3 * All rights reserved. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * Copyright 1988, 1991 by Carnegie Mellon University 10 * 11 * All Rights Reserved 12 * 13 * Permission to use, copy, modify, and distribute this software and its 14 * documentation for any purpose and without fee is hereby granted, provided 15 * that the above copyright notice appear in all copies and that both that 16 * copyright notice and this permission notice appear in supporting 17 * documentation, and that the name of Carnegie Mellon University not be used 18 * in advertising or publicity pertaining to distribution of the software 19 * without specific, written prior permission. 20 * 21 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 22 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 23 * IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 24 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 25 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 26 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 27 * THIS SOFTWARE. 28 */ 29 30 /* 31 * Generalized hash table ADT 32 * 33 * Provides multiple, dynamically-allocated, variable-sized hash tables on 34 * various data and keys. 35 * 36 * This package attempts to follow some of the coding conventions suggested 37 * by Bob Sidebotham and the AFS Clean Code Committee of the 38 * Information Technology Center at Carnegie Mellon. 39 * 40 * Additions for per bucket locking, and configurable dynamic free of 41 * unused entries. 42 */ 43 44 #include <stdlib.h> 45 #include <string.h> 46 #include <sys/types.h> 47 #include <sys/param.h> 48 #include <sys/sysmacros.h> 49 #include <stdarg.h> 50 #include <stddef.h> 51 #include <assert.h> 52 #include <synch.h> 53 #include "dhcpd.h" 54 #include "hash.h" 55 56 /* 57 * Hash table size calculation routine. 58 * 59 * Estimate the size of a hash table based on the expected number of 60 * entries, up to a maximum of HASHTABLESIZE. 61 */ 62 static unsigned 63 hashi_Hsize(unsigned hint) 64 { 65 unsigned f; 66 67 if (hint == 0) /* Default size. */ 68 hint = HASHTABLESIZE; 69 else if (hint < 16) /* Minimal size. */ 70 hint = 16; 71 72 hint /= 4; 73 for (f = 2; f * f <= hint; f++) { /* Find next largest prime. */ 74 if (hint % f == 0) { 75 f = 1; 76 hint++; 77 } 78 } 79 return (MIN(HASHTABLESIZE, hint)); 80 } 81 82 /* 83 * Frees an entire linked list of bucket members (used in the 84 * open hashing scheme). Does nothing if the passed pointer is NULL. 85 * 86 * Returns B_FALSE and members which could not be freed in bucketptr, when 87 * force variable is set to B_FALSE, and free_data routine indicates 88 * free did not occur. 89 */ 90 static boolean_t 91 hashi_FreeMember(hash_member **bucketptr, boolean_t (*free_data)(), 92 boolean_t force) 93 { 94 hash_member *prev, *next, *unfree = NULL; 95 boolean_t ret = B_TRUE; 96 97 if (bucketptr) { 98 for (prev = *bucketptr; prev; prev = next) { 99 next = prev->next; 100 prev->next = NULL; 101 if (free_data != NULL) { 102 if ((*free_data)(prev->data, force) == 103 B_FALSE) { 104 ret = B_FALSE; 105 prev->next = unfree; 106 unfree = prev; 107 } else { 108 free(prev); 109 } 110 } else 111 free(prev); 112 } 113 *bucketptr = unfree; 114 } 115 return (ret); 116 } 117 118 /* 119 * Dynamic free initialization. 120 */ 121 static void 122 hashi_Dinit(hash_tbl *hashtable, hash_member *memberptr) 123 { 124 (void) mutex_init(&memberptr->h_mtx, USYNC_THREAD, NULL); 125 memberptr->h_time = time(NULL) + hashtable->dfree_time; 126 memberptr->h_count = 1; 127 } 128 129 /* 130 * Dynamic free reference count increment. 131 */ 132 static void 133 hashi_Dhold(hash_member *memberptr) 134 { 135 (void) mutex_lock(&memberptr->h_mtx); 136 memberptr->h_count++; 137 (void) mutex_unlock(&memberptr->h_mtx); 138 } 139 140 /* 141 * Dynamic free expired data. Return NULL if memberptr is successfully 142 * dynamically freed, otherwise return memberptr. 143 */ 144 static hash_member * 145 hashi_Dfree(hash_member *memberptr, boolean_t (*free_data)()) 146 { 147 hash_member *next; 148 149 next = memberptr->next; 150 memberptr->next = NULL; 151 if (hashi_FreeMember(&memberptr, free_data, B_FALSE) == B_TRUE) 152 memberptr = NULL; 153 else 154 memberptr->next = next; 155 return (memberptr); 156 } 157 158 /* 159 * Hash table initialization routine. 160 * 161 * This routine creates and intializes a hash table of size "tablesize" 162 * entries. Successful calls return a pointer to the hash table (which must 163 * be passed to other hash routines to identify the hash table). Failed 164 * calls return NULL. 165 */ 166 hash_tbl * 167 hash_Init(unsigned tablesize, boolean_t (*dfree_data)(), time_t dtime, 168 boolean_t lck) 169 { 170 hash_tbl *hashtblptr; 171 unsigned totalsize; 172 unsigned i; 173 174 tablesize = hashi_Hsize(tablesize); 175 176 totalsize = sizeof (hash_tbl) + (sizeof (hash_bucket) * 177 (tablesize - 1)); 178 179 hashtblptr = (hash_tbl *)smalloc(totalsize); 180 181 hashtblptr->size = tablesize; /* Success! */ 182 hashtblptr->bucketnum = 0; 183 hashtblptr->dfree_data = dfree_data; 184 hashtblptr->dfree_lck = lck; 185 hashtblptr->dfree_time = dtime; 186 hashtblptr->table = &hashtblptr->data[0]; 187 for (i = 0; i < tablesize; i++) { 188 hashtblptr->table[i].table = hashtblptr; 189 if (lck == B_TRUE) { 190 (void) rwlock_init(&(hashtblptr->table[i].rwlock), 191 USYNC_THREAD, NULL); 192 } 193 } 194 195 return (hashtblptr); /* NULL if failure */ 196 } 197 198 /* 199 * Generic hash function to calculate a hash code from the given string. 200 * 201 * For each byte of the string, this function left-shifts the value in an 202 * accumulator and then adds the byte into the accumulator. The contents of 203 * the accumulator is returned after the entire string has been processed. 204 * It is assumed that this result will be used as the "hashcode" parameter in 205 * calls to other functions in this package. These functions automatically 206 * adjust the hashcode for the size of each hashtable. 207 * 208 * This algorithm probably works best when the hash table size is a prime 209 * number. 210 * 211 * Hopefully, this function is better than the previous one which returned 212 * the sum of the squares of all the bytes. I'm still open to other 213 * suggestions for a default hash function. The programmer is more than 214 * welcome to supply his/her own hash function as that is one of the design 215 * features of this package. 216 */ 217 static unsigned 218 hashi_HashFunction(unsigned char *string, unsigned len) 219 { 220 unsigned accum; 221 222 /* 223 * Special case: allow hash_Delete() to iterate over buckets. 224 */ 225 if (string == NULL) 226 return (len); 227 228 for (accum = 0; len != 0; len--) { 229 accum <<= 1; 230 accum += (unsigned)(*string++ & 0xFF); 231 } 232 return (accum); 233 } 234 235 /* 236 * This routine re-initializes the hash table. It frees all the allocated 237 * memory and resets all bucket pointers to NULL. For the macro hash 238 * table, the table will be reused. Other tables (with bucket locks) 239 * will be destroyed. 240 */ 241 void 242 hash_Reset(hash_tbl *hashtable, boolean_t (*free_data)()) 243 { 244 hash_bucket *bucketptr; 245 unsigned i; 246 247 bucketptr = &((hashtable->table)[0]); 248 for (i = 0; i < hashtable->size; i++) { 249 if (hashtable->dfree_lck == B_TRUE) 250 (void) rw_wrlock(&bucketptr->rwlock); 251 /* 252 * Unequivocally free member, using the force parameter. 253 */ 254 (void) hashi_FreeMember(&bucketptr->next, free_data, B_TRUE); 255 bucketptr->next = NULL; 256 if (hashtable->dfree_lck == B_TRUE) { 257 (void) rw_unlock(&bucketptr->rwlock); 258 (void) rwlock_destroy(&(bucketptr->rwlock)); 259 } 260 bucketptr++; 261 } 262 hashtable->bucketnum = 0; 263 } 264 265 /* 266 * Returns B_TRUE if at least one entry for the given key exists; B_FALSE 267 * otherwise. Dynamically free expired data as searched. 268 */ 269 static int 270 hashi_Exists(hash_bucket *bucketptr, int (*compare)(), hash_datum *key, 271 boolean_t (*free_data)(), hash_member **prev) 272 { 273 hash_member *prevptr = (hash_member *)bucketptr; 274 hash_member *memberptr = bucketptr->next; 275 hash_tbl *hashtable = bucketptr->table; 276 hash_member *next; 277 boolean_t ret = B_FALSE; 278 time_t now = time(NULL); 279 280 while (memberptr != NULL) { 281 /* 282 * Dynamically free expired data. 283 */ 284 if (free_data != NULL && hashtable->dfree_data != NULL && 285 memberptr->h_time < now) { 286 next = memberptr->next; 287 if ((memberptr = hashi_Dfree(memberptr, free_data)) == 288 NULL) { 289 prevptr->next = memberptr = next; 290 continue; 291 } 292 } 293 294 /* 295 * Entry exists, or we are randomly selecting any 296 * element (compare function is NULL). 297 */ 298 if (compare == NULL || (*compare)(key, memberptr->data)) { 299 ret = B_TRUE; 300 break; 301 } else 302 prevptr = memberptr; 303 memberptr = memberptr->next; 304 } 305 306 if (prev != NULL) 307 *prev = prevptr; 308 return (ret); 309 } 310 311 /* 312 * Returns number of Dynamically freed expired entries. 313 */ 314 static int 315 hashi_Expire(hash_bucket *bucketptr, boolean_t (*free_data)()) 316 { 317 hash_member *prevptr = (hash_member *)bucketptr; 318 hash_member *memberptr = bucketptr->next; 319 hash_tbl *hashtable = bucketptr->table; 320 hash_member *next; 321 int rcount = 0; 322 time_t now = time(NULL); 323 324 while (memberptr) { 325 /* 326 * Dynamically free expired data. 327 */ 328 if (free_data != NULL && hashtable->dfree_data != NULL && 329 memberptr->h_time < now) { 330 next = memberptr->next; 331 if ((memberptr = hashi_Dfree(memberptr, free_data)) == 332 NULL) { 333 rcount++; 334 prevptr->next = memberptr = next; 335 continue; 336 } 337 } 338 prevptr = memberptr; 339 memberptr = memberptr->next; 340 } 341 return (rcount); 342 } 343 344 /* 345 * Insert the data item "element" into the hash table using "hashcode" 346 * to determine the bucket number, and "compare" and "key" to determine 347 * its uniqueness. 348 * 349 * If the insertion is successful the element is returned. If a matching entry 350 * already exists in the given bucket of the hash table, then NULL is returned, 351 * signifying that the entry is already in the table. This happens when some 352 * other thread has already inserted the entry. 353 */ 354 void * 355 hash_Insert(hash_tbl *hashtable, void *hashdata, unsigned hashlen, 356 int (*compare)(), hash_datum *key, hash_datum *element) 357 { 358 hash_member *temp = NULL; 359 hash_bucket *bucketptr; 360 hash_member *prev = NULL; 361 unsigned hashcode = hashi_HashFunction(hashdata, hashlen); 362 363 bucketptr = &((hashtable->table)[hashcode % hashtable->size]); 364 if (hashtable->dfree_lck) 365 (void) rw_wrlock(&bucketptr->rwlock); 366 367 if (hashi_Exists(bucketptr, compare, key, hashtable->dfree_data, 368 &prev)) { 369 /* Some other thread got there first, so just return */ 370 if (hashtable->dfree_lck) 371 (void) rw_unlock(&bucketptr->rwlock); 372 return (NULL); 373 } 374 375 temp = (hash_member *)smalloc(sizeof (hash_member)); 376 377 prev->next = temp; 378 temp->data = element; 379 temp->next = NULL; 380 381 /* 382 * Dynamic free initialization. 383 */ 384 if (hashtable->dfree_data != NULL) 385 hashi_Dinit(hashtable, temp); 386 387 if (hashtable->dfree_lck) 388 (void) rw_unlock(&bucketptr->rwlock); 389 390 return ((void *)temp); 391 } 392 393 /* 394 * Release the reference count on an item. Performance: if item is to be 395 * deleted, mark for future dynamic free. 396 */ 397 void 398 hash_Rele(void *hashp, boolean_t delete) 399 { 400 hash_member *memberptr = (hash_member *)hashp; 401 402 (void) mutex_lock(&memberptr->h_mtx); 403 memberptr->h_count--; 404 assert(memberptr->h_count >= 0); 405 if (delete == B_TRUE) 406 memberptr->h_time = 0; 407 (void) mutex_unlock(&memberptr->h_mtx); 408 } 409 410 /* 411 * Report the reference count on an item. 412 */ 413 int 414 hash_Refcount(void *hashp) 415 { 416 hash_member *memberptr = (hash_member *)hashp; 417 int ret; 418 419 (void) mutex_lock(&memberptr->h_mtx); 420 ret = memberptr->h_count; 421 (void) mutex_unlock(&memberptr->h_mtx); 422 return (ret); 423 } 424 425 /* 426 * Report the dynamic free time on an item. 427 */ 428 int 429 hash_Htime(void *hashp) 430 { 431 hash_member *memberptr = (hash_member *)hashp; 432 int ret; 433 434 (void) mutex_lock(&memberptr->h_mtx); 435 ret = memberptr->h_time; 436 (void) mutex_unlock(&memberptr->h_mtx); 437 return (ret); 438 } 439 440 /* 441 * Increase the dynamic free time on an item. 442 */ 443 void 444 hash_Age(void *hashp) 445 { 446 hash_member *memberptr = (hash_member *)hashp; 447 448 (void) mutex_lock(&memberptr->h_mtx); 449 memberptr->h_time++; 450 (void) mutex_unlock(&memberptr->h_mtx); 451 } 452 453 /* 454 * Set the dynamic free time on an item. 455 */ 456 void 457 hash_Dtime(void *hashp, time_t tm) 458 { 459 hash_member *memberptr = (hash_member *)hashp; 460 461 (void) mutex_lock(&memberptr->h_mtx); 462 memberptr->h_time = tm; 463 (void) mutex_unlock(&memberptr->h_mtx); 464 } 465 466 /* 467 * Delete a data item from the hash table using "hashcode" 468 * to determine the bucket number, and "compare" and "key" to determine 469 * its uniqueness. 470 * 471 * If the deletion is successful 0 is returned. If a matching entry 472 * does not exist in the given bucket of the hash table, or some other error 473 * occurs, -1 is returned and the insertion is not done. 474 */ 475 boolean_t 476 hash_Delete(hash_tbl *hashtable, void *hashdata, unsigned hashlen, 477 int (*compare)(), hash_datum *key, boolean_t (*free_data)()) 478 { 479 hash_member *prev = NULL; 480 hash_member *temp; 481 hash_bucket *bucketptr; 482 unsigned hashcode = hashi_HashFunction(hashdata, hashlen); 483 484 bucketptr = &((hashtable->table)[hashcode % hashtable->size]); 485 if (hashtable->dfree_lck == B_TRUE) 486 (void) rw_wrlock(&bucketptr->rwlock); 487 488 if (hashi_Exists(bucketptr, compare, key, free_data, &prev) == 489 B_FALSE || prev == NULL) { 490 if (hashtable->dfree_lck == B_TRUE) 491 (void) rw_unlock(&bucketptr->rwlock); 492 return (B_FALSE); /* Entry does not exist */ 493 } 494 495 temp = prev->next; 496 if (temp) { 497 prev->next = temp->next; 498 temp->next = NULL; 499 (void) hashi_FreeMember(&temp, free_data, B_TRUE); 500 } else 501 prev->next = NULL; 502 if (hashtable->dfree_lck == B_TRUE) 503 (void) rw_unlock(&bucketptr->rwlock); 504 return (B_TRUE); 505 } 506 507 /* 508 * Locate and return the data entry associated with the given key. 509 * 510 * If the data entry is found, a pointer to it is returned. Otherwise, 511 * NULL is returned. 512 */ 513 hash_datum * 514 hash_Lookup(hash_tbl *hashtable, void *hashdata, unsigned hashlen, 515 int (*compare)(), hash_datum *key, boolean_t hold) 516 { 517 hash_datum *ret = NULL; 518 hash_bucket *bucketptr; 519 hash_member *prev = NULL; 520 unsigned hashcode = hashi_HashFunction(hashdata, hashlen); 521 522 bucketptr = &((hashtable->table)[hashcode % hashtable->size]); 523 if (hashtable->dfree_lck == B_TRUE) 524 (void) rw_wrlock(&bucketptr->rwlock); 525 526 if (hashi_Exists(bucketptr, compare, key, hashtable->dfree_data, 527 &prev) == B_TRUE) { 528 /* 529 * Dynamic free increment reference. 530 */ 531 if (hold) 532 hashi_Dhold(prev->next); 533 ret = prev->next->data; 534 535 } 536 if (hashtable->dfree_lck == B_TRUE) 537 (void) rw_unlock(&bucketptr->rwlock); 538 return (ret); 539 } 540 541 /* 542 * Reap expired data items, or a random data item from the hash table. 543 */ 544 void 545 hash_Reap(hash_tbl *hashtable, boolean_t (*free_data)()) 546 { 547 hash_bucket *bucketptr; 548 int rcount; 549 unsigned i; 550 551 bucketptr = &((hashtable->table)[0]); 552 rcount = 0; 553 554 /* 555 * Walk the buckets, reaping expired clients. 556 */ 557 for (i = 0; i < hashtable->size; i++) { 558 if (hashtable->dfree_lck == B_TRUE) 559 (void) rw_wrlock(&bucketptr->rwlock); 560 rcount += hashi_Expire(bucketptr, hashtable->dfree_data); 561 if (hashtable->dfree_lck == B_TRUE) 562 (void) rw_unlock(&bucketptr->rwlock); 563 bucketptr++; 564 } 565 566 /* 567 * Nothing to be reaped, delete a random element. Note that 568 * the unhash_data routine will wait for current references 569 * before deletion. 570 */ 571 if (rcount == 0) { 572 for (i = 0; i < hashtable->size; i++) { 573 if (hash_Delete(hashtable, NULL, i, NULL, NULL, 574 free_data) == B_TRUE) { 575 break; 576 } 577 } 578 } 579 }