]>
git.saurik.com Git - apple/libc.git/blob - stdlib/strhash-fbsd.c
   4  *               Terry Jones & Jordan Hubbard 
   6  *                PCS Computer Systeme, GmbH. 
  10  *  All rights reserved. 
  12  *  This is unsupported software and is subject to change without notice. 
  13  *  the author makes no representations about the suitability of this software 
  14  *  for any purpose. It is supplied "as is" without express or implied 
  17  *  Permission to use, copy, modify, and distribute this software and its 
  18  *  documentation for any purpose and without fee is hereby granted, provided 
  19  *  that the above copyright notice appear in all copies and that both that 
  20  *  copyright notice and this permission notice appear in supporting 
  21  *  documentation, and that the name of the author not be used in 
  22  *  advertising or publicity pertaining to distribution of the software 
  23  *  without specific, written prior permission. 
  28  * This is a fairly simple open addressing hash scheme. 
  29  * Terry did all the code, I just did the spec. 
  30  * Thanks again, you crazy Aussie.. 
  36  * Revision 2.0  90/03/26  01:44:26  jkh 
  39  * Revision 1.8  90/03/09  19:22:35  jkh 
  40  * Fixed bogus comment. 
  42  * Revision 1.7  90/03/09  19:01:08  jkh 
  43  * Added comments, GPL. 
  45  * Revision 1.6  90/03/08  17:55:58  terry 
  46  * Rearranged hash_purge to be a tiny bit more efficient. 
  47  * Added verbose option to hash_stats. 
  49  * Revision 1.5  90/03/08  17:19:54  terry 
  50  * Added hash_purge. Added arg to hash_traverse. Changed all 
  53  * Revision 1.4  90/03/08  12:02:35  terry 
  54  * Fixed problems with allocation that I screwed up last night. 
  55  * Changed bucket lists to be singly linked. Thanks to JKH, my hero. 
  57  * Revision 1.3  90/03/07  21:33:33  terry 
  58  * Cleaned up a few decls to keep gcc -Wall quiet. 
  60  * Revision 1.2  90/03/07  21:14:53  terry 
  61  * Comments. Added HASH_STATS define. Removed hash_find() 
  64  * Revision 1.1  90/03/07  20:49:45  terry 
  70 #include <sys/cdefs.h> 
  71 __FBSDID("$FreeBSD: src/lib/libc/stdlib/strhash.c,v 1.10 2002/03/22 21:53:10 obrien Exp $"); 
  76 #include <sys/types.h> 
  79 #define HASH_NULL    (hash_table *)0 
  80 #define NODE_NULL    (hash_node *)0 
  81 #define GENERIC_NULL (void *)0 
  86 static int _hash(int size
, char *key
); 
  87 static hash_node 
*list_find(caddr_t key
, hash_node 
*head
); 
  88 static int assign_key(char *key
, hash_node 
*node
); 
  94  * Malloc room for a new hash table and then room for its 
  95  * bucket pointers. Then set all the buckets to 
  96  * point to 0. Return the address of the new table. 
 102     hash_table 
