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