LOP/
LOP/area/
LOP/boards/
LOP/channels/
LOP/clans/
LOP/color/
LOP/councils/
LOP/deity/
LOP/src/specials/
/*****************************************************************************
 * DikuMUD (C) 1990, 1991 by:                                                *
 *   Sebastian Hammer, Michael Seifert, Hans Henrik Staefeldt, Tom Madsen,   *
 *   and Katja Nyboe.                                                        *
 *---------------------------------------------------------------------------*
 * MERC 2.1 (C) 1992, 1993 by:                                               *
 *   Michael Chastain, Michael Quan, and Mitchell Tse.                       *
 *---------------------------------------------------------------------------*
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by: Derek Snider.                    *
 *   Team: Thoric, Altrag, Blodkai, Narn, Haus, Scryn, Rennard, Swordbearer, *
 *         gorog, Grishnakh, Nivek, Tricops, and Fireblade.                  *
 *---------------------------------------------------------------------------*
 * SMAUG 1.7 FUSS by: Samson and others of the SMAUG community.              *
 *                    Their contributions are greatly appreciated.           *
 *---------------------------------------------------------------------------*
 * LoP (C) 2006, 2007, 2008, 2009 by: the LoP team.                          *
 *****************************************************************************
 * 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 free/DISPOSE 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "h/mud.h"

extern bool mud_down;

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

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, const char *filename, int line )
{
   register int len, hash, psize;
   register struct hashstr_data *ptr;

   if( !str || str[0] == '\0' )
   {
//      bug( "%s: %s@%d trying to allocate an empty/null string", __FUNCTION__, filename, line );
      return NULL;
   }
   len = strlen( str );
   psize = sizeof( struct hashstr_data );
   hash = len % STR_HASH_SIZE;
   for( ptr = string_hash[hash]; ptr; ptr = ptr->next )
   {
      if( len == ptr->length && !strcmp( str, ( char * )ptr + psize ) )
      {
         if( ( ptr->links + 1 ) > 65500 )
            continue;
         else
            ++ptr->links;
         return ( char * )ptr + psize;
      }
   }
   ptr = ( struct hashstr_data * )malloc( len + psize + 1 );
   ptr->links = 1;
   ptr->length = len;
   if( len )
      strcpy( ( char * )ptr + psize, str );
   else
      strcpy( ( char * )ptr + psize, "" );
   ptr->next = string_hash[hash];
   string_hash[hash] = ptr;
   return ( char * )ptr + psize;
}

/*
 * 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, const char *filename, int line )
{
   register struct hashstr_data *ptr;

   if( !str || str[0] == '\0' )
   {
//      bug( "%s: %s@%d trying to allocate an empty/null string", __FUNCTION__, filename, line );
      return NULL;
   }
   ptr = ( struct hashstr_data * )( str - sizeof( struct hashstr_data ) );
   if( !ptr || ptr->links == 0 )
   {
      fprintf( stderr, "%s: %s@%d called bad pointer\n", __FUNCTION__, filename, line );
      return NULL;
   }
   if( ( ptr->links + 1 ) > 65500 )
      return str_alloc( str, filename, line );
   else
      ++ptr->links;
   return (char *)str;
}

/*
 * 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( const char *str, const char *filename, int line )
{
   register int len, hash;
   register struct hashstr_data *ptr, *ptr2, *ptr2_next;

   if( !str || str[0] == '\0' )
   {
//      bug( "%s: %s@%d trying to free an empty/null string", __FUNCTION__, filename, line );
      return -1;
   }
   len = strlen( str );
   hash = len % STR_HASH_SIZE;
   ptr = ( struct hashstr_data * )( str - sizeof( struct hashstr_data ) );
   if( !ptr || ptr->links == 0 )
   {
      fprintf( stderr, "%s: %s@%d calling bad pointer\n", __FUNCTION__, filename, line );
      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;
         }
      }
      fprintf( stderr, "%s: %s@%d calling pointer thats not found for string: %s\n", __FUNCTION__, filename, line, 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++ );
      fprintf( stderr, " %d", c );
   }
   fprintf( stderr, "\n" );
}

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

   if( hash > STR_HASH_SIZE || hash < 0 )
   {
      fprintf( stderr, "%s: invalid hash size\n", __FUNCTION__ );
      return;
   }
   psize = sizeof( struct hashstr_data );
   for( c = 0, ptr = string_hash[hash]; ptr; ptr = ptr->next, c++ )
   {
      str = ( char * )( ( ( long )ptr ) + psize );
      fprintf( stderr, "Len: %4d Lnks: %5d Str: %s\r\n", ptr->length, ptr->links, strip_cr( str ) );
   }
   if( !mud_down || ( mud_down && c > 0 ) )
      fprintf( stderr, "Total strings in hash %d: %d\r\n", hash, c );
}

char *check_hash( const char *str )
{
   static char buf[1024];
   int len, hash, psize, p = 0, 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 )
      snprintf( buf, sizeof( buf ), "Hash info on string: %s\r\nLinks: %d  Position: %d/%d  Hash: %d  Length: %d\r\n",
         str, fnd->links, p, c, hash, fnd->length );
   else
      snprintf( buf, sizeof( buf ), "%s not found.\r\n", str );
   return buf;
}

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

   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 + hashsize );
         /*
          * Wouldhave is done in what str_dup would use instead of the hash if bytes saved shows -
          * your wasteing memory because of to many unique links
          */
         wouldhave += ( ptr->links * ( ptr->length + 1 ) );
      }
   }
   saved = ( wouldhave - bytesused );
   snprintf( buf, sizeof( buf ),
      "Total Strings: %8d  Bytes Used  : %8d\r\n"
      "Total Links  : %8d  Bytes %s: %8d\r\n"
      "Unique Links : %8d\r\n"
      "Hi Link Count: %8d\r\n",
         total, bytesused, totlinks, saved >= 0 ? "Saved " : "Wasted", abs( saved ), 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 * )( ( ( long )ptr ) + psize );
            fprintf( stderr, "Links: %5d  String: >%s<\r\n", ptr->links, strip_cr( str ) );
         }
      }
   }
}

bool in_hash_table( const char *str )
{
   register int len, hash, psize;
   register struct hashstr_data *ptr;

   len = strlen( str );
   psize = sizeof( struct hashstr_data );
   hash = len % STR_HASH_SIZE;
   for( ptr = string_hash[hash]; ptr; ptr = ptr->next )
      if( len == ptr->length && str == ( ( char * )ptr + psize ) )
         return true;
   return false;
}