hash.c revision 1.8 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.8 2018/02/08 09:05:21 dholland 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 #include <strings.h>
44
45 #include "hash.h"
46
47 #define TRUE 1
48 #define FALSE 0
49 #ifndef NULL
50 #define NULL 0
51 #endif
52
53 /*
54 * This can be changed to make internal routines visible to debuggers, etc.
55 */
56 #ifndef PRIVATE
57 #define PRIVATE static
58 #endif
59
60 PRIVATE void hashi_FreeMembers(hash_member *, hash_freefp);
61
62
63
65
66 /*
67 * Hash table initialization routine.
68 *
69 * This routine creates and initializes a hash table of size "tablesize"
70 * entries. Successful calls return a pointer to the hash table (which must
71 * be passed to other hash routines to identify the hash table). Failed
72 * calls return NULL.
73 */
74
75 hash_tbl *
76 hash_Init(unsigned int tablesize)
77 {
78 hash_tbl *hashtblptr;
79 unsigned totalsize;
80
81 if (tablesize > 0) {
82 totalsize = sizeof(hash_tbl)
83 + sizeof(hash_member *) * (tablesize - 1);
84 hashtblptr = (hash_tbl *) malloc(totalsize);
85 if (hashtblptr) {
86 bzero((char *) hashtblptr, totalsize);
87 hashtblptr->size = tablesize; /* Success! */
88 hashtblptr->bucketnum = 0;
89 hashtblptr->member = (hashtblptr->table)[0];
90 }
91 } else {
92 hashtblptr = NULL; /* Disallow zero-length tables */
93 }
94 return hashtblptr; /* NULL if failure */
95 }
96
97
99
100 /*
101 * Frees an entire linked list of bucket members (used in the open
102 * hashing scheme). Does nothing if the passed pointer is NULL.
103 */
104
105 PRIVATE void
106 hashi_FreeMembers(hash_member *bucketptr, hash_freefp free_data)
107 {
108 hash_member *nextbucket;
109 while (bucketptr) {
110 nextbucket = bucketptr->next;
111 (*free_data) (bucketptr->data);
112 free((char *) bucketptr);
113 bucketptr = nextbucket;
114 }
115 }
116
117
118
119
120 /*
121 * This routine re-initializes the hash table. It frees all the allocated
122 * memory and resets all bucket pointers to NULL.
123 */
124
125 void
126 hash_Reset(hash_tbl *hashtable, hash_freefp free_data)
127 {
128 hash_member **bucketptr;
129 unsigned i;
130
131 bucketptr = hashtable->table;
132 for (i = 0; i < hashtable->size; i++) {
133 hashi_FreeMembers(*bucketptr, free_data);
134 *bucketptr++ = NULL;
135 }
136 hashtable->bucketnum = 0;
137 hashtable->member = (hashtable->table)[0];
138 }
139
140
142
143 /*
144 * Generic hash function to calculate a hash code from the given string.
145 *
146 * For each byte of the string, this function left-shifts the value in an
147 * accumulator and then adds the byte into the accumulator. The contents of
148 * the accumulator is returned after the entire string has been processed.
149 * It is assumed that this result will be used as the "hashcode" parameter in
150 * calls to other functions in this package. These functions automatically
151 * adjust the hashcode for the size of each hashtable.
152 *
153 * This algorithm probably works best when the hash table size is a prime
154 * number.
155 *
156 * Hopefully, this function is better than the previous one which returned
157 * the sum of the squares of all the bytes. I'm still open to other
158 * suggestions for a default hash function. The programmer is more than
159 * welcome to supply his/her own hash function as that is one of the design
160 * features of this package.
161 */
162
163 unsigned
164 hash_HashFunction(unsigned char *string, unsigned int len)
165 {
166 unsigned accum;
167
168 accum = 0;
169 for (; len > 0; len--) {
170 accum <<= 1;
171 accum += (unsigned) (*string++ & 0xFF);
172 }
173 return accum;
174 }
175
176
178
179 /*
180 * Returns TRUE if at least one entry for the given key exists; FALSE
181 * otherwise.
182 */
183
184 int
185 hash_Exists(hash_tbl *hashtable, unsigned int hashcode, hash_cmpfp compare,
186 hash_datum *key)
187 {
188 hash_member *memberptr;
189
190 memberptr = (hashtable->table)[hashcode % (hashtable->size)];
191 while (memberptr) {
192 if ((*compare) (key, memberptr->data)) {
193 return TRUE; /* Entry does exist */
194 }
195 memberptr = memberptr->next;
196 }
197 return FALSE; /* Entry does not exist */
198 }
199
200
202
203 /*
204 * Insert the data item "element" into the hash table using "hashcode"
205 * to determine the bucket number, and "compare" and "key" to determine
206 * its uniqueness.
207 *
208 * If the insertion is successful 0 is returned. If a matching entry
209 * already exists in the given bucket of the hash table, or some other error
210 * occurs, -1 is returned and the insertion is not done.
211 */
212
213 int
214 hash_Insert(hash_tbl *hashtable, unsigned int hashcode, hash_cmpfp compare,
215 hash_datum *key, hash_datum *element)
216 {
217 hash_member *temp;
218
219 hashcode %= hashtable->size;
220 if (hash_Exists(hashtable, hashcode, compare, key)) {
221 return -1; /* At least one entry already exists */
222 }
223 temp = (hash_member *) malloc(sizeof(hash_member));
224 if (!temp)
225 return -1; /* malloc failed! */
226
227 temp->data = element;
228 temp->next = (hashtable->table)[hashcode];
229 (hashtable->table)[hashcode] = temp;
230 return 0; /* Success */
231 }
232
233
235
236 /*
237 * Delete all data elements which match the given key. If at least one
238 * element is found and the deletion is successful, 0 is returned.
239 * If no matching elements can be found in the hash table, -1 is returned.
240 */
241
242 int
243 hash_Delete(hash_tbl *hashtable, unsigned int hashcode, hash_cmpfp compare,
244 hash_datum *key, hash_freefp free_data)
245 {
246 hash_member *memberptr, *tempptr;
247 hash_member *previous = NULL;
248 int retval;
249
250 retval = -1;
251 hashcode %= hashtable->size;
252
253 /*
254 * Delete the first member of the list if it matches. Since this moves
255 * the second member into the first position we have to keep doing this
256 * over and over until it no longer matches.
257 */
258 memberptr = (hashtable->table)[hashcode];
259 while (memberptr && (*compare) (key, memberptr->data)) {
260 (hashtable->table)[hashcode] = memberptr->next;
261 /*
262 * Stop hashi_FreeMembers() from deleting the whole list!
263 */
264 memberptr->next = NULL;
265 hashi_FreeMembers(memberptr, free_data);
266 memberptr = (hashtable->table)[hashcode];
267 retval = 0;
268 }
269
270 /*
271 * Now traverse the rest of the list
272 */
273 if (memberptr) {
274 previous = memberptr;
275 memberptr = memberptr->next;
276 }
277 while (memberptr) {
278 if ((*compare) (key, memberptr->data)) {
279 tempptr = memberptr;
280 previous->next = memberptr = memberptr->next;
281 /*
282 * Put the brakes on hashi_FreeMembers(). . . .
283 */
284 tempptr->next = NULL;
285 hashi_FreeMembers(tempptr, free_data);
286 retval = 0;
287 } else {
288 previous = memberptr;
289 memberptr = memberptr->next;
290 }
291 }
292 return retval;
293 }
294
295
297
298 /*
299 * Locate and return the data entry associated with the given key.
300 *
301 * If the data entry is found, a pointer to it is returned. Otherwise,
302 * NULL is returned.
303 */
304
305 hash_datum *
306 hash_Lookup(hash_tbl *hashtable, unsigned int hashcode, hash_cmpfp compare,
307 hash_datum *key)
308 {
309 hash_member *memberptr;
310
311 memberptr = (hashtable->table)[hashcode % (hashtable->size)];
312 while (memberptr) {
313 if ((*compare) (key, memberptr->data)) {
314 return (memberptr->data);
315 }
316 memberptr = memberptr->next;
317 }
318 return NULL;
319 }
320
321
323
324 /*
325 * Return the next available entry in the hashtable for a linear search
326 */
327
328 hash_datum *
329 hash_NextEntry(hash_tbl *hashtable)
330 {
331 unsigned bucket;
332 hash_member *memberptr;
333
334 /*
335 * First try to pick up where we left off.
336 */
337 memberptr = hashtable->member;
338 if (memberptr) {
339 hashtable->member = memberptr->next; /* Set up for next call */
340 return memberptr->data; /* Return the data */
341 }
342 /*
343 * We hit the end of a chain, so look through the array of buckets
344 * until we find a new chain (non-empty bucket) or run out of buckets.
345 */
346 bucket = hashtable->bucketnum + 1;
347 while ((bucket < hashtable->size) &&
348 !(memberptr = (hashtable->table)[bucket])) {
349 bucket++;
350 }
351
352 /*
353 * Check to see if we ran out of buckets.
354 */
355 if (bucket >= hashtable->size) {
356 /*
357 * Reset to top of table for next call.
358 */
359 hashtable->bucketnum = 0;
360 hashtable->member = (hashtable->table)[0];
361 /*
362 * But return end-of-table indication to the caller this time.
363 */
364 return NULL;
365 }
366 /*
367 * Must have found a non-empty bucket.
368 */
369 hashtable->bucketnum = bucket;
370 hashtable->member = memberptr->next; /* Set up for next call */
371 return memberptr->data; /* Return the data */
372 }
373
374
376
377 /*
378 * Return the first entry in a hash table for a linear search
379 */
380
381 hash_datum *
382 hash_FirstEntry(hash_tbl *hashtable)
383 {
384 hashtable->bucketnum = 0;
385 hashtable->member = (hashtable->table)[0];
386 return hash_NextEntry(hashtable);
387 }
388
389 /*
390 * Local Variables:
391 * tab-width: 4
392 * c-indent-level: 4
393 * c-argdecl-indent: 4
394 * c-continued-statement-offset: 4
395 * c-continued-brace-offset: -4
396 * c-label-offset: -4
397 * c-brace-offset: 0
398 * End:
399 */
400