/*************************************************************************** * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. * * * * Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * Ack 2.2 improvements copyright (C) 1994 by Stephen Dooley * * * * In order to use any part of this Merc Diku Mud, you must comply with * * both the original Diku license in 'license.doc' as well the Merc * * license in 'license.txt'. In particular, you may not remove either of * * these copyright notices. * * * * _/ _/_/_/ _/ _/ _/ ACK! MUD is modified * * _/_/ _/ _/ _/ _/ Merc2.0/2.1/2.2 code * * _/ _/ _/ _/_/ _/ (c)Stephen Zepp 1998 * * _/_/_/_/ _/ _/ _/ Version #: 4.3 * * _/ _/ _/_/_/ _/ _/ _/ * * * * http://ackmud.nuc.net/ * * zenithar@ackmud.nuc.net * * Much time and thought has gone into this software and you are * * benefitting. We hope that you share your changes too. What goes * * around, comes around. * ***************************************************************************/ /*************************************************************************** * _/_/_/_/ _/ _/ _/_/_/_/ _/_/_/_/ AckFUSS is modified ACK!MUD 4.3.1 * * _/ _/ _/ _/ _/ copyright Matt Goff (Kline) 2008 * * _/_/ _/ _/ _/_/_/_/ _/_/_/_/ * * _/ _/ _/ _/ _/ Support for this code is provided * * _/ _/_/_/_/ _/_/_/_/ _/_/_/_/ at www.ackmud.net -- check it out!* ***************************************************************************/ /****************************************************************************** * SSM v2.1.9 (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 * ******************************************************************************/ #include "h/globals.h" #ifndef DEC_DB_H #include "h/db.h" #endif #ifndef DEC_SSM_H #include "h/ssm.h" #endif TempHash **temp_string_hash; /* 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; long hwOverFlow; int numFree; bool Full; extern bool fBootDb; /* * ssm_buf_head points to start of shared space, * ssm_buf_free points to next free block */ BufEntry *ssm_buf_head, *ssm_buf_free; long MAX_STRING = MAX_CHUNKS * CHUNK_SIZE; int HEADER_SIZE; long tot = 0; void init_string_space( ) { BufEntry *walk; int i; string_space = ( char * )malloc( MAX_STRING ); if( !string_space ) { bugf( "SSM: Can't allocate %ld bytes shared string space.", MAX_STRING ); raise( SIGSEGV ); } top_string = string_space + MAX_STRING - 1; ssm_buf_head = ( BufEntry * ) string_space; HEADER_SIZE = ( int )( ( char * )&ssm_buf_head->buf[0] - ( char * )ssm_buf_head ); walk = ssm_buf_head; for( i = 0;; i++ ) { walk->usage = 0; walk->size = CHUNK_SIZE - HEADER_SIZE; if( i < MAX_CHUNKS - 1 ) { walk->next = ( BufEntry * ) ( ( char * )walk + CHUNK_SIZE ); walk = walk->next; continue; } walk->next = 0; break; } ssm_buf_free = ssm_buf_head; temp_string_hash = ( TempHash ** ) calloc( sizeof( TempHash * ), MAX_KEY_HASH ); } int defrag_heap( ) { /* * Walk through the shared heap and merge adjacent free blocks. * Free blocks are merged in str_free if free->next is free but * if the block preceding free is free, it stays unmerged. I would * rather not have the heap as a DOUBLE linked list for 2 reasons... * (1) Extra 4 bytes per struct uses more mem * (2) Speed - don't want to bog down str_ functions with heap management * The "orphaned" blocks will eventually be either merged or reused. * The str_dup function will call defrag if it cant allocate a buf. */ BufEntry *walk, *last_free, *next; int merges = 0; ssm_buf_free = 0; for( walk = ssm_buf_head, last_free = 0; walk; walk = next ) { next = walk->next; if( walk->usage > 0 ) { /* * this block is in use so set last_free to NULL */ last_free = 0; continue; } else if( !last_free ) { /* * OK found a NEW free block, set last_free and move to next */ last_free = walk; if( !ssm_buf_free ) ssm_buf_free = walk; continue; } else { /* * previous block free so merge walk into last_free and move on */ if( ( long )last_free->size + ( long )walk->size <= CHUNK_SIZE ) { merges++; last_free->size += walk->size + HEADER_SIZE; last_free->next = walk->next; last_free->usage = 0; } else last_free = walk; } } if( merges ) log_f( "SSM: defrag_heap: made %d block merges.", merges ); else log_f( "SSM: defrag_heap: resulted in 0 merges." ); /* * Start count over again */ numFree = 0; return merges; } /* * 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 is a linked list first fit algorithm, so strings can be * freed. Upon bootup, there is a seperate hash table constructed in order * to do crunching, then the table is destroyed. */ /* for alarm_update crosschecking */ int ssm_dup_count; int ssm_loops; int ssm_recent_loops; char *_str_dup( const char *str, const char *caller ) { BufEntry *ptr; int len; int rlen; char *str_new; ssm_dup_count++; ssm_recent_loops = 0; if( !str || !*str ) return &str_empty[0]; if( str > string_space && str < top_string ) { ptr = ( BufEntry * ) ( str - HEADER_SIZE ); if( ptr->usage <= 0 ) { bugf( "str_dup: invalid string from %s: %20.20s", caller, str ); ptr->usage = 0; /* make it valid again */ } ptr->usage++; return ( char * )str; } rlen = len = ( int )strlen( str ) + 1; /* * Round up to machine dependant address size. * Don't remove this, because when the BufEntry struct is overlaid * the struct must be aligned correctly. */ if( ( len + HEADER_SIZE ) & addrSizeMask ) len += addrTypeSize - ( ( len + HEADER_SIZE ) & addrSizeMask ); RETRY: for( ptr = ssm_buf_free; ptr; ptr = ptr->next ) { ssm_recent_loops++; ssm_loops++; if( ptr->usage == 0 && ptr->size >= len ) break; } if( ptr ) ptr->usage = 1; if( !ptr ) { if( numFree > 1 ) numFree++; if( numFree >= MAX_FREE ) { int merges; log_f( "SSM: Attempting to optimize shared string heap." ); merges = defrag_heap( ); /* * goto is fine because defrag will return 0 next time */ if( merges ) goto RETRY; } /* * A one time toggle just for bugging purposes */ if( !Full ) { bugf( "SSM: The shared string heap is full!" ); Full = 1; } str_new = ( char * )malloc( rlen ); strcpy( str_new, str ); sOverFlowString += rlen; nOverFlowString++; if( sOverFlowString > hwOverFlow ) hwOverFlow = sOverFlowString; return str_new; } /* * If there is at least header size excess break it up */ else if( ptr->size - len >= ( HEADER_SIZE + 8 ) ) { BufEntry *temp; /* * WARNING! - DONT REMOVE THE CASTS BELOW! - Fusion */ temp = ( BufEntry * ) ( ( char * )ptr + HEADER_SIZE + len ); temp->size = ptr->size - ( len + HEADER_SIZE ); temp->next = ptr->next; temp->usage = 0; ptr->size = len; ptr->next = temp; ssm_buf_free = temp; } else { if( ptr != ssm_buf_free ) ssm_buf_free->usage--; /* buf_free was skipped */ /* * spec: don't start from the start of the heap again! */ for( ; ssm_buf_free; ssm_buf_free = ssm_buf_free->next ) { ssm_loops++; ssm_recent_loops++; if( ssm_buf_free->usage == 0 ) break; } } str_new = ( char * )&ptr->buf[0]; strcpy( str_new, str ); ptr->caller = caller; nAllocString++; sAllocString += ptr->size + HEADER_SIZE; return str_new; } /* * If string is in shared space, decrement usage, if usage then is 0, * free the chunk and attempt to merge with next node. Other * strings are freed with standard free. * Never call free/delete externally on a shared string. */ void _free_string( char *str, const char *caller ) { BufEntry *ptr; if( !str || str == &str_empty[0] ) return; if( str > string_space && str < top_string ) { ptr = ( BufEntry * ) ( str - HEADER_SIZE ); if( --ptr->usage > 0 ) return; else if( ptr->usage < 0 ) { bugf( "SSM: free_string: multiple free/invalid from %s: %20.20s", caller, ( char * )&ptr->buf[0] ); return; } numFree++; sAllocString -= ( ptr->size + HEADER_SIZE ); nAllocString--; if( !ssm_buf_free || ssm_buf_free > ptr ) ssm_buf_free = ptr; if( fBootDb ) { TempHash *hptr; TempHash *walk; int ihash = strlen( str ) % MAX_KEY_HASH; for( hptr = temp_string_hash[ihash]; hptr; hptr = hptr->next ) { if( hptr->str != str ) continue; else if( hptr == temp_string_hash[ihash] ) temp_string_hash[ihash] = hptr->next; else for( walk = temp_string_hash[ihash]; walk; walk = walk->next ) { if( walk->next == hptr ) { walk->next = hptr->next; break; } } free( hptr ); break; } } return; } sOverFlowString -= strlen( str ) + 1; nOverFlowString--; free( str ); } /* * 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, const char *caller ) { char buf[MAX_STRING_LENGTH * 4]; char *ptr = buf; char c; do { c = getc( fp ); } while( isspace( c ) ); if( ( *ptr++ = c ) == '~' ) return &str_empty[0]; for( ;; ) { switch ( *ptr = getc( fp ) ) { default: ptr++; break; case EOF: bugf( "Fread_string: EOF" ); raise( SIGSEGV ); break; case '\n': ptr++; *ptr++ = '\r'; break; case '\r': break; case '~': *ptr = '\0'; if( fBootDb ) { ptr = temp_hash_find( buf ); if( ptr ) return _str_dup( ptr, caller ); ptr = _str_dup( buf, caller ); temp_hash_add( ptr ); return ptr; } ptr = _str_dup( buf, caller ); return ptr; } } } /* * This is a modified version of fread_string: * It reads till a '\n' or a '\r' instead of a '~' (like fread_string). * ROM uses this function to read in the socials. * -- Wreck */ char *_fread_string_eol( FILE * fp, const char *caller ) { char buf[MAX_STRING_LENGTH * 4]; char *ptr = buf; char c; do { c = getc( fp ); } while( isspace( c ) ); if( ( *ptr++ = c ) == '\n' ) return &str_empty[0]; for( ;; ) { switch ( *ptr = getc( fp ) ) { default: ptr++; break; case EOF: bugf( "Fread_string: EOF" ); raise( SIGSEGV ); break; case '\n': case '\r': *ptr = '\0'; if( fBootDb ) { ptr = temp_hash_find( buf ); if( ptr ) return _str_dup( ptr, caller ); ptr = _str_dup( buf, caller ); temp_hash_add( ptr ); return ptr; } return _str_dup( buf, caller ); } } } /* * Read string into user supplied buffer. * Modified version of Furey's fread_string */ void temp_fread_string( FILE * fp, char *buf ) { char *ptr = buf; char c; do { c = getc( fp ); } while( isspace( c ) ); if( ( *ptr++ = c ) == '~' ) { *buf = '\0'; return; } for( ;; ) { switch ( *ptr = getc( fp ) ) { default: ptr++; break; case EOF: bugf( "Fread_string: EOF" ); raise( SIGSEGV ); break; case '\n': ptr++; *ptr++ = '\r'; break; case '\r': break; case '~': *ptr = '\0'; return; } } } /* Lookup the string in the boot-time hash table. */ char *temp_hash_find( const char *str ) { TempHash *ptr; int len; int ihash; if( !fBootDb || !*str ) return 0; len = strlen( str ); ihash = len % MAX_KEY_HASH; for( ptr = temp_string_hash[ihash]; ptr; ptr = ptr->next ) { if( *ptr->str != *str ) continue; else if( strcmp( ptr->str, str ) ) continue; else return ptr->str; } return 0; } /* * Add a reference in the temporary hash table. * String is still in the linked list structure but * reference is kept here for quick lookup at boot time; */ void temp_hash_add( char *str ) { int len; int ihash; TempHash *add; if( !fBootDb || !*str || ( str <= string_space && str >= top_string ) ) return; len = strlen( str ); ihash = len % MAX_KEY_HASH; add = ( TempHash * ) malloc( sizeof( TempHash ) ); add->next = temp_string_hash[ihash]; temp_string_hash[ihash] = add; add->len = len; add->str = str; } /* Free the temp boot string hash table */ void boot_done( void ) { TempHash *ptr, *next; int ihash; for( ihash = 0; ihash < MAX_KEY_HASH; ihash++ ) { for( ptr = temp_string_hash[ihash]; ptr; ptr = next ) { next = ptr->next; free( ptr ); } } free( temp_string_hash ); temp_string_hash = 0; /* Bug check in case someone accesses later */ }