1
0
mirror of https://github.com/alliedmodders/hl2sdk.git synced 2025-01-11 11:22:09 +08:00
Vladimir f3b44f206d
Update KeyValues3Context & its allocation lifecycle ()
Co-authored-by: GAMMACASE <31375974+GAMMACASE@users.noreply.github.com>
Co-authored-by: Nyano <dison2233@126.com>
2025-01-01 22:36:06 +03:00

1950 lines
45 KiB
C++

#include "keyvalues3.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// Nasty hack to redefine gcc's offsetof which doesn't like GET_OUTER macro
#ifdef COMPILER_GCC
#undef offsetof
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
KeyValues3::KeyValues3( KV3TypeEx_t type, KV3SubType_t subtype ) :
KeyValues3( -1, type, subtype )
{
}
KeyValues3::KeyValues3( int cluster_elem, KV3TypeEx_t type, KV3SubType_t subtype ) :
m_bExternalStorage( true ),
m_TypeEx( type ),
m_SubType( subtype ),
m_nFlags( 0 ),
m_nClusterElement( (uint16)-1 ),
m_nNumArrayElements( 0 ),
m_nReserved( 0 )
{
SetClusterElement( cluster_elem );
ResolveUnspecified();
Alloc();
}
KeyValues3::~KeyValues3()
{
Free();
};
void KeyValues3::Alloc( int initial_size, Data_t data, int preallocated_size, bool should_free )
{
switch ( GetTypeEx() )
{
case KV3_TYPEEX_ARRAY:
{
if(preallocated_size <= 0)
{
m_Data.m_pArray = AllocArray();
m_bFreeArrayMemory = true;
}
else
{
AllocArrayInPlace( initial_size, data, preallocated_size, should_free );
}
break;
}
case KV3_TYPEEX_TABLE:
{
if(preallocated_size <= 0)
{
m_Data.m_pTable = AllocTable();
m_bFreeArrayMemory = true;
}
else
{
AllocTableInPlace( initial_size, data, preallocated_size, should_free );
}
break;
}
case KV3_TYPEEX_ARRAY_FLOAT32:
case KV3_TYPEEX_ARRAY_FLOAT64:
case KV3_TYPEEX_ARRAY_INT16:
case KV3_TYPEEX_ARRAY_INT32:
case KV3_TYPEEX_ARRAY_UINT8_SHORT:
case KV3_TYPEEX_ARRAY_INT16_SHORT:
{
m_bFreeArrayMemory = false;
m_nNumArrayElements = 0;
m_Data.m_pMemory = NULL;
break;
}
default:
break;
}
}
void KeyValues3::AllocArrayInPlace( int initial_size, Data_t data, int preallocated_size, bool should_free )
{
int bytes_needed = MAX( CKeyValues3Array::TotalSizeOf( 0 ), CKeyValues3Array::TotalSizeOf( initial_size ) );
if(bytes_needed > preallocated_size)
{
Plat_FatalErrorFunc( "KeyValues3: pre-allocated array memory is too small for %u elements (%u bytes available, %u bytes needed)\n", initial_size, preallocated_size, bytes_needed );
DebuggerBreak();
}
Construct( data.m_pArray, -1, initial_size );
m_Data.m_pArray = data.m_pArray;
m_bFreeArrayMemory = should_free;
}
void KeyValues3::AllocTableInPlace( int initial_size, Data_t data, int preallocated_size, bool should_free )
{
int bytes_needed = MAX( CKeyValues3Array::TotalSizeOf( 0 ), CKeyValues3Array::TotalSizeOf( initial_size ) );
if(bytes_needed > preallocated_size)
{
Plat_FatalErrorFunc( "KeyValues3: pre-allocated table memory is too small for %u members (%u bytes available, %u bytes needed)\n", initial_size, preallocated_size, bytes_needed );
DebuggerBreak();
}
Construct( data.m_pTable, -1, initial_size );
m_Data.m_pTable = data.m_pTable;
m_bFreeArrayMemory = should_free;
}
CKeyValues3Array *KeyValues3::AllocArray( int initial_size )
{
auto context = GetContext();
if(context)
{
auto arr = context->AllocArray( initial_size );
if(arr)
return arr;
}
return AllocateOnHeap<CKeyValues3Array>( initial_size );
}
CKeyValues3Table* KeyValues3::AllocTable( int initial_size )
{
auto context = GetContext();
if(context)
{
auto table = context->AllocTable( initial_size );
if(table)
return table;
}
return AllocateOnHeap<CKeyValues3Table>( initial_size );
}
void KeyValues3::FreeArray( CKeyValues3Array *element, bool clearing_context )
{
if(!element)
return;
element->PurgeContent( this, clearing_context );
if(!m_bFreeArrayMemory)
{
Destruct( element );
}
else
{
auto context = GetContext();
bool raw_allocated = context && context->IsArrayRawAllocated( element );
if(!raw_allocated && element->GetClusterElement() < 0)
{
FreeOnHeap( element );
}
else if(!clearing_context)
{
if(!raw_allocated)
context->FreeArray( element );
else
Destruct( element );
}
}
}
void KeyValues3::FreeTable( CKeyValues3Table *element, bool clearing_context )
{
if(!element)
return;
element->PurgeContent( this, clearing_context );
if(!m_bFreeArrayMemory)
{
Destruct( element );
}
else
{
auto context = GetContext();
bool raw_allocated = context && context->IsTableRawAllocated( element );
if(!raw_allocated && element->GetClusterElement() < 0)
{
FreeOnHeap( element );
}
else if(!clearing_context)
{
if(!raw_allocated)
context->FreeTable( element );
else
Destruct( element );
}
}
}
KeyValues3 *KeyValues3::AllocMember( KV3TypeEx_t type, KV3SubType_t subtype )
{
auto context = GetContext();
if(context)
return context->AllocKV( type, subtype );
else
return new KeyValues3( type, subtype );
}
void KeyValues3::FreeMember( KeyValues3 *member )
{
auto context = GetContext();
if(context)
{
auto cluster = member->GetCluster();
cluster->PurgeMetaData( cluster->GetNodeIndex( member ) );
context->FreeKV( member );
}
else
{
delete member;
}
}
void KeyValues3::Free( bool bClearingContext )
{
switch ( GetTypeEx() )
{
case KV3_TYPEEX_STRING:
{
free( (void*)m_Data.m_pString );
m_Data.m_pString = nullptr;
break;
}
case KV3_TYPEEX_BINARY_BLOB:
{
if ( m_Data.m_pBinaryBlob )
free( m_Data.m_pBinaryBlob );
m_Data.m_pBinaryBlob = nullptr;
break;
}
case KV3_TYPEEX_BINARY_BLOB_EXTERN:
{
if ( m_Data.m_pBinaryBlob )
{
if ( m_Data.m_pBinaryBlob->m_bFreeMemory )
free( (void*)m_Data.m_pBinaryBlob->m_pubData );
free( m_Data.m_pBinaryBlob );
}
m_Data.m_pBinaryBlob = nullptr;
break;
}
case KV3_TYPEEX_ARRAY:
{
FreeArray( m_Data.m_pArray );
m_bFreeArrayMemory = false;
m_Data.m_pArray = nullptr;
break;
}
case KV3_TYPEEX_TABLE:
{
FreeTable( m_Data.m_pTable );
m_bFreeArrayMemory = false;
m_Data.m_pTable = nullptr;
break;
}
case KV3_TYPEEX_ARRAY_FLOAT32:
case KV3_TYPEEX_ARRAY_FLOAT64:
case KV3_TYPEEX_ARRAY_INT16:
case KV3_TYPEEX_ARRAY_INT32:
case KV3_TYPEEX_ARRAY_UINT8_SHORT:
case KV3_TYPEEX_ARRAY_INT16_SHORT:
{
if ( m_bFreeArrayMemory )
free( m_Data.m_pMemory );
m_bFreeArrayMemory = false;
m_nNumArrayElements = 0;
m_Data.m_nMemory = 0;
break;
}
default:
break;
}
}
void KeyValues3::ResolveUnspecified()
{
if ( GetSubType() == KV3_SUBTYPE_UNSPECIFIED )
{
switch ( GetType() )
{
case KV3_TYPE_NULL:
m_SubType = KV3_SUBTYPE_NULL;
break;
case KV3_TYPE_BOOL:
m_SubType = KV3_SUBTYPE_BOOL8;
break;
case KV3_TYPE_INT:
m_SubType = KV3_SUBTYPE_INT64;
break;
case KV3_TYPE_UINT:
m_SubType = KV3_SUBTYPE_UINT64;
break;
case KV3_TYPE_DOUBLE:
m_SubType = KV3_SUBTYPE_FLOAT64;
break;
case KV3_TYPE_STRING:
m_SubType = KV3_SUBTYPE_STRING;
break;
case KV3_TYPE_BINARY_BLOB:
m_SubType = KV3_SUBTYPE_BINARY_BLOB;
break;
case KV3_TYPE_ARRAY:
m_SubType = KV3_SUBTYPE_ARRAY;
break;
case KV3_TYPE_TABLE:
m_SubType = KV3_SUBTYPE_TABLE;
break;
default:
m_SubType = KV3_SUBTYPE_INVALID;
break;
}
}
}
void KeyValues3::PrepareForType( KV3TypeEx_t type, KV3SubType_t subtype )
{
if ( GetTypeEx() == type )
{
switch ( type )
{
case KV3_TYPEEX_STRING:
case KV3_TYPEEX_BINARY_BLOB:
case KV3_TYPEEX_BINARY_BLOB_EXTERN:
case KV3_TYPEEX_ARRAY_FLOAT32:
case KV3_TYPEEX_ARRAY_FLOAT64:
case KV3_TYPEEX_ARRAY_INT16:
case KV3_TYPEEX_ARRAY_INT32:
case KV3_TYPEEX_ARRAY_UINT8_SHORT:
case KV3_TYPEEX_ARRAY_INT16_SHORT:
{
Free();
break;
}
default:
break;
}
}
else
{
Free();
m_TypeEx = type;
Alloc();
}
m_SubType = subtype;
}
void KeyValues3::OnClearContext()
{
Free( true );
m_TypeEx = KV3_TYPEEX_NULL;
m_Data.m_nMemory = 0;
}
CKeyValues3Cluster* KeyValues3::GetCluster() const
{
if ( m_bExternalStorage )
return NULL;
return GET_OUTER( CKeyValues3Cluster, m_Values[ m_nClusterElement ] );
}
CKeyValues3Context* KeyValues3::GetContext() const
{
CKeyValues3Cluster* cluster = GetCluster();
if ( cluster )
return cluster->GetContext();
else
return NULL;
}
KV3MetaData_t* KeyValues3::GetMetaData( CKeyValues3Context** ppCtx ) const
{
CKeyValues3Cluster* cluster = GetCluster();
if ( cluster )
{
if ( ppCtx )
*ppCtx = cluster->GetContext();
return cluster->GetMetaData( m_nClusterElement );
}
else
{
if ( ppCtx )
*ppCtx = NULL;
return NULL;
}
}
const char* KeyValues3::GetString( const char* defaultValue ) const
{
switch ( GetTypeEx() )
{
case KV3_TYPEEX_STRING:
case KV3_TYPEEX_STRING_EXTERN:
return m_Data.m_pString;
case KV3_TYPEEX_STRING_SHORT:
return m_Data.m_szStringShort;
default:
return defaultValue;
}
}
void KeyValues3::SetString( const char* pString, KV3SubType_t subtype )
{
if ( !pString )
pString = "";
if ( strlen( pString ) < sizeof( m_Data.m_szStringShort ) )
{
PrepareForType( KV3_TYPEEX_STRING_SHORT, subtype );
V_strncpy( m_Data.m_szStringShort, pString, sizeof( m_Data.m_szStringShort ) );
}
else
{
PrepareForType( KV3_TYPEEX_STRING, subtype );
m_Data.m_pString = strdup( pString );
}
}
void KeyValues3::SetStringExternal( const char* pString, KV3SubType_t subtype )
{
if ( strlen( pString ) < sizeof( m_Data.m_szStringShort ) )
{
PrepareForType( KV3_TYPEEX_STRING_SHORT, subtype );
V_strncpy( m_Data.m_szStringShort, pString, sizeof( m_Data.m_szStringShort ) );
}
else
{
PrepareForType( KV3_TYPEEX_STRING_EXTERN, subtype );
m_Data.m_pString = pString;
}
}
const byte* KeyValues3::GetBinaryBlob() const
{
switch ( GetTypeEx() )
{
case KV3_TYPEEX_BINARY_BLOB:
return m_Data.m_pBinaryBlob ? m_Data.m_pBinaryBlob->m_ubData : NULL;
case KV3_TYPEEX_BINARY_BLOB_EXTERN:
return m_Data.m_pBinaryBlob ? m_Data.m_pBinaryBlob->m_pubData : NULL;
default:
return NULL;
}
}
int KeyValues3::GetBinaryBlobSize() const
{
if ( GetType() != KV3_TYPE_BINARY_BLOB || !m_Data.m_pBinaryBlob )
return 0;
return ( int )m_Data.m_pBinaryBlob->m_nSize;
}
void KeyValues3::SetToBinaryBlob( const byte* blob, int size )
{
PrepareForType( KV3_TYPEEX_BINARY_BLOB, KV3_SUBTYPE_BINARY_BLOB );
if ( size > 0 )
{
m_Data.m_pBinaryBlob = (KV3BinaryBlob_t*)malloc( sizeof( size_t ) + size );
m_Data.m_pBinaryBlob->m_nSize = size;
memcpy( m_Data.m_pBinaryBlob->m_ubData, blob, size );
}
else
{
m_Data.m_pBinaryBlob = NULL;
}
}
void KeyValues3::SetToBinaryBlobExternal( const byte* blob, int size, bool free_mem )
{
PrepareForType( KV3_TYPEEX_BINARY_BLOB_EXTERN, KV3_SUBTYPE_BINARY_BLOB );
if ( size > 0 )
{
m_Data.m_pBinaryBlob = (KV3BinaryBlob_t*)malloc( sizeof( KV3BinaryBlob_t ) );
m_Data.m_pBinaryBlob->m_nSize = size;
m_Data.m_pBinaryBlob->m_pubData = blob;
m_Data.m_pBinaryBlob->m_bFreeMemory = free_mem;
}
else
{
m_Data.m_pBinaryBlob = NULL;
}
}
Color KeyValues3::GetColor( const Color &defaultValue ) const
{
int32 color[4];
if ( ReadArrayInt32( 4, color ) )
{
return Color( color[0], color[1], color[2], color[3] );
}
else if ( ReadArrayInt32( 3, color ) )
{
return Color( color[0], color[1], color[2], 255 );
}
else
{
return defaultValue;
}
}
void KeyValues3::SetColor( const Color &color )
{
if ( color.a() == 255 )
AllocArray<uint8>( 3, &color[0], KV3_ARRAY_ALLOC_NORMAL, KV3_TYPEEX_ARRAY_UINT8_SHORT, KV3_TYPEEX_INVALID, KV3_SUBTYPE_COLOR32, KV3_TYPEEX_UINT, KV3_SUBTYPE_UINT8 );
else
AllocArray<uint8>( 4, &color[0], KV3_ARRAY_ALLOC_NORMAL, KV3_TYPEEX_ARRAY_UINT8_SHORT, KV3_TYPEEX_INVALID, KV3_SUBTYPE_COLOR32, KV3_TYPEEX_UINT, KV3_SUBTYPE_UINT8 );
}
int KeyValues3::GetArrayElementCount() const
{
if ( GetType() != KV3_TYPE_ARRAY )
return 0;
if ( GetTypeEx() == KV3_TYPEEX_ARRAY )
return m_Data.m_pArray->Count();
else
return m_nNumArrayElements;
}
KeyValues3** KeyValues3::GetArrayBase()
{
if ( GetTypeEx() != KV3_TYPEEX_ARRAY )
return NULL;
return m_Data.m_pArray->Base();
}
KeyValues3* KeyValues3::GetArrayElement( int elem )
{
if ( GetTypeEx() != KV3_TYPEEX_ARRAY )
return NULL;
if ( elem < 0 || elem >= m_Data.m_pArray->Count() )
return NULL;
return m_Data.m_pArray->Element( elem );
}
KeyValues3* KeyValues3::InsertArrayElementBefore( int elem )
{
if ( GetTypeEx() != KV3_TYPEEX_ARRAY )
return NULL;
return *m_Data.m_pArray->InsertMultipleBefore( this, elem, 1 );
}
KeyValues3* KeyValues3::AddArrayElementToTail()
{
if ( GetTypeEx() != KV3_TYPEEX_ARRAY )
return NULL;
return *m_Data.m_pArray->InsertMultipleBefore( this, m_Data.m_pArray->Count(), 1 );
}
void KeyValues3::SetArrayElementCount( int count, KV3TypeEx_t type, KV3SubType_t subtype )
{
if ( GetTypeEx() != KV3_TYPEEX_ARRAY )
return;
m_Data.m_pArray->SetCount( this, count, type, subtype );
}
void KeyValues3::RemoveArrayElements( int elem, int num )
{
if ( GetTypeEx() != KV3_TYPEEX_ARRAY )
return;
m_Data.m_pArray->RemoveMultiple( this, elem, num );
}
void KeyValues3::NormalizeArray()
{
switch ( GetTypeEx() )
{
case KV3_TYPEEX_ARRAY_FLOAT32:
{
NormalizeArray<float32>( KV3_TYPEEX_DOUBLE, KV3_SUBTYPE_FLOAT32, m_nNumArrayElements, m_Data.m_Array.m_f32, m_bFreeArrayMemory );
break;
}
case KV3_TYPEEX_ARRAY_FLOAT64:
{
NormalizeArray<float64>( KV3_TYPEEX_DOUBLE, KV3_SUBTYPE_FLOAT64, m_nNumArrayElements, m_Data.m_Array.m_f64, m_bFreeArrayMemory );
break;
}
case KV3_TYPEEX_ARRAY_INT16:
{
NormalizeArray<int16>( KV3_TYPEEX_INT, KV3_SUBTYPE_INT16, m_nNumArrayElements, m_Data.m_Array.m_i16, m_bFreeArrayMemory );
break;
}
case KV3_TYPEEX_ARRAY_INT32:
{
NormalizeArray<int32>( KV3_TYPEEX_INT, KV3_SUBTYPE_INT32, m_nNumArrayElements, m_Data.m_Array.m_i32, m_bFreeArrayMemory );
break;
}
case KV3_TYPEEX_ARRAY_UINT8_SHORT:
{
uint8 u8ArrayShort[8];
memcpy( u8ArrayShort, m_Data.m_Array.m_u8Short, sizeof( u8ArrayShort ) );
NormalizeArray<uint8>( KV3_TYPEEX_UINT, KV3_SUBTYPE_UINT8, m_nNumArrayElements, u8ArrayShort, false );
break;
}
case KV3_TYPEEX_ARRAY_INT16_SHORT:
{
int16 i16ArrayShort[4];
memcpy( i16ArrayShort, m_Data.m_Array.m_u8Short, sizeof( i16ArrayShort ) );
NormalizeArray<int16>( KV3_TYPEEX_INT, KV3_SUBTYPE_INT16, m_nNumArrayElements, i16ArrayShort, false );
break;
}
default:
break;
}
}
bool KeyValues3::ReadArrayInt32( int dest_size, int32* data ) const
{
int src_size = 0;
if ( GetType() == KV3_TYPE_STRING )
{
CSplitString values( GetString(), " " );
src_size = values.Count();
int count = MIN( src_size, dest_size );
for ( int i = 0; i < count; ++i )
data[ i ] = V_StringToInt32( values[ i ], 0, NULL, NULL, PARSING_FLAG_SKIP_ASSERT );
}
else
{
switch ( GetTypeEx() )
{
case KV3_TYPEEX_ARRAY:
{
src_size = m_Data.m_pArray->Count();
int count = MIN( src_size, dest_size );
KeyValues3** arr = m_Data.m_pArray->Base();
for ( int i = 0; i < count; ++i )
data[ i ] = arr[ i ]->GetInt();
break;
}
case KV3_TYPEEX_ARRAY_INT16:
{
src_size = m_nNumArrayElements;
int count = MIN( src_size, dest_size );
for ( int i = 0; i < count; ++i )
data[ i ] = ( int32 )m_Data.m_Array.m_u8Short[ i ];
break;
}
case KV3_TYPEEX_ARRAY_INT32:
{
src_size = m_nNumArrayElements;
int count = MIN( src_size, dest_size );
memcpy( data, m_Data.m_Array.m_i32, count * sizeof( int32 ) );
break;
}
case KV3_TYPEEX_ARRAY_UINT8_SHORT:
{
src_size = m_nNumArrayElements;
int count = MIN( src_size, dest_size );
for ( int i = 0; i < count; ++i )
data[ i ] = ( int32 )m_Data.m_Array.m_u8Short[ i ];
break;
}
case KV3_TYPEEX_ARRAY_INT16_SHORT:
{
src_size = m_nNumArrayElements;
int count = MIN( src_size, dest_size );
for ( int i = 0; i < count; ++i )
data[ i ] = ( int32 )m_Data.m_Array.m_u8Short[ i ];
break;
}
default:
break;
}
}
if ( src_size < dest_size )
memset( &data[ src_size ], 0, ( dest_size - src_size ) * sizeof( int32 ) );
return ( src_size == dest_size );
}
bool KeyValues3::ReadArrayFloat32( int dest_size, float32* data ) const
{
int src_size = 0;
if ( GetType() == KV3_TYPE_STRING )
{
CSplitString values( GetString(), " " );
src_size = values.Count();
int count = MIN( src_size, dest_size );
for ( int i = 0; i < count; ++i )
data[ i ] = ( float32 )V_StringToFloat64( values[ i ], 0, NULL, NULL, PARSING_FLAG_SKIP_ASSERT );
}
else
{
switch ( GetTypeEx() )
{
case KV3_TYPEEX_ARRAY:
{
src_size = m_Data.m_pArray->Count();
int count = MIN( src_size, dest_size );
KeyValues3** arr = m_Data.m_pArray->Base();
for ( int i = 0; i < count; ++i )
data[ i ] = arr[ i ]->GetFloat();
break;
}
case KV3_TYPEEX_ARRAY_FLOAT32:
{
src_size = m_nNumArrayElements;
int count = MIN( src_size, dest_size );
memcpy( data, m_Data.m_Array.m_f32, count * sizeof( float32 ) );
break;
}
case KV3_TYPEEX_ARRAY_FLOAT64:
{
src_size = m_nNumArrayElements;
int count = MIN( src_size, dest_size );
for ( int i = 0; i < count; ++i )
data[ i ] = ( float32 )m_Data.m_Array.m_f64[ i ];
break;
}
default:
break;
}
}
if ( src_size < dest_size )
memset( &data[ src_size ], 0, ( dest_size - src_size ) * sizeof( float32 ) );
return ( src_size == dest_size );
}
int KeyValues3::GetMemberCount() const
{
if ( GetType() != KV3_TYPE_TABLE )
return 0;
return m_Data.m_pTable->GetMemberCount();
}
KeyValues3* KeyValues3::GetMember( KV3MemberId_t id )
{
if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_Data.m_pTable->GetMemberCount() )
return NULL;
return m_Data.m_pTable->GetMember( id );
}
const char* KeyValues3::GetMemberName( KV3MemberId_t id ) const
{
if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_Data.m_pTable->GetMemberCount() )
return NULL;
return m_Data.m_pTable->GetMemberName( id );
}
CKV3MemberName KeyValues3::GetMemberNameEx( KV3MemberId_t id ) const
{
if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_Data.m_pTable->GetMemberCount() )
return CKV3MemberName();
return CKV3MemberName( m_Data.m_pTable->GetMemberHash( id ), m_Data.m_pTable->GetMemberName( id ) );
}
unsigned int KeyValues3::GetMemberHash( KV3MemberId_t id ) const
{
if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_Data.m_pTable->GetMemberCount() )
return 0;
return m_Data.m_pTable->GetMemberHash( id );
}
KeyValues3* KeyValues3::FindMember( const CKV3MemberName &name, KeyValues3* defaultValue )
{
if ( GetType() != KV3_TYPE_TABLE )
return defaultValue;
KV3MemberId_t id = m_Data.m_pTable->FindMember( name );
if ( id == KV3_INVALID_MEMBER )
return defaultValue;
return m_Data.m_pTable->GetMember( id );
}
KeyValues3* KeyValues3::FindOrCreateMember( const CKV3MemberName &name, bool *pCreated )
{
if ( GetType() != KV3_TYPE_TABLE )
PrepareForType( KV3_TYPEEX_TABLE, KV3_SUBTYPE_TABLE );
KV3MemberId_t id = m_Data.m_pTable->FindMember( name );
if ( id == KV3_INVALID_MEMBER )
{
if ( pCreated )
*pCreated = true;
id = m_Data.m_pTable->CreateMember( this, name );
}
else
{
if ( pCreated )
*pCreated = false;
}
return m_Data.m_pTable->GetMember( id );
}
void KeyValues3::SetToEmptyTable()
{
PrepareForType( KV3_TYPEEX_TABLE, KV3_SUBTYPE_TABLE );
m_Data.m_pTable->RemoveAll( this );
}
bool KeyValues3::RemoveMember( KV3MemberId_t id )
{
if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_Data.m_pTable->GetMemberCount() )
return false;
m_Data.m_pTable->RemoveMember( this, id );
return true;
}
bool KeyValues3::RemoveMember( const KeyValues3* kv )
{
if ( GetType() != KV3_TYPE_TABLE )
return false;
KV3MemberId_t id = m_Data.m_pTable->FindMember( kv );
if ( id == KV3_INVALID_MEMBER )
return false;
m_Data.m_pTable->RemoveMember( this, id );
return true;
}
bool KeyValues3::RemoveMember( const CKV3MemberName &name )
{
if ( GetType() != KV3_TYPE_TABLE )
return false;
KV3MemberId_t id = m_Data.m_pTable->FindMember( name );
if ( id == KV3_INVALID_MEMBER )
return false;
m_Data.m_pTable->RemoveMember( this, id );
return true;
}
const char* KeyValues3::GetTypeAsString() const
{
static const char* s_Types[] =
{
"invalid",
"null",
"bool",
"int",
"uint",
"double",
"string",
"binary_blob",
"array",
"table",
NULL
};
KV3Type_t type = GetType();
if ( type < KV3_TYPE_COUNT )
return s_Types[type];
return "<unknown>";
}
const char* KeyValues3::GetSubTypeAsString() const
{
static const char* s_SubTypes[] =
{
"invalid",
"resource",
"resource_name",
"panorama",
"soundevent",
"subclass",
"entity_name",
"localize",
"unspecified",
"null",
"binary_blob",
"array",
"table",
"bool8",
"char8",
"uchar32",
"int8",
"uint8",
"int16",
"uint16",
"int32",
"uint32",
"int64",
"uint64",
"float32",
"float64",
"string",
"pointer",
"color32",
"vector",
"vector2d",
"vector4d",
"rotation_vector",
"quaternion",
"qangle",
"matrix3x4",
"transform",
"string_token",
"ehandle",
NULL
};
KV3SubType_t subtype = GetSubType();
if ( subtype < KV3_SUBTYPE_COUNT )
return s_SubTypes[subtype];
return "<unknown>";
}
const char* KeyValues3::ToString( CBufferString& buff, uint flags ) const
{
if ( ( flags & KV3_TO_STRING_DONT_CLEAR_BUFF ) != 0 )
flags &= ~KV3_TO_STRING_DONT_APPEND_STRINGS;
else
buff.ToGrowable()->Clear();
KV3Type_t type = GetType();
switch ( type )
{
case KV3_TYPE_NULL:
{
return buff.ToGrowable()->Get();
}
case KV3_TYPE_BOOL:
{
const char* str = m_Data.m_Bool ? "true" : "false";
if ( ( flags & KV3_TO_STRING_DONT_APPEND_STRINGS ) != 0 )
return str;
buff.Insert( buff.ToGrowable()->GetTotalNumber(), str );
return buff.ToGrowable()->Get();
}
case KV3_TYPE_INT:
{
buff.AppendFormat( "%lld", m_Data.m_Int );
return buff.ToGrowable()->Get();
}
case KV3_TYPE_UINT:
{
if ( GetSubType() == KV3_SUBTYPE_POINTER )
{
if ( ( flags & KV3_TO_STRING_APPEND_ONLY_NUMERICS ) == 0 )
buff.Insert( buff.ToGrowable()->GetTotalNumber(), "<pointer>" );
if ( ( flags & KV3_TO_STRING_RETURN_NON_NUMERICS ) == 0 )
return NULL;
return buff.ToGrowable()->Get();
}
buff.AppendFormat( "%llu", m_Data.m_UInt );
return buff.ToGrowable()->Get();
}
case KV3_TYPE_DOUBLE:
{
buff.AppendFormat( "%g", m_Data.m_Double );
return buff.ToGrowable()->Get();
}
case KV3_TYPE_STRING:
{
const char* str = GetString();
if ( ( flags & KV3_TO_STRING_DONT_APPEND_STRINGS ) != 0 )
return str;
buff.Insert( buff.ToGrowable()->GetTotalNumber(), str );
return buff.ToGrowable()->Get();
}
case KV3_TYPE_BINARY_BLOB:
{
if ( ( flags & KV3_TO_STRING_APPEND_ONLY_NUMERICS ) == 0 )
buff.AppendFormat( "<binary blob: %u bytes>", GetBinaryBlobSize() );
if ( ( flags & KV3_TO_STRING_RETURN_NON_NUMERICS ) == 0 )
return NULL;
return buff.ToGrowable()->Get();
}
case KV3_TYPE_ARRAY:
{
int elements = GetArrayElementCount();
if ( elements > 0 && elements <= 4 )
{
switch ( GetTypeEx() )
{
case KV3_TYPEEX_ARRAY:
{
bool unprintable = false;
CBufferStringGrowable<128> temp;
CKeyValues3Array::Element_t* arr = m_Data.m_pArray->Base();
for ( int i = 0; i < elements; ++i )
{
switch ( arr[i]->GetType() )
{
case KV3_TYPE_INT:
temp.AppendFormat( "%lld", arr[i]->m_Data.m_Int );
break;
case KV3_TYPE_UINT:
if ( arr[i]->GetSubType() == KV3_SUBTYPE_POINTER )
unprintable = true;
else
temp.AppendFormat( "%llu", arr[i]->m_Data.m_UInt );
break;
case KV3_TYPE_DOUBLE:
temp.AppendFormat( "%g", arr[i]->m_Data.m_Double );
break;
default:
unprintable = true;
break;
}
if ( unprintable )
break;
if ( i != elements - 1 ) temp.Insert( temp.ToGrowable()->GetTotalNumber(), " " );
}
if ( unprintable )
break;
buff.Insert( buff.ToGrowable()->GetTotalNumber(), temp.Get() );
return buff.ToGrowable()->Get();
}
case KV3_TYPEEX_ARRAY_FLOAT32:
{
for ( int i = 0; i < elements; ++i )
{
buff.AppendFormat( "%g", m_Data.m_Array.m_f32[i] );
if ( i != elements - 1 ) buff.Insert( buff.ToGrowable()->GetTotalNumber(), " " );
}
return buff.ToGrowable()->Get();
}
case KV3_TYPEEX_ARRAY_FLOAT64:
{
for ( int i = 0; i < elements; ++i )
{
buff.AppendFormat( "%g", m_Data.m_Array.m_f64[i] );
if ( i != elements - 1 ) buff.Insert( buff.ToGrowable()->GetTotalNumber(), " " );
}
return buff.ToGrowable()->Get();
}
case KV3_TYPEEX_ARRAY_INT16:
{
for ( int i = 0; i < elements; ++i )
{
buff.AppendFormat( "%d", m_Data.m_Array.m_i16Short[i] );
if ( i != elements - 1 ) buff.Insert( buff.ToGrowable()->GetTotalNumber(), " " );
}
return buff.ToGrowable()->Get();
}
case KV3_TYPEEX_ARRAY_INT32:
{
for ( int i = 0; i < elements; ++i )
{
buff.AppendFormat( "%d", m_Data.m_Array.m_i32[i] );
if ( i != elements - 1 ) buff.Insert( buff.ToGrowable()->GetTotalNumber(), " " );
}
return buff.ToGrowable()->Get();
}
case KV3_TYPEEX_ARRAY_UINT8_SHORT:
{
for ( int i = 0; i < elements; ++i )
{
buff.AppendFormat( "%u", m_Data.m_Array.m_u8Short[i] );
if ( i != elements - 1 ) buff.Insert( buff.ToGrowable()->GetTotalNumber(), " " );
}
return buff.ToGrowable()->Get();
}
case KV3_TYPEEX_ARRAY_INT16_SHORT:
{
for ( int i = 0; i < elements; ++i )
{
buff.AppendFormat( "%d", m_Data.m_Array.m_i16Short[i] );
if ( i != elements - 1 ) buff.Insert( buff.ToGrowable()->GetTotalNumber(), " " );
}
return buff.ToGrowable()->Get();
}
default:
break;
}
}
if ( ( flags & KV3_TO_STRING_APPEND_ONLY_NUMERICS ) == 0 )
buff.AppendFormat( "<array: %u elements>", elements );
if ( ( flags & KV3_TO_STRING_RETURN_NON_NUMERICS ) == 0 )
return NULL;
return buff.ToGrowable()->Get();
}
case KV3_TYPE_TABLE:
{
if ( ( flags & KV3_TO_STRING_APPEND_ONLY_NUMERICS ) == 0 )
buff.AppendFormat( "<table: %u members>", GetMemberCount() );
if ( ( flags & KV3_TO_STRING_RETURN_NON_NUMERICS ) == 0 )
return NULL;
return buff.ToGrowable()->Get();
}
default:
{
if ( ( flags & KV3_TO_STRING_APPEND_ONLY_NUMERICS ) == 0 )
buff.AppendFormat( "<unknown KV3 basic type '%s' (%d)>", GetTypeAsString(), type );
if ( ( flags & KV3_TO_STRING_RETURN_NON_NUMERICS ) == 0 )
return NULL;
return buff.ToGrowable()->Get();
}
}
}
void KeyValues3::CopyFrom( const KeyValues3* pSrc )
{
if(this == pSrc)
return;
SetToNull();
CKeyValues3Context* context;
KV3MetaData_t* pDestMetaData = GetMetaData( &context );
if ( pDestMetaData )
{
KV3MetaData_t* pSrcMetaData = pSrc->GetMetaData();
if ( pSrcMetaData )
context->CopyMetaData( pDestMetaData, pSrcMetaData );
else
pDestMetaData->Clear();
}
KV3SubType_t eSrcSubType = pSrc->GetSubType();
switch ( pSrc->GetType() )
{
case KV3_TYPE_BOOL:
SetBool( pSrc->m_Data.m_Bool );
break;
case KV3_TYPE_INT:
SetValue<int64>( pSrc->m_Data.m_Int, KV3_TYPEEX_INT, eSrcSubType );
break;
case KV3_TYPE_UINT:
SetValue<uint64>( pSrc->m_Data.m_UInt, KV3_TYPEEX_UINT, eSrcSubType );
break;
case KV3_TYPE_DOUBLE:
SetValue<float64>( pSrc->m_Data.m_Double, KV3_TYPEEX_DOUBLE, eSrcSubType );
break;
case KV3_TYPE_STRING:
SetString( pSrc->GetString(), eSrcSubType );
break;
case KV3_TYPE_BINARY_BLOB:
SetToBinaryBlob( pSrc->GetBinaryBlob(), pSrc->GetBinaryBlobSize() );
break;
case KV3_TYPE_ARRAY:
{
switch ( pSrc->GetTypeEx() )
{
case KV3_TYPEEX_ARRAY:
{
PrepareForType( KV3_TYPEEX_ARRAY, KV3_SUBTYPE_ARRAY );
m_Data.m_pArray->CopyFrom( this, pSrc->m_Data.m_pArray );
break;
}
case KV3_TYPEEX_ARRAY_FLOAT32:
AllocArray<float32>( pSrc->m_nNumArrayElements, pSrc->m_Data.m_Array.m_f32, KV3_ARRAY_ALLOC_NORMAL, KV3_TYPEEX_INVALID, KV3_TYPEEX_ARRAY_FLOAT32, eSrcSubType, KV3_TYPEEX_DOUBLE, KV3_SUBTYPE_FLOAT32 );
break;
case KV3_TYPEEX_ARRAY_FLOAT64:
AllocArray<float64>( pSrc->m_nNumArrayElements, pSrc->m_Data.m_Array.m_f64, KV3_ARRAY_ALLOC_NORMAL, KV3_TYPEEX_INVALID, KV3_TYPEEX_ARRAY_FLOAT64, eSrcSubType, KV3_TYPEEX_DOUBLE, KV3_SUBTYPE_FLOAT64 );
break;
case KV3_TYPEEX_ARRAY_INT16:
AllocArray<int16>( pSrc->m_nNumArrayElements, pSrc->m_Data.m_Array.m_i16, KV3_ARRAY_ALLOC_NORMAL, KV3_TYPEEX_ARRAY_INT16_SHORT, KV3_TYPEEX_ARRAY_INT16, eSrcSubType, KV3_TYPEEX_INT, KV3_SUBTYPE_INT16 );
break;
case KV3_TYPEEX_ARRAY_INT32:
AllocArray<int32>( pSrc->m_nNumArrayElements, pSrc->m_Data.m_Array.m_i32, KV3_ARRAY_ALLOC_NORMAL, KV3_TYPEEX_INVALID, KV3_TYPEEX_ARRAY_INT32, eSrcSubType, KV3_TYPEEX_INT, KV3_SUBTYPE_INT32 );
break;
case KV3_TYPEEX_ARRAY_UINT8_SHORT:
AllocArray<uint8>( pSrc->m_nNumArrayElements, pSrc->m_Data.m_Array.m_u8Short, KV3_ARRAY_ALLOC_NORMAL, KV3_TYPEEX_ARRAY_UINT8_SHORT, KV3_TYPEEX_INVALID, eSrcSubType, KV3_TYPEEX_UINT, KV3_SUBTYPE_UINT8 );
break;
case KV3_TYPEEX_ARRAY_INT16_SHORT:
AllocArray<int16>( pSrc->m_nNumArrayElements, pSrc->m_Data.m_Array.m_i16Short, KV3_ARRAY_ALLOC_NORMAL, KV3_TYPEEX_ARRAY_INT16_SHORT, KV3_TYPEEX_ARRAY_INT16, eSrcSubType, KV3_TYPEEX_INT, KV3_SUBTYPE_INT16 );
break;
default:
break;
}
break;
}
case KV3_TYPE_TABLE:
{
SetToEmptyTable();
m_Data.m_pTable->CopyFrom( this, pSrc->m_Data.m_pTable );
break;
}
default:
break;
}
m_SubType = eSrcSubType;
m_nFlags = pSrc->m_nFlags;
}
KeyValues3& KeyValues3::operator=( const KeyValues3& src )
{
if ( this == &src )
return *this;
CopyFrom( &src );
return *this;
}
CKeyValues3Array::CKeyValues3Array( int cluster_elem, int alloc_size ) :
m_nClusterElement( cluster_elem ),
m_nAllocatedChunks( alloc_size ),
m_nCount( 0 ),
m_nInitialSize( MIN( alloc_size, 255 ) ),
m_bIsDynamicallySized( false ),
m_unk001( false ),
m_unk002( false ),
m_pDynamicElements( nullptr )
{
}
CKeyValues3ArrayCluster* CKeyValues3Array::GetCluster() const
{
if ( m_nClusterElement == -1 )
return NULL;
return GET_OUTER( CKeyValues3ArrayCluster, m_Values[ m_nClusterElement ] );
}
CKeyValues3Context* CKeyValues3Array::GetContext() const
{
CKeyValues3ArrayCluster* cluster = GetCluster();
if ( cluster )
return cluster->GetContext();
else
return NULL;
}
KeyValues3* CKeyValues3Array::Element( int i )
{
Assert( 0 <= i && i < m_nCount );
return Base()[i];
}
void CKeyValues3Array::EnsureElementCapacity( int count, bool force, bool dont_move )
{
if(count <= m_nAllocatedChunks)
return;
if(count > ALLOC_KV3ARRAY_MAX)
{
Plat_FatalErrorFunc( "%s: element count overflow (%u)\n", __FUNCTION__, count );
DebuggerBreak();
}
const int new_count = force ? count : KV3Helpers::CalcNewBufferSize( m_nAllocatedChunks, count, ALLOC_KV3ARRAY_MIN, ALLOC_KV3ARRAY_MAX );
const int new_byte_size = TotalSizeOfData( new_count );
Element_t *new_base = nullptr;
if(m_bIsDynamicallySized)
{
new_base = (Element_t *)realloc( m_pDynamicElements, new_byte_size );
}
else
{
new_base = (Element_t *)malloc( new_byte_size );
if(m_nCount > 0 && !dont_move)
{
memmove( new_base, Base(), sizeof( Element_t ) * m_nCount );
}
}
m_pDynamicElements = new_base;
m_nAllocatedChunks = new_count;
m_bIsDynamicallySized = true;
}
void CKeyValues3Array::SetCount( KeyValues3 *parent, int count, KV3TypeEx_t type, KV3SubType_t subtype )
{
Element_t *elements_base = Base();
for(int i = count; i < m_nCount; i++)
{
parent->FreeMember( elements_base[i] );
}
EnsureElementCapacity( count );
elements_base = Base();
for(int i = m_nCount; i < count; i++)
{
elements_base[i] = parent->AllocMember( type, subtype );
}
m_nCount = count;
}
CKeyValues3Array::Element_t* CKeyValues3Array::InsertMultipleBefore( KeyValues3 *parent, int from, int num )
{
if(from < 0 || from > m_nCount)
{
Plat_FatalErrorFunc( "%s: invalid insert point %u (current count %u)\n", __FUNCTION__, from, m_nCount );
DebuggerBreak();
}
if(num > ALLOC_KV3ARRAY_MAX - m_nCount)
{
Plat_FatalErrorFunc( "%s: max element overflow, cur count %u + %u\n", __FUNCTION__, m_nCount, num );
DebuggerBreak();
}
int new_size = m_nCount + num;
EnsureElementCapacity( new_size );
Element_t *base = Base();
if(from < m_nCount)
{
memmove( (void *)base[from + num], (void *)base[from], sizeof(Element_t) * (m_nCount - from) );
}
for ( int i = 0; i < num; ++i )
{
base[from + i] = parent->AllocMember();
}
m_nCount = new_size;
return base;
}
void CKeyValues3Array::CopyFrom( KeyValues3 *parent, const CKeyValues3Array* pSrc )
{
int nNewSize = pSrc->Count();
SetCount( parent, nNewSize );
Element_t *base = Base();
Element_t const *pSrcKV = pSrc->Base();
for ( int i = 0; i < nNewSize; ++i )
*base[i] = *pSrcKV[i];
}
void CKeyValues3Array::RemoveMultiple( KeyValues3 *parent, int from, int num )
{
Element_t *base = Base();
for ( int i = 0; i <= num; ++i )
{
parent->FreeMember( base[from + i] );
}
m_nCount -= num;
}
void CKeyValues3Array::PurgeBuffers()
{
if(m_bIsDynamicallySized)
{
free( m_pDynamicElements );
m_nAllocatedChunks = m_nInitialSize;
m_bIsDynamicallySized = false;
}
m_nCount = 0;
}
void CKeyValues3Array::PurgeContent( KeyValues3 *parent, bool clearing_context )
{
if(!clearing_context && parent)
{
auto elements_base = Base();
for(int i = 0; i < m_nCount; i++)
{
parent->FreeMember( elements_base[i] );
}
}
}
CKeyValues3Table::CKeyValues3Table( int cluster_elem, int alloc_size ) :
m_nClusterElement( cluster_elem ),
m_nAllocatedChunks( alloc_size ),
m_pFastSearch( nullptr ),
m_nCount( 0 ),
m_nInitialSize( MIN( alloc_size, 255 ) ),
m_bIsDynamicallySized( false ),
m_unk001( false ),
m_unk002( false ),
m_pDynamicBuffer( nullptr )
{
}
CKeyValues3TableCluster* CKeyValues3Table::GetCluster() const
{
if ( m_nClusterElement == -1 )
return NULL;
return GET_OUTER( CKeyValues3TableCluster, m_Values[ m_nClusterElement ] );
}
CKeyValues3Context* CKeyValues3Table::GetContext() const
{
CKeyValues3TableCluster* cluster = GetCluster();
if ( cluster )
return cluster->GetContext();
else
return NULL;
}
KeyValues3* CKeyValues3Table::GetMember( KV3MemberId_t id )
{
Assert( 0 <= id && id < m_nCount );
return MembersBase()[id];
}
const CKeyValues3Table::Name_t CKeyValues3Table::GetMemberName( KV3MemberId_t id ) const
{
Assert( 0 <= id && id < m_nCount );
return NamesBase()[id];
}
const CKeyValues3Table::Hash_t CKeyValues3Table::GetMemberHash( KV3MemberId_t id ) const
{
Assert( 0 <= id && id < m_nCount );
return HashesBase()[id];
}
void CKeyValues3Table::EnableFastSearch()
{
if ( m_pFastSearch )
m_pFastSearch->m_member_ids.RemoveAll();
else
m_pFastSearch = new kv3tablefastsearch_t;
const Hash_t* pHashes = HashesBase();
for ( int i = 0; i < m_nCount; ++i )
{
m_pFastSearch->m_member_ids.Insert( pHashes[i], i );
}
m_pFastSearch->m_ignore = false;
m_pFastSearch->m_ignores_counter = 0;
}
void CKeyValues3Table::EnsureMemberCapacity( int count, bool force, bool dont_move )
{
if(count <= m_nAllocatedChunks)
return;
if(count > ALLOC_KV3TABLE_MAX)
{
Plat_FatalErrorFunc( "%s member count overflow (%u)\n", __FUNCTION__, count );
DebuggerBreak();
}
const int new_count = force ? count : KV3Helpers::CalcNewBufferSize( m_nAllocatedChunks, count, ALLOC_KV3TABLE_MIN, ALLOC_KV3TABLE_MAX );
const int new_byte_size = TotalSizeOfData( new_count );
void *new_base = nullptr;
if(m_bIsDynamicallySized)
{
new_base = realloc( m_pDynamicBuffer, new_byte_size );
memmove( (uint8 *)new_base + OffsetToFlagsBase( new_count ), FlagsBase(), m_nCount * sizeof( Flags_t ) );
memmove( (uint8 *)new_base + OffsetToNamesBase( new_count ), NamesBase(), m_nCount * sizeof( Name_t ) );
memmove( (uint8 *)new_base + OffsetToMembersBase( new_count ), MembersBase(), m_nCount * sizeof( Member_t ) );
}
else
{
new_base = malloc( new_byte_size );
if(m_nCount > 0 && !dont_move)
{
memmove( (uint8 *)new_base + OffsetToHashesBase( new_count ), HashesBase(), m_nCount * sizeof( Hash_t ) );
memmove( (uint8 *)new_base + OffsetToMembersBase( new_count ), MembersBase(), m_nCount * sizeof( Member_t ) );
memmove( (uint8 *)new_base + OffsetToNamesBase( new_count ), NamesBase(), m_nCount * sizeof( Name_t ) );
memmove( (uint8 *)new_base + OffsetToFlagsBase( new_count ), FlagsBase(), m_nCount * sizeof( Flags_t ) );
}
}
m_pDynamicBuffer = new_base;
m_nAllocatedChunks = new_count;
m_bIsDynamicallySized = true;
}
KV3MemberId_t CKeyValues3Table::FindMember( const KeyValues3* kv ) const
{
const Member_t* pMembers = MembersBase();
for ( int i = 0; i < m_nCount; ++i )
{
if ( pMembers[i] == kv )
return i;
}
return KV3_INVALID_MEMBER;
}
KV3MemberId_t CKeyValues3Table::FindMember( const CKV3MemberName &name )
{
bool bFastSearch = false;
if ( m_pFastSearch )
{
if ( m_pFastSearch->m_ignore )
{
if ( ++m_pFastSearch->m_ignores_counter > 4 )
{
EnableFastSearch();
bFastSearch = true;
}
}
else
{
bFastSearch = true;
}
}
if ( bFastSearch )
{
UtlHashHandle_t h = m_pFastSearch->m_member_ids.Find( name.GetHashCode() );
if ( h != m_pFastSearch->m_member_ids.InvalidHandle() )
return m_pFastSearch->m_member_ids[ h ];
}
else
{
const Hash_t* pHashes = HashesBase();
for ( int i = 0; i < m_nCount; ++i )
{
if ( pHashes[i] == name.GetHashCode() )
return i;
}
}
return KV3_INVALID_MEMBER;
}
KV3MemberId_t CKeyValues3Table::CreateMember( KeyValues3 *parent, const CKV3MemberName &name, bool name_external )
{
if ( GetMemberCount() >= 128 && !m_pFastSearch )
EnableFastSearch();
KV3MemberId_t curr = m_nCount;
int new_size = m_nCount + 1;
EnsureMemberCapacity( new_size );
Hash_t *hashes_base = HashesBase();
Member_t *members_base = MembersBase();
Name_t *names_base = NamesBase();
Flags_t *flags_base = FlagsBase();
members_base[curr] = parent->AllocMember();
hashes_base[curr] = name.GetHashCode();
Flags_t flags = TABLEFL_NONE;
if(name_external)
{
names_base[curr] = name.GetString();
flags |= TABLEFL_NAME_EXTERNAL;
}
else
{
auto context = parent->GetContext();
if(context)
names_base[curr] = context->AllocString( name.GetString() );
else
names_base[curr] = strdup( name.GetString() );
}
flags_base[curr] = flags;
if ( m_pFastSearch && !m_pFastSearch->m_ignore )
m_pFastSearch->m_member_ids.Insert( name.GetHashCode(), curr );
m_nCount = new_size;
return curr;
}
void CKeyValues3Table::CopyFrom( KeyValues3 *parent, const CKeyValues3Table* src )
{
int new_size = src->GetMemberCount();
RemoveAll( parent, new_size );
EnsureMemberCapacity( new_size, true, true );
auto context = parent->GetContext();
Hash_t *hashes_base = HashesBase();
Member_t *members_base = MembersBase();
Name_t *names_base = NamesBase();
Flags_t *flags_base = FlagsBase();
const Hash_t *src_hashes_base = src->HashesBase();
const Member_t *src_members_base = src->MembersBase();
const Name_t *src_names_base = src->NamesBase();
const Flags_t *src_flags_base = src->FlagsBase();
memmove( hashes_base, src_hashes_base, sizeof(Hash_t) * new_size );
for(int i = 0; i < new_size; i++)
{
flags_base[i] = src_flags_base[i] & ~TABLEFL_NAME_EXTERNAL;
if(context)
names_base[i] = context->AllocString( src_names_base[i] );
else
names_base[i] = strdup( src_names_base[i] );
members_base[i] = parent->AllocMember();
members_base[i]->CopyFrom( src_members_base[i] );
}
if ( new_size >= 128 )
EnableFastSearch();
}
void CKeyValues3Table::RemoveMember( KeyValues3 *parent, KV3MemberId_t id )
{
m_nCount--;
Hash_t* hashes_base = HashesBase();
Member_t* members_base = MembersBase();
Name_t* names_base = NamesBase();
Flags_t* flags_base = FlagsBase();
parent->FreeMember( members_base[id] );
if((flags_base[id] & TABLEFL_NAME_EXTERNAL) == 0 && !parent->GetContext() && names_base[id])
{
free( (void *)names_base[id] );
}
if ( id < m_nCount )
{
int shift_size = m_nCount - id;
int shift_from = id + 1;
memmove( &hashes_base[id], &hashes_base[shift_from], shift_size * sizeof(Hash_t) );
memmove( &members_base[id], &members_base[shift_from], shift_size * sizeof(Member_t) );
memmove( &names_base[id], &names_base[shift_from], shift_size * sizeof(Name_t) );
memmove( &flags_base[id], &flags_base[shift_from], shift_size * sizeof(Flags_t) );
}
if ( m_pFastSearch )
{
m_pFastSearch->m_ignore = true;
m_pFastSearch->m_ignores_counter = 1;
}
}
void CKeyValues3Table::RemoveAll( KeyValues3 *parent, int new_size )
{
Member_t *members_base = MembersBase();
Name_t *names_base = NamesBase();
Flags_t *flags_base = FlagsBase();
for(int i = 0; i < m_nCount; i++)
{
parent->FreeMember( members_base[i] );
if((flags_base[i] & TABLEFL_NAME_EXTERNAL) == 0 && !parent->GetContext() && names_base[i])
{
free( (void *)names_base[i] );
}
}
m_nCount = 0;
if(new_size > 0)
{
EnsureMemberCapacity( new_size, true, true );
}
if(new_size < 128)
{
PurgeFastSearch();
}
else
{
EnableFastSearch();
m_pFastSearch->m_member_ids.Reserve( new_size );
}
}
void CKeyValues3Table::PurgeFastSearch()
{
if(m_pFastSearch)
delete m_pFastSearch;
m_pFastSearch = nullptr;
}
void CKeyValues3Table::PurgeContent( KeyValues3 *parent, bool bClearingContext )
{
Member_t *members_base = MembersBase();
Name_t *names_base = NamesBase();
Flags_t *flags_base = FlagsBase();
for ( int i = 0; i < m_nCount; ++i )
{
if(!bClearingContext && parent)
{
parent->FreeMember( members_base[i] );
}
if((flags_base[i] & TABLEFL_NAME_EXTERNAL) == 0 && parent && !parent->GetContext() && names_base[i])
{
free( (void *)names_base[i] );
}
}
m_nCount = 0;
PurgeFastSearch();
}
void CKeyValues3Table::PurgeBuffers()
{
if(m_bIsDynamicallySized)
{
free( m_pDynamicBuffer );
m_nAllocatedChunks = m_nInitialSize;
m_bIsDynamicallySized = false;
}
m_nCount = 0;
}
CKeyValues3ContextBase::CKeyValues3ContextBase( CKeyValues3Context* context ) :
m_pContext( context ),
m_KV3BaseCluster( context ),
m_bMetaDataEnabled( false ),
m_bFormatConverted( false ),
m_bRootAvailabe( false ),
m_pParsingErrorListener( nullptr )
{
m_KV3PartialClusters.AddToChain( &m_KV3BaseCluster );
}
void CKeyValues3ContextBase::Clear()
{
m_BinaryData.Clear();
m_KV3BaseCluster.Clear();
m_Symbols.RemoveAll();
m_bFormatConverted = false;
}
void CKeyValues3ContextBase::Purge()
{
m_BinaryData.Purge();
m_KV3BaseCluster.Purge();
m_Symbols.Purge();
m_bFormatConverted = false;
}
CKeyValues3Context::CKeyValues3Context( bool bNoRoot ) : BaseClass( this ), pad{}
{
if ( bNoRoot )
{
m_bRootAvailabe = false;
}
else
{
m_bRootAvailabe = true;
m_KV3BaseCluster.Alloc();
}
m_bMetaDataEnabled = false;
m_bFormatConverted = false;
}
void CKeyValues3Context::Clear()
{
BaseClass::Clear();
ClearClusterNodeChain( m_KV3PartialClusters );
ClearClusterNodeChain( m_KV3FullClusters );
MoveToPartial( m_KV3FullClusters, m_KV3PartialClusters );
ClearClusterNodeChain( m_PartialArrayClusters );
ClearClusterNodeChain( m_FullArrayClusters );
MoveToPartial( m_FullArrayClusters, m_PartialArrayClusters );
m_RawArrayEntries.Clear();
ClearClusterNodeChain( m_PartialTableClusters );
ClearClusterNodeChain( m_FullTableClusters );
MoveToPartial( m_FullTableClusters, m_PartialTableClusters );
m_RawTableEntries.Clear();
if ( m_bRootAvailabe )
m_KV3BaseCluster.Alloc();
}
void CKeyValues3Context::Purge()
{
BaseClass::Purge();
PurgeClusterNodeChain( m_KV3PartialClusters );
PurgeClusterNodeChain( m_KV3FullClusters );
m_KV3PartialClusters.AddToChain( &m_KV3BaseCluster );
PurgeClusterNodeChain( m_PartialArrayClusters );
PurgeClusterNodeChain( m_FullArrayClusters );
m_RawArrayEntries.Purge();
PurgeClusterNodeChain( m_PartialTableClusters );
PurgeClusterNodeChain( m_FullTableClusters );
m_RawTableEntries.Purge();
if ( m_bRootAvailabe )
m_KV3BaseCluster.Alloc();
}
KeyValues3* CKeyValues3Context::Root()
{
if ( !m_bRootAvailabe )
{
Plat_FatalErrorFunc( "FATAL: %s called on a pool context (no root available)\n", __FUNCTION__ );
DebuggerBreak();
}
return &m_KV3BaseCluster.Head()->m_Value;
}
const char* CKeyValues3Context::AllocString( const char* pString )
{
return m_Symbols.AddString( pString ).String();
}
void CKeyValues3Context::EnableMetaData( bool bEnable )
{
if ( bEnable != m_bMetaDataEnabled )
{
m_KV3BaseCluster.EnableMetaData( bEnable );
m_bMetaDataEnabled = bEnable;
}
}
void CKeyValues3Context::CopyMetaData( KV3MetaData_t* pDest, const KV3MetaData_t* pSrc )
{
pDest->m_nLine = pSrc->m_nLine;
pDest->m_nColumn = pSrc->m_nColumn;
pDest->m_nFlags = pSrc->m_nFlags;
pDest->m_sName = m_Symbols.AddString( pSrc->m_sName.String() );
pDest->m_Comments.Purge();
pDest->m_Comments.EnsureCapacity( pSrc->m_Comments.Count() );
FOR_EACH_MAP_FAST( pSrc->m_Comments, iter )
{
pDest->m_Comments.Insert( pSrc->m_Comments.Key( iter ), pSrc->m_Comments.Element( iter ) );
}
}
KeyValues3* CKeyValues3Context::AllocKV( KV3TypeEx_t type, KV3SubType_t subtype )
{
return Alloc( m_KV3PartialClusters, m_KV3FullClusters, CKeyValues3Cluster::CLUSTER_SIZE, type, subtype );
}
void CKeyValues3Context::FreeKV( KeyValues3* kv )
{
CKeyValues3Context* context;
KV3MetaData_t* metadata = kv->GetMetaData( &context );
if ( metadata )
metadata->Clear();
// Free<KeyValues3, CKeyValues3Cluster>( kv, &m_KV3BaseCluster, m_pKV3FreeCluster );
}
#include "tier0/memdbgoff.h"