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