*new = (hash_table 
*)malloc(sizeof(hash_table
)); 
 104     if (!new || size 
< 0){ 
 112     if (!(new->buckets 
= (hash_node 
**)malloc(size 
* sizeof(hash_node 
*)))){ 
 116     for (i 
= 0; i 
< size
; i
++){ 
 117         new->buckets
[i
] = NODE_NULL
; 
 128  * Find the key in the linked list pointed to by head. 
 131 list_find(caddr_t key
, hash_node 
*head
) 
 134         if (!strcmp(head
->key
, key
)){ 
 146  * Compute the hash value for the given key. 
 149 _hash(int size
, char *key
) 
 151     unsigned int h 
= 0x0; 
 154         h 
= (h 
<< 1) ^ (h 
^ (unsigned char) *key
++); 
 164  * Find the key and (if it's there) remove it entirely. 
 165  * The function (*nukefunc)() is in charge of disposing 
 166  * of the storage help by the data associated with the node. 
 169 hash_destroy(hash_table 
*table
, char *key
, void (*nukefunc
)()) 
 171     int bucket 
= _hash(table
->size
, key
); 
 172     hash_node 
*found 
= table
->buckets
[bucket
]; 
 173     hash_node 
*to_free 
= NODE_NULL
; 
 179     if (!strcmp(found
->key
, key
)) { 
 181          * It was the head of the list. 
 183         table
->buckets
[bucket
] = found
->next
; 
 188          * Walk the list, looking one ahead. 
 190         while (found
->next
) { 
 191             if (!strcmp(found
->next
->key
, key
)) { 
 192                 to_free 
= found
->next
; 
 193                 found
->next 
= found
->next
->next
; 
 205         (*nukefunc
)(to_free
->key
, to_free
->data
); 
 214  * Search the table for the given key. Then: 
 216  * 1) If you find it and there is no replacement function, just 
 217  *    return what you found. (This is a simple search). 
 218  * 2) If you find it and there is a replacement function, run 
 219  *    the function on the data you found, and replace the old 
 220  *    data with whatever is passed in datum. Return 0. 
 221  * 3) If you don't find it and there is some datum, insert a 
 222  *    new item into the table. Insertions go at the front of 
 223  *    the bucket. Return 0. 
 224  * 4) Otherwise just return 0. 
 228 hash_search(hash_table 
*table
, caddr_t key
, void *datum
, 
 229             void (*replace_func
)()) 
 231     int bucket 
= _hash(table
->size
, key
); 
 232     hash_node 
*found 
= list_find(key
, table
->buckets
[bucket
]); 
 239             (*replace_func
)(found
->data
); 
 246             hash_node 
*new = (hash_node 
*)malloc(sizeof(hash_node
)); 
 248             if (!new || !assign_key(key
, new)){ 
 252             new->next 
= table
->buckets
[bucket
]; 
 253             table
->buckets
[bucket
] = new; 
 264  * Set the key value of a node to be 'key'. Get some space from 
 265  * malloc and copy it in etc. Return 1 if all is well, 0 otherwise. 
 268 assign_key(char *key
, hash_node 
*node
) 
 274     if (!(node
->key 
= (char *)malloc(strlen(key
) + 1))){ 
 279     strcat(node
->key
, key
); 
 286  * Traverse the hash table and run the function func on the 
 287  * data found at each node and the argument we're passed for it. 
 290 hash_traverse(hash_table 
*table
, int (*func
)(), void *arg
) 
 293     int size 
= table
->size
; 
 298     for (i 
= 0; i 
< size
; i
++) { 
 299         hash_node 
*n 
= table
->buckets
[i
]; 
 301             if ((*func
)(n
->key
, n
->data
, arg
) == 0) 
 312  * Run through the entire hash table. Call purge_func 
 313  * on the data found at each node, and then free the node. 
 314  * Set all the bucket pointers to 0. 
 317 hash_purge(hash_table 
*table
, void (*purge_func
)(char *p1
, void *p2
)) 
 320     int size 
= table
->size
; 
 322     for (i 
= 0; i 
< size
; i
++) { 
 323         hash_node 
*n 
= table
->buckets
[i
]; 
 326                 hash_node 
*to_free 
= n
; 
 328                     (*purge_func
)(n
->key
, n
->data
); 
 333             table
->buckets
[i
] = NODE_NULL
; 
 339 #define min(a, b) (a) < (b) ? (a) : (b) 
 344  * Print statistics about the current table allocation to stdout. 
 347 hash_stats(hash_table 
*table
, int verbose
) 
 350     int total_elements 
= 0; 
 351     int non_empty_buckets 
= 0; 
 355     int size 
= table
->size
; 
 357     if (!(counts 
= (int *)malloc(size 
* sizeof(int)))){ 
 358         fprintf(stderr
, "malloc returns 0\n"); 
 362     for (i 
= 0; i 
< size
; i
++){ 
 364         hash_node 
*n 
= table
->buckets
[i
]; 
 371                     printf("bucket %2d: ", i
); 
 375                 printf(" %s", n
->key
); 
 381         total_elements 
+= counts
[i
]; 
 382         if (counts
[i
] > max_count
){ 
 383             max_count 
= counts
[i
]; 
 386         else if (counts
[i
] == max_count
){ 
 390         if (counts
[i
] && verbose
){ 
 391             printf(" (%d)\n", counts
[i
]); 
 396     printf("%d element%s in storage.\n", total_elements
, total_elements 
== 1 ? "" : "s"); 
 399         printf("%d of %d (%.2f%%) buckets are in use\n", non_empty_buckets
, size
, 
 400                (double)100 * (double)non_empty_buckets 
/ (double)(size
)); 
 401         printf("the maximum number of elements in a bucket is %d (%d times)\n", max_count
, max_repeats
); 
 402         printf("average per bucket is %f\n", (double)total_elements 
/ (double)non_empty_buckets
); 
 403         printf("optimal would be %f\n", (double)total_elements 
/ (double)(min(size
, total_elements
)));