#include "keyvalues3.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #if defined( POSIX ) #undef offsetof #define offsetof(s,m) (size_t)&(((s *)0)->m) #endif // https://www.chessprogramming.org/BitScan static int BitScanFwd( uint64 bb ) { static const int index64[64] = { 0, 47, 1, 56, 48, 27, 2, 60, 57, 49, 41, 37, 28, 16, 3, 61, 54, 58, 35, 52, 50, 42, 21, 44, 38, 32, 29, 23, 17, 11, 4, 62, 46, 55, 26, 59, 40, 36, 15, 53, 34, 51, 20, 43, 31, 22, 10, 45, 25, 39, 14, 33, 19, 30, 9, 24, 13, 18, 8, 12, 7, 6, 5, 63 }; const uint64 debruijn64 = 0x03f79d71b4cb0a89ull; Assert( bb != 0 ); return index64[ ( ( bb ^ ( bb - 1 ) ) * debruijn64 ) >> 58 ]; } // https://www.chessprogramming.org/Population_Count static int PopCount( uint64 x ) { x = x - ( ( x >> 1 ) & 0x5555555555555555ull ); x = ( x & 0x3333333333333333ull ) + ( ( x >> 2 ) & 0x3333333333333333ull ); x = ( x + ( x >> 4 ) ) & 0x0f0f0f0f0f0f0f0full; x = ( x * 0x0101010101010101ull ) >> 56; return ( int )x; } KeyValues3::KeyValues3( KV3TypeEx_t type, KV3SubType_t subtype ) : m_bExternalStorage( true ), m_TypeEx( type ), m_SubType( subtype ), m_nFlags( 0 ), m_nReserved( 0 ), m_Value( 0 ) { ResolveUnspecified(); Alloc(); } KeyValues3::KeyValues3( int cluster_elem, KV3TypeEx_t type, KV3SubType_t subtype ) : m_bExternalStorage( false ), m_TypeEx( type ), m_SubType( subtype ), m_nFlags( 0 ), m_nClusterElement( cluster_elem ), m_nReserved( 0 ), m_Value( 0 ) { ResolveUnspecified(); Alloc(); } KeyValues3::~KeyValues3() { Free(); }; void KeyValues3::Alloc() { switch ( GetTypeEx() ) { case KV3_TYPEEX_ARRAY: { CKeyValues3Context* context = GetContext(); if ( context ) m_pArray = context->AllocArray(); else m_pArray = new CKeyValues3Array; break; } case KV3_TYPEEX_TABLE: { CKeyValues3Context* context = GetContext(); if ( context ) m_pTable = context->AllocTable(); else m_pTable = new CKeyValues3Table; 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_Value = 0; break; } default: break; } } void KeyValues3::Free( bool bClearingContext ) { switch ( GetTypeEx() ) { case KV3_TYPEEX_STRING: { free( (void*)m_pString ); m_pString = NULL; break; } case KV3_TYPEEX_BINARY_BLOB: { if ( m_pBinaryBlob ) free( m_pBinaryBlob ); m_pBinaryBlob = NULL; break; } case KV3_TYPEEX_BINARY_BLOB_EXTERN: { if ( m_pBinaryBlob ) { if ( m_pBinaryBlob->m_bFreeMemory ) free( (void*)m_pBinaryBlob->m_pubData ); free( m_pBinaryBlob ); } m_pBinaryBlob = NULL; break; } case KV3_TYPEEX_ARRAY: { m_pArray->Purge( bClearingContext ); CKeyValues3Context* context = GetContext(); if ( context ) { if ( !bClearingContext ) context->FreeArray( m_pArray ); } else { delete m_pArray; } m_pArray = NULL; break; } case KV3_TYPEEX_TABLE: { m_pTable->Purge( bClearingContext ); CKeyValues3Context* context = GetContext(); if ( context ) { if ( !bClearingContext ) context->FreeTable( m_pTable ); } else { delete m_pTable; } m_pTable = NULL; 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_pData ); m_bFreeArrayMemory = false; m_nNumArrayElements = 0; m_Value = 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; m_Value = 0; Alloc(); } m_SubType = subtype; } void KeyValues3::OnClearContext() { Free( true ); m_TypeEx = KV3_TYPEEX_NULL; m_Value = 0; } CKeyValues3Cluster* KeyValues3::GetCluster() const { if ( m_bExternalStorage ) return NULL; return GET_OUTER( CKeyValues3Cluster, m_KeyValues[ 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_pString; case KV3_TYPEEX_STRING_SHORT: return m_szStringShort; default: return defaultValue; } } void KeyValues3::SetString( const char* pString, KV3SubType_t subtype ) { if ( !pString ) pString = ""; if ( strlen( pString ) < sizeof( m_szStringShort ) ) { PrepareForType( KV3_TYPEEX_STRING_SHORT, subtype ); V_strncpy( m_szStringShort, pString, sizeof( m_szStringShort ) ); } else { PrepareForType( KV3_TYPEEX_STRING, subtype ); m_pString = strdup( pString ); } } void KeyValues3::SetStringExternal( const char* pString, KV3SubType_t subtype ) { if ( strlen( pString ) < sizeof( m_szStringShort ) ) { PrepareForType( KV3_TYPEEX_STRING_SHORT, subtype ); V_strncpy( m_szStringShort, pString, sizeof( m_szStringShort ) ); } else { PrepareForType( KV3_TYPEEX_STRING_EXTERN, subtype ); m_pString = pString; } } const byte* KeyValues3::GetBinaryBlob() const { switch ( GetTypeEx() ) { case KV3_TYPEEX_BINARY_BLOB: return m_pBinaryBlob ? m_pBinaryBlob->m_ubData : NULL; case KV3_TYPEEX_BINARY_BLOB_EXTERN: return m_pBinaryBlob ? m_pBinaryBlob->m_pubData : NULL; default: return NULL; } } int KeyValues3::GetBinaryBlobSize() const { if ( GetType() != KV3_TYPE_BINARY_BLOB || !m_pBinaryBlob ) return 0; return ( int )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_pBinaryBlob = (KV3BinaryBlob_t*)malloc( sizeof( size_t ) + size ); m_pBinaryBlob->m_nSize = size; memcpy( m_pBinaryBlob->m_ubData, blob, size ); } else { 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_pBinaryBlob = (KV3BinaryBlob_t*)malloc( sizeof( KV3BinaryBlob_t ) ); m_pBinaryBlob->m_nSize = size; m_pBinaryBlob->m_pubData = blob; m_pBinaryBlob->m_bFreeMemory = free_mem; } else { m_pBinaryBlob = NULL; } } Color KeyValues3::GetColor( 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 ) { AllocArray( 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_pArray->Count(); else return m_nNumArrayElements; } KeyValues3** KeyValues3::GetArrayBase() { if ( GetType() != KV3_TYPE_ARRAY ) return NULL; NormalizeArray(); return m_pArray->Base(); } KeyValues3* KeyValues3::GetArrayElement( int elem ) { if ( GetType() != KV3_TYPE_ARRAY ) return NULL; NormalizeArray(); if ( elem < 0 || elem >= m_pArray->Count() ) return NULL; return m_pArray->Element( elem ); } KeyValues3* KeyValues3::InsertArrayElementBefore( int elem ) { if ( GetType() != KV3_TYPE_ARRAY ) return NULL; NormalizeArray(); return *m_pArray->InsertBeforeGetPtr( elem, 1 ); } KeyValues3* KeyValues3::AddArrayElementToTail() { if ( GetType() != KV3_TYPE_ARRAY ) PrepareForType( KV3_TYPEEX_ARRAY, KV3_SUBTYPE_ARRAY ); else NormalizeArray(); return *m_pArray->InsertBeforeGetPtr( m_pArray->Count(), 1 ); } void KeyValues3::SetArrayElementCount( int count, KV3TypeEx_t type, KV3SubType_t subtype ) { if ( GetType() != KV3_TYPE_ARRAY ) PrepareForType( KV3_TYPEEX_ARRAY, KV3_SUBTYPE_ARRAY ); else NormalizeArray(); m_pArray->SetCount( count, type, subtype ); } void KeyValues3::RemoveArrayElements( int elem, int num ) { if ( GetType() != KV3_TYPE_ARRAY ) return; NormalizeArray(); m_pArray->RemoveMultiple( elem, num ); } void KeyValues3::NormalizeArray() { switch ( GetTypeEx() ) { case KV3_TYPEEX_ARRAY_FLOAT32: { NormalizeArray( KV3_TYPEEX_DOUBLE, KV3_SUBTYPE_FLOAT32, m_nNumArrayElements, m_f32Array, m_bFreeArrayMemory ); break; } case KV3_TYPEEX_ARRAY_FLOAT64: { NormalizeArray( KV3_TYPEEX_DOUBLE, KV3_SUBTYPE_FLOAT64, m_nNumArrayElements, m_f64Array, m_bFreeArrayMemory ); break; } case KV3_TYPEEX_ARRAY_INT16: { NormalizeArray( KV3_TYPEEX_INT, KV3_SUBTYPE_INT16, m_nNumArrayElements, m_i16Array, m_bFreeArrayMemory ); break; } case KV3_TYPEEX_ARRAY_INT32: { NormalizeArray( KV3_TYPEEX_INT, KV3_SUBTYPE_INT32, m_nNumArrayElements, m_i32Array, m_bFreeArrayMemory ); break; } case KV3_TYPEEX_ARRAY_UINT8_SHORT: { uint8 u8ArrayShort[8]; memcpy( u8ArrayShort, m_u8ArrayShort, sizeof( u8ArrayShort ) ); NormalizeArray( KV3_TYPEEX_UINT, KV3_SUBTYPE_UINT8, m_nNumArrayElements, u8ArrayShort, false ); break; } case KV3_TYPEEX_ARRAY_INT16_SHORT: { int16 i16ArrayShort[4]; memcpy( i16ArrayShort, m_i16ArrayShort, sizeof( i16ArrayShort ) ); NormalizeArray( 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_pArray->Count(); int count = MIN( src_size, dest_size ); KeyValues3** arr = 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_i16Array[ i ]; break; } case KV3_TYPEEX_ARRAY_INT32: { src_size = m_nNumArrayElements; int count = MIN( src_size, dest_size ); for ( int i = 0; i < count; ++i ) data[ i ] = m_i32Array[ i ]; 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_u8ArrayShort[ 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_i16ArrayShort[ 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_pArray->Count(); int count = MIN( src_size, dest_size ); KeyValues3** arr = 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 ); for ( int i = 0; i < count; ++i ) data[ i ] = m_f32Array[ i ]; 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_f64Array[ 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_pTable->GetMemberCount(); } KeyValues3* KeyValues3::GetMember( KV3MemberId_t id ) const { if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_pTable->GetMemberCount() ) return NULL; return m_pTable->GetMember( id ); } const char* KeyValues3::GetMemberName( KV3MemberId_t id ) const { if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_pTable->GetMemberCount() ) return NULL; return m_pTable->GetMemberName( id ); } CKV3MemberName KeyValues3::GetMemberNameEx( KV3MemberId_t id ) const { if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_pTable->GetMemberCount() ) return CKV3MemberName(); return CKV3MemberName( m_pTable->GetMemberHash( id ), m_pTable->GetMemberName( id ) ); } unsigned int KeyValues3::GetMemberHash( KV3MemberId_t id ) const { if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_pTable->GetMemberCount() ) return 0; return m_pTable->GetMemberHash( id ); } KeyValues3* KeyValues3::FindMember( const CKV3MemberName &name, KeyValues3* defaultValue ) const { if ( GetType() != KV3_TYPE_TABLE ) return defaultValue; KV3MemberId_t id = m_pTable->FindMember( name ); if ( id == KV3_INVALID_MEMBER ) return defaultValue; return 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_pTable->FindMember( name ); if ( id == KV3_INVALID_MEMBER ) { if ( pCreated ) *pCreated = true; id = m_pTable->CreateMember( name ); } else { if ( pCreated ) *pCreated = false; } return m_pTable->GetMember( id ); } void KeyValues3::SetToEmptyTable() { PrepareForType( KV3_TYPEEX_TABLE, KV3_SUBTYPE_TABLE ); m_pTable->RemoveAll(); } bool KeyValues3::RemoveMember( KV3MemberId_t id ) { if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_pTable->GetMemberCount() ) return false; m_pTable->RemoveMember( id ); return true; } bool KeyValues3::RemoveMember( const KeyValues3* kv ) { if ( GetType() != KV3_TYPE_TABLE ) return false; KV3MemberId_t id = m_pTable->FindMember( kv ); if ( id == KV3_INVALID_MEMBER ) return false; m_pTable->RemoveMember( id ); return true; } bool KeyValues3::RemoveMember( const CKV3MemberName &name ) { if ( GetType() != KV3_TYPE_TABLE ) return false; KV3MemberId_t id = m_pTable->FindMember( name ); if ( id == KV3_INVALID_MEMBER ) return false; m_pTable->RemoveMember( id ); return true; } void KeyValues3::CopyFrom( const KeyValues3* pSrc ) { 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_Bool ); break; case KV3_TYPE_INT: SetValue( pSrc->m_Int, KV3_TYPEEX_INT, eSrcSubType ); break; case KV3_TYPE_UINT: SetValue( pSrc->m_UInt, KV3_TYPEEX_UINT, eSrcSubType ); break; case KV3_TYPE_DOUBLE: SetValue( pSrc->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_pArray->CopyFrom( pSrc->m_pArray ); break; } case KV3_TYPEEX_ARRAY_FLOAT32: AllocArray( pSrc->m_nNumArrayElements, pSrc->m_f32Array, 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( pSrc->m_nNumArrayElements, pSrc->m_f64Array, 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( pSrc->m_nNumArrayElements, pSrc->m_i16Array, 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( pSrc->m_nNumArrayElements, pSrc->m_i32Array, 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( pSrc->m_nNumArrayElements, pSrc->m_u8ArrayShort, 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( pSrc->m_nNumArrayElements, pSrc->m_i16ArrayShort, 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: { PrepareForType( KV3_TYPEEX_TABLE, KV3_SUBTYPE_TABLE ); m_pTable->CopyFrom( pSrc->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; } CKeyValues3ArrayCluster* CKeyValues3Array::GetCluster() const { if ( m_nClusterElement == -1 ) return NULL; return GET_OUTER( CKeyValues3ArrayCluster, m_Arrays[ m_nClusterElement ] ); } CKeyValues3Context* CKeyValues3Array::GetContext() const { CKeyValues3ArrayCluster* cluster = GetCluster(); if ( cluster ) return cluster->GetContext(); else return NULL; } void CKeyValues3Array::SetCount( int count, KV3TypeEx_t type, KV3SubType_t subtype ) { int nOldSize = m_Elements.Count(); CKeyValues3Context* context = GetContext(); for ( int i = count; i < nOldSize; ++i ) { if ( context ) context->FreeKV( m_Elements[ i ] ); else delete m_Elements[ i ]; } m_Elements.SetCount( count ); for ( int i = nOldSize; i < count; ++i ) { if ( context ) m_Elements[ i ] = context->AllocKV( type, subtype ); else m_Elements[ i ] = new KeyValues3( type, subtype ); } } KeyValues3** CKeyValues3Array::InsertBeforeGetPtr( int elem, int num ) { KeyValues3** kv = m_Elements.InsertBeforeGetPtr( elem, num ); CKeyValues3Context* context = GetContext(); for ( int i = 0; i < num; ++i ) { if ( context ) m_Elements[ elem + i ] = context->AllocKV(); else m_Elements[ elem + i ] = new KeyValues3; } return kv; } void CKeyValues3Array::CopyFrom( const CKeyValues3Array* pSrc ) { int nNewSize = pSrc->m_Elements.Count(); SetCount( nNewSize ); for ( int i = 0; i < nNewSize; ++i ) *m_Elements[i] = *pSrc->m_Elements[i]; } void CKeyValues3Array::RemoveMultiple( int elem, int num ) { CKeyValues3Context* context = GetContext(); for ( int i = 0; i < num; ++i ) { if ( context ) context->FreeKV( m_Elements[ elem + i ] ); else delete m_Elements[ elem + i ]; } m_Elements.RemoveMultiple( elem, num ); } void CKeyValues3Array::Purge( bool bClearingContext ) { CKeyValues3Context* context = GetContext(); FOR_EACH_LEANVEC( m_Elements, iter ) { if ( context ) { if ( !bClearingContext ) context->FreeKV( m_Elements[ iter ] ); } else { delete m_Elements[ iter ]; } } m_Elements.Purge(); } CKeyValues3TableCluster* CKeyValues3Table::GetCluster() const { if ( m_nClusterElement == -1 ) return NULL; return GET_OUTER( CKeyValues3TableCluster, m_Tables[ m_nClusterElement ] ); } CKeyValues3Context* CKeyValues3Table::GetContext() const { CKeyValues3TableCluster* cluster = GetCluster(); if ( cluster ) return cluster->GetContext(); else return NULL; } void CKeyValues3Table::EnableFastSearch() { if ( m_pFastSearch ) m_pFastSearch->m_member_ids.RemoveAll(); else m_pFastSearch = new kv3tablefastsearch_t; for ( int i = 0; i < m_Hashes.Count(); ++i ) m_pFastSearch->m_member_ids.Insert( m_Hashes[i], i ); m_pFastSearch->m_ignore = false; m_pFastSearch->m_ignores_counter = 0; } KV3MemberId_t CKeyValues3Table::FindMember( const KeyValues3* kv ) const { for ( int i = 0; i < m_Hashes.Count(); ++i ) { if ( m_Members[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 { for ( int i = 0; i < m_Hashes.Count(); ++i ) { if ( m_Hashes[i] == name.GetHashCode() ) return i; } } return KV3_INVALID_MEMBER; } KV3MemberId_t CKeyValues3Table::CreateMember( const CKV3MemberName &name ) { if ( m_Hashes.Count() >= 128 && !m_pFastSearch ) EnableFastSearch(); *m_Hashes.AddToTailGetPtr() = name.GetHashCode(); KV3MemberId_t memberId = m_Hashes.Count() - 1; CKeyValues3Context* context = GetContext(); if ( context ) { *m_Members.AddToTailGetPtr() = context->AllocKV(); *m_Names.AddToTailGetPtr() = context->AllocString( name.GetString() ); } else { *m_Members.AddToTailGetPtr() = new KeyValues3; *m_Names.AddToTailGetPtr() = strdup( name.GetString() ); } m_IsExternalName.AddToTail( false ); if ( m_pFastSearch && !m_pFastSearch->m_ignore ) m_pFastSearch->m_member_ids.Insert( name.GetHashCode(), memberId ); return memberId; } void CKeyValues3Table::CopyFrom( const CKeyValues3Table* pSrc ) { int nNewSize = pSrc->m_Hashes.Count(); RemoveAll( nNewSize ); m_Hashes.SetCount( nNewSize ); m_Members.SetCount( nNewSize ); m_Names.SetCount( nNewSize ); m_IsExternalName.SetCount( nNewSize ); CKeyValues3Context* context = GetContext(); for ( int i = 0; i < nNewSize; ++i ) { m_Hashes[i] = pSrc->m_Hashes[i]; if ( context ) { m_Members[i] = context->AllocKV(); m_Names[i] = context->AllocString( pSrc->m_Names[i] ); } else { m_Members[i] = new KeyValues3; m_Names[i] = strdup( pSrc->m_Names[i] ); } m_IsExternalName[i] = false; *m_Members[i] = *pSrc->m_Members[i]; } if ( nNewSize >= 128 ) EnableFastSearch(); } void CKeyValues3Table::RemoveMember( KV3MemberId_t id ) { CKeyValues3Context* context = GetContext(); if ( context ) { context->FreeKV( m_Members[ id ] ); } else { delete m_Members[ id ]; delete m_Names[ id ]; } m_Hashes.Remove( id ); m_Members.Remove( id ); m_Names.Remove( id ); m_IsExternalName.Remove( id ); if ( m_pFastSearch ) { m_pFastSearch->m_ignore = true; m_pFastSearch->m_ignores_counter = 1; } } void CKeyValues3Table::RemoveAll( int nAllocSize ) { CKeyValues3Context* context = GetContext(); for ( int i = 0; i < m_Hashes.Count(); ++i ) { if ( context ) { context->FreeKV( m_Members[i] ); } else { delete m_Members[i]; delete m_Names[i]; } } m_Hashes.RemoveAll(); m_Members.RemoveAll(); m_Names.RemoveAll(); m_IsExternalName.RemoveAll(); if ( nAllocSize > 0 ) { m_Hashes.EnsureCapacity( nAllocSize ); m_Members.EnsureCapacity( nAllocSize ); m_Names.EnsureCapacity( nAllocSize ); m_IsExternalName.EnsureCapacity( nAllocSize ); } if ( m_pFastSearch ) { if ( nAllocSize >= 128 ) { m_pFastSearch->Clear(); } else { delete m_pFastSearch; m_pFastSearch = NULL; } } } void CKeyValues3Table::Purge( bool bClearingContext ) { CKeyValues3Context* context = GetContext(); for ( int i = 0; i < m_Hashes.Count(); ++i ) { if ( context ) { if ( !bClearingContext ) context->FreeKV( m_Members[i] ); } else { delete m_Members[i]; delete m_Names[i]; } } if ( m_pFastSearch ) delete m_pFastSearch; m_pFastSearch = NULL; m_Hashes.Purge(); m_Members.Purge(); m_Names.Purge(); m_IsExternalName.Purge(); } #include "tier0/memdbgoff.h" KeyValues3* CKeyValues3Cluster::Alloc( KV3TypeEx_t type, KV3SubType_t subtype ) { int element = BitScanFwd( ~m_nAllocatedElements ); m_nAllocatedElements |= ( 1ull << element ); KeyValues3* kv = &m_KeyValues[ element ]; new( kv ) KeyValues3( element, type, subtype ); return kv; } #include "tier0/memdbgon.h" void CKeyValues3Cluster::Free( int element ) { KeyValues3* kv = &m_KeyValues[ element ]; Destruct( kv ); memset( (void *)kv, 0, sizeof( KeyValues3 ) ); m_nAllocatedElements &= ~( 1ull << element ); } void CKeyValues3Cluster::PurgeElements() { uint64 mask = 1; for ( int i = 0; i < KV3_CLUSTER_MAX_ELEMENTS; ++i ) { if ( ( m_nAllocatedElements & mask ) != 0 ) m_KeyValues[ i ].OnClearContext(); mask <<= 1; } m_nAllocatedElements = 0; } void CKeyValues3Cluster::Purge() { PurgeElements(); if ( m_pMetaData ) { for ( int i = 0; i < KV3_CLUSTER_MAX_ELEMENTS; ++i ) m_pMetaData->m_elements[ i ].Purge(); } } void CKeyValues3Cluster::Clear() { PurgeElements(); if ( m_pMetaData ) { for ( int i = 0; i < KV3_CLUSTER_MAX_ELEMENTS; ++i ) m_pMetaData->m_elements[ i ].Clear(); } } void CKeyValues3Cluster::EnableMetaData( bool bEnable ) { if ( bEnable ) { if ( !m_pMetaData ) m_pMetaData = new kv3metadata_t; } else { FreeMetaData(); } } void CKeyValues3Cluster::FreeMetaData() { if ( m_pMetaData ) delete m_pMetaData; m_pMetaData = NULL; } KV3MetaData_t* CKeyValues3Cluster::GetMetaData( int element ) const { if ( !m_pMetaData ) return NULL; return &m_pMetaData->m_elements[ element ]; } int CKeyValues3Cluster::NumAllocated() const { return PopCount( m_nAllocatedElements ); } CKeyValues3Array* CKeyValues3ArrayCluster::Alloc() { int element = BitScanFwd( ~m_nAllocatedElements ); m_nAllocatedElements |= ( 1ull << element ); CKeyValues3Array* arr = &m_Arrays[ element ]; Construct( arr, element ); return arr; } void CKeyValues3ArrayCluster::Free( int element ) { CKeyValues3Array* arr = &m_Arrays[ element ]; Destruct( arr ); memset( (void *)arr, 0, sizeof( CKeyValues3Array ) ); m_nAllocatedElements &= ~( 1ull << element ); } void CKeyValues3ArrayCluster::Purge() { uint64 mask = 1; for ( int i = 0; i < KV3_CLUSTER_MAX_ELEMENTS; ++i ) { if ( ( m_nAllocatedElements & mask ) != 0 ) m_Arrays[ i ].Purge( true ); mask <<= 1; } m_nAllocatedElements = 0; } int CKeyValues3ArrayCluster::NumAllocated() const { return PopCount( m_nAllocatedElements ); } CKeyValues3Table* CKeyValues3TableCluster::Alloc() { int element = BitScanFwd( ~m_nAllocatedElements ); m_nAllocatedElements |= ( 1ull << element ); CKeyValues3Table* table = &m_Tables[ element ]; Construct( table, element ); return table; } void CKeyValues3TableCluster::Free( int element ) { CKeyValues3Table* table = &m_Tables[ element ]; Destruct( table ); memset( (void *)table, 0, sizeof( CKeyValues3Table ) ); m_nAllocatedElements &= ~( 1ull << element ); } void CKeyValues3TableCluster::Purge() { uint64 mask = 1; for ( int i = 0; i < KV3_CLUSTER_MAX_ELEMENTS; ++i ) { if ( ( m_nAllocatedElements & mask ) != 0 ) m_Tables[ i ].Purge( true ); mask <<= 1; } m_nAllocatedElements = 0; } int CKeyValues3TableCluster::NumAllocated() const { return PopCount( m_nAllocatedElements ); } CKeyValues3ContextBase::CKeyValues3ContextBase( CKeyValues3Context* context ) : m_pContext( context ), m_KV3BaseCluster( context ), m_pKV3FreeCluster( &m_KV3BaseCluster ), m_pArrayFreeCluster( NULL ), m_pTableFreeCluster( NULL ), m_pParsingErrorListener( NULL ) { } CKeyValues3ContextBase::~CKeyValues3ContextBase() { Purge(); } void CKeyValues3ContextBase::Clear() { m_BinaryData.Clear(); m_KV3BaseCluster.Clear(); m_KV3BaseCluster.SetNextFree( NULL ); m_pKV3FreeCluster = &m_KV3BaseCluster; FOR_EACH_LEANVEC( m_KV3Clusters, iter ) { m_KV3Clusters[ iter ]->Clear(); m_KV3Clusters[ iter ]->SetNextFree( m_pKV3FreeCluster ); m_pKV3FreeCluster = m_KV3Clusters[ iter ]; } m_pArrayFreeCluster = NULL; FOR_EACH_LEANVEC( m_ArrayClusters, iter ) { m_ArrayClusters[ iter ]->Clear(); m_ArrayClusters[ iter ]->SetNextFree( m_pArrayFreeCluster ); m_pArrayFreeCluster = m_ArrayClusters[ iter ]; } m_pTableFreeCluster = NULL; FOR_EACH_LEANVEC( m_TableClusters, iter ) { m_TableClusters[ iter ]->Clear(); m_TableClusters[ iter ]->SetNextFree( m_pTableFreeCluster ); m_pTableFreeCluster = m_TableClusters[ iter ]; } m_Symbols.RemoveAll(); m_bFormatConverted = false; } void CKeyValues3ContextBase::Purge() { m_BinaryData.Purge(); m_KV3BaseCluster.Purge(); m_KV3BaseCluster.SetNextFree( NULL ); m_pKV3FreeCluster = &m_KV3BaseCluster; FOR_EACH_LEANVEC( m_KV3Clusters, iter ) { m_KV3Clusters[ iter ]->Purge(); delete m_KV3Clusters[ iter ]; } m_KV3Clusters.Purge(); FOR_EACH_LEANVEC( m_ArrayClusters, iter ) { m_ArrayClusters[ iter ]->Purge(); delete m_ArrayClusters[ iter ]; } m_pArrayFreeCluster = NULL; m_ArrayClusters.Purge(); FOR_EACH_LEANVEC( m_TableClusters, iter ) { m_TableClusters[ iter ]->Purge(); delete m_TableClusters[ iter ]; } m_pTableFreeCluster = NULL; m_TableClusters.Purge(); m_Symbols.Purge(); m_bFormatConverted = false; } CKeyValues3Context::CKeyValues3Context( bool bNoRoot ) : BaseClass( this ) { if ( bNoRoot ) { m_bRootAvailabe = false; } else { m_bRootAvailabe = true; m_KV3BaseCluster.Alloc(); } m_bMetaDataEnabled = false; m_bFormatConverted = false; } CKeyValues3Context::~CKeyValues3Context() { } void CKeyValues3Context::Clear() { BaseClass::Clear(); if ( m_bRootAvailabe ) m_KV3BaseCluster.Alloc(); } void CKeyValues3Context::Purge() { BaseClass::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(); } 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 ); for ( int i = 0; i < m_KV3Clusters.Count(); ++i ) m_KV3Clusters[ i ]->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 ) { CBufferStringGrowable<8> buff; buff.Insert( 0, pSrc->m_Comments[ iter ].Get() ); pDest->m_Comments.Insert( pSrc->m_Comments.Key( iter ), buff ); } } KeyValues3* CKeyValues3Context::AllocKV( KV3TypeEx_t type, KV3SubType_t subtype ) { KeyValues3* kv; if ( m_pKV3FreeCluster ) { kv = m_pKV3FreeCluster->Alloc( type, subtype ); if ( !m_pKV3FreeCluster->IsFree() ) { CKeyValues3Cluster* cluster = m_pKV3FreeCluster->GetNextFree(); m_pKV3FreeCluster->SetNextFree( NULL ); m_pKV3FreeCluster = cluster; } } else { CKeyValues3Cluster* cluster = new CKeyValues3Cluster( m_pContext ); cluster->EnableMetaData( m_bMetaDataEnabled ); *m_KV3Clusters.AddToTailGetPtr() = cluster; m_pKV3FreeCluster = cluster; kv = cluster->Alloc( type, subtype ); } return kv; } void CKeyValues3Context::FreeKV( KeyValues3* kv ) { KV3MetaData_t* metadata = kv->GetMetaData(); if ( metadata ) metadata->Clear(); Free( kv, &m_KV3BaseCluster, m_pKV3FreeCluster, m_KV3Clusters ); }