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