/*
 * Copyright 1990 by Baylor College of Medicine ALL RIGHTS RESERVED. 
 *
 * This program is subject to a license agreement between 
 * Baylor College of Medicine and MIT. Any use inconsistent with
 * said license and any use by persons other than the faculty, 
 * students and staff at MIT or any use on a computer not operated 
 * as part of the Athena Computing Environment (ACE) is expressly 
 * prohibited.
 */
/****************************************************************
 * File: hash.c
 * Date: 02/05/91
 *
 * Description:
 *   Hash table funcitons.
 *
 * Revisions:
 ****************************************************************/
#include <malloc.h>
#include <stdio.h>

typedef struct Hash *Hash ;

static int nhash = 5003 ;
static Hash entries[5003] ;

/* Hash bucket */
struct Hash {
	int nref ;		/* Number of references */
	Hash next ;		/* Next entry */
	char s[1] ;		/* String (padded out by malloc) */
} ;

/****************************************************************
 *                   PRIVATE FUNCTIONS
 ****************************************************************/

/* Find a string in hash table using string comparison */
static
Hash *
find_hash(s)
	char *s ;
{
	unsigned long sum = 0 ;
	Hash *h ;
	char *t = s ;
	while(*t)
	{
		sum = sum * 3 + *t++ ;
	}
	for(h = entries + sum % nhash; (*h) != NULL && strcmp((*h)->s,s); h = &(*h)->next) ;
	return h ;
}

/* Find a string in hash table using pointer comparison */
static
Hash *
find_hashp(s)
	char *s ;
{
	unsigned long sum = 0 ;
	Hash *h ;
	char *t = s ;
	while(*t)
	{
		sum = sum * 3 + *t++ ;
	}
	for(h = entries + sum % nhash; (*h) != NULL && (*h)->s != s; h = &(*h)->next) ;
	return h ;
}

/****************************************************************
 *                   PRIVATE FUNCTIONS
 ****************************************************************/

/* Adds a string to hash table.  If already there then update reference count */
char *
add_string(s)
	char *s ;
{
	if (s == NULL)
	{
		return NULL ;
	}
	else
	{
		Hash *h = find_hash(s) ;
		if (*h != NULL)
		{
			(*h)->nref++ ;
			return (*h)->s ;
		}
		else
		{
			Hash new = (Hash)malloc((sizeof *new) + strlen(s)) ;
			strcpy(new->s,s) ;
			new->nref = 1 ;
			new->next  = NULL ;
			*h = new ;
			return new->s ;
		}
	}
}

/* Remove a string from hash table.  Free space only when there are no refernces */
rmv_string(s)
	char *s ;
{
	if (s != NULL)
	{
		Hash *h = find_hashp(s) ;
		Hash hsh = *h ;
		if (hsh != NULL)
		{
			hsh->nref-- ;
			if (!hsh->nref)
			{
				*h = hsh->next ;
				free((char *)hsh) ;
			}
		}
	}
}
