hash.c revision 1.4 1 /************************************************************************
2 Copyright 1988, 1991 by Carnegie Mellon University
3
4 All Rights Reserved
5
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted, provided
8 that the above copyright notice appear in all copies and that both that
9 copyright notice and this permission notice appear in supporting
10 documentation, and that the name of Carnegie Mellon University not be used
11 in advertising or publicity pertaining to distribution of the software
12 without specific, written prior permission.
13
14 CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
15 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
16 IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
17 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18 PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19 ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 SOFTWARE.
21 ************************************************************************/
22
23 #include <sys/cdefs.h>
24 #ifndef lint
25 __RCSID("$NetBSD: hash.c,v 1.4 1998/03/14 04:39:54 lukem Exp $");
26 #endif
27
28
29 /*
30 * Generalized hash table ADT
31 *
32 * Provides multiple, dynamically-allocated, variable-sized hash tables on
33 * various data and keys.
34 *
35 * This package attempts to follow some of the coding conventions suggested
36 * by Bob Sidebotham and the AFS Clean Code Committee of the
37 * Information Technology Center at Carnegie Mellon.
38 */
39
40
41 #include <sys/types.h>
42 #include <stdlib.h>
43
44 #ifndef USE_BFUNCS
45 #include <memory.h>
46 /* Yes, memcpy is OK here (no overlapped copies). */
47 #define bcopy(a,b,c) memcpy(b,a,c)
48 #define bzero(p,l) memset(p,0,l)
49 #define bcmp(a,b,c) memcmp(a,b,c)
50 #endif
51
52 #include "hash.h"
53
54 #define TRUE 1
55 #define FALSE 0
56 #ifndef NULL
57 #define NULL 0
58 #endif
59
60 /*
61 * This can be changed to make internal routines visible to debuggers, etc.
62 */
63 #ifndef PRIVATE
64 #define PRIVATE static
65 #endif
66
67 #ifdef __STDC__
68 #define P(args) args
69 #else
70 #define P(args) ()
71 #endif
72
73 PRIVATE void hashi_FreeMembers P((hash_member *, hash_freefp));
74
75 #undef P
76
77
79
80 /*
81 * Hash table initialization routine.
82 *
83 * This routine creates and intializes a hash table of size "tablesize"
84 * entries. Successful calls return a pointer to the hash table (which must
85 * be passed to other hash routines to identify the hash table). Failed
86 * calls return NULL.
87 */
88
89 hash_tbl *
90 hash_Init(tablesize)
91 unsigned tablesize;
92 {
93 register hash_tbl *hashtblptr;
94 register unsigned totalsize;
95
96 if (tablesize > 0) {
97 totalsize = sizeof(hash_tbl)
98 + sizeof(hash_member *) * (tablesize - 1);
99 hashtblptr = (hash_tbl *) malloc(totalsize);
100 if (hashtblptr) {
101 bzero((char *) hashtblptr, totalsize);
102 hashtblptr->size = tablesize; /* Success! */
103 hashtblptr->bucketnum = 0;
104 hashtblptr->member = (hashtblptr->table)[0];
105 }
106 } else {
107 hashtblptr = NULL; /* Disallow zero-length tables */
108 }
109 return hashtblptr; /* NULL if failure */
110 }
111
112
114
115 /*
116 * Frees an entire linked list of bucket members (used in the open
117 * hashing scheme). Does nothing if the passed pointer is NULL.
118 */
119
120 PRIVATE void
121 hashi_FreeMembers(bucketptr, free_data)
122 hash_member *bucketptr;
123 hash_freefp free_data;
124 {
125 hash_member *nextbucket;
126 while (bucketptr) {
127 nextbucket = bucketptr->next;
128 (*free_data) (bucketptr->data);
129 free((char *) bucketptr);
130 bucketptr = nextbucket;
131 }
132 }
133
134
135
136
137 /*
138 * This routine re-initializes the hash table. It frees all the allocated
139 * memory and resets all bucket pointers to NULL.
140 */
141
142 void
143 hash_Reset(hashtable, free_data)
144 hash_tbl *hashtable;
145 hash_freefp free_data;
146 {
147 hash_member **bucketptr;
148 unsigned i;
149
150 bucketptr = hashtable->table;
151 for (i = 0; i < hashtable->size; i++) {
152 hashi_FreeMembers(*bucketptr, free_data);
153 *bucketptr++ = NULL;
154 }
155 hashtable->bucketnum = 0;
156 hashtable->member = (hashtable->table)[0];
157 }
158
159
161
162 /*
163 * Generic hash function to calculate a hash code from the given string.
164 *
165 * For each byte of the string, this function left-shifts the value in an
166 * accumulator and then adds the byte into the accumulator. The contents of
167 * the accumulator is returned after the entire string has been processed.
168 * It is assumed that this result will be used as the "hashcode" parameter in
169 * calls to other functions in this package. These functions automatically
170 * adjust the hashcode for the size of each hashtable.
171 *
172 * This algorithm probably works best when the hash table size is a prime
173 * number.
174 *
175 * Hopefully, this function is better than the previous one which returned
176 * the sum of the squares of all the bytes. I'm still open to other
177 * suggestions for a default hash function. The programmer is more than
178 * welcome to supply his/her own hash function as that is one of the design
179 * features of this package.
180 */
181
182 unsigned
183 hash_HashFunction(string, len)
184 unsigned char *string;
185 register unsigned len;
186 {
187 register unsigned accum;
188
189 accum = 0;
190 for (; len > 0; len--) {
191 accum <<= 1;
192 accum += (unsigned) (*string++ & 0xFF);
193 }
194 return accum;
195 }
196
197
199
200 /*
201 * Returns TRUE if at least one entry for the given key exists; FALSE
202 * otherwise.
203 */
204
205 int
206 hash_Exists(hashtable, hashcode, compare, key)
207 hash_tbl *hashtable;
208 unsigned hashcode;
209 hash_cmpfp compare;
210 hash_datum *key;
211 {
212 register hash_member *memberptr;
213
214 memberptr = (hashtable->table)[hashcode % (hashtable->size)];
215 while (memberptr) {
216 if ((*compare) (key, memberptr->data)) {
217 return TRUE; /* Entry does exist */
218 }
219 memberptr = memberptr->next;
220 }
221 return FALSE; /* Entry does not exist */
222 }
223
224
226
227 /*
228 * Insert the data item "element" into the hash table using "hashcode"
229 * to determine the bucket number, and "compare" and "key" to determine
230 * its uniqueness.
231 *
232 * If the insertion is successful 0 is returned. If a matching entry
233 * already exists in the given bucket of the hash table, or some other error
234 * occurs, -1 is returned and the insertion is not done.
235 */
236
237 int
238 hash_Insert(hashtable, hashcode, compare, key, element)
239 hash_tbl *hashtable;
240 unsigned hashcode;
241 hash_cmpfp compare;
242 hash_datum *key, *element;
243 {
244 hash_member *temp;
245
246 hashcode %= hashtable->size;
247 if (hash_Exists(hashtable, hashcode, compare, key)) {
248 return -1; /* At least one entry already exists */
249 }
250 temp = (hash_member *) malloc(sizeof(hash_member));
251 if (!temp)
252 return -1; /* malloc failed! */
253
254 temp->data = element;
255 temp->next = (hashtable->table)[hashcode];
256 (hashtable->table)[hashcode] = temp;
257 return 0; /* Success */
258 }
259
260
262
263 /*
264 * Delete all data elements which match the given key. If at least one
265 * element is found and the deletion is successful, 0 is returned.
266 * If no matching elements can be found in the hash table, -1 is returned.
267 */
268
269 int
270 hash_Delete(hashtable, hashcode, compare, key, free_data)
271 hash_tbl *hashtable;
272 unsigned hashcode;
273 hash_cmpfp compare;
274 hash_datum *key;
275 hash_freefp free_data;
276 {
277 hash_member *memberptr, *tempptr;
278 hash_member *previous = NULL;
279 int retval;
280
281 retval = -1;
282 hashcode %= hashtable->size;
283
284 /*
285 * Delete the first member of the list if it matches. Since this moves
286 * the second member into the first position we have to keep doing this
287 * over and over until it no longer matches.
288 */
289 memberptr = (hashtable->table)[hashcode];
290 while (memberptr && (*compare) (key, memberptr->data)) {
291 (hashtable->table)[hashcode] = memberptr->next;
292 /*
293 * Stop hashi_FreeMembers() from deleting the whole list!
294 */
295 memberptr->next = NULL;
296 hashi_FreeMembers(memberptr, free_data);
297 memberptr = (hashtable->table)[hashcode];
298 retval = 0;
299 }
300
301 /*
302 * Now traverse the rest of the list
303 */
304 if (memberptr) {
305 previous = memberptr;
306 memberptr = memberptr->next;
307 }
308 while (memberptr) {
309 if ((*compare) (key, memberptr->data)) {
310 tempptr = memberptr;
311 previous->next = memberptr = memberptr->next;
312 /*
313 * Put the brakes on hashi_FreeMembers(). . . .
314 */
315 tempptr->next = NULL;
316 hashi_FreeMembers(tempptr, free_data);
317 retval = 0;
318 } else {
319 previous = memberptr;
320 memberptr = memberptr->next;
321 }
322 }
323 return retval;
324 }
325
326
328
329 /*
330 * Locate and return the data entry associated with the given key.
331 *
332 * If the data entry is found, a pointer to it is returned. Otherwise,
333 * NULL is returned.
334 */
335
336 hash_datum *
337 hash_Lookup(hashtable, hashcode, compare, key)
338 hash_tbl *hashtable;
339 unsigned hashcode;
340 hash_cmpfp compare;
341 hash_datum *key;
342 {
343 hash_member *memberptr;
344
345 memberptr = (hashtable->table)[hashcode % (hashtable->size)];
346 while (memberptr) {
347 if ((*compare) (key, memberptr->data)) {
348 return (memberptr->data);
349 }
350 memberptr = memberptr->next;
351 }
352 return NULL;
353 }
354
355
357
358 /*
359 * Return the next available entry in the hashtable for a linear search
360 */
361
362 hash_datum *
363 hash_NextEntry(hashtable)
364 hash_tbl *hashtable;
365 {
366 register unsigned bucket;
367 register hash_member *memberptr;
368
369 /*
370 * First try to pick up where we left off.
371 */
372 memberptr = hashtable->member;
373 if (memberptr) {
374 hashtable->member = memberptr->next; /* Set up for next call */
375 return memberptr->data; /* Return the data */
376 }
377 /*
378 * We hit the end of a chain, so look through the array of buckets
379 * until we find a new chain (non-empty bucket) or run out of buckets.
380 */
381 bucket = hashtable->bucketnum + 1;
382 while ((bucket < hashtable->size) &&
383 !(memberptr = (hashtable->table)[bucket])) {
384 bucket++;
385 }
386
387 /*
388 * Check to see if we ran out of buckets.
389 */
390 if (bucket >= hashtable->size) {
391 /*
392 * Reset to top of table for next call.
393 */
394 hashtable->bucketnum = 0;
395 hashtable->member = (hashtable->table)[0];
396 /*
397 * But return end-of-table indication to the caller this time.
398 */
399 return NULL;
400 }
401 /*
402 * Must have found a non-empty bucket.
403 */
404 hashtable->bucketnum = bucket;
405 hashtable->member = memberptr->next; /* Set up for next call */
406 return memberptr->data; /* Return the data */
407 }
408
409
411
412 /*
413 * Return the first entry in a hash table for a linear search
414 */
415
416 hash_datum *
417 hash_FirstEntry(hashtable)
418 hash_tbl *hashtable;
419 {
420 hashtable->bucketnum = 0;
421 hashtable->member = (hashtable->table)[0];
422 return hash_NextEntry(hashtable);
423 }
424
425 /*
426 * Local Variables:
427 * tab-width: 4
428 * c-indent-level: 4
429 * c-argdecl-indent: 4
430 * c-continued-statement-offset: 4
431 * c-continued-brace-offset: -4
432 * c-label-offset: -4
433 * c-brace-offset: 0
434 * End:
435 */
436