]>
git.saurik.com Git - apple/libc.git/blob - stdlib/FreeBSD/strhash.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 #pragma clang diagnostic push
71 #pragma clang diagnostic ignored "-Wstrict-prototypes"
73 #include <sys/cdefs.h>
74 __FBSDID("$FreeBSD: src/lib/libc/stdlib/strhash.c,v 1.10 2002/03/22 21:53:10 obrien Exp $");
79 #include <sys/types.h>
82 #define HASH_NULL (hash_table *)0
83 #define NODE_NULL (hash_node *)0
84 #define GENERIC_NULL (void *)0
89 static int _hash(int size
, char *key
);
90 static hash_node
*list_find(caddr_t key
, hash_node
*head
);
91 static int assign_key(char *key
, hash_node
*node
);
97 * Malloc room for a new hash table and then room for its
98 * bucket pointers. Then set all the buckets to
99 * point to 0. Return the address of the new table.
102 hash_create(int size
)
105 hash_table
*new = (hash_table
*)malloc(sizeof(hash_table
));
107 if (!new || size
< 0){
115 if (!(new->buckets
= (hash_node
**)malloc(size
* sizeof(hash_node
*)))){
119 for (i
= 0; i
< size
; i
++){
120 new->buckets
[i
] = NODE_NULL
;
131 * Find the key in the linked list pointed to by head.
134 list_find(caddr_t key
, hash_node
*head
)
137 if (!strcmp(head
->key
, key
)){
149 * Compute the hash value for the given key.
152 _hash(int size
, char *key
)
154 unsigned int h
= 0x0;
157 h
= (h
<< 1) ^ (h
^ (unsigned char) *key
++);
167 * Find the key and (if it's there) remove it entirely.
168 * The function (*nukefunc)() is in charge of disposing
169 * of the storage help by the data associated with the node.
172 hash_destroy(hash_table
*table
, char *key
, void (*nukefunc
)())
174 int bucket
= _hash(table
->size
, key
);
175 hash_node
*found
= table
->buckets
[bucket
];
176 hash_node
*to_free
= NODE_NULL
;
182 if (!strcmp(found
->key
, key
)) {
184 * It was the head of the list.
186 table
->buckets
[bucket
] = found
->next
;
191 * Walk the list, looking one ahead.
193 while (found
->next
) {
194 if (!strcmp(found
->next
->key
, key
)) {
195 to_free
= found
->next
;
196 found
->next
= found
->next
->next
;
208 (*nukefunc
)(to_free
->key
, to_free
->data
);
217 * Search the table for the given key. Then:
219 * 1) If you find it and there is no replacement function, just
220 * return what you found. (This is a simple search).
221 * 2) If you find it and there is a replacement function, run
222 * the function on the data you found, and replace the old
223 * data with whatever is passed in datum. Return 0.
224 * 3) If you don't find it and there is some datum, insert a
225 * new item into the table. Insertions go at the front of
226 * the bucket. Return 0.
227 * 4) Otherwise just return 0.
231 hash_search(hash_table
*table
, caddr_t key
, void *datum
,
232 void (*replace_func
)())
234 int bucket
= _hash(table
->size
, key
);
235 hash_node
*found
= list_find(key
, table
->buckets
[bucket
]);
242 (*replace_func
)(found
->data
);
249 hash_node
*new = (hash_node
*)malloc(sizeof(hash_node
));
251 if (!new || !assign_key(key
, new)){
255 new->next
= table
->buckets
[bucket
];
256 table
->buckets
[bucket
] = new;
267 * Set the key value of a node to be 'key'. Get some space from
268 * malloc and copy it in etc. Return 1 if all is well, 0 otherwise.
271 assign_key(char *key
, hash_node
*node
)
277 if (!(node
->key
= (char *)malloc(strlen(key
) + 1))){
282 strcat(node
->key
, key
);
289 * Traverse the hash table and run the function func on the
290 * data found at each node and the argument we're passed for it.
293 hash_traverse(hash_table
*table
, int (*func
)(), void *arg
)
296 int size
= table
->size
;
301 for (i
= 0; i
< size
; i
++) {
302 hash_node
*n
= table
->buckets
[i
];
304 if ((*func
)(n
->key
, n
->data
, arg
) == 0)
315 * Run through the entire hash table. Call purge_func
316 * on the data found at each node, and then free the node.
317 * Set all the bucket pointers to 0.
320 hash_purge(hash_table
*table
, void (*purge_func
)(char *p1
, void *p2
))
323 int size
= table
->size
;
325 for (i
= 0; i
< size
; i
++) {
326 hash_node
*n
= table
->buckets
[i
];
329 hash_node
*to_free
= n
;
331 (*purge_func
)(n
->key
, n
->data
);
336 table
->buckets
[i
] = NODE_NULL
;
342 #define min(a, b) (a) < (b) ? (a) : (b)
347 * Print statistics about the current table allocation to stdout.
350 hash_stats(hash_table
*table
, int verbose
)
353 int total_elements
= 0;
354 int non_empty_buckets
= 0;
358 int size
= table
->size
;
360 if (!(counts
= (int *)malloc(size
* sizeof(int)))){
361 fprintf(stderr
, "malloc returns 0\n");
365 for (i
= 0; i
< size
; i
++){
367 hash_node
*n
= table
->buckets
[i
];
374 printf("bucket %2d: ", i
);
378 printf(" %s", n
->key
);
384 total_elements
+= counts
[i
];
385 if (counts
[i
] > max_count
){
386 max_count
= counts
[i
];
389 else if (counts
[i
] == max_count
){
393 if (counts
[i
] && verbose
){
394 printf(" (%d)\n", counts
[i
]);
399 printf("%d element%s in storage.\n", total_elements
, total_elements
== 1 ? "" : "s");
402 printf("%d of %d (%.2f%%) buckets are in use\n", non_empty_buckets
, size
,
403 (double)100 * (double)non_empty_buckets
/ (double)(size
));
404 printf("the maximum number of elements in a bucket is %d (%d times)\n", max_count
, max_repeats
);
405 printf("average per bucket is %f\n", (double)total_elements
/ (double)non_empty_buckets
);
406 printf("optimal would be %f\n", (double)total_elements
/ (double)(min(size
, total_elements
)));
410 #pragma clang diagnostic pop