1: /* hash.c
2:
3: Routines for manipulating hash tables... */
4:
5: /*
6: * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
7: * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
8: * Copyright (c) 1995-2003 by Internet Software Consortium
9: *
10: * Permission to use, copy, modify, and distribute this software for any
11: * purpose with or without fee is hereby granted, provided that the above
12: * copyright notice and this permission notice appear in all copies.
13: *
14: * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
15: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
17: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
20: * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21: *
22: * Internet Systems Consortium, Inc.
23: * 950 Charter Street
24: * Redwood City, CA 94063
25: * <info@isc.org>
26: * https://www.isc.org/
27: *
28: * This software has been written for Internet Systems Consortium
29: * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
30: * To learn more about Internet Systems Consortium, see
31: * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
32: * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
33: * ``http://www.nominum.com''.
34: */
35:
36: #include "dhcpd.h"
37:
38: #include <omapip/omapip_p.h>
39: #include <limits.h>
40: #include <ctype.h>
41:
42: static unsigned
43: find_length(const void *key,
44: unsigned (*do_hash)(const void *, unsigned, unsigned))
45: {
46: if (do_hash == do_case_hash || do_hash == do_string_hash)
47: return strlen((const char *)key);
48: if (do_hash == do_number_hash)
49: return sizeof(unsigned);
50: if (do_hash == do_ip4_hash)
51: return 4;
52:
53: log_debug("Unexpected hash function at %s:%d.", MDL);
54: /*
55: * If we get a hash function we don't specifically expect
56: * return a length of 0, this covers the case where a client
57: * id has a length of 0.
58: */
59: return 0;
60: }
61:
62: int new_hash_table (tp, count, file, line)
63: struct hash_table **tp;
64: unsigned count;
65: const char *file;
66: int line;
67: {
68: struct hash_table *rval;
69: unsigned extra;
70:
71: if (!tp) {
72: log_error ("%s(%d): new_hash_table called with null pointer.",
73: file, line);
74: #if defined (POINTER_DEBUG)
75: abort ();
76: #endif
77: return 0;
78: }
79: if (*tp) {
80: log_error ("%s(%d): non-null target for new_hash_table.",
81: file, line);
82: #if defined (POINTER_DEBUG)
83: abort ();
84: #endif
85: }
86:
87: /* There is one hash bucket in the structure. Allocate extra
88: * memory beyond the end of the structure to fulfill the requested
89: * count ("count - 1"). Do not let there be less than one.
90: */
91: if (count <= 1)
92: extra = 0;
93: else
94: extra = count - 1;
95:
96: rval = dmalloc(sizeof(struct hash_table) +
97: (extra * sizeof(struct hash_bucket *)), file, line);
98: if (!rval)
99: return 0;
100: rval -> hash_count = count;
101: *tp = rval;
102: return 1;
103: }
104:
105: void free_hash_table (tp, file, line)
106: struct hash_table **tp;
107: const char *file;
108: int line;
109: {
110: struct hash_table *ptr = *tp;
111:
112: #if defined (DEBUG_MEMORY_LEAKAGE) || \
113: defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
114: int i;
115: struct hash_bucket *hbc, *hbn = (struct hash_bucket *)0;
116:
117: for (i = 0; i < ptr -> hash_count; i++) {
118: for (hbc = ptr -> buckets [i]; hbc; hbc = hbn) {
119: hbn = hbc -> next;
120: if (ptr -> dereferencer && hbc -> value)
121: (*ptr -> dereferencer) (&hbc -> value, MDL);
122: }
123: for (hbc = ptr -> buckets [i]; hbc; hbc = hbn) {
124: hbn = hbc -> next;
125: free_hash_bucket (hbc, MDL);
126: }
127: ptr -> buckets [i] = (struct hash_bucket *)0;
128: }
129: #endif
130:
131: dfree((void *)ptr, MDL);
132: *tp = (struct hash_table *)0;
133: }
134:
135: struct hash_bucket *free_hash_buckets;
136:
137: #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
138: struct hash_bucket *hash_bucket_hunks;
139:
140: void relinquish_hash_bucket_hunks ()
141: {
142: struct hash_bucket *c, *n, **p;
143:
144: /* Account for all the hash buckets on the free list. */
145: p = &free_hash_buckets;
146: for (c = free_hash_buckets; c; c = c -> next) {
147: for (n = hash_bucket_hunks; n; n = n -> next) {
148: if (c > n && c < n + 127) {
149: *p = c -> next;
150: n -> len++;
151: break;
152: }
153: }
154: /* If we didn't delete the hash bucket from the free list,
155: advance the pointer. */
156: if (!n)
157: p = &c -> next;
158: }
159:
160: for (c = hash_bucket_hunks; c; c = n) {
161: n = c -> next;
162: if (c -> len != 126) {
163: log_info ("hashbucket %lx hash_buckets %d free %u",
164: (unsigned long)c, 127, c -> len);
165: }
166: dfree (c, MDL);
167: }
168: }
169: #endif
170:
171: struct hash_bucket *new_hash_bucket (file, line)
172: const char *file;
173: int line;
174: {
175: struct hash_bucket *rval;
176: int i = 0;
177: if (!free_hash_buckets) {
178: rval = dmalloc (127 * sizeof (struct hash_bucket),
179: file, line);
180: if (!rval)
181: return rval;
182: # if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
183: rval -> next = hash_bucket_hunks;
184: hash_bucket_hunks = rval;
185: hash_bucket_hunks -> len = 0;
186: i++;
187: rval++;
188: #endif
189: for (; i < 127; i++) {
190: rval -> next = free_hash_buckets;
191: free_hash_buckets = rval;
192: rval++;
193: }
194: }
195: rval = free_hash_buckets;
196: free_hash_buckets = rval -> next;
197: return rval;
198: }
199:
200: void free_hash_bucket (ptr, file, line)
201: struct hash_bucket *ptr;
202: const char *file;
203: int line;
204: {
205: #if defined (DEBUG_MALLOC_POOL)
206: struct hash_bucket *hp;
207:
208: for (hp = free_hash_buckets; hp; hp = hp -> next) {
209: if (hp == ptr) {
210: log_error ("hash bucket freed twice!");
211: abort ();
212: }
213: }
214: #endif
215: ptr -> next = free_hash_buckets;
216: free_hash_buckets = ptr;
217: }
218:
219: int new_hash(struct hash_table **rp,
220: hash_reference referencer,
221: hash_dereference dereferencer,
222: unsigned hsize,
223: unsigned (*hasher)(const void *, unsigned, unsigned),
224: const char *file, int line)
225: {
226: if (hsize == 0)
227: hsize = DEFAULT_HASH_SIZE;
228:
229: if (!new_hash_table (rp, hsize, file, line))
230: return 0;
231:
232: memset ((*rp)->buckets, 0, hsize * sizeof(struct hash_bucket *));
233:
234: (*rp)->referencer = referencer;
235: (*rp)->dereferencer = dereferencer;
236: (*rp)->do_hash = hasher;
237:
238: if (hasher == do_case_hash)
239: (*rp)->cmp = casecmp;
240: else
241: (*rp)->cmp = memcmp;
242:
243: return 1;
244: }
245:
246: unsigned
247: do_case_hash(const void *name, unsigned len, unsigned size)
248: {
249: register unsigned accum = 0;
250: register const unsigned char *s = name;
251: int i = len;
252: register unsigned c;
253:
254: while (i--) {
255: /* Make the hash case-insensitive. */
256: c = *s++;
257: if (isascii(c))
258: c = tolower(c);
259:
260: /* Add the character in... */
261: accum = (accum << 1) + c;
262:
263: /* Add carry back in... */
264: while (accum > 65535) {
265: accum = (accum & 65535) + (accum >> 16);
266: }
267:
268: }
269: return accum % size;
270: }
271:
272: unsigned
273: do_string_hash(const void *name, unsigned len, unsigned size)
274: {
275: register unsigned accum = 0;
276: register const unsigned char *s = (const unsigned char *)name;
277: int i = len;
278:
279: while (i--) {
280: /* Add the character in... */
281: accum = (accum << 1) + *s++;
282:
283: /* Add carry back in... */
284: while (accum > 65535) {
285: accum = (accum & 65535) + (accum >> 16);
286: }
287: }
288: return accum % size;
289: }
290:
291: /* Client identifiers are generally 32-bits of ordinary
292: * non-randomness followed by 24-bits of unordinary randomness.
293: * So, end-align in 24-bit chunks, and xor any preceding data
294: * just to mix it up a little.
295: */
296: unsigned
297: do_id_hash(const void *name, unsigned len, unsigned size)
298: {
299: register unsigned accum = 0;
300: register const unsigned char *s = (const unsigned char *)name;
301: const unsigned char *end = s + len;
302:
303: if (len == 0)
304: return 0;
305:
306: /*
307: * The switch handles our starting conditions, then we hash the
308: * remaining bytes in groups of 3
309: */
310:
311: switch (len % 3) {
312: case 0:
313: break;
314: case 2:
315: accum ^= *s++ << 8;
316: case 1:
317: accum ^= *s++;
318: break;
319: }
320:
321: while (s < end) {
322: accum ^= *s++ << 16;
323: accum ^= *s++ << 8;
324: accum ^= *s++;
325: }
326:
327: return accum % size;
328: }
329:
330: unsigned
331: do_number_hash(const void *key, unsigned len, unsigned size)
332: {
333: register unsigned number = *((const unsigned *)key);
334:
335: return number % size;
336: }
337:
338: unsigned
339: do_ip4_hash(const void *key, unsigned len, unsigned size)
340: {
341: u_int32_t number;
342:
343: memcpy(&number, key, 4);
344:
345: number = ntohl(number);
346:
347: return number % size;
348: }
349:
350: unsigned char *
351: hash_report(struct hash_table *table)
352: {
353: static unsigned char retbuf[sizeof("Contents/Size (%): "
354: "2147483647/2147483647 "
355: "(2147483647%). "
356: "Min/max: 2147483647/2147483647")];
357: unsigned curlen, pct, contents=0, minlen=UINT_MAX, maxlen=0;
358: unsigned i;
359: struct hash_bucket *bp;
360:
361: if (table == NULL)
362: return (unsigned char *) "No table.";
363:
364: if (table->hash_count == 0)
365: return (unsigned char *) "Invalid hash table.";
366:
367: for (i = 0 ; i < table->hash_count ; i++) {
368: curlen = 0;
369:
370: bp = table->buckets[i];
371: while (bp != NULL) {
372: curlen++;
373: bp = bp->next;
374: }
375:
376: if (curlen < minlen)
377: minlen = curlen;
378: if (curlen > maxlen)
379: maxlen = curlen;
380:
381: contents += curlen;
382: }
383:
384: if (contents >= (UINT_MAX / 100))
385: pct = contents / ((table->hash_count / 100) + 1);
386: else
387: pct = (contents * 100) / table->hash_count;
388:
389: if (contents > 2147483647 ||
390: table->hash_count > 2147483647 ||
391: pct > 2147483647 ||
392: minlen > 2147483647 ||
393: maxlen > 2147483647)
394: return (unsigned char *) "Report out of range for display.";
395:
396: sprintf((char *)retbuf,
397: "Contents/Size (%%): %u/%u (%u%%). Min/max: %u/%u",
398: contents, table->hash_count, pct, minlen, maxlen);
399:
400: return retbuf;
401: }
402:
403: void add_hash (table, key, len, pointer, file, line)
404: struct hash_table *table;
405: unsigned len;
406: const void *key;
407: hashed_object_t *pointer;
408: const char *file;
409: int line;
410: {
411: int hashno;
412: struct hash_bucket *bp;
413: void *foo;
414:
415: if (!table)
416: return;
417:
418: if (!len)
419: len = find_length(key, table->do_hash);
420:
421: hashno = (*table->do_hash)(key, len, table->hash_count);
422: bp = new_hash_bucket (file, line);
423:
424: if (!bp) {
425: log_error ("Can't add entry to hash table: no memory.");
426: return;
427: }
428: bp -> name = key;
429: if (table -> referencer) {
430: foo = &bp -> value;
431: (*(table -> referencer)) (foo, pointer, file, line);
432: } else
433: bp -> value = pointer;
434: bp -> next = table -> buckets [hashno];
435: bp -> len = len;
436: table -> buckets [hashno] = bp;
437: }
438:
439: void delete_hash_entry (table, key, len, file, line)
440: struct hash_table *table;
441: unsigned len;
442: const void *key;
443: const char *file;
444: int line;
445: {
446: int hashno;
447: struct hash_bucket *bp, *pbp = (struct hash_bucket *)0;
448: void *foo;
449:
450: if (!table)
451: return;
452:
453: if (!len)
454: len = find_length(key, table->do_hash);
455:
456: hashno = (*table->do_hash)(key, len, table->hash_count);
457:
458: /* Go through the list looking for an entry that matches;
459: if we find it, delete it. */
460: for (bp = table -> buckets [hashno]; bp; bp = bp -> next) {
461: if ((!bp -> len &&
462: !strcmp ((const char *)bp->name, key)) ||
463: (bp -> len == len &&
464: !(table -> cmp)(bp->name, key, len))) {
465: if (pbp) {
466: pbp -> next = bp -> next;
467: } else {
468: table -> buckets [hashno] = bp -> next;
469: }
470: if (bp -> value && table -> dereferencer) {
471: foo = &bp -> value;
472: (*(table -> dereferencer)) (foo, file, line);
473: }
474: free_hash_bucket (bp, file, line);
475: break;
476: }
477: pbp = bp; /* jwg, 9/6/96 - nice catch! */
478: }
479: }
480:
481: int hash_lookup (vp, table, key, len, file, line)
482: hashed_object_t **vp;
483: struct hash_table *table;
484: const void *key;
485: unsigned len;
486: const char *file;
487: int line;
488: {
489: int hashno;
490: struct hash_bucket *bp;
491:
492: if (!table)
493: return 0;
494: if (!len)
495: len = find_length(key, table->do_hash);
496:
497: if (*vp != NULL) {
498: log_fatal("Internal inconsistency: storage value has not been "
499: "initialized to zero (from %s:%d).", file, line);
500: }
501:
502: hashno = (*table->do_hash)(key, len, table->hash_count);
503:
504: for (bp = table -> buckets [hashno]; bp; bp = bp -> next) {
505: if (len == bp -> len
506: && !(*table->cmp)(bp->name, key, len)) {
507: if (table -> referencer)
508: (*table -> referencer) (vp, bp -> value,
509: file, line);
510: else
511: *vp = bp -> value;
512: return 1;
513: }
514: }
515: return 0;
516: }
517:
518: int hash_foreach (struct hash_table *table, hash_foreach_func func)
519: {
520: int i;
521: struct hash_bucket *bp, *next;
522: int count = 0;
523:
524: if (!table)
525: return 0;
526:
527: for (i = 0; i < table -> hash_count; i++) {
528: bp = table -> buckets [i];
529: while (bp) {
530: next = bp -> next;
531: if ((*func)(bp->name, bp->len, bp->value)
532: != ISC_R_SUCCESS)
533: return count;
534: bp = next;
535: count++;
536: }
537: }
538: return count;
539: }
540:
541: int casecmp (const void *v1, const void *v2, size_t len)
542: {
543: size_t i;
544: const unsigned char *s = v1;
545: const unsigned char *t = v2;
546:
547: for (i = 0; i < len; i++)
548: {
549: int c1, c2;
550: if (isascii(s[i]))
551: c1 = tolower(s[i]);
552: else
553: c1 = s[i];
554:
555: if (isascii(t[i]))
556: c2 = tolower(t[i]);
557: else
558: c2 = t[i];
559:
560: if (c1 < c2)
561: return -1;
562: if (c1 > c2)
563: return 1;
564: }
565: return 0;
566: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>