/************************************************************************1Copyright 1988, 1991 by Carnegie Mellon University23All Rights Reserved45Permission to use, copy, modify, and distribute this software and its6documentation for any purpose and without fee is hereby granted, provided7that the above copyright notice appear in all copies and that both that8copyright notice and this permission notice appear in supporting9documentation, and that the name of Carnegie Mellon University not be used10in advertising or publicity pertaining to distribution of the software11without specific, written prior permission.1213CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS14SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.15IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL16DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR17PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS18ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS19SOFTWARE.2021************************************************************************/2223/*24* Generalized hash table ADT25*26* Provides multiple, dynamically-allocated, variable-sized hash tables on27* various data and keys.28*29* This package attempts to follow some of the coding conventions suggested30* by Bob Sidebotham and the AFS Clean Code Committee of the31* Information Technology Center at Carnegie Mellon.32*/333435#include <sys/types.h>36#include <stdlib.h>37#include <strings.h>3839#include "hash.h"4041#define TRUE 142#define FALSE 043#ifndef NULL44#define NULL 045#endif4647/*48* This can be changed to make internal routines visible to debuggers, etc.49*/50#ifndef PRIVATE51#define PRIVATE static52#endif5354PRIVATE void hashi_FreeMembers(hash_member *, hash_freefp);5556575859/*60* Hash table initialization routine.61*62* This routine creates and intializes a hash table of size "tablesize"63* entries. Successful calls return a pointer to the hash table (which must64* be passed to other hash routines to identify the hash table). Failed65* calls return NULL.66*/6768hash_tbl *69hash_Init(unsigned tablesize)70{71hash_tbl *hashtblptr;72unsigned totalsize;7374if (tablesize > 0) {75totalsize = sizeof(hash_tbl)76+ sizeof(hash_member *) * (tablesize - 1);77hashtblptr = (hash_tbl *) malloc(totalsize);78if (hashtblptr) {79bzero((char *) hashtblptr, totalsize);80hashtblptr->size = tablesize; /* Success! */81hashtblptr->bucketnum = 0;82hashtblptr->member = (hashtblptr->table)[0];83}84} else {85hashtblptr = NULL; /* Disallow zero-length tables */86}87return hashtblptr; /* NULL if failure */88}89909192/*93* Frees an entire linked list of bucket members (used in the open94* hashing scheme). Does nothing if the passed pointer is NULL.95*/9697PRIVATE void98hashi_FreeMembers(hash_member *bucketptr, hash_freefp free_data)99{100hash_member *nextbucket;101while (bucketptr) {102nextbucket = bucketptr->next;103(*free_data) (bucketptr->data);104free((char *) bucketptr);105bucketptr = nextbucket;106}107}108109110111112/*113* This routine re-initializes the hash table. It frees all the allocated114* memory and resets all bucket pointers to NULL.115*/116117void118hash_Reset(hash_tbl *hashtable, hash_freefp free_data)119{120hash_member **bucketptr;121unsigned i;122123bucketptr = hashtable->table;124for (i = 0; i < hashtable->size; i++) {125hashi_FreeMembers(*bucketptr, free_data);126*bucketptr++ = NULL;127}128hashtable->bucketnum = 0;129hashtable->member = (hashtable->table)[0];130}131132133134/*135* Generic hash function to calculate a hash code from the given string.136*137* For each byte of the string, this function left-shifts the value in an138* accumulator and then adds the byte into the accumulator. The contents of139* the accumulator is returned after the entire string has been processed.140* It is assumed that this result will be used as the "hashcode" parameter in141* calls to other functions in this package. These functions automatically142* adjust the hashcode for the size of each hashtable.143*144* This algorithm probably works best when the hash table size is a prime145* number.146*147* Hopefully, this function is better than the previous one which returned148* the sum of the squares of all the bytes. I'm still open to other149* suggestions for a default hash function. The programmer is more than150* welcome to supply his/her own hash function as that is one of the design151* features of this package.152*/153154unsigned155hash_HashFunction(unsigned char *string, unsigned len)156{157unsigned accum;158159accum = 0;160for (; len > 0; len--) {161accum <<= 1;162accum += (unsigned) (*string++ & 0xFF);163}164return accum;165}166167168169/*170* Returns TRUE if at least one entry for the given key exists; FALSE171* otherwise.172*/173174int175hash_Exists(hash_tbl *hashtable, unsigned hashcode, hash_cmpfp compare,176hash_datum *key)177{178hash_member *memberptr;179180memberptr = (hashtable->table)[hashcode % (hashtable->size)];181while (memberptr) {182if ((*compare) (key, memberptr->data)) {183return TRUE; /* Entry does exist */184}185memberptr = memberptr->next;186}187return FALSE; /* Entry does not exist */188}189190191192/*193* Insert the data item "element" into the hash table using "hashcode"194* to determine the bucket number, and "compare" and "key" to determine195* its uniqueness.196*197* If the insertion is successful 0 is returned. If a matching entry198* already exists in the given bucket of the hash table, or some other error199* occurs, -1 is returned and the insertion is not done.200*/201202int203hash_Insert(hash_tbl *hashtable, unsigned hashcode, hash_cmpfp compare,204hash_datum *key, hash_datum *element)205{206hash_member *temp;207208hashcode %= hashtable->size;209if (hash_Exists(hashtable, hashcode, compare, key)) {210return -1; /* At least one entry already exists */211}212temp = (hash_member *) malloc(sizeof(hash_member));213if (!temp)214return -1; /* malloc failed! */215216temp->data = element;217temp->next = (hashtable->table)[hashcode];218(hashtable->table)[hashcode] = temp;219return 0; /* Success */220}221222223224/*225* Delete all data elements which match the given key. If at least one226* element is found and the deletion is successful, 0 is returned.227* If no matching elements can be found in the hash table, -1 is returned.228*/229230int231hash_Delete(hash_tbl *hashtable, unsigned hashcode, hash_cmpfp compare,232hash_datum *key, hash_freefp free_data)233{234hash_member *memberptr, *tempptr;235hash_member *previous = NULL;236int retval;237238retval = -1;239hashcode %= hashtable->size;240241/*242* Delete the first member of the list if it matches. Since this moves243* the second member into the first position we have to keep doing this244* over and over until it no longer matches.245*/246memberptr = (hashtable->table)[hashcode];247while (memberptr && (*compare) (key, memberptr->data)) {248(hashtable->table)[hashcode] = memberptr->next;249/*250* Stop hashi_FreeMembers() from deleting the whole list!251*/252memberptr->next = NULL;253hashi_FreeMembers(memberptr, free_data);254memberptr = (hashtable->table)[hashcode];255retval = 0;256}257258/*259* Now traverse the rest of the list260*/261if (memberptr) {262previous = memberptr;263memberptr = memberptr->next;264}265while (memberptr) {266if ((*compare) (key, memberptr->data)) {267tempptr = memberptr;268previous->next = memberptr = memberptr->next;269/*270* Put the brakes on hashi_FreeMembers(). . . .271*/272tempptr->next = NULL;273hashi_FreeMembers(tempptr, free_data);274retval = 0;275} else {276previous = memberptr;277memberptr = memberptr->next;278}279}280return retval;281}282283284285/*286* Locate and return the data entry associated with the given key.287*288* If the data entry is found, a pointer to it is returned. Otherwise,289* NULL is returned.290*/291292hash_datum *293hash_Lookup(hash_tbl *hashtable, unsigned hashcode, hash_cmpfp compare,294hash_datum *key)295{296hash_member *memberptr;297298memberptr = (hashtable->table)[hashcode % (hashtable->size)];299while (memberptr) {300if ((*compare) (key, memberptr->data)) {301return (memberptr->data);302}303memberptr = memberptr->next;304}305return NULL;306}307308309310/*311* Return the next available entry in the hashtable for a linear search312*/313314hash_datum *315hash_NextEntry(hash_tbl *hashtable)316{317unsigned bucket;318hash_member *memberptr;319320/*321* First try to pick up where we left off.322*/323memberptr = hashtable->member;324if (memberptr) {325hashtable->member = memberptr->next; /* Set up for next call */326return memberptr->data; /* Return the data */327}328/*329* We hit the end of a chain, so look through the array of buckets330* until we find a new chain (non-empty bucket) or run out of buckets.331*/332bucket = hashtable->bucketnum + 1;333while ((bucket < hashtable->size) &&334!(memberptr = (hashtable->table)[bucket])) {335bucket++;336}337338/*339* Check to see if we ran out of buckets.340*/341if (bucket >= hashtable->size) {342/*343* Reset to top of table for next call.344*/345hashtable->bucketnum = 0;346hashtable->member = (hashtable->table)[0];347/*348* But return end-of-table indication to the caller this time.349*/350return NULL;351}352/*353* Must have found a non-empty bucket.354*/355hashtable->bucketnum = bucket;356hashtable->member = memberptr->next; /* Set up for next call */357return memberptr->data; /* Return the data */358}359360361362/*363* Return the first entry in a hash table for a linear search364*/365366hash_datum *367hash_FirstEntry(hash_tbl *hashtable)368{369hashtable->bucketnum = 0;370hashtable->member = (hashtable->table)[0];371return hash_NextEntry(hashtable);372}373374/*375* Local Variables:376* tab-width: 4377* c-indent-level: 4378* c-argdecl-indent: 4379* c-continued-statement-offset: 4380* c-continued-brace-offset: -4381* c-label-offset: -4382* c-brace-offset: 0383* End:384*/385386387