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