/**************************************************************************** * [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; } }