Home | History | Annotate | Line # | Download | only in common
      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