mud_dist/area/
/******************************************************************************
 *  SSM v2.2 (shared string manager)                                          *
 *                                                                            *
 *  Copyright(C) 1996 Melvin Smith (Fusion) for EnvyMUD 2.2                   *
 *                                                                            *
 *  Due to alignment differences on 32 bit and 64 bit machines, memory        *
 *  usage is now virtually identical to standard Merc on 32-bit               *
 *  architecture, but slightly larger on 64-bit. Memory usage is still        *
 *  smaller than SSM 2.0 or earlier. The manager now uses short ints for      *
 *  the count and size of chunks, so to increase MAX_STRING you must          *
 *  increase MAX_CHUNKS instead. String have a max reference count of         *
 *  +32766 and max size of CHUNK_SIZE (0xfff0). Fragmentation is also         *
 *  handled more efficiently by marking failed chunks with -1 to temporarily  *
 *  disable them until a defrag_heap() recycles them. This helps when a       *
 *  4 byte chunk is freed low in the heap, so string_dup() doesn't walk       *
 *  the whole heap every time.                                                *
 *                                                                            *
 *  <msmith@falcon.mercer.peachnet.edu>                                       *
 *                                                                            *
 *  ROM2.4 modifications by Tom Adriaenssen (Jan 1996) -- Wreck               *
 *                                                                            *
 *  <tadriaen@zorro.ruca.ua.ac.be>                                            *
 *                                                                            *
 *  Removed ROM 2.4 modifications as Envy doesnt need *fread_string_eol -Kahn *
 *                                                                            *
 *****************************************************************************/

/*$Id: ssm.c,v 1.14 2005/02/22 23:55:19 ahsile Exp $*/

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>


#if !defined( ultrix )
#include <memory.h>
#endif


#include "merc.h"

#define intType        short int
#define uintType       unsigned intType
#define intTypeSize  ( sizeof( intType ) )
#define addrType       void * 
#define addrTypeSize ( sizeof( addrType ) )
#define addrSizeMask ( sizeof( addrType ) - 1 )

#define MAX_STR_KEY  21173
#define HASH(pt)    (strlen(pt) < 3)?((unsigned int)pt%MAX_STR_KEY):(((((((unsigned int)pt[0]<< 3) ^ (unsigned int)pt[2])<< 3)^(unsigned int)pt[1])) % MAX_STR_KEY)

/* To allocate more memory increase MAX_CHUNKS in merc.h. */
#define CHUNK_SIZE   0xfff0                  /* DO NOT mess with this! */ 
long    MAX_STRING = MAX_CHUNKS * CHUNK_SIZE;
int     HEADER_SIZE;


typedef struct BE BufEntry;

struct BE
{
    BufEntry *next;
    uintType  size;   /* size of the chunk (regardless of NULL CHAR) */ 
    intType   usage;  /* how many pointers to the string */ 
    char      buf[1]; /* chunk starts here */
};

/*
 * This is for the temporary hashing of strings at bootup to speedup
 * comparison/crunching of the string space. The temp_string_hash will
 * be freed after boot_done() is called.
 */
/*
typedef struct TH TempHash;

struct TH
{
    TempHash *next;
    uintType  len;
    char     *str;
};

TempHash **temp_string_hash;
*/
typedef struct STR_HASH STRHASH;
typedef STRHASH* STR_HASH_PTR;

struct STR_HASH {
 	char	*str;
	int usage;
	STR_HASH_PTR next;
};

/* These are the original Merc vars in db.c */
char         str_empty[1];
char        *string_space;
char        *top_string;
long         nAllocString;
long         sAllocString;
long         nOverFlowString;
long         sOverFlowString;

bool         Full;

char         *str_dup        ( const char * );
char         *fread_string   ( FILE * );

#ifdef MEM_DEBUG

void          f_string    ( char * );
STR_HASH_PTR StrTableStart = NULL;
STR_HASH_PTR StrTableEnd = NULL;
bool 	     free_on_stack = FALSE;

#else

void          free_string    ( char * );
STR_HASH_PTR  StrTable[MAX_STR_KEY];

#endif
/*
 * ssm_buf_head points to start of shared space,
 * ssm_buf_free points to next free block
 */ 
BufEntry *ssm_buf_head, *ssm_buf_free;

/*
 * Not sure what is a good value for MAX_FREE 
 * If a dup fails str_dup will not defrag unless the number
 * of numFree >= MAX_FREE. numFree is NOT the current number of free blocks,
 * it is just a counter so defrag doesnt start dragging the game in the
 * case of a lot of failed dups.
 */
#define MAX_FREE     8000

void init_string_space()
{
#ifndef MEM_DEBUG
/*
    temp_string_hash    = (TempHash **)calloc( 
					      MAX_KEY_HASH,sizeof(TempHash *) );
*/
    memset( StrTable, 0, sizeof(STR_HASH_PTR) * MAX_STR_KEY );
    str_empty[0] = '\0';
    nAllocString=0;
	  sAllocString=0;
#endif    
}
    
/*
 * Dup a string into shared space. If string exists, the usage count
 * gets incremented and the reference is returned. If the string does
 * not exist in heap, space is allocated and usage is 1.
 * This algorithm uses a hash table to keep track of strings. Ensuring that
 * only one copy of a string can exists in that table at any time.
 */
