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