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