char *str_dup( const char *str )
{
#ifndef MEM_DEBUG
    char     *str_new;
    int       len;
    STR_HASH_PTR ptr;
    int       iIdx;

    if( !str || !*str )
        return str_empty;

    iIdx = HASH(str);
    for( ptr = StrTable[iIdx]; ptr; ptr = ptr->next ) {
      if( !strcmp(str, ptr->str) ) {
/*
	if (find_corruption(ptr->str))
	{
		char buf[MAX_STRING_LENGTH];
		sprintf(buf, "A corrupted string has been found in the string table. Address:0x%x", (unsigned int)ptr->str);
		bug(buf,0);
		bug("A dummy string has been used in its place.",0);
		return OFFENDING_STRING;
	} else
	{
	}
*/
        	ptr->usage++;
        	return ptr->str;

      }
    }
    
    len = (int)strlen( str ) + 1;
    ptr = malloc( sizeof(STRHASH));
    str_new = (char *)calloc( len , sizeof(char) );
    
    if( !str_new || !ptr) {
	    return str_empty;
    }
    strcpy( str_new, str ); 
    ptr->str = str_new;
    ptr->usage = 1;
    ptr->next = StrTable[iIdx];
    StrTable[iIdx] = ptr;

    nAllocString++;
    sAllocString+= len;

    return str_new;
#else
    /* Our brand spanking new str_dup function
       		- Ahsile
    */
    char* str_new = NULL;

    if (!str || str == str_empty)
	return str_empty;

    str_new = (char*)calloc( strlen( str ) + 1, sizeof(char));
    strcpy( str_new, str );

    if(!StrTableStart)
    {
	StrTableStart = (STR_HASH_PTR)calloc(1, sizeof(STRHASH));
        StrTableEnd   = StrTableStart;
    } else
    {
        STR_HASH_PTR  temp = (STR_HASH_PTR)calloc(1, sizeof(STRHASH));		
	StrTableEnd->next  = temp;
	StrTableEnd 	   = temp;
    }
    StrTableEnd->next = NULL;
    StrTableEnd->str  = str_new;

    return str_new;
#endif
}

/*
 * If string is in shared space, decrement usage, if usage then is 0,
 * free the string and the space used for the hash pointer str.
 */
#ifdef MEM_DEBUG
void f_string( char *str )
#else
void free_string( char *str )
#endif
{
#ifndef MEM_DEBUG
  STR_HASH_PTR ptr,ptr_last;
  int       iIdx;

  if( !str || str == str_empty )
    return;

  iIdx = HASH(str);
  for( ptr = StrTable[iIdx],ptr_last=NULL; ptr; ptr_last=ptr, ptr = ptr->next ) {
    if( /*!strcmp(str, ptr->str)*/ str == ptr->str ) {
      ptr->usage--;
      if( ptr->usage <= 0 ) {
		 sAllocString-= strlen(str);
         free(ptr->str);
         nAllocString--;
         if(ptr_last == NULL ) {
           StrTable[iIdx] = ptr->next;
         } else {
           ptr_last->next = ptr->next;
         }
         free(ptr);
         return;
      } else
	  {
		  break;
	  }
    }
  }
  return;
#else
  /* Our brand spanking new free_string function
			- Ahsile
  */
  STR_HASH_PTR cur  = NULL;
  STR_HASH_PTR last = NULL;
  char	       buf[MAX_STRING_LENGTH];
  if (!str || str==str_empty)
	return;
  
  for(cur = StrTableStart; cur; cur = cur->next )
  {
	if (str==cur->str)
	{
		free( str );
		last->next = cur->next;
		free( cur );	
		return;
	} else
	{
	    last = cur;
	}
  } 

  // avoid recursive calling of free_string
  free_on_stack = TRUE;

  if (!free_on_stack)
  {  
	sprintf(buf, "Function: %s is tried to free string 0x%x but it doesn't exist", callee_name, str);
  	bug(buf, 0);
  }

  free_on_stack = FALSE;

  return;
#endif
}



/*
 * Read and allocate space for a string from a file.
 * This replaces db.c fread_string
 * This is modified version of Furey's fread_string from Merc
 */
char *fread_string( FILE *fp )
{
    char buf[ MAX_STRING_LENGTH*4 ];
    char *ptr = buf;
    char  c;
    bool err = FALSE;

    do { c = getc( fp ); }
    while( isspace( c ) );

    if( ( *ptr++ = c ) == '~' )
        return str_empty;

    for ( ;; )
    {
	switch ( *ptr = getc( fp ) )
	{
	    default:
	    {
		// make sure string is in valid printable range
		// or TAB
	        if ((*ptr < 32 || *ptr > 126) && (*ptr != 9)) 
		{
			err = TRUE;
		}
		ptr++;
		break;
  	    }
	    case EOF:
		bug( "Fread_string: EOF", 0 );
		exit( 1 );
		break;

	    case '\n':
		ptr++;
		*ptr++ = '\r';
		break;

	    case '\r':
		break;

	    case '~':
		if (err)
			return str_dup(OFFENDING_STRING);

		*ptr = '\0';

		return str_dup( buf );
	}
    }
}

void cleanup_strings( void )
{
	int i = 0;

	for (i = 0; i < MAX_STR_KEY; i++)
	{
		if (StrTable[i])
		{
			STR_HASH_PTR ptr = StrTable[i];
			STR_HASH_PTR ptr_next = NULL;
			while(ptr)
			{
				ptr_next = ptr->next;
				if (ptr->str)
				{
					free( ptr->str );
				}
				free( ptr );
				ptr = ptr_next;
			}
		}
	}
}