SmaugWizard/Backup/
SmaugWizard/Backup/L/
SmaugWizard/Boards/
SmaugWizard/Building/
SmaugWizard/Corpses/
SmaugWizard/Councils/
SmaugWizard/Deity/
SmaugWizard/Gods/
SmaugWizard/MudProgs/
SmaugWizard/Player/L/
SmaugWizard/Src/
SmaugWizard/Src/res/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |				*
 * -----------------------------------------------------------|   \\._.//	*
 * SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version)   |   (0...0)	*
 * -----------------------------------------------------------|    ).:.(	*
 * SMAUG (C) 1994, 1995, 1996 by Derek Snider                 |    {o o}	*
 * -----------------------------------------------------------|   / ' ' \	*
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |~'~.VxvxV.~'~*
 * Scryn, Swordbearer, Rennard, Tricops, and Gorog.           |				*
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe.    *
 ****************************************************************************/

/****************************************************************************
 * Advanced string hashing functions (c)1996 D.S.D. Software, written by    *
 * Derek Snider for use in SMAUG.										    *
 *																		    *
 * These functions keep track of how many "links" are pointing to the	    *
 * memory allocated, and will free the memory if all the links are removed. *
 * Make absolutely sure you do not mix use of strdup and free with these    *
 * functions, or nasty stuff will happen!								    *
 * Most occurances of strdup/str_dup should be replaced with str_alloc, and *
 * any delete used on the same pointer should be replaced with			    *
 * str_free.  If a function uses strdup for temporary use... it is best if  *
 * it is left as is.  Just don't get usage mixed up between conventions.    *
 * The hashstr_data size is 8 bytes of overhead.  Don't be concerned about  *
 * this as you still save lots of space on duplicate strings.	-Thoric	    *
 ****************************************************************************/

#include	"stdafx.h"
#include	"smaug.h"
#include	"log.h"
#include	"SmaugWizDoc.h"

#define STR_HASH_SIZE	1024

struct hashstr_data
{
    struct hashstr_data	*next;		/* next hash element */
    unsigned short int	 links;		/* number of links to this string */
    unsigned short int	 length;	/* length of string */
};

int			str_free (char *str);
void		show_hash (int count);
char *		hash_stats ();

struct hashstr_data *string_hash [STR_HASH_SIZE];

// Check hash table for existing occurance of string.
// If found, increase link count, and return pointer,
// otherwise add new string to hash table, and return pointer.
char *str_alloc (const char *str)
{
	int		len = strlen (str);
	int		psize = sizeof (hashstr_data);
	int		hash = len % STR_HASH_SIZE;

	hashstr_data	*hd = string_hash [hash];
	while (hd) {
		if (len == hd->length && ! strcmp (str, (char*) hd + psize)) {
			if (hd->links < 65535)
				++hd->links;
			return (char*) hd + psize;
		}
		hd = hd->next;
	}
		
	char	*ptr = (char*) malloc (len + psize + 1);
	hd = (hashstr_data*) ptr;
	ptr += psize;

	strcpy (ptr, len ? str : "");

	hd->links = 1;
	hd->length = len;
	hd->next = string_hash [hash];
	string_hash [hash] = hd;

	return ptr;
}


// Used to make a quick copy of a string pointer that is known to be already
// in the hash table.  Function increments the link count and returns the
// same pointer passed.
char *quick_link (const char *str)
{
	register hashstr_data	*ptr;

	ptr = (hashstr_data*) (str - sizeof (hashstr_data));

	if (ptr->links == 0) {
		gpDoc->m_pLog->Printf (LOG_ALWAYS, "quick_link: bad pointer\n");
		return NULL;
	}
	if (ptr->links < 65535)
		++ptr->links;

	return NCCP str;
}


void StrFree (char *str, const char* file, int line)
{
	if (str)
		if (str_free (str) == -1)
			gpDoc->m_pLog->Printf (LOG_ALL,
				"STRFREEing bad pointer in %s, line %d\n", file, line);
}


// Used to remove a link to a string in the hash table.
// If all existing links are removed, the string is removed from the
// hash table and disposed of.
// returns how many links are left, or -1 if an error occurred.
int str_free (char *str)
{
	register int	len, hash;
	register struct	hashstr_data *ptr, *ptr2, *ptr2_next;

	len = strlen (str);
	hash = len % STR_HASH_SIZE;
	ptr = (struct hashstr_data *) (str - sizeof (struct hashstr_data));
	if (ptr->links == 65535)				// permanent
		return ptr->links;

	if (ptr->links == 0) {
		gpDoc->m_pLog->Printf (LOG_ALL, "str_free: bad pointer\n");
		return -1;
	}

	if (--ptr->links == 0) {
		if (string_hash [hash] == ptr) {
			string_hash[hash] = ptr->next;
			free (ptr);
			return 0;
		}

		for (ptr2 = string_hash [hash]; ptr2; ptr2 = ptr2_next) {
			ptr2_next = ptr2->next;
			if (ptr2_next == ptr) {
				ptr2->next = ptr->next;
				free (ptr);
				return 0;
			}
		}
		gpDoc->m_pLog->Printf (LOG_ALWAYS,
			"str_free: pointer not found for string: %s\n", str);
		return -1;
	}
	return ptr->links;
}


void show_hash (int count)
{
	struct	hashstr_data *ptr;
	int		x, c;

	for (x = 0; x < count; x++) {
		for (c = 0, ptr = string_hash[x]; ptr; ptr = ptr->next, c++);
		gpDoc->m_pLog->Printf (LOG_ALWAYS, " %d", c);
	}
	gpDoc->m_pLog->Printf (LOG_ALWAYS, "\n");
}


void hash_dump (int hash)
{
	struct	hashstr_data *ptr;
	char	*str;
	int		c, psize;

	if (hash > STR_HASH_SIZE || hash < 0) {
		gpDoc->m_pLog->Printf (LOG_ALWAYS, "hash_dump: invalid hash size\n\r");
		return;
	}

	psize = sizeof (struct hashstr_data);
	for (c=0, ptr = string_hash [hash]; ptr; ptr = ptr->next, c++) {
		str = (char *) (( (int) ptr) + psize);
		gpDoc->m_pLog->Printf (LOG_ALWAYS, "Len:%4d Lnks:%5d Str: %s\n\r",
			ptr->length, ptr->links, str);
	}
	gpDoc->m_pLog->Printf (LOG_ALWAYS,
		"Total strings in hash %d: %d\n\r", hash, c);
}


char *check_hash (char *str)
{
   static char buf[1024];
   int len, hash, psize, p, c;
   struct hashstr_data *ptr, *fnd;

   buf[0] = '\0';
   len = strlen (str);
   psize = sizeof (struct hashstr_data);
   hash = len % STR_HASH_SIZE;
   for (fnd = NULL, ptr = string_hash[hash], c = 0; ptr; ptr = ptr->next, c++)
     if (len == ptr->length && !strcmp (str, (char *)ptr+psize))
     {
	fnd = ptr;
	p = c+1;
     }
   if (fnd)
     sprintf (buf, "Hash info on string: %s\n\rLinks: %d  Position: %d/%d  Hash: %d  Length: %d\n\r",
	  str, fnd->links, p, c, hash, fnd->length);
   else
     sprintf (buf, "%s not found.\n\r", str);
   return buf;
}

char *hash_stats (void)
{
    static char buf[1024];
    struct hashstr_data *ptr;
    int x, c, total, totlinks, unique, bytesused, wouldhave, hilink;

    totlinks = unique = total = bytesused = wouldhave = hilink = 0;
    for (x = 0; x < STR_HASH_SIZE; x++)
    {
	for (c = 0, ptr = string_hash[x]; ptr; ptr = ptr->next, c++)
	{
	   total++;
	   if (ptr->links == 1)
	     unique++;
	   if (ptr->links > hilink)
	     hilink = ptr->links;
	   totlinks += ptr->links;
	   bytesused += (ptr->length + 1 + sizeof (struct hashstr_data));
	   wouldhave += (ptr->links * (ptr->length + 1));
	}
    }
    sprintf (buf, "Hash strings allocated:%8d  Total links  : %d\n\rString bytes allocated:%8d  Bytes saved  : %d\n\rUnique (wasted) links :%8d  Hi-Link count: %d\n\r",
	total, totlinks, bytesused, wouldhave - bytesused, unique, hilink);
    return buf;
}


void show_high_hash (int top)
{
	struct	hashstr_data *ptr;
	int		x, psize;
	char	*str;

	psize = sizeof (struct hashstr_data);
	for (x = 0; x < STR_HASH_SIZE; x++)
		for (ptr = string_hash [x]; ptr; ptr = ptr->next)
			if (ptr->links >= top) {
				str = (char *) (( (int) ptr) + psize);
				gpDoc->m_pLog->Printf (LOG_ALWAYS,
					"Links: %5d  String: >%s<\n\r", ptr->links, str);
			}
}


void DeleteAllHash ()
{
	hashstr_data	*pNext, *pStr;

	for (int hash=0; hash < STR_HASH_SIZE; ++hash) {
		pStr = string_hash [hash];
		while (pStr) {
			pNext = pStr->next;
			free (pStr);
			pStr = pNext;
		}
		string_hash [hash] = NULL;
	}
}