quake4-sdk/source/idlib/Dict.cpp

822 lines
16 KiB
C++
Raw Permalink Normal View History

2007-06-15 00:00:00 +00:00
#include "precompiled.h"
#pragma hdrstop
idStrPool idDict::globalKeys;
idStrPool idDict::globalValues;
/*
================
idDict::operator=
clear existing key/value pairs and copy all key/value pairs from other
================
*/
idDict &idDict::operator=( const idDict &other ) {
int i;
// RAVEN BEGIN
// jnewquist: Tag scope and callees to track allocations using "new".
MEM_SCOPED_TAG(tag,MA_STRING);
// RAVEN END
// check for assignment to self
if ( this == &other ) {
return *this;
}
Clear();
args = other.args;
argHash = other.argHash;
for ( i = 0; i < args.Num(); i++ ) {
args[i].key = globalKeys.CopyString( args[i].key );
args[i].value = globalValues.CopyString( args[i].value );
}
return *this;
}
/*
================
idDict::Copy
copy all key value pairs without removing existing key/value pairs not present in the other dict
================
*/
void idDict::Copy( const idDict &other ) {
int i, n, *found;
idKeyValue kv;
// check for assignment to self
if ( this == &other ) {
return;
}
n = other.args.Num();
if ( args.Num() ) {
found = (int *) _alloca16( other.args.Num() * sizeof( int ) );
for ( i = 0; i < n; i++ ) {
found[i] = FindKeyIndex( other.args[i].GetKey() );
}
} else {
found = NULL;
}
for ( i = 0; i < n; i++ ) {
if ( found && found[i] != -1 ) {
// first set the new value and then free the old value to allow proper self copying
const idPoolStr *oldValue = args[found[i]].value;
args[found[i]].value = globalValues.CopyString( other.args[i].value );
globalValues.FreeString( oldValue );
} else {
kv.key = globalKeys.CopyString( other.args[i].key );
kv.value = globalValues.CopyString( other.args[i].value );
argHash.Add( argHash.GenerateKey( kv.GetKey(), false ), args.Append( kv ) );
}
}
}
/*
================
idDict::TransferKeyValues
clear existing key/value pairs and transfer key/value pairs from other
================
*/
void idDict::TransferKeyValues( idDict &other ) {
int i, n;
// RAVEN BEGIN
// jnewquist: Tag scope and callees to track allocations using "new".
MEM_SCOPED_TAG(tag,MA_STRING);
// RAVEN END
if ( this == &other ) {
return;
}
if ( other.args.Num() && other.args[0].key->GetPool() != &globalKeys ) {
common->FatalError( "idDict::TransferKeyValues: can't transfer values across a DLL boundary" );
return;
}
Clear();
n = other.args.Num();
args.SetNum( n );
for ( i = 0; i < n; i++ ) {
args[i].key = other.args[i].key;
args[i].value = other.args[i].value;
}
argHash = other.argHash;
other.args.Clear();
other.argHash.Free();
}
/*
================
idDict::Parse
================
*/
bool idDict::Parse( idParser &parser ) {
idToken token;
idToken token2;
bool errors;
errors = false;
parser.ExpectTokenString( "{" );
parser.ReadToken( &token );
while( ( token.type != TT_PUNCTUATION ) || ( token != "}" ) ) {
if ( token.type != TT_STRING ) {
parser.Error( "Expected quoted string, but found '%s'", token.c_str() );
}
if ( !parser.ReadToken( &token2 ) ) {
parser.Error( "Unexpected end of file" );
}
if ( FindKey( token ) ) {
parser.Warning( "'%s' already defined", token.c_str() );
errors = true;
}
Set( token, token2 );
if ( !parser.ReadToken( &token ) ) {
parser.Error( "Unexpected end of file" );
}
}
return !errors;
}
/*
================
idDict::SetDefaults
================
*/
void idDict::SetDefaults( const idDict *dict ) {
int i, n;
const idKeyValue *kv, *def;
idKeyValue newkv;
n = dict->args.Num();
for( i = 0; i < n; i++ ) {
def = &dict->args[i];
kv = FindKey( def->GetKey() );
if ( !kv ) {
newkv.key = globalKeys.CopyString( def->key );
newkv.value = globalValues.CopyString( def->value );
argHash.Add( argHash.GenerateKey( newkv.GetKey(), false ), args.Append( newkv ) );
}
}
}
/*
================
idDict::Clear
================
*/
void idDict::Clear( void ) {
int i;
for( i = 0; i < args.Num(); i++ ) {
globalKeys.FreeString( args[i].key );
globalValues.FreeString( args[i].value );
}
args.Clear();
argHash.Free();
}
/*
================
idDict::Print
================
*/
void idDict::Print() const {
int i;
int n;
n = args.Num();
for( i = 0; i < n; i++ ) {
idLib::common->Printf( "%s = %s\n", args[i].GetKey().c_str(), args[i].GetValue().c_str() );
}
}
int KeyCompare( const idKeyValue *a, const idKeyValue *b ) {
return idStr::Cmp( a->GetKey(), b->GetKey() );
}
/*
================
idDict::Checksum
================
*/
int idDict::Checksum( void ) const {
unsigned long ret;
int i, n;
idList<idKeyValue> sorted = args;
sorted.Sort( KeyCompare );
n = sorted.Num();
CRC32_InitChecksum( ret );
for( i = 0; i < n; i++ ) {
CRC32_UpdateChecksum( ret, sorted[i].GetKey().c_str(), sorted[i].GetKey().Length() );
CRC32_UpdateChecksum( ret, sorted[i].GetValue().c_str(), sorted[i].GetValue().Length() );
}
CRC32_FinishChecksum( ret );
return ret;
}
/*
================
idDict::Allocated
================
*/
size_t idDict::Allocated( void ) const {
int i;
size_t size;
size = args.Allocated() + argHash.Allocated();
for( i = 0; i < args.Num(); i++ ) {
size += args[i].Size();
}
return size;
}
/*
================
idDict::Set
================
*/
void idDict::Set( const char *key, const char *value ) {
int i;
idKeyValue kv;
// RAVEN BEGIN
// jnewquist: Tag scope and callees to track allocations using "new".
MEM_SCOPED_TAG(tag,MA_STRING);
// RAVEN END
if ( key == NULL || key[0] == '\0' ) {
return;
}
i = FindKeyIndex( key );
if ( i != -1 ) {
// first set the new value and then free the old value to allow proper self copying
const idPoolStr *oldValue = args[i].value;
args[i].value = globalValues.AllocString( value );
globalValues.FreeString( oldValue );
} else {
kv.key = globalKeys.AllocString( key );
kv.value = globalValues.AllocString( value );
argHash.Add( argHash.GenerateKey( kv.GetKey(), false ), args.Append( kv ) );
}
}
/*
================
idDict::GetFloat
================
*/
bool idDict::GetFloat( const char *key, const char *defaultString, float &out ) const {
const char *s;
bool found;
found = GetString( key, defaultString, &s );
out = atof( s );
return found;
}
/*
================
idDict::GetInt
================
*/
bool idDict::GetInt( const char *key, const char *defaultString, int &out ) const {
const char *s;
bool found;
found = GetString( key, defaultString, &s );
out = atoi( s );
return found;
}
/*
================
idDict::GetBool
================
*/
bool idDict::GetBool( const char *key, const char *defaultString, bool &out ) const {
const char *s;
bool found;
found = GetString( key, defaultString, &s );
out = ( atoi( s ) != 0 );
return found;
}
/*
================
idDict::GetAngles
================
*/
bool idDict::GetAngles( const char *key, const char *defaultString, idAngles &out ) const {
bool found;
const char *s;
if ( !defaultString ) {
defaultString = "0 0 0";
}
found = GetString( key, defaultString, &s );
out.Zero();
sscanf( s, "%f %f %f", &out.pitch, &out.yaw, &out.roll );
return found;
}
/*
================
idDict::GetVector
================
*/
bool idDict::GetVector( const char *key, const char *defaultString, idVec3 &out ) const {
bool found;
const char *s;
if ( !defaultString ) {
defaultString = "0 0 0";
}
found = GetString( key, defaultString, &s );
out.Zero();
sscanf( s, "%f %f %f", &out.x, &out.y, &out.z );
return found;
}
/*
================
idDict::GetVec2
================
*/
bool idDict::GetVec2( const char *key, const char *defaultString, idVec2 &out ) const {
bool found;
const char *s;
if ( !defaultString ) {
defaultString = "0 0";
}
found = GetString( key, defaultString, &s );
out.Zero();
sscanf( s, "%f %f", &out.x, &out.y );
return found;
}
/*
================
idDict::GetVec4
================
*/
bool idDict::GetVec4( const char *key, const char *defaultString, idVec4 &out ) const {
bool found;
const char *s;
if ( !defaultString ) {
defaultString = "0 0 0 0";
}
found = GetString( key, defaultString, &s );
out.Zero();
sscanf( s, "%f %f %f %f", &out.x, &out.y, &out.z, &out.w );
return found;
}
/*
================
idDict::GetMatrix
================
*/
bool idDict::GetMatrix( const char *key, const char *defaultString, idMat3 &out ) const {
const char *s;
bool found;
if ( !defaultString ) {
defaultString = "1 0 0 0 1 0 0 0 1";
}
found = GetString( key, defaultString, &s );
out.Identity(); // sccanf has a bug in it on Mac OS 9. Sigh.
sscanf( s, "%f %f %f %f %f %f %f %f %f", &out[0].x, &out[0].y, &out[0].z, &out[1].x, &out[1].y, &out[1].z, &out[2].x, &out[2].y, &out[2].z );
return found;
}
/*
================
WriteString
================
*/
static void WriteString( const char *s, idFile *f ) {
int len = strlen( s );
if ( len >= MAX_STRING_CHARS-1 ) {
idLib::common->Error( "idDict::WriteToFileHandle: bad string" );
}
f->Write( s, strlen(s) + 1 );
}
/*
================
idDict::FindKey
================
*/
const idKeyValue *idDict::FindKey( const char *key ) const {
int i, hash;
if ( key == NULL || key[0] == '\0' ) {
idLib::common->DWarning( "idDict::FindKey: empty key" );
return NULL;
}
hash = argHash.GenerateKey( key, false );
for ( i = argHash.First( hash ); i != -1; i = argHash.Next( i ) ) {
if ( args[i].GetKey().Icmp( key ) == 0 ) {
return &args[i];
}
}
return NULL;
}
/*
================
idDict::FindKeyIndex
================
*/
int idDict::FindKeyIndex( const char *key ) const {
if ( key == NULL || key[0] == '\0' ) {
idLib::common->DWarning( "idDict::FindKeyIndex: empty key" );
return NULL;
}
int hash = argHash.GenerateKey( key, false );
for ( int i = argHash.First( hash ); i != -1; i = argHash.Next( i ) ) {
if ( args[i].GetKey().Icmp( key ) == 0 ) {
return i;
}
}
return -1;
}
/*
================
idDict::Delete
================
*/
void idDict::Delete( const char *key ) {
int hash, i;
hash = argHash.GenerateKey( key, false );
for ( i = argHash.First( hash ); i != -1; i = argHash.Next( i ) ) {
if ( args[i].GetKey().Icmp( key ) == 0 ) {
globalKeys.FreeString( args[i].key );
globalValues.FreeString( args[i].value );
args.RemoveIndex( i );
argHash.RemoveIndex( hash, i );
break;
}
}
#if 0
// make sure all keys can still be found in the hash index
for ( i = 0; i < args.Num(); i++ ) {
assert( FindKey( args[i].GetKey() ) != NULL );
}
#endif
}
/*
================
idDict::MatchPrefix
================
*/
const idKeyValue *idDict::MatchPrefix( const char *prefix, const idKeyValue *lastMatch ) const {
int i;
int len;
int start;
assert( prefix );
len = strlen( prefix );
start = -1;
if ( lastMatch ) {
start = args.FindIndex( *lastMatch );
assert( start >= 0 );
if ( start < 1 ) {
start = 0;
}
}
for( i = start + 1; i < args.Num(); i++ ) {
if ( !args[i].GetKey().Icmpn( prefix, len ) ) {
return &args[i];
}
}
return NULL;
}
/*
================
idDict::RandomPrefix
================
*/
// RAVEN BEGIN
// abahr: added default value param
const char *idDict::RandomPrefix( const char *prefix, idRandom &random, const char* defaultValue ) const {
int count;
const int MAX_RANDOM_KEYS = 2048;
const char *list[MAX_RANDOM_KEYS];
const idKeyValue *kv;
// RAVEN BEGIN
// abahr: added defaultValue param
list[0] = defaultValue;
// RAVEN END
for ( count = 0, kv = MatchPrefix( prefix ); kv && count < MAX_RANDOM_KEYS; kv = MatchPrefix( prefix, kv ) ) {
list[count++] = kv->GetValue().c_str();
}
return list[random.RandomInt( count )];
}
/*
================
idDict::WriteToFileHandle
================
*/
void idDict::WriteToFileHandle( idFile *f ) const {
int c = LittleLong( args.Num() );
f->Write( &c, sizeof( c ) );
// RAVEN BEGIN
// jnewquist: For loop was looking at c, which got swapped on Xenon!
for ( int i = 0; i < args.Num(); i++ ) {
// RAVEN END
WriteString( args[i].GetKey().c_str(), f );
WriteString( args[i].GetValue().c_str(), f );
}
}
/*
================
ReadString
================
*/
static idStr ReadString( idFile *f ) {
char str[MAX_STRING_CHARS];
int len;
for ( len = 0; len < MAX_STRING_CHARS; len++ ) {
f->Read( (void *)&str[len], 1 );
if ( str[len] == 0 ) {
break;
}
}
if ( len == MAX_STRING_CHARS ) {
idLib::common->Error( "idDict::ReadFromFileHandle: bad string" );
}
return idStr( str );
}
/*
================
idDict::ReadFromFileHandle
================
*/
void idDict::ReadFromFileHandle( idFile *f ) {
int c;
idStr key, val;
Clear();
f->Read( &c, sizeof( c ) );
c = LittleLong( c );
for ( int i = 0; i < c; i++ ) {
key = ReadString( f );
val = ReadString( f );
Set( key, val );
}
}
// RAVEN BEGIN
/*
================
idDict::WriteToMemory
================
*/
int idDict::WriteToMemory( void *mem, int maxSize ) const {
int bytesWritten = 0;
char *out = (char*)mem;
if ( out ) {
*((int*)out) = args.Num();
out+=sizeof(int);
}
bytesWritten+=sizeof(int);
int l;
for ( int i = 0; i < args.Num(); i++ ) {
l = args[i].GetKey().Length();
++l;
if ( bytesWritten + l > maxSize ) {
assert( 0 );
return bytesWritten;
}
if ( out ) {
memcpy( out, args[i].GetKey().c_str(), l );
}
if ( out ) {
out+=l;
}
bytesWritten+=l;
l = args[i].GetValue().Length();
++l;
if ( bytesWritten + l > maxSize ) {
assert( 0 );
return bytesWritten;
}
if ( out ) {
memcpy( out, args[i].GetValue().c_str(), l );
}
if ( out ) {
out+=l;
}
bytesWritten+=l;
}
return bytesWritten;
}
/*
================
idDict::ReadFromMemory
================
*/
void idDict::ReadFromMemory( void *mem, int size ) {
int bytesRead = 0;
char *in = (char*)mem;
int c = *((int*)in);
in+=sizeof(int);
idStr key, val;
Clear();
int readLen;
for ( int i = 0; i < c; i++ ) {
key = in;
readLen = key.Length() + 1;
in+=readLen;
bytesRead+=readLen;
if ( bytesRead >= size ) {
assert( 0 );
return;
}
val = in;
readLen = val.Length() + 1;
in+=readLen;
bytesRead+=readLen;
if ( bytesRead >= size ) {
assert( 0 );
return;
}
Set( key, val );
}
}
// RAVEN END
/*
================
idDict::Init
================
*/
void idDict::Init( void ) {
globalKeys.SetCaseSensitive( false );
globalValues.SetCaseSensitive( true );
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
#if defined(_RV_MEM_SYS_SUPPORT)
globalKeys.SetAllocatorHeap(rvGetSysHeap(RV_HEAP_ID_PERMANENT));
globalValues.SetAllocatorHeap(rvGetSysHeap(RV_HEAP_ID_PERMANENT));
#endif
// RAVEN END
}
/*
================
idDict::Shutdown
================
*/
void idDict::Shutdown( void ) {
globalKeys.Clear();
globalValues.Clear();
}
/*
================
idDict::ShowMemoryUsage_f
================
*/
void idDict::ShowMemoryUsage_f( const idCmdArgs &args ) {
idLib::common->Printf( "%5d KB in %d keys\n", globalKeys.Size() >> 10, globalKeys.Num() );
idLib::common->Printf( "%5d KB in %d values\n", globalValues.Size() >> 10, globalValues.Num() );
}
/*
================
idDictStringSortCmp
================
*/
// NOTE: the const wonkyness is required to make msvc happy
template<>
ID_INLINE int idListSortCompare( const idPoolStr * const *a, const idPoolStr * const *b ) {
return (*a)->Icmp( **b );
}
/*
================
idDict::ListKeys_f
================
*/
void idDict::ListKeys_f( const idCmdArgs &args ) {
int i;
idList<const idPoolStr *> keyStrings;
for ( i = 0; i < globalKeys.Num(); i++ ) {
keyStrings.Append( globalKeys[i] );
}
keyStrings.Sort();
for ( i = 0; i < keyStrings.Num(); i++ ) {
idLib::common->Printf( "%s\n", keyStrings[i]->c_str() );
}
idLib::common->Printf( "%5d keys\n", keyStrings.Num() );
}
/*
================
idDict::ListValues_f
================
*/
void idDict::ListValues_f( const idCmdArgs &args ) {
int i;
idList<const idPoolStr *> valueStrings;
for ( i = 0; i < globalValues.Num(); i++ ) {
valueStrings.Append( globalValues[i] );
}
valueStrings.Sort();
for ( i = 0; i < valueStrings.Num(); i++ ) {
idLib::common->Printf( "%s\n", valueStrings[i]->c_str() );
}
idLib::common->Printf( "%5d values\n", valueStrings.Num() );
}
// RAVEN BEGIN
// jsinger: allow support for serialization/deserialization of binary decls
#ifdef RV_BINARYDECLS
void idDict::Write( SerialOutputStream &stream ) const
{
stream.Write(GetNumKeyVals());
for(int i=0;i<GetNumKeyVals();i++)
{
idKeyValue const *keyVal = GetKeyVal(i);
stream.Write(keyVal->GetKey());
stream.Write(keyVal->GetValue());
}
}
idDict::idDict( SerialInputStream &stream )
{
int numKeyVals = stream.ReadIntValue();
for(int i=0; i<numKeyVals; i++)
{
idStr key = stream.ReadStringValue();
idStr value = stream.ReadStringValue();
Set(key.c_str(), value.c_str());
}
}
#endif
// RAVEN END