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