cleanup .changes
[profile/ivi/dhcp.git] / omapip / hash.c
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 }