pdirt/data/
pdirt/data/HELP/
pdirt/data/HELP/0/
pdirt/data/HELP/F/
pdirt/data/HELP/G/
pdirt/data/HELP/H/
pdirt/data/HELP/J/
pdirt/data/HELP/K/
pdirt/data/HELP/O/
pdirt/data/HELP/Q/
pdirt/data/HELP/R/
pdirt/data/HELP/U/
pdirt/data/HELP/V/
pdirt/data/HELP/Y/
pdirt/data/HELP/Z/
pdirt/data/MESSAGES/
pdirt/data/POWERINFO/
pdirt/data/WIZ_ZONES/
pdirt/drv/
pdirt/drv/bin/
pdirt/drv/compiler/converter/
pdirt/drv/compiler/libs/
pdirt/drv/compiler/scripts/
pdirt/drv/include/AberChat/
pdirt/drv/include/InterMud/
pdirt/drv/include/machine/
pdirt/drv/src/InterMud/
pdirt/drv/src/Players/
pdirt/drv/utils/UAFPort/
pdirt/drv/utils/dnsresolv/
pdirt/drv/utils/gdbm/
/*****************************************************************************
 ** FILE	: hash.c
 ** AUTHOR	: Peter Eussen 
 ** DATE        : 21 Dec 1997
 ** VERSION	: 1.0
 ** DESCRIPTION : A set of routines to store and retrieve records needed by
 **               the generation process. It can handle an unlimited amount
 **               of tables if required, varying in size.
 ** MODIFICATIONS:
 **
 ***************************************************************************/
#define HASH_C
#include <stdlib.h>
#include <stdio.h>
#include "hash.h"
#include "gen.h"

/****************************************************************************
 ** freeHashRecord: Releases memory in a  hash Record.
 ** 
 ** input parameters:
 **    hashRecord * = Pointer to a record that needs to be freed.
 ** output parameters:
 **    none.
 ***************************************************************************/
void freeHashRecord(hashRecord *ptr)
{   if (ptr == NULL)
       return;

    /* Free all pointers in the data structure */
    if (ptr->id != NULL)
       xfree(ptr->id,REC_CHAR);
 
    /* Free the ptr */
    xfree(ptr,REC_HRECORD);
}

/***************************************************************************
 ** createHashRecord: Creates a record for the given id
 **
 ** input parameters:
 **     id   = identification for this record.
 ** output parameters:
 **     NULL         = memory allocation failed.
 **     hashRecord * = pointer to the record.
 **************************************************************************/
hashRecord *createHashRecord(char *id,long idnum)
{   hashRecord *p;

    /* if we were to allow NULL type keys, we would have a problem with 
     * calculating a hash index and with retrieving the record, so we check
     * to make sure it doesnt happen in any case
     */
    if (id == NULL)			
       return NULL;

    p = memAlloc(hashRecord,1,REC_HRECORD);
    if (p != NULL)
    {   /* Ok, we have reserved a record for this key, now copy the key into
         * the id field. But first allocate enough bytes to allocate the key
         * since the length of the keys can differ drastically in size (this
         * code can handle almost all type of keys if you modify the 
         * calHashKey function.
         */
        p->id = memAlloc(char,strlen(id) + 1,REC_CHAR);
        p->idnumber = idnum;
        strcpy(p->id,id);
        /* Little bit of protection for ourselves. Make all records all
         * lowercase to prevent Zonename confusion.
         */
        lowercase(p->id);
    }
    return p;
}

/****************************************************************************
 ** createHashTable: Creates a hash table, with the given size, how original! 
 **
 ** Input parameters:
 **    size_t size = The size of the table.
 ** Output parameters:
 **    hashTable * = pointer to the main hashtable record, or NULL if the
 **                  table wasn't created for some reason.
 ****************************************************************************/
hashTable *createHashTable(size_t size)
{   hashTable  *thashTableptr;		/* Temporary hashTable pointer */
    hashRecord **thashRecordptr;	/* Temporary hashRecord Pointer */
    hashRecord *p;			/* I couldnt figure out how to get
					 * memAlloc working so i did it with
					 * this pointer
					 */

    /* First we try to reserve space for the overall hashTable record type.
     * If this fails, then we don't have to try attempting to allocate space
     * for the table.
     */
    if ((thashTableptr = (hashTable *) memAlloc(hashTable,1,REC_HTABLE)) != NULL) 
    {   /* We have the hashTable record available now, now lets see if we
         * have enough memory to create the table.
         */
        thashRecordptr = memAlloc((p),size,REC_HRECORD);

        if (thashRecordptr != NULL)
        {   thashTableptr->size = size;
            thashTableptr->hashtable = thashRecordptr;
            /* Since memAlloc uses calloc, we dont have to set all pointers to
             * NULL, since calloc does that for us.
             */
        }
        else
        {   /* allocation of hashtable failed, release memory of reserved 
             * hashTable record.
             */
            xfree(thashTableptr,REC_HTABLE);
            thashTableptr = NULL;
        }
    }
    return thashTableptr;
}

/******************************************************************************
 ** calcHashKey: Calculates the hashkey from a character string. Since much of
 **             the performance of the hashtable will depend on this, the 
 **             calculation of the key may have to be different than the way
 **             it is now.. it all depends on the size of the table and the
 **             keys.
 ** 
 ** input parameters: 
 **     char   *id  = string that is used to calculate the hashIndex
 **     size_t size = size of the table.
 ** output parameters:
 **     hashKey  = hash key.
 *****************************************************************************/
hashKey calcHashKey(char *id,size_t tablesize)
{  hashKey tempkey=1;
   int     i;

   for (i = 0; id[i] != '\0'; i++)
   {   if (id[i] == '@')
          continue;

       if ((i+1 % 3) > 0)
          tempkey += (long) id[i] * tempkey;
       else
          tempkey *= (long) id[i];
   }

   /* Mod tablesize to ensure the returned index lies within the table limit,
    * so we don't get SIGSEGV's etc. The functio above can give results
    * ranging from 90 till 2 Billion (long int limit). Only way it will crash
    * is when the multiplication goes above 2 Billion.
    */
   return tempkey % tablesize;		
}

/*****************************************************************************
 ** addHashRecord: Adds a record to the table
 ** 
 ** Input parameters:
 **     hashRecord * = A pointer to the record that should be added.
 **     hashTable  * = A pointer into which the record should be added.
 ** Output parameters:
 **     -1 = Faulty parameters give to the function.
 **     0  = Eep! the record was already in the hashtable.
 **     1  = Record has been added. 
 *****************************************************************************/
int addHashRecord(hashRecord *newptr, hashTable *to)
{   hashKey index;
    
    /* Not even going to consider adding in these situations
     */
    if (newptr == NULL || newptr->id == NULL || to == NULL)
       return -1;

    /* If there already is a record with this ID, then we can't add it. We
     * have to have an unique id for each Record.
     */
    if (findHashRecord(newptr->id,to) != NULL)
       return 0;

    /* Calculate a key for the id so we can start inserting it in the right
     * place.
     */
    index = calcHashKey(newptr->id,to->size);
   
    /* This is for debugging of the hashtable only. The number of collisions
     * should be as small as possible when inserting, cause that will mean
     * lookup will be more succesfull as well.
     */
    if (to->hashtable[index] != NULL)
       to->insertcollisions++;

    /* Simply insert it at the top, so we dont need to check for collisions
     */ 
    newptr->next = to->hashtable[index];
    to->hashtable[index] = newptr;
    to->elems_stored++;
    return 1; 
}

/*****************************************************************************
 ** findHashRecord: Finds a hashtable record for a given id.
 **
 ** input parameters:
 **    id       = identifier for that record.
 **    hashTable= The table in which it supposidly was stored.
 ** output parameters:
 **    NULL        = record not found, or table is non-exsistant
 **    HashRecord *= pointer to the hashrecord for that id.
 ***************************************************************************/
hashRecord *findHashRecord(char *id,hashTable *table)
{   hashKey    key;
    hashRecord *tptr;

    if (id == NULL || table == NULL)
       return NULL;		/* Can't find anything with NULL's */

    /* Find the index within the hash table we should be able to find the key
     * if its inserted.
     */
    key = calcHashKey(id,table->size);

    tptr = table->hashtable[key];

    /* Now loop through the possible linked list at this position in the 
     * hashtable. If the id is in this list, then we return a pointer to
     * that record.
     */
    while (tptr != NULL)
    {   if (tptr->id[0] == id[0] && tptr->id[strlen(tptr->id)-1] == id[strlen(id)-1])
          if (strcmp(id,tptr->id) == 0)
             return tptr;

        /* This is for debugging only. It counts the amount of failed lookups 
         * its to do before the record requested is found. 
         */
        table->findcollisions++;
        tptr = tptr->next;
    }

    /* Key not found in chain, or there was no chain at this key.
     */
    return NULL;
}

/****************************************************************************
 ** deleteHashRecord: Deletes a record from the hashtable
 **
 ** input parameters:
 **    id    = String to identify the record that needs to be deleted.
 **    from  = Table where the record should be deleted from.
 ** output parameters:
 **    -1    = invalid parameters
 **    0     = record not found.
 **    1     = record deleted.
 **************************************************************************/
int deleteHashRecord(char *id,hashTable *from)
{  hashKey    key;
   hashRecord *recptr,*t;

   /* We do not store NULL id's and we can't delete from a non-exsistant
    * hashtable.
    */
   if (id == NULL || from == NULL)
      return -1;

   /* First calculate the key and try to find it. So we have the position and
    * a pointer to the record to clearly identify the target when we find it.
    */
   key = calcHashKey(id,from->size);
   recptr = findHashRecord(id,from);

   /* Hmm, the find failed.. That must mean the id is not stored in this table.
    * Nothing to do.. return 0.
    */
   if (recptr == NULL)
      return 0;

   if (from->hashtable[key] == recptr)
   {   /* First see if the one we want to delete is the first in line, cause
        * they always require special attention.
        */
       from->hashtable[key] = recptr->next;
       freeHashRecord(recptr);
       return 1;
   }  
   else
   {   t = from->hashtable[key];

       /* Cycle through the linked list untill we find a record which next 
        * value points to the one we are seeking.
        */
       while (t->next != recptr && t->next != NULL)
         t = t->next;

       if (t->next == recptr)
       {   /* Ok we found it. Now remove the to be deleted record from the
            * linked list and delete it.
            */
           t->next = recptr->next;
           freeHashRecord(recptr);
           from->elems_stored--;
           return 1;
       }
   }

   /* Hmm we reached the end of the linked list and still havent found it, so
    * it must mean that for some reason it cant be found. Well.. nothing to do
    * so return 0.
    */
   return 0;
}
   
/*****************************************************************************
 ** deleteHashTable: Oh my, we're gonna free up some memory here!
 **
 ** input parameters:
 **     thistable = pointer to the table that should be deleted.
 ** output parameters:
 **    -1 = invalid parameter.
 **    1  = The table is history!
 **************************************************************************/
int deleteHashTable(hashTable *thistable)
{   hashRecord *p, *q;
    hashKey    i;

    /* Nothing to delete if the table doesnt exsist. 
     */
    if (thistable == NULL || thistable->hashtable == NULL)
      return -1;

    /* Delete all records in the table 
     */
    for (i=0 ; i < thistable->size; i++)
    {   p = thistable->hashtable[i];
        
        /* Delete all records that are in this entry of the linked list.
         */
        /*printf("Slot %ld\n",i);*/
        while (p != NULL)
        {   q = p->next;
            /*printf("   Freeing: %s\n",p->id);*/
            freeHashRecord(p);
            p = q;
        }
    }

    /* Delete table 
     */
    xfree(thistable->hashtable,REC_HRECORD);
    xfree(thistable,REC_HTABLE);
    return 1;
}

/*****************************************************************************
 ** printHashTableStatistics: Prints some statistics about the usage of the
 **       hashtable, as elements stored and collisions.
 ** 
 ** input parameters:
 **    h = pointer to a valid hashtable.
 *****************************************************************************/
void printHashTableStatistics(hashTable *h)
{    if (h != NULL)
        printf("HashTable statistics:\n"
               " Hash Table size : %d\n"
               " Elements stored : %d\n"
               " Store Collisions: %d\n"
               " Find Collisions : %d\n",
               h->size,h->elems_stored,h->insertcollisions,h->findcollisions);
}

/*****************************************************************************
 ** ymalloc: A memory allocation function which does some checking and 
 **          produces some debugging info for when it occurs. To use this
 **          function call the memAlloc() macro.
 **          memAlloc requires to parameters:
 **              Element: the type or record/datatype you wish to allocate 
 **                       (ie char, hashTable or somesuch).
 **              Number : The number of elements you want to allocate.
 ** input parameters:
 **    size    = size of the record/datatypes structure
 **    nmembers= amount of records/datatypes to reserve (for arrays)
 **    file    = filename of the code file where the error occured.
 **    line    = linenumber of the code file where this function was called
 **              from.
 **
 ** output parameters:
 **    a pointer of the type void that points to the assignmed memory or NULL
 **    if the memory allocation failed.
 *****************************************************************************/
void *ymalloc(size_t size, size_t nmembers,int typenr,char *file, int line)
{   void *ptr;

    ptr = calloc(nmembers,size);
    
    if (ptr == NULL)
    {  fprintf(stderr,"Allocation of memory failed, could not allocate %d bytes\n"
                      "    Total Memory usage: %ld\n"
                      "    Size of element   : %d\n"
                      "    Number of elements: %d\n"
                      "    File: %s    Line: %d\n",nmembers * size,mem_usage,
		      size,nmembers,file,line);
       exit(1);
    }
#ifdef MEMORY_STATISTIC
    updateMemStat(typenr,nmembers);
#endif
    mem_usage += size * nmembers;
    return ptr;
}