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