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
hashi_Hsize(unsigned hint)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
hashi_FreeMember(hash_member ** bucketptr,boolean_t (* free_data)(),boolean_t force)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
hashi_Dinit(hash_tbl * hashtable,hash_member * memberptr)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
hashi_Dhold(hash_member * memberptr)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 *
hashi_Dfree(hash_member * memberptr,boolean_t (* free_data)())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 *
hash_Init(unsigned tablesize,boolean_t (* dfree_data)(),time_t dtime,boolean_t lck)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
hashi_HashFunction(unsigned char * string,unsigned len)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
hash_Reset(hash_tbl * hashtable,boolean_t (* free_data)())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
hashi_Exists(hash_bucket * bucketptr,int (* compare)(),hash_datum * key,boolean_t (* free_data)(),hash_member ** prev)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
hashi_Expire(hash_bucket * bucketptr,boolean_t (* free_data)())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 *
hash_Insert(hash_tbl * hashtable,void * hashdata,unsigned hashlen,int (* compare)(),hash_datum * key,hash_datum * element)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
hash_Rele(void * hashp,boolean_t delete)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
hash_Refcount(void * hashp)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
hash_Htime(void * hashp)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
hash_Age(void * hashp)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
hash_Dtime(void * hashp,time_t tm)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
hash_Delete(hash_tbl * hashtable,void * hashdata,unsigned hashlen,int (* compare)(),hash_datum * key,boolean_t (* free_data)())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 *
hash_Lookup(hash_tbl * hashtable,void * hashdata,unsigned hashlen,int (* compare)(),hash_datum * key,boolean_t hold)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
hash_Reap(hash_tbl * hashtable,boolean_t (* free_data)())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 }
580