diff --git a/interfaces/interfaces.cpp b/interfaces/interfaces.cpp index 864af2049..e3a389d61 100644 --- a/interfaces/interfaces.cpp +++ b/interfaces/interfaces.cpp @@ -6,8 +6,8 @@ /* This is totally reverse-engineered code and may be wrong */ -#include "interfaces/interfaces.h" #include "tier0/dbg.h" +#include "interfaces/interfaces.h" IApplication *g_pApplication; ICvar *cvar, *g_pCVar; diff --git a/public/entity2/entitykeyvalues.h b/public/entity2/entitykeyvalues.h index 255f1d23c..447f6ff81 100644 --- a/public/entity2/entitykeyvalues.h +++ b/public/entity2/entitykeyvalues.h @@ -248,7 +248,7 @@ inline EntityKeyId_t CEntityKeyValues::GetEntityKeyId( const CEntityKeyValues::I if ( it.index >= it.keys->GetMemberCount() ) return EntityKeyId_t(); - return it.keys->GetMemberNameEx( it.index ); + return it.keys->GetKV3MemberName( it.index ); } inline const char* CEntityKeyValues::GetAttributeName( const CEntityKeyValues::Iterator_t &it ) const diff --git a/public/tier0/dbg.h b/public/tier0/dbg.h index 37cc3655a..d11c008f0 100644 --- a/public/tier0/dbg.h +++ b/public/tier0/dbg.h @@ -243,6 +243,9 @@ PLATFORM_INTERFACE void Msg( const tchar* pMsg, ... ); PLATFORM_INTERFACE void Warning( const tchar *pMsg, ... ) FMTFUNCTION( 1, 2 ); PLATFORM_INTERFACE void Warning_SpewCallStack( int iMaxCallStackLength, const tchar *pMsg, ... ) FMTFUNCTION( 2, 3 ); +#define Plat_FatalError( ... ) do { Log_Error( LOG_GENERAL, ##__VA_ARGS__ ); Plat_ExitProcess( EXIT_FAILURE ); } while( 0 ) +#define Plat_FatalErrorFunc + // This is gone in Source2. Provide helper to roughly mimic Source1 behavior void Error( const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); inline void Error( const tchar* pMsg, ... ) @@ -312,25 +315,6 @@ PLATFORM_INTERFACE void COM_TimestampedLog( char const *fmt, ... ) FMTFUNCTION( #endif /* _DEBUG */ -//----------------------------------------------------------------------------- -// Macro to assist in asserting constant invariants during compilation - -#define COMPILE_TIME_ASSERT( pred ) static_assert( pred, "Compile time assert constraint is not true: " #pred ) -// ASSERT_INVARIANT used to be needed in order to allow COMPILE_TIME_ASSERTs at global -// scope. However the new COMPILE_TIME_ASSERT macro supports that by default. -#define ASSERT_INVARIANT( pred ) COMPILE_TIME_ASSERT( pred ) - -#ifdef _DEBUG -template -inline DEST_POINTER_TYPE assert_cast(SOURCE_POINTER_TYPE* pSource) -{ - Assert( static_cast(pSource) == dynamic_cast(pSource) ); - return static_cast(pSource); -} -#else -#define assert_cast static_cast -#endif - //----------------------------------------------------------------------------- // Templates to assist in validating pointers: diff --git a/public/tier0/fasttimer.h b/public/tier0/fasttimer.h index 116aa72a9..11bdc119d 100644 --- a/public/tier0/fasttimer.h +++ b/public/tier0/fasttimer.h @@ -1,4 +1,4 @@ -//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // diff --git a/public/tier0/platform.h b/public/tier0/platform.h index 1450957b4..ecdb17a28 100644 --- a/public/tier0/platform.h +++ b/public/tier0/platform.h @@ -81,6 +81,17 @@ class CBufferString; // scope. However the new COMPILE_TIME_ASSERT macro supports that by default. #define ASSERT_INVARIANT( pred ) COMPILE_TIME_ASSERT( pred ) +#ifdef _DEBUG +template +inline DEST_POINTER_TYPE assert_cast(SOURCE_POINTER_TYPE* pSource) +{ + Assert( static_cast(pSource) == dynamic_cast(pSource) ); + return static_cast(pSource); +} +#else +#define assert_cast static_cast +#endif + // feature enables #define NEW_SOFTWARE_LIGHTING #if !defined( _X360 ) @@ -1117,8 +1128,6 @@ PLATFORM_INTERFACE void Plat_ExitProcess( int nCode ); PLATFORM_INTERFACE bool Plat_ShouldCollectMiniDumpsForFatalErrors(); -PLATFORM_INTERFACE void Plat_FatalErrorFunc( const tchar* pMsg, ... ) FMTFUNCTION( 1, 2 ); - // b/w compatibility #define Sys_FloatTime Plat_FloatTime diff --git a/public/tier1/CommandBuffer.h b/public/tier1/CommandBuffer.h index 0a3c4433d..529a4799b 100644 --- a/public/tier1/CommandBuffer.h +++ b/public/tier1/CommandBuffer.h @@ -69,6 +69,10 @@ class CCommandBuffer // Indicates how long to delay when encoutering a 'wait' command void SetWaitDelayTime( int nTickDelay ); + // Compartmentalizes cfg-like commands. + // nLength can be -1 + DLL_CLASS_IMPORT void SplitCommands( const char *pText, int nLength, CUtlVector< CUtlString > &outString ); + // Returns a handle to the next command to process // (useful when inserting commands into the buffer during processing // of commands to force immediate execution of those commands, diff --git a/public/tier1/UtlStringMap.h b/public/tier1/UtlStringMap.h index c14334865..f346a6b17 100644 --- a/public/tier1/UtlStringMap.h +++ b/public/tier1/UtlStringMap.h @@ -6,19 +6,23 @@ #ifndef UTLSTRINGMAP_H #define UTLSTRINGMAP_H + #ifdef _WIN32 #pragma once #endif #include "utlsymbol.h" +#define FOR_EACH_STRING_MAP( mapName, iter ) \ + for ( auto iter = (mapName).Head(); iter < (mapName).GetNumStrings() && iter != (mapName).InvalidIndex(); iter = (mapName).Next( iter ) ) + template class CUtlStringMap { public: CUtlStringMap( bool caseInsensitive = true, int initsize = 32 ) : - m_SymbolTable( 0, 32, caseInsensitive ), - m_Vector( initsize ) + m_Vector( initsize ), + m_SymbolTable( 0, initsize, caseInsensitive ) { } @@ -26,7 +30,7 @@ class CUtlStringMap T& operator[]( const char *pString ) { CUtlSymbol symbol = m_SymbolTable.AddString( pString ); - int index = ( int )( UtlSymId_t )symbol; + int index = ( int )symbol; if( m_Vector.Count() <= index ) { m_Vector.EnsureCount( index + 1 ); @@ -47,20 +51,27 @@ class CUtlStringMap return m_Vector[n]; } + unsigned int Count() const + { + Assert( m_Vector.Count() == m_SymbolTable.GetNumStrings() ); + + return m_Vector.Count(); + } + bool Defined( const char *pString ) const { return m_SymbolTable.Find( pString ).IsValid(); } - UtlSymId_t Find( const char *pString ) const + CUtlSymbol Find( const char *pString ) const { return m_SymbolTable.Find( pString ); } - UtlSymId_t AddString( const char *pString, bool* created = NULL ) + CUtlSymbol AddString( const char *pString, bool* created = NULL ) { CUtlSymbol symbol = m_SymbolTable.AddString( pString, created ); - int index = ( int )( UtlSymId_t )symbol; + int index = ( int )symbol; if( m_Vector.Count() <= index ) { m_Vector.EnsureCount( index + 1 ); @@ -68,11 +79,67 @@ class CUtlStringMap return symbol; } - static UtlSymId_t InvalidIndex() + /// Add a string to the map and also insert an item at + /// its location in the same operation. Returns the + /// newly created index (or the one that was just + /// overwritten, if pString already existed.) + CUtlSymbol Insert( const char *pString, const T &item ) + { + CUtlSymbol symbol = m_SymbolTable.AddString( pString ); // implicit coercion + if ( m_Vector.Count() > symbol ) + { + // this string is already in the dictionary. + + } + else if ( m_Vector.Count() == symbol ) + { + // this is the expected case when we've added one more to the tail. + m_Vector.AddToTail( item ); + } + else // ( m_Vector.Count() < symbol ) + { + // this is a strange shouldn't-happen case. + AssertMsg( false, "CUtlStringMap insert unexpected entries." ); + m_Vector.EnsureCount( symbol + 1 ); + m_Vector[symbol] = item; + } + return symbol; + } + + bool FindAndRemove( const char *pString ) + { + CUtlSymbol symbol = m_SymbolTable.Find( pString ); + + if ( !symbol.IsValid() ) + { + return false; + } + + Destruct( &m_Vector[ symbol ] ); + m_Vector[ symbol ] = {}; + m_SymbolTable.Remove( symbol ); + + return true; + } + + static CUtlSymbol InvalidIndex() + { + return {}; + } + + // iterators (for uniformity with other map types) + inline CUtlSymbol Head() const + { + return m_SymbolTable.GetNumStrings() > 0 ? CUtlSymbol( 0 ) : InvalidIndex(); + } + + inline CUtlSymbol Next( const CUtlSymbol &i ) const { - return UTL_INVAL_SYMBOL; + CUtlSymbol n = i+1; + return n < m_SymbolTable.GetNumStrings() ? n : InvalidIndex(); } + int GetNumStrings( void ) const { return m_SymbolTable.GetNumStrings(); @@ -109,4 +176,16 @@ class CUtlStringMap CUtlSymbolTable m_SymbolTable; }; + +template< class T > +class CUtlStringMapAutoPurge : public CUtlStringMap < T > +{ +public: + ~CUtlStringMapAutoPurge( void ) + { + this->PurgeAndDeleteElements(); + } + +}; + #endif // UTLSTRINGMAP_H diff --git a/public/tier1/bufferstring.h b/public/tier1/bufferstring.h index 356aa092a..b4df7f75e 100644 --- a/public/tier1/bufferstring.h +++ b/public/tier1/bufferstring.h @@ -5,9 +5,13 @@ #pragma once #endif -#include "tier0/platform.h" +#define __need_size_t +#include +#undef __need_size_t + #include "strtools.h" -#include "utlstring.h" +#include "tier0/platform.h" +#include "tier1/utlcommon.h" class CFormatStringElement; class IFormatOutputStream; @@ -83,7 +87,6 @@ class CBufferString m_nAllocatedSize( (bAllowHeapAllocation * ALLOW_HEAP_ALLOCATION) | STACK_ALLOCATED_MARKER | (nAllocatedSize + sizeof( m_szString )) ), m_pString( nullptr ) { - Assert( nAllocatedSize > 8 ); } public: @@ -122,11 +125,11 @@ class CBufferString bool IsAllocationEmpty() const { return AllocatedNum() == 0; } protected: - char *Base() { return IsStackAllocated() ? m_szString : (!IsAllocationEmpty() ? m_pString : nullptr); } + char *Base() { return IsStackAllocated() ? m_szString : m_pString; } const char *Base() const { return const_cast( this )->Base(); } public: - const char *Get() const { auto base = Base(); return base ? base : StringFuncs::EmptyString(); } + const char *Get() const { return IsStackAllocated() ? m_szString : (IsAllocationEmpty() ? StringFuncs::EmptyString() : m_pString); } void Clear() { @@ -269,8 +272,8 @@ class CBufferString DLL_CLASS_IMPORT const char *StripExtension(); DLL_CLASS_IMPORT const char *StripTrailingSlash(); - DLL_CLASS_IMPORT void ToLowerFast(int nStart); - DLL_CLASS_IMPORT void ToUpperFast(int nStart); + DLL_CLASS_IMPORT void ToLowerFast(int nStart = 0); + DLL_CLASS_IMPORT void ToUpperFast(int nStart = 0); DLL_CLASS_IMPORT const char *Trim(const char *pTrimChars = "\t\r\n "); DLL_CLASS_IMPORT const char *TrimHead(const char *pTrimChars = "\t\r\n "); @@ -292,19 +295,14 @@ class CBufferString }; }; -template +template< size_t SIZE > class CBufferStringN : public CBufferString { public: static const size_t DATA_SIZE = ALIGN_VALUE( SIZE - sizeof( char[8] ), 8 ); - CBufferStringN( bool bAllowHeapAllocation = true ) : CBufferString( DATA_SIZE, bAllowHeapAllocation ), m_FixedData{} {} - CBufferStringN( const char *pString, bool bAllowHeapAllocation = true ) : CBufferStringN( bAllowHeapAllocation ) - { - Insert( 0, pString ); - } - - ~CBufferStringN() { PurgeN(); } + CBufferStringN( bool bAllowHeapAllocation = true ) : CBufferString( DATA_SIZE, bAllowHeapAllocation ) {} + CBufferStringN( const char *pString, int nLen = -1, bool bAllowHeapAllocation = true ) : CBufferStringN( bAllowHeapAllocation ) { Insert( 0, pString, nLen ); } // Should be preferred over CBufferString::Purge as it preserves stack space correctly void PurgeN() { Purge( DATA_SIZE ); } @@ -314,7 +312,7 @@ class CBufferStringN : public CBufferString }; // AMNOTE: CBufferStringN name is preferred to be used, altho CBufferStringGrowable is left as a small bcompat -template +template< size_t SIZE > using CBufferStringGrowable = CBufferStringN; #endif /* BUFFERSTRING_H */ \ No newline at end of file diff --git a/public/tier1/keyvalues3.h b/public/tier1/keyvalues3.h index 443a98274..e6493aca1 100644 --- a/public/tier1/keyvalues3.h +++ b/public/tier1/keyvalues3.h @@ -111,9 +111,21 @@ PLATFORM_OVERLOAD bool SaveKV3Text_ToString( const KV3ID_t& format, const KeyVal PLATFORM_OVERLOAD bool SaveKV3Text_ToString( const KV3ID_t& format, const KeyValues3* kv, CUtlString* error, CUtlString* output, uint flags = KV3_SAVE_TEXT_NONE ); PLATFORM_OVERLOAD bool SaveKV3ToFile( const KV3ID_t& encoding, const KV3ID_t& format, const KeyValues3* kv, CUtlString* error, const char* filename, const char* path, uint flags = KV3_SAVE_TEXT_NONE ); -typedef int32 KV3MemberId_t; +using KV3MemberId_t = int32; #define KV3_INVALID_MEMBER ((KV3MemberId_t)-1) +#define KV3_INVALID_CLUSTER_ELEMENT (~0) + +#define FOR_EACH_KV3_ARRAY( arrayName, iter ) \ + for ( int iter = 0; iter < (arrayName).Count(); iter++ ) +#define FOR_EACH_KV3_ARRAY_BACK( arrayName, iter ) \ + for ( int iter = iter < (arrayName).Count()-1; iter >= 0; iter-- ) + +#define FOR_EACH_KV3_TABLE( tableName, iter ) \ + for ( KV3MemberId_t iter = 0; iter < (tableName).GetMemberCount(); iter++ ) +#define FOR_EACH_KV3_TABLE_BACK( tableName, iter ) \ + for ( KV3MemberId_t iter = (tableName).GetMemberCount()-1; iter >= 0; iter-- ) + // AMNOTE: These constants aren't actual constants, but rather calculated at compile time // but the way they are calculated is unknown, previously it was using CUtlLeanVector min/max calculations // but in here they seem to not match that behaviour. @@ -283,6 +295,24 @@ enum KeyValues3Flag_t : uint8 KEYVALUES3_FLAG_LAST_VALUE = (1 << 2) }; +union KeyValues3Array_t +{ + float32* m_f32; + Vector *m_vec; + Vector2D *m_vec2; + Vector4D *m_vec4; + Quaternion *m_quat; + QAngle *m_ang; + matrix3x4_t *m_mat; + float64* m_f64; + int16* m_i16; + int32* m_i32; + uint8 m_u8Short[8]; + int16 m_i16Short[4]; + + CKeyValues3Array* m_pRoot; +}; + namespace KV3Helpers { template @@ -361,29 +391,49 @@ struct KV3BinaryBlob_t bool m_bFreeMemory; }; -class CKV3MemberName +using KeyValues3LowercaseHash_t = CUtlStringToken; +using CKV3MemberHash = KeyValues3LowercaseHash_t; + +class CKV3MemberName : public CKV3MemberHash { public: - inline CKV3MemberName(const char* pszString): m_nHashCode(), m_pszString("") - { - if (!pszString || !pszString[0]) - return; + template< uintp N > constexpr CKV3MemberName( const char (&szInit)[N] ) : CUtlStringToken( szInit ), m_pszString( (const char *)szInit ) {} + CKV3MemberName( const char* pszString, int nLen ) : CUtlStringToken( MakeStringToken2( pszString, nLen ) ), m_pszString( pszString ) {} + CKV3MemberName( uint32 nHash = 0, const char* pszString = StringFuncs::EmptyString() ) : CUtlStringToken( nHash ), m_pszString( pszString ) {} - m_nHashCode = MakeStringToken( pszString ); - m_pszString = pszString; - } + static CKV3MemberName Make( const char *pszInit, int nLen = -1 ) + { + Assert( pszInit && pszInit[0] ); - inline CKV3MemberName(): m_nHashCode(), m_pszString("") {} - inline CKV3MemberName( CUtlStringToken nHashCode, const char* pszString = ""): m_nHashCode(nHashCode), m_pszString(pszString) {} + return CKV3MemberName( pszInit, nLen ); + } - inline unsigned int GetHashCode() const { return m_nHashCode.GetHashCode(); } - inline const char* GetString() const { return m_pszString; } + bool IsEmpty() const { return !m_pszString || !m_pszString[0]; } + const char* GetString() const { return m_pszString; } private: - CUtlStringToken m_nHashCode; const char* m_pszString; }; +using CKeyValues3StringAndHash = CKV3MemberName; + +// Pulse thing +class CKV3MemberNameWithStorage : public CKV3MemberName +{ +public: + template< uintp N > constexpr CKV3MemberNameWithStorage( const char (&szInit)[N] ) : CKV3MemberName( szInit ), m_Storage( (const char*)szInit, N - 1 ) {} + CKV3MemberNameWithStorage( const char* pszString, int nLen ): CKV3MemberName( pszString, nLen ), m_Storage( pszString, nLen ) {} + CKV3MemberNameWithStorage( uint32 nHash = 0, const char* pszString = StringFuncs::EmptyString(), int nLen = -1 ) : CKV3MemberName( nHash, pszString ), m_Storage( pszString, nLen ) {} + + const CBufferString &GetStorage() const { return m_Storage; } + +private: + CBufferStringN< 32 > m_Storage; +}; + +// Pulse thing +using CKV3MemberNameSet = KeyValues3; // Allocates with KV_TYPE_ARRAY. + template class CKeyValues3ClusterImpl; @@ -417,13 +467,21 @@ class KeyValues3 KV3TypeEx_t GetTypeEx() const { return ( KV3TypeEx_t )m_TypeEx; } KV3SubType_t GetSubType() const { return ( KV3SubType_t )m_SubType; } + bool HasInvalidMemberNames() const; + void SetHasInvalidMemberNames( bool bValue = true ); + const char* GetTypeAsString() const; const char* GetSubTypeAsString() const; const char* ToString( CBufferString& buff, uint flags = KV3_TO_STRING_NONE ) const; - void SetToNull() { PrepareForType( KV3_TYPEEX_NULL, KV3_SUBTYPE_NULL ); } bool IsNull() const { return GetType() == KV3_TYPE_NULL; } + void SetToNull() { PrepareForType( KV3_TYPEEX_NULL, KV3_SUBTYPE_NULL ); } + + bool IsArray() const { return GetType() == KV3_TYPE_ARRAY; } + bool IsKV3Array() const { return GetTypeEx() == KV3_TYPEEX_ARRAY; } + bool IsTable() const { return GetType() == KV3_TYPE_TABLE; } + bool IsString() const { return GetType() == KV3_TYPE_STRING; } bool GetBool( bool defaultValue = false ) const { return GetValue( defaultValue ); } char8 GetChar( char8 defaultValue = 0 ) const { return GetValue( defaultValue ); } @@ -488,10 +546,15 @@ class KeyValues3 void SetQAngle( const QAngle &ang ) { SetVecBasedObj( ang, 3, KV3_SUBTYPE_QANGLE ); } void SetMatrix3x4( const matrix3x4_t &matrix ) { SetVecBasedObj( matrix, 3*4, KV3_SUBTYPE_MATRIX3X4 ); } + KeyValues3Array_t *GetArray() { return IsArray() ? &m_Data.m_Array : nullptr; } + const KeyValues3Array_t *GetArray() const { return const_cast(this)->GetArray(); }; + CKeyValues3Array *GetKV3Array() { return IsKV3Array() ? m_Data.m_Array.m_pRoot : nullptr; } + const CKeyValues3Array *GetKV3Array() const { return const_cast(this)->GetKV3Array(); }; + int GetArrayElementCount() const; void SetArrayElementCount( int count, KV3TypeEx_t type = KV3_TYPEEX_NULL, KV3SubType_t subtype = KV3_SUBTYPE_UNSPECIFIED ); - void SetToEmptyArray() { PrepareForType( KV3_TYPEEX_ARRAY, KV3_SUBTYPE_ARRAY ); } + void SetToEmptyKV3Array() { PrepareForType( KV3_TYPEEX_ARRAY, KV3_SUBTYPE_ARRAY ); } KeyValues3** GetArrayBase(); KeyValues3* GetArrayElement( int elem ); @@ -506,24 +569,26 @@ class KeyValues3 void ArrayRemoveElements( int elem, int num ); void ArrayRemoveElement( int elem ) { ArrayRemoveElements( elem, 1 ); } + CKeyValues3Table *GetTable() { return IsTable() ? m_Data.m_pTable : nullptr; } + const CKeyValues3Table *GetTable() const { return const_cast(this)->GetTable(); } + void SetToEmptyTable(); int GetMemberCount() const; - CKeyValues3Table *GetTableRaw(); - CKeyValues3Table *GetTableRaw() const { return const_cast(this)->GetTableRaw(); }; - KeyValues3* GetMember( KV3MemberId_t id ); const KeyValues3* GetMember( KV3MemberId_t id ) const { return const_cast(this)->GetMember( id ); } - const char* GetMemberName( KV3MemberId_t id ) const; - CKV3MemberName GetMemberNameEx( KV3MemberId_t id ) const; + CKV3MemberHash GetMemberHash( KV3MemberId_t id ) const; + CKV3MemberName GetKV3MemberName( KV3MemberId_t id ) const; - CUtlStringToken GetMemberHash( KV3MemberId_t id ) const; +protected: + KeyValues3* Internal_FindMember( const CKV3MemberName &name, KV3MemberId_t &next, KeyValues3* defaultValue = nullptr ); - KeyValues3* FindMember( const CKV3MemberName &name, KeyValues3* defaultValue = nullptr ); +public: + KeyValues3* FindMember( const CKV3MemberName &name, KeyValues3* defaultValue = nullptr ) { KV3MemberId_t next = KV3_INVALID_MEMBER; return Internal_FindMember( name, next, defaultValue ); } const KeyValues3 *FindMember( const CKV3MemberName &name, KeyValues3 *defaultValue = nullptr ) const { return const_cast(this)->FindMember( name, defaultValue ); }; KeyValues3* FindOrCreateMember( const CKV3MemberName &name, bool *pCreated = nullptr ); - + KeyValues3* RenameMember( const CKV3MemberName &name, const CKV3MemberName &newName ); bool RemoveMember( KV3MemberId_t id ); bool RemoveMember( const KeyValues3* kv ); bool RemoveMember( const CKV3MemberName &name ); @@ -554,7 +619,7 @@ class KeyValues3 matrix3x4_t GetMemberMatrix3x4( const CKV3MemberName &name, const matrix3x4_t &defaultValue = matrix3x4_t( Vector( 0.0f, 0.0f, 0.0f ), Vector( 0.0f, 0.0f, 0.0f ), Vector( 0.0f, 0.0f, 0.0f ), Vector( 0.0f, 0.0f, 0.0f ) ) ) const { auto kv = FindMember( name ); return kv ? kv->GetMatrix3x4( defaultValue ) : defaultValue; } void SetMemberToNull( const CKV3MemberName &name ) { FindOrCreateMember( name )->SetToNull(); } - void SetMemberToEmptyArray( const CKV3MemberName &name ) { FindOrCreateMember( name )->SetToEmptyArray(); } + void SetMemberToEmptyArray( const CKV3MemberName &name ) { FindOrCreateMember( name )->SetToEmptyKV3Array(); } void SetMemberToEmptyTable( const CKV3MemberName &name ) { FindOrCreateMember( name )->SetToEmptyTable(); } void SetMemberToBinaryBlob( const CKV3MemberName &name, const byte *blob, int size ) { FindOrCreateMember( name )->SetToBinaryBlob( blob, size ); } void SetMemberToBinaryBlobExternal( const CKV3MemberName &name, const byte *blob, int size, bool free_mem ) { FindOrCreateMember( name )->SetToBinaryBlobExternal( blob, size, free_mem ); } @@ -606,25 +671,9 @@ class KeyValues3 KV3BinaryBlob_t* m_pBinaryBlob; - CKeyValues3Array* m_pArray; + KeyValues3Array_t m_Array; CKeyValues3Table* m_pTable; - union Array_t - { - float32* m_f32; - Vector *m_vec; - Vector2D *m_vec2; - Vector4D *m_vec4; - Quaternion *m_quat; - QAngle *m_ang; - matrix3x4_t *m_mat; - float64* m_f64; - int16* m_i16; - int32* m_i32; - uint8 m_u8Short[8]; - int16 m_i16Short[4]; - } m_Array; - uint64 m_nMemory; void* m_pMemory; char m_Memory[1]; @@ -652,11 +701,14 @@ class KeyValues3 void Free( bool bClearingContext = false ); void ResolveUnspecified(); - void PrepareForType( KV3TypeEx_t type, KV3SubType_t subtype ); + void PrepareForType( KV3TypeEx_t type, KV3SubType_t subtype, int initial_size = 0, Data_t data = {}, int bytes_available = 0, bool should_free = false ); + void CopyFrom( const KeyValues3* pSrc ); + void OverlayKeysFrom( KeyValues3 *parent, bool depth = false ); + bool HasCluster() const { return m_nClusterElement != KV3_INVALID_CLUSTER_ELEMENT; } int GetClusterElement() const { return m_nClusterElement; } - void SetClusterElement( int element ) { m_bContextIndependent = (element == -1); m_nClusterElement = element; } + void SetClusterElement( int element ) { m_bContextIndependent = ( element == KV3_INVALID_CLUSTER_ELEMENT ); m_nClusterElement = element; } CKeyValues3Cluster* GetCluster() const; template < typename T > T FromString( T defaultValue ) const; @@ -733,9 +785,10 @@ class CKeyValues3Array static const size_t DATA_SIZE = KV3_ARRAY_MAX_FIXED_MEMBERS; static const size_t DATA_ALIGNMENT = KV3Helpers::PackAlignOf(); - CKeyValues3Array( int cluster_elem = -1, int alloc_size = DATA_SIZE ); + CKeyValues3Array( int cluster_elem = KV3_INVALID_CLUSTER_ELEMENT, int alloc_size = DATA_SIZE ); ~CKeyValues3Array() { Free(); } + bool HasCluster() const { return m_nClusterElement != KV3_INVALID_CLUSTER_ELEMENT; } int GetClusterElement() const { return m_nClusterElement; } void SetClusterElement( int element ) { m_nClusterElement = element; } @@ -797,23 +850,40 @@ class CKeyValues3Table MEMBER_FLAG_EXTERNAL_NAME = (1 << 0) }; - typedef CUtlStringToken Hash_t; - typedef KeyValues3* Member_t; - typedef const char* Name_t; - typedef uint8 Flags_t; + typedef KeyValues3LowercaseHash_t Hash_t; + typedef KeyValues3* Member_t; + typedef const char* Name_t; + typedef uint8 Flags_t; static const size_t DATA_SIZE = KV3_TABLE_MAX_FIXED_MEMBERS; static const size_t DATA_ALIGNMENT = KV3Helpers::PackAlignOf(); - CKeyValues3Table( int cluster_elem = -1, int alloc_size = DATA_SIZE ); + CKeyValues3Table( int cluster_elem = KV3_INVALID_CLUSTER_ELEMENT, int alloc_size = DATA_SIZE ); ~CKeyValues3Table() { Free(); } + bool HasCluster() const { return m_nClusterElement != KV3_INVALID_CLUSTER_ELEMENT; } int GetClusterElement() const { return m_nClusterElement; } void SetClusterElement( int element ) { m_nClusterElement = element; } + bool HasInvalidMemberNames() const { return m_bHasInvalidMemberNames; } + void SetHasInvalidMemberNames( bool bValue = true ) { m_bHasInvalidMemberNames = bValue; } + CKeyValues3TableCluster* GetCluster() const; CKeyValues3Context* GetContext() const; + // Gets the base address (can change when adding elements!) + void *Base() { return IsBaseStatic() ? &m_StaticBuffer : m_pDynamicBuffer; }; + Hash_t *HashesBase() { return reinterpret_cast((uint8 *)Base() + OffsetToHashesBase( GetAllocatedChunks() )); } + Member_t *MembersBase() { return reinterpret_cast((uint8 *)Base() + OffsetToMembersBase( GetAllocatedChunks() )); } + Name_t *NamesBase() { return reinterpret_cast((uint8 *)Base() + OffsetToNamesBase( GetAllocatedChunks() )); } + Flags_t *FlagsBase() { return reinterpret_cast((uint8 *)Base() + OffsetToFlagsBase( GetAllocatedChunks() )); } + + const void *Base() const { return const_cast(this)->Base(); } + const Hash_t *HashesBase() const { return const_cast(this)->HashesBase(); } + const Member_t *MembersBase() const { return const_cast(this)->MembersBase(); } + const Name_t *NamesBase() const { return const_cast(this)->NamesBase(); } + const Flags_t *FlagsBase() const { return const_cast(this)->FlagsBase(); } + int GetMemberCount() const { return m_nCount; } Member_t GetMember( KV3MemberId_t id ); const Member_t GetMember( KV3MemberId_t id ) const { return const_cast(this)->GetMember( id ); } @@ -824,11 +894,14 @@ class CKeyValues3Table void EnableFastSearch(); void EnsureMemberCapacity( int num, bool force = false, bool dont_move = false ); + KV3MemberId_t Internal_FindMember( const CKV3MemberName &name, KV3MemberId_t &next ); + KV3MemberId_t FindMember( const CKV3MemberName &name ) { KV3MemberId_t next = KV3_INVALID_MEMBER; return Internal_FindMember( name, next ); } KV3MemberId_t FindMember( const KeyValues3* kv ) const; - KV3MemberId_t FindMember( const CKV3MemberName &name ); KV3MemberId_t CreateMember( KeyValues3 *parent, const CKV3MemberName &name, bool name_external = false ); void CopyFrom( KeyValues3 *parent, const CKeyValues3Table* src ); + + void RenameMember( KeyValues3 *parent, KV3MemberId_t id, const CKV3MemberName &newName ); void RemoveMember( KeyValues3 *parent, KV3MemberId_t id ); void RemoveAll( KeyValues3 *parent, int new_size = 0 ); @@ -851,24 +924,12 @@ class CKeyValues3Table constexpr size_t OffsetToNamesBase( int size ) const { return KV3Helpers::PackSizeOf( size ); } constexpr size_t OffsetToFlagsBase( int size ) const { return KV3Helpers::PackSizeOf( size ); } - // Gets the base address (can change when adding elements!) - void *Base() { return IsBaseStatic() ? &m_StaticBuffer : m_pDynamicBuffer; }; - Hash_t *HashesBase() { return reinterpret_cast((uint8 *)Base() + OffsetToHashesBase( GetAllocatedChunks() )); } - Member_t *MembersBase() { return reinterpret_cast((uint8 *)Base() + OffsetToMembersBase( GetAllocatedChunks() )); } - Name_t *NamesBase() { return reinterpret_cast((uint8 *)Base() + OffsetToNamesBase( GetAllocatedChunks() )); } - Flags_t *FlagsBase() { return reinterpret_cast((uint8 *)Base() + OffsetToFlagsBase( GetAllocatedChunks() )); } - - const void *Base() const { return const_cast(this)->Base(); } - const Hash_t *HashesBase() const { return const_cast(this)->HashesBase(); } - const Member_t *MembersBase() const { return const_cast(this)->MembersBase(); } - const Name_t *NamesBase() const { return const_cast(this)->NamesBase(); } - const Flags_t *FlagsBase() const { return const_cast(this)->FlagsBase(); } - private: int m_nClusterElement; int m_nAllocatedChunks; - struct kv3tablefastsearch_t { + struct kv3tablefastsearch_t + { kv3tablefastsearch_t() : m_ignore( false ), m_ignores_counter( 0 ) {} ~kv3tablefastsearch_t() { Clear(); } @@ -892,7 +953,7 @@ class CKeyValues3Table uint8 m_nInitialSize; bool m_bIsDynamicallySized; - bool m_unk001; + bool m_bHasInvalidMemberNames; bool m_unk002; union @@ -1144,8 +1205,8 @@ class CKeyValues3Context : public CKeyValues3ContextBase template void PurgeClusterNodeChain( ClusterNodeChain &cluster_node ); - bool IsArrayRawAllocated( CKeyValues3Array *element ) { return m_RawArrayEntries.IsWithinRange( element ); } - bool IsTableRawAllocated( CKeyValues3Table *element ) { return m_RawTableEntries.IsWithinRange( element ); } + bool IsArrayAllocated( CKeyValues3Array *element ) { return m_RawArrayEntries.IsWithinRange( element ); } + bool IsTableAllocated( CKeyValues3Table *element ) { return m_RawTableEntries.IsWithinRange( element ); } private: template @@ -1249,10 +1310,12 @@ void KeyValues3::NormalizeArray( KV3TypeEx_t type, KV3SubType_t subtype, int siz m_TypeEx = KV3_TYPEEX_ARRAY; Alloc( size ); - m_Data.m_pArray->SetCount( this, size, type, subtype ); + CKeyValues3Array *pNewArray = m_Data.m_Array.m_pRoot; - CKeyValues3Array::Element_t* arr = m_Data.m_pArray->Base(); - for ( int i = 0; i < m_Data.m_pArray->Count(); ++i ) + pNewArray->SetCount( this, size, type, subtype ); + + CKeyValues3Array::Element_t* arr = pNewArray->Base(); + for ( int i = 0; i < pNewArray->Count(); ++i ) arr[ i ]->SetDirect( data[ i ] ); if ( bFree ) @@ -1266,7 +1329,7 @@ inline T *KeyValues3::AllocateOnHeap( int initial_size ) initial_size = T::DATA_SIZE; auto element = (T *)g_pMemAlloc->RegionAlloc( MEMALLOC_REGION_ALLOC_4, T::TotalSizeOf( initial_size ) ); - Construct( element, -1, initial_size ); + Construct( element, KV3_INVALID_CLUSTER_ELEMENT, initial_size ); return element; } @@ -1334,10 +1397,12 @@ void KeyValues3::AllocArray( int size, const T* data, KV3ArrayAllocType_t alloc_ { PrepareForType( KV3_TYPEEX_ARRAY, subtype ); - m_Data.m_pArray->SetCount( this, size, type_elem, subtype_elem ); + CKeyValues3Array *pNewArray = m_Data.m_Array.m_pRoot; + + pNewArray->SetCount( this, size, type_elem, subtype_elem ); - CKeyValues3Array::Element_t* arr = m_Data.m_pArray->Base(); - for ( int i = 0; i < m_Data.m_pArray->Count(); ++i ) + CKeyValues3Array::Element_t* arr = pNewArray->Base(); + for ( int i = 0; i < pNewArray->Count(); ++i ) arr[ i ]->SetValue( data[ i ], type_elem, subtype_elem ); if ( alloc_type == KV3_ARRAY_ALLOC_EXTERN_FREE ) @@ -1584,7 +1649,7 @@ inline NODE *CKeyValues3ContextBase::NodeList::Alloc( int initial_size ) auto entry = Tail(); m_nUsedBytes = byte_size_needed; - Construct( &entry->m_Value, -1, initial_size ); + Construct( &entry->m_Value, KV3_INVALID_CLUSTER_ELEMENT, initial_size ); entry->m_pNext = Tail(); return &entry->m_Value; diff --git a/public/tier1/utlcommon.h b/public/tier1/utlcommon.h index e75f7e168..d08efa029 100644 --- a/public/tier1/utlcommon.h +++ b/public/tier1/utlcommon.h @@ -10,7 +10,6 @@ #define UTLCOMMON_H #pragma once -#include "utlstring.h" #include "type_traits" //----------------------------------------------------------------------------- @@ -135,6 +134,55 @@ struct PointerLessFunctor { bool operator()( const void *a, const void *b ) cons struct PointerEqualFunctor { bool operator()( const void *a, const void *b ) const { return a == b; } }; struct PointerHashFunctor { unsigned int operator()( const void* s ) const { return Mix32HashFunctor()((uint32)POINTER_TO_INT(s)); } }; +//----------------------------------------------------------------------------- +// Purpose: Implementation of low-level string functionality for character types. +//----------------------------------------------------------------------------- + +template < typename T > +class StringFuncs +{ +public: + static T *Duplicate( const T *pValue ); + // Note that this function takes a character count, and does not guarantee null-termination. + static void Copy( T *out_pOut, const T *pIn, int iLengthInChars ); + static int Compare( const T *pLhs, const T *pRhs ); + static int CaselessCompare( const T *pLhs, const T *pRhs ); + static int Length( const T *pValue ); + static const T *FindChar( const T *pStr, const T cSearch ); + static const T *EmptyString(); + static const T *NullDebugString(); +}; + +template < > +class StringFuncs +{ +public: + static char *Duplicate( const char *pValue ) { return strdup( pValue ); } + // Note that this function takes a character count, and does not guarantee null-termination. + static void Copy( char *out_pOut, const char *pIn, int iLengthInChars ) { strncpy( out_pOut, pIn, iLengthInChars ); } + static int Compare( const char *pLhs, const char *pRhs ) { return strcmp( pLhs, pRhs ); } + static int CaselessCompare( const char *pLhs, const char *pRhs ) { return Q_strcasecmp( pLhs, pRhs ); } + static int Length( const char *pValue ) { return (int)strlen( pValue ); } + static const char *FindChar( const char *pStr, const char cSearch ) { return strchr( pStr, cSearch ); } + static const char *EmptyString() { return ""; } + static const char *NullDebugString() { return "(null)"; } +}; + +template < > +class StringFuncs +{ +public: + static wchar_t *Duplicate( const wchar_t *pValue ) { return wcsdup( pValue ); } + // Note that this function takes a character count, and does not guarantee null-termination. + static void Copy( wchar_t *out_pOut, const wchar_t *pIn, int iLengthInChars ) { wcsncpy( out_pOut, pIn, iLengthInChars ); } + static int Compare( const wchar_t *pLhs, const wchar_t *pRhs ) { return wcscmp( pLhs, pRhs ); } + static int CaselessCompare( const wchar_t *pLhs, const wchar_t *pRhs ); // no implementation? + static int Length( const wchar_t *pValue ) { return (int)wcslen( pValue ); } + static const wchar_t *FindChar( const wchar_t *pStr, const wchar_t cSearch ) { return wcschr( pStr, cSearch ); } + static const wchar_t *EmptyString() { return L""; } + static const wchar_t *NullDebugString() { return L"(null)"; } +}; + // Generic implementation of Less and Equal functors template < typename T > @@ -153,6 +201,8 @@ struct DefaultEqualFunctor bool operator()( typename ArgumentTypeInfo< T >::Arg_t a, typename ArgumentTypeInfo< T >::Alt_t b ) const { return a == b; } }; +class CUtlStringToken; + // Hashes for basic types template <> struct DefaultHashFunctor : Mix32HashFunctor { }; template <> struct DefaultHashFunctor : Mix32HashFunctor { }; diff --git a/public/tier1/utlhashtable.h b/public/tier1/utlhashtable.h index c2d686df3..8e78ec91b 100644 --- a/public/tier1/utlhashtable.h +++ b/public/tier1/utlhashtable.h @@ -265,6 +265,19 @@ class CUtlHashtable // it is up to the caller to ensure that they are compatible!) void Swap( CUtlHashtable &other ) { m_table.Swap(other.m_table); ::V_swap(m_nUsed, other.m_nUsed); ::V_swap(m_nTableSize, other.m_nTableSize); } + // GetMemoryUsage returns all memory held by this class + // and its held classes. It does not include sizeof(*this). + size_t GetMemoryUsage() const + { + return m_table.AllocSize(); + } + + size_t GetReserveCount() const + { + return m_nTableSize; + } + + #if _DEBUG // Validate the integrity of the hashtable void DbgCheckIntegrity() const; @@ -495,7 +508,6 @@ int CUtlHashtable::DoInser return idx; } - // Key lookup. Can also return previous-in-chain if result is a chained slot. template template @@ -602,6 +614,7 @@ int CUtlHashtable::DoRemov unsigned int slotmask = m_nTableSize-1; handle_t previous = (handle_t) -1; int idx = (int) DoLookup( x, h, &previous ); + if (idx == -1) { return -1; diff --git a/public/tier1/utlstring.h b/public/tier1/utlstring.h index 87cc8521a..4d4182243 100644 --- a/public/tier1/utlstring.h +++ b/public/tier1/utlstring.h @@ -10,10 +10,10 @@ #pragma once #endif -#include "tier1/utlstringtoken.h" +#include +#include "strtools.h" +#include "tier1/utlcommon.h" #include "tier1/utlmemory.h" -#include "tier1/strtools.h" -#include "limits.h" #if defined( OSX ) inline wchar_t *wcsdup(const wchar_t *pString) @@ -148,8 +148,8 @@ class CUtlString DLL_CLASS_IMPORT static CUtlString PathJoin(const char *pStr1, const char *pStr2); // These can be used for utlvector sorts. - static int __cdecl SortCaseInsensitive(const CUtlString *pString1, const CUtlString *pString2); - static int __cdecl SortCaseSensitive(const CUtlString *pString1, const CUtlString *pString2); + static int SortCaseInsensitive(const CUtlString *pString1, const CUtlString *pString2); + static int SortCaseSensitive(const CUtlString *pString1, const CUtlString *pString2); DLL_CLASS_IMPORT void Purge(); void Clear() { Purge(); } @@ -303,7 +303,7 @@ inline const char *CUtlString::Get() const { if(!m_pString) { - return ""; + return StringFuncs::EmptyString(); } return m_pString; } @@ -322,67 +322,18 @@ inline bool CUtlString::IsEmpty() const return !m_pString || m_pString[0] == 0; } -inline int __cdecl CUtlString::SortCaseInsensitive( const CUtlString *pString1, const CUtlString *pString2 ) +inline int CUtlString::SortCaseInsensitive( const CUtlString *pString1, const CUtlString *pString2 ) { return V_stricmp( pString1->String(), pString2->String() ); } -inline int __cdecl CUtlString::SortCaseSensitive( const CUtlString *pString1, const CUtlString *pString2 ) +inline int CUtlString::SortCaseSensitive( const CUtlString *pString1, const CUtlString *pString2 ) { return V_strcmp( pString1->String(), pString2->String() ); } -//----------------------------------------------------------------------------- -// Purpose: Implementation of low-level string functionality for character types. -//----------------------------------------------------------------------------- - -template < typename T > -class StringFuncs -{ -public: - static T *Duplicate( const T *pValue ); - // Note that this function takes a character count, and does not guarantee null-termination. - static void Copy( T *out_pOut, const T *pIn, int iLengthInChars ); - static int Compare( const T *pLhs, const T *pRhs ); - static int CaselessCompare( const T *pLhs, const T *pRhs ); - static int Length( const T *pValue ); - static const T *FindChar( const T *pStr, const T cSearch ); - static const T *EmptyString(); - static const T *NullDebugString(); -}; - -template < > -class StringFuncs -{ -public: - static char *Duplicate( const char *pValue ) { return strdup( pValue ); } - // Note that this function takes a character count, and does not guarantee null-termination. - static void Copy( char *out_pOut, const char *pIn, int iLengthInChars ) { strncpy( out_pOut, pIn, iLengthInChars ); } - static int Compare( const char *pLhs, const char *pRhs ) { return strcmp( pLhs, pRhs ); } - static int CaselessCompare( const char *pLhs, const char *pRhs ) { return Q_strcasecmp( pLhs, pRhs ); } - static int Length( const char *pValue ) { return (int)strlen( pValue ); } - static const char *FindChar( const char *pStr, const char cSearch ) { return strchr( pStr, cSearch ); } - static const char *EmptyString() { return ""; } - static const char *NullDebugString() { return "(null)"; } -}; - -template < > -class StringFuncs -{ -public: - static wchar_t *Duplicate( const wchar_t *pValue ) { return wcsdup( pValue ); } - // Note that this function takes a character count, and does not guarantee null-termination. - static void Copy( wchar_t *out_pOut, const wchar_t *pIn, int iLengthInChars ) { wcsncpy( out_pOut, pIn, iLengthInChars ); } - static int Compare( const wchar_t *pLhs, const wchar_t *pRhs ) { return wcscmp( pLhs, pRhs ); } - static int CaselessCompare( const wchar_t *pLhs, const wchar_t *pRhs ); // no implementation? - static int Length( const wchar_t *pValue ) { return (int)wcslen( pValue ); } - static const wchar_t *FindChar( const wchar_t *pStr, const wchar_t cSearch ) { return wcschr( pStr, cSearch ); } - static const wchar_t *EmptyString() { return L""; } - static const wchar_t *NullDebugString() { return L"(null)"; } -}; - //----------------------------------------------------------------------------- // Dirt-basic auto-release string class. Not intended for manipulation, // can be stored in a container or forwarded as a functor parameter. diff --git a/public/tier1/utlstringtoken.h b/public/tier1/utlstringtoken.h index b06240b11..5769e097d 100644 --- a/public/tier1/utlstringtoken.h +++ b/public/tier1/utlstringtoken.h @@ -14,42 +14,110 @@ #endif #include +#include +#include "bufferstring.h" #include "tier1/generichash.h" +#define DEBUG_STRINGTOKENS 0 #define STRINGTOKEN_MURMURHASH_SEED 0x31415926 -class CUtlString; +// Macros are intended to be used between CUtlStringToken (always lowercase) +#define MAKE_STRINGTOKEN_HASH(pstr) CUtlStringToken::Hash( (pstr), strlen(pstr), STRINGTOKEN_MURMURHASH_SEED ) +#define MAKE_STRINGTOKEN_HASH_UTL(containerName) CUtlStringToken::Hash( (containerName).Get(), (containerName).Length(), STRINGTOKEN_MURMURHASH_SEED ) + class IFormatOutputStream; class CFormatStringElement; -// AMNOTE: See VStringTokenSystem001 + +// See VStringTokenSystem001 // Interact with stringtokendatabase.txt PLATFORM_INTERFACE bool g_bUpdateStringTokenDatabase; PLATFORM_INTERFACE void RegisterStringToken( uint32 nHashCode, const char *pStart, const char *pEnd = NULL, bool bExtraAddToDatabase = true ); +#define TOLOWERU( c ) ( ( uint32 ) ( ( ( c >= 'A' ) && ( c <= 'Z' ) ) ? ( c | ( 1 << 5 ) ) : c ) ) class CUtlStringToken { public: - FORCEINLINE CUtlStringToken( uint32 nHashCode = 0 ) : m_nHashCode( nHashCode ) {} - FORCEINLINE CUtlStringToken( const char *str ) : m_nHashCode( 0 ) + static constexpr uint32 sm_nMagic = 0x5bd1e995; + static constexpr uint32 sm_nRotation = 24; + + template< bool CASEINSENSITIVE = true > + FORCEINLINE static constexpr uint32 Hash( const char *pString, int n, uint32 nSeed = STRINGTOKEN_MURMURHASH_SEED ) { - if(str && *str) + // They're not really 'magic', they just happen to work well. + + constexpr uint32 m = sm_nMagic; + constexpr uint32 r = sm_nRotation; + + // Initialize the hash to a 'random' value + + uint32 nLength = static_cast< uint32 >( n ); + + uint32 h = nSeed ^ nLength; + + // Mix 4 bytes at a time into the hash + + while ( nLength >= 4 ) { - m_nHashCode = MurmurHash2LowerCase( str, STRINGTOKEN_MURMURHASH_SEED ); - if(g_bUpdateStringTokenDatabase) - { - RegisterStringToken( m_nHashCode, str, 0, true ); - } + uint32 k = CASEINSENSITIVE ? ( TOLOWERU( pString[ 0 ] ) | ( TOLOWERU( pString[ 1 ] ) << 8 ) | ( TOLOWERU( pString[ 2 ] ) << 16 ) | ( TOLOWERU( pString[ 3 ] ) << 24 ) ) : LittleDWord( *(uint32 *)pString ); + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + pString += 4; + nLength -= 4; } + + // Handle the last few bytes of the input array + + switch ( nLength ) + { + case 3: h ^= ( CASEINSENSITIVE ? TOLOWERU( pString[ 2 ] ) : pString[ 2 ] ) << 16; [[fallthrough]]; + case 2: h ^= ( CASEINSENSITIVE ? TOLOWERU( pString[ 1 ] ) : pString[ 1 ] ) << 8; [[fallthrough]]; + case 1: h ^= ( CASEINSENSITIVE ? TOLOWERU( pString[ 0 ] ) : pString[ 0 ] ); + h *= m; + }; + + // Do a few final mixes of the hash to ensure the last few + // bytes are well-incorporated. + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; } - FORCEINLINE bool operator==( CUtlStringToken const &other ) const { return ( other.m_nHashCode == m_nHashCode ); } - FORCEINLINE bool operator!=( CUtlStringToken const &other ) const { return !operator==( other ); } - FORCEINLINE bool operator<( CUtlStringToken const &other ) const { return ( m_nHashCode < other.m_nHashCode ); } + CUtlStringToken( uint32 nHashCode = 0 ) : m_nHashCode( nHashCode ) {} + template < uintp N > FORCEINLINE constexpr CUtlStringToken( const char (&str)[N] ) : m_nHashCode( Hash( str, N - 1 ) ) {} + CUtlStringToken( const char *pString, int nLen ) : m_nHashCode( Hash( pString, nLen ) ) {} + CUtlStringToken( const CBufferString &buffer ) : CUtlStringToken( buffer.Get(), buffer.Length() ) {} + + // operator== + bool operator==( const uint32 nHash ) const { return m_nHashCode == nHash; } + bool operator==( const CUtlStringToken &other ) const { return operator==( other.GetHashCode() ); } + bool operator==( const char *pString ) const { return operator==( MAKE_STRINGTOKEN_HASH( pString ) ); } + bool operator==( const CBufferString &buffer ) const { return operator==( MAKE_STRINGTOKEN_HASH_UTL( buffer ) ); } + + // operator!= + bool operator!=( const uint32 nHash ) const { return !operator==( nHash ); } + bool operator!=( const CUtlStringToken &other ) const { return !operator==( other ); } + bool operator!=( const char *pString ) const { return !operator==( pString ); } + bool operator!=( const CBufferString &buffer ) const { return !operator==( buffer ); } - FORCEINLINE bool IsValid() const { return m_nHashCode != 0; } - FORCEINLINE uint32 GetHashCode() const { return m_nHashCode; } - FORCEINLINE void SetHashCode( uint32 hash ) { m_nHashCode = hash; } + // opertator< + bool operator<( const uint32 nHash ) const { return ( m_nHashCode < nHash ); } + bool operator<( CUtlStringToken const &other ) const { return operator<( other.GetHashCode() ); } + bool operator<( const char *pString ) const { return operator<( MAKE_STRINGTOKEN_HASH( pString ) ); } + bool operator<( const CBufferString &buffer ) const { return !operator<( MAKE_STRINGTOKEN_HASH_UTL( buffer ) ); } + + /// access to the hash code for people who need to store thse as 32-bits, regardless of the + operator uint32() const { return m_nHashCode; } + uint32 GetHashCode() const { return m_nHashCode; } DLL_CLASS_IMPORT void FormatTo( IFormatOutputStream* pOutputStream, CFormatStringElement pElement ) const; DLL_CLASS_IMPORT static bool TrackTokenCreation( const char *s1, const char *s2 ); @@ -58,9 +126,55 @@ class CUtlStringToken uint32 m_nHashCode; }; -FORCEINLINE CUtlStringToken MakeStringToken( const char *str ) +FORCEINLINE bool TrackStringToken( uint32 nHash, const char *pString ) { - return CUtlStringToken( str ); + if ( g_bUpdateStringTokenDatabase ) + { + RegisterStringToken( nHash, pString ); + + return true; + } + + return false; +} + +template< bool CASEINSENSITIVE = true, bool TRACKCREATION = true > +FORCEINLINE uint32 MakeStringToken( const char *pString, int nLen ) +{ + uint32 nHash = CASEINSENSITIVE ? MurmurHash2LowerCase( pString, nLen, STRINGTOKEN_MURMURHASH_SEED ) : MurmurHash2( pString, nLen, STRINGTOKEN_MURMURHASH_SEED ); + + if constexpr ( TRACKCREATION ) + TrackStringToken( nHash, pString ); + + return nHash; +} + +template< bool CASEINSENSITIVE = true, bool TRACKCREATION = true > +FORCEINLINE uint32 MakeStringToken( const char *pString ) +{ + return MakeStringToken< CASEINSENSITIVE, TRACKCREATION >( pString, strlen(pString) ); +} + +template< bool CASEINSENSITIVE = true, uintp SIZE = 128 > +FORCEINLINE uint32 HashStringWithBuffer( const char *pString, int nLen = -1 ) +{ + CBufferStringN< SIZE > buffer( pString, nLen ); + + if constexpr ( CASEINSENSITIVE ) + buffer.ToLowerFast(); + + return CUtlStringToken::Hash< CASEINSENSITIVE >( buffer.Get(), buffer.Length() ); +} + +template< bool CASEINSENSITIVE = true, bool TRACKCREATION = true > +FORCEINLINE CUtlStringToken MakeStringToken2( const char *pString, int nLen = -1 ) +{ + CUtlStringToken nHashToken = HashStringWithBuffer< CASEINSENSITIVE >( pString, nLen ); + + if constexpr ( TRACKCREATION ) + TrackStringToken( nHashToken, pString ); + + return nHashToken; } #endif // UTLSTRINGTOKEN_H diff --git a/public/tier1/utlsymbol.h b/public/tier1/utlsymbol.h index 08e9c441f..a19da4d02 100644 --- a/public/tier1/utlsymbol.h +++ b/public/tier1/utlsymbol.h @@ -21,7 +21,6 @@ #include "tier1/utlhashtable.h" #include "tier1/memblockallocator.h" - //----------------------------------------------------------------------------- // forward declarations //----------------------------------------------------------------------------- @@ -33,9 +32,15 @@ class CUtlSymbolTableMT; // This is a symbol, which is a easier way of dealing with strings. //----------------------------------------------------------------------------- typedef unsigned short UtlSymId_t; +typedef unsigned int UtlSymElm_t; #define UTL_INVAL_SYMBOL ((UtlSymId_t)~0) +#define FOR_EACH_SYMBOL( tableName, iter ) \ + for ( UtlSymElm_t iter = 0; iter < (tableName).GetNumStrings(); iter++ ) +#define FOR_EACH_SYMBOL_BACK( tableName, iter ) \ + for ( UtlSymElm_t iter = (tableName).GetNumStrings()-1; iter >= 0; iter-- ) + class CUtlSymbol { public: @@ -46,15 +51,24 @@ class CUtlSymbol // operator= CUtlSymbol& operator=( CUtlSymbol const& src ) { m_Id = src.m_Id; return *this; } - + // operator== bool operator==( CUtlSymbol const& src ) const { return m_Id == src.m_Id; } + static uint32 MakeHash( bool bInsensitive, const char *pString, int nLength ) + { + return bInsensitive ? MakeStringToken2< true >( pString, nLength ) + : MakeStringToken2< false >( pString, nLength ); + } + + UtlSymId_t GetId() const { return m_Id; } + // Is valid? - bool IsValid() const { return m_Id != UTL_INVAL_SYMBOL; } - + static UtlSymId_t Invalid() { return UTL_INVAL_SYMBOL; } + bool IsValid() const { return GetId() != Invalid(); } + // Gets at the symbol - operator UtlSymId_t () const { return m_Id; } + operator UtlSymId_t () const { return m_Id; } protected: CUtlSymbol( const char* pStr ); @@ -64,6 +78,7 @@ class CUtlSymbol UtlSymId_t m_Id; }; +CUtlSymbol CUtlSymbol_Make( const CUtlSymbolTable *pTable, const char *pString, int nLength, uint32 hash ); //----------------------------------------------------------------------------- // CUtlSymbolTable: @@ -77,7 +92,6 @@ class CUtlSymbol // two bytes of each string are decorated with a hash to speed up // comparisons. //----------------------------------------------------------------------------- - class CUtlSymbolTable { public: @@ -92,10 +106,18 @@ class CUtlSymbolTable // Finds the symbol for pString DLL_CLASS_IMPORT CUtlSymbol Find( const char* pString ) const; DLL_CLASS_IMPORT CUtlSymbol Find( const char* pString, int nLength ) const; - + // Look up the string associated with a particular symbol DLL_CLASS_IMPORT const char* String( CUtlSymbol id ) const; - + + uint32 Hash( const char *pString, int nLength ) const { return CUtlSymbol::MakeHash( IsInsensitive(), pString, nLength ); } + uint32 Hash( const char *pString ) const { return Hash( pString, strlen( pString ) ); } + uint32 Hash( CUtlSymbol id ) const { return Hash( (const char *)m_MemBlockAllocator.GetBlock( m_MemBlocks[ id ] ) ); } + + // Remove once symbol element. + // @Wend4r: The table is not designed for that. + void Remove( CUtlSymbol id ) { m_HashTable.Remove( id ); m_MemBlocks[ id ] = MEMBLOCKHANDLE_INVALID; } + // Remove all symbols in the table. DLL_CLASS_IMPORT void RemoveAll(); DLL_CLASS_IMPORT void Purge(); @@ -110,12 +132,6 @@ class CUtlSymbolTable DLL_CLASS_IMPORT bool SaveToBuffer( CUtlBuffer& buff ) const; DLL_CLASS_IMPORT bool RestoreFromBuffer( CUtlBuffer& buff ); - int GetNumStrings( void ) const - { - return m_MemBlocks.Count(); - } - -protected: struct UtlSymTableAltKey { const CUtlSymbolTable* m_pTable; @@ -127,31 +143,113 @@ class CUtlSymbolTable { ptrdiff_t m_ownerOffset; - UtlSymTableHashFunctor(); - unsigned int operator()( UtlSymTableAltKey k ) const; - unsigned int operator()( int k ) const; + UtlSymTableHashFunctor() + { + const ptrdiff_t tableoffset = (uintp)(&((Hashtable_t*)1024)->GetHashRef()) - 1024; + const ptrdiff_t owneroffset = offsetof(CUtlSymbolTable, m_HashTable) + tableoffset; + m_ownerOffset = -owneroffset; + } + + unsigned int operator()( UtlSymTableAltKey k ) const + { + const CUtlSymbolTable* pTable = (const CUtlSymbolTable*)((uintp)this + m_ownerOffset); + + return pTable->Hash( k.m_pString ); + } + + unsigned int operator()( UtlSymElm_t k ) const + { + const CUtlSymbolTable* pTable = (const CUtlSymbolTable*)((uintp)this + m_ownerOffset); + + return pTable->Hash( k ); + } }; struct UtlSymTableEqualFunctor { ptrdiff_t m_ownerOffset; - UtlSymTableEqualFunctor(); - bool operator()( int a, int b ) const; - bool operator()( UtlSymTableAltKey a, int b ) const; - bool operator()( int a, UtlSymTableAltKey b ) const; + UtlSymTableEqualFunctor() + { + const ptrdiff_t tableoffset = (uintp)(&((Hashtable_t*)1024)->GetEqualRef()) - 1024; + const ptrdiff_t owneroffset = offsetof(CUtlSymbolTable, m_HashTable) + tableoffset; + m_ownerOffset = -owneroffset; + } + + bool operator()( UtlSymElm_t a, UtlSymElm_t b ) const + { + const CUtlSymbolTable* pTable = (const CUtlSymbolTable*)((uintp)this + m_ownerOffset); + + if ( pTable->IsInsensitive() ) + return V_stricmp( pTable->String( a ), pTable->String( b ) ) == 0; + else + return V_strcmp( pTable->String( a ), pTable->String( b ) ) == 0; + } + + bool operator()( UtlSymTableAltKey a, UtlSymElm_t b ) const + { + const char* pString = a.m_pTable->String( b ); + int nLength = ( int )strlen( pString ); + + if ( a.m_nLength != nLength ) + return false; + + if ( a.m_pTable->IsInsensitive() ) + return V_strnicmp( a.m_pString, pString, a.m_nLength ) == 0; + else + return V_strncmp( a.m_pString, pString, a.m_nLength ) == 0; + } + + bool operator()( UtlSymElm_t a, UtlSymTableAltKey b ) const + { + return operator()( b, a ); + } }; - typedef CUtlHashtable Hashtable_t; + typedef CUtlHashtable Hashtable_t; typedef CUtlVector MemBlocksVec_t; + const Hashtable_t &GetHashtable() const + { + return m_HashTable; + } + + UtlSymElm_t GetNumStrings( void ) const + { + return m_MemBlocks.Count(); + } + + bool IsInsensitive() const + { + return m_bInsensitive; + } + +protected: + // By "UtlSymId_t" elements. Hashtable_t m_HashTable; + + // By "const char *" elements. MemBlocksVec_t m_MemBlocks; CUtlMemoryBlockAllocator m_MemBlockAllocator; bool m_bInsensitive; }; +inline CUtlSymbol CUtlSymbol_Make( const CUtlSymbolTable *pTable, const char *pString, int nLength, unsigned int hash ) +{ + auto &hashtable = pTable->GetHashtable(); + + CUtlSymbolTable::UtlSymTableAltKey key; + + key.m_pTable = pTable; + key.m_pString = pString; + key.m_nLength = nLength; + + UtlHashHandle_t h = hashtable.Find( key, hash ); + + return h == hashtable.InvalidHandle() ? UTL_INVAL_SYMBOL : hashtable[ h ]; +} + class CUtlSymbolTableMT : public CUtlSymbolTable { public: diff --git a/public/tier1/utlsymbollarge.h b/public/tier1/utlsymbollarge.h index 0e839bb4e..858e21628 100644 --- a/public/tier1/utlsymbollarge.h +++ b/public/tier1/utlsymbollarge.h @@ -13,6 +13,7 @@ #pragma once #endif +#include "tier0/platform.h" #include "tier0/threadtools.h" #include "tier1/generichash.h" #include "tier1/utlvector.h" @@ -29,90 +30,82 @@ // to the string data, the hash precedes it in memory and is used to speed up searching, etc. //----------------------------------------------------------------------------- -typedef unsigned int UtlSymLargeId_t; +typedef intp UtlSymLargeId_t; +typedef uint UtlSymLargeElm_t; -#define UTL_INVAL_SYMBOL_LARGE ((UtlSymLargeId_t)~0) +#define UTL_INVAL_SYMBOL_LARGE ((UtlSymLargeId_t)0) + +#define FOR_EACH_SYMBOL_LARGE( table, iter ) \ + for ( UtlSymLargeElm_t iter = 0; iter < (table).GetNumStrings(); iter++ ) +#define FOR_EACH_SYMBOL_LARGE_BACK( table, iter ) \ + for ( UtlSymLargeElm_t iter = (table).GetNumStrings()-1; iter >= 0; iter-- ) class CUtlSymbolLarge { public: // constructor, destructor - CUtlSymbolLarge() - { - m_pString = NULL; - } - - CUtlSymbolLarge( const char* pString ) - { - m_pString = pString; - } + CUtlSymbolLarge( UtlSymLargeId_t id = UTL_INVAL_SYMBOL_LARGE ) : u( id ) {} + CUtlSymbolLarge( const char* pString ) : u( pString ) {}; // operator== - bool operator==( CUtlSymbolLarge const& src ) const - { - return m_pString == src.m_pString; - } + bool operator==( CUtlSymbolLarge const& src ) const { return u.m_Id == src.u.m_Id; } + bool operator==( const char* pString ) const = delete; // disallow since we don't know if the table this is from was case sensitive or not... maybe we don't care // operator!= - bool operator!=( CUtlSymbolLarge const& src ) const - { - return m_pString != src.m_pString; - } + bool operator!=( CUtlSymbolLarge const& src ) const { return u.m_Id != src.u.m_Id; } // operator< - bool operator<( CUtlSymbolLarge const& src ) const - { - return ( intp )m_pString < ( intp )src.m_pString; - } + bool operator<( CUtlSymbolLarge const& src ) const { return u.m_Id < src.u.m_Id; } - inline const char* String() const - { - if ( !m_pString ) - return ""; - return m_pString; - } + template< bool CASEINSENSITIVE = true > + static uint32 Hash( const char *pString, int nLength = -1 ) { return MakeStringToken2< CASEINSENSITIVE >( pString, nLength ); } - inline bool IsValid() const - { - return m_pString != NULL; - } + bool IsValid() const { return u.m_Id != UTL_INVAL_SYMBOL_LARGE; } + UtlSymLargeId_t GetId() { return u.m_Id; }; + const char* String() const { return IsValid() ? u.m_pAsString : ""; } private: - // Disallowed - bool operator==( const char* pString ) const; // disallow since we don't know if the table this is from was case sensitive or not... maybe we don't care + union Data_t + { + Data_t( UtlSymLargeId_t id ) : m_Id( id ) {} + Data_t( const char *pString ) : m_pAsString( pString ) {} - const char* m_pString; + UtlSymLargeId_t m_Id; + const char *m_pAsString; + } u; }; -inline uint32 CUtlSymbolLarge_Hash( bool CASEINSENSITIVE, const char *pString, int len ) -{ - return ( CASEINSENSITIVE ? MurmurHash2LowerCase( pString, len, 0x31415926 ) : MurmurHash2( pString, len, 0x31415926 ) ); -} - -typedef uint32 LargeSymbolTableHashDecoration_t; +typedef uint32 LargeSymbolTableHashDecoration_t; // The structure consists of the hash immediately followed by the string data -struct CUtlSymbolTableLargeBaseTreeEntry_t +struct ALIGN8_POST CUtlSymbolTableLargeBaseTreeEntry_t { LargeSymbolTableHashDecoration_t m_Hash; // Variable length string data - char m_String[1]; + char m_szString[1]; bool IsEmpty() const { - return ( ( m_Hash == 0 ) && ( 0 == m_String[0] ) ); + return ( !m_Hash && !m_szString[0] ); + } + + const char *String() const + { + return (const char *)m_szString; } - char const *String() const + void Replace( LargeSymbolTableHashDecoration_t nNewHash, const char *pNewString, int nLength ) { - return (const char *)&m_String[ 0 ]; + m_Hash = nNewHash; + Q_memcpy( (char *)m_szString, pNewString, nLength ); + m_szString[nLength] = '\0'; } CUtlSymbolLarge ToSymbol() const { return CUtlSymbolLarge( String() ); } - + LargeSymbolTableHashDecoration_t HashValue() const { return m_Hash; @@ -143,16 +136,18 @@ class CUtlSymbolTableLargeBase CUtlSymbolLarge Find( const char* pString ) const; CUtlSymbolLarge Find( const char* pString, int nLength ) const; + const char* String( UtlSymLargeElm_t id ) const; + uint32 Hash( UtlSymLargeElm_t id ) const; + + int GetNumStrings() const { return m_MemBlocks.Count(); }; + // Remove all symbols in the table. void RemoveAll(); void Purge(); private: - CUtlSymbolLarge AddString( unsigned int hash, const char* pString, int nLength, bool* created ); - CUtlSymbolLarge Find( unsigned int hash, const char* pString, int nLength ) const; - - const char* String( UtlSymLargeId_t id ) const; - unsigned int HashValue( UtlSymLargeId_t id ) const; + CUtlSymbolLarge AddString( uint32 hash, const char* pString, int nLength, bool* created ); + CUtlSymbolLarge Find( uint32 hash, const char* pString, int nLength ) const; struct UtlSymTableLargeAltKey { @@ -172,16 +167,16 @@ class CUtlSymbolTableLargeBase m_ownerOffset = -owneroffset; } - unsigned int operator()( UtlSymTableLargeAltKey k ) const + uint32 operator()( UtlSymTableLargeAltKey k ) const { - return CUtlSymbolLarge_Hash( CASEINSENSITIVE, k.m_pString, k.m_nLength ); + return CUtlSymbolLarge::Hash< CASEINSENSITIVE >( k.m_pString, k.m_nLength ); } - unsigned int operator()( UtlSymLargeId_t k ) const + uint32 operator()( UtlSymLargeElm_t k ) const { const CUtlSymbolTableLargeBase* pTable = (const CUtlSymbolTableLargeBase*)((uintp)this + m_ownerOffset); - return pTable->HashValue( k ); + return pTable->Hash( k ); } }; @@ -196,17 +191,17 @@ class CUtlSymbolTableLargeBase m_ownerOffset = -owneroffset; } - bool operator()( UtlSymLargeId_t a, UtlSymLargeId_t b ) const + bool operator()( UtlSymLargeElm_t a, UtlSymLargeElm_t b ) const { const CUtlSymbolTableLargeBase* pTable = (const CUtlSymbolTableLargeBase*)((uintp)this + m_ownerOffset); - if ( !CASEINSENSITIVE ) - return strcmp( pTable->String( a ), pTable->String( b ) ) == 0; + if ( CASEINSENSITIVE ) + return V_stricmp( pTable->String( a ), pTable->String( b ) ) == 0; else - return V_stricmp_fast( pTable->String( a ), pTable->String( b ) ) == 0; + return V_strcmp( pTable->String( a ), pTable->String( b ) ) == 0; } - bool operator()( UtlSymTableLargeAltKey a, UtlSymLargeId_t b ) const + bool operator()( UtlSymTableLargeAltKey a, UtlSymLargeElm_t b ) const { const char* pString = a.m_pTable->String( b ); int nLength = strlen( pString ); @@ -214,20 +209,20 @@ class CUtlSymbolTableLargeBase if ( a.m_nLength != nLength ) return false; - if ( !CASEINSENSITIVE ) - return strncmp( a.m_pString, pString, a.m_nLength ) == 0; + if ( CASEINSENSITIVE ) + return V_strnicmp( a.m_pString, pString, a.m_nLength ) == 0; else - return _V_strnicmp_fast( a.m_pString, pString, a.m_nLength ) == 0; + return V_strncmp( a.m_pString, pString, a.m_nLength ) == 0; } - bool operator()( UtlSymLargeId_t a, UtlSymTableLargeAltKey b ) const + bool operator()( UtlSymLargeElm_t a, UtlSymTableLargeAltKey b ) const { return operator()( b, a ); } }; - typedef CUtlHashtable>> Hashtable_t; - typedef CUtlVector< MemBlockHandle_t, CUtlMemory_RawAllocator > MemBlocksVec_t; + typedef CUtlHashtable< UtlSymLargeElm_t, empty_t, UtlSymTableLargeHashFunctor, UtlSymTableLargeEqualFunctor, UtlSymTableLargeAltKey, CUtlMemory_RawAllocator< CUtlHashtableEntry< UtlSymLargeElm_t, empty_t > > > Hashtable_t; + typedef CUtlVector< MemBlockHandle_t, CUtlMemory_RawAllocator< MemBlockHandle_t > > MemBlocksVec_t; Hashtable_t m_HashTable; MemBlocksVec_t m_MemBlocks; @@ -238,7 +233,7 @@ class CUtlSymbolTableLargeBase }; template < bool CASEINSENSITIVE, size_t PAGE_SIZE, class MUTEX_TYPE > -inline CUtlSymbolLarge CUtlSymbolTableLargeBase< CASEINSENSITIVE, PAGE_SIZE, MUTEX_TYPE >::Find( unsigned int hash, const char* pString, int nLength ) const +inline CUtlSymbolLarge CUtlSymbolTableLargeBase< CASEINSENSITIVE, PAGE_SIZE, MUTEX_TYPE >::Find( uint32 hash, const char* pString, int nLength ) const { UtlSymTableLargeAltKey key; @@ -255,7 +250,7 @@ inline CUtlSymbolLarge CUtlSymbolTableLargeBase< CASEINSENSITIVE, PAGE_SIZE, MUT } template < bool CASEINSENSITIVE, size_t PAGE_SIZE, class MUTEX_TYPE > -inline CUtlSymbolLarge CUtlSymbolTableLargeBase< CASEINSENSITIVE, PAGE_SIZE, MUTEX_TYPE >::AddString( unsigned int hash, const char* pString, int nLength, bool* created ) +inline CUtlSymbolLarge CUtlSymbolTableLargeBase< CASEINSENSITIVE, PAGE_SIZE, MUTEX_TYPE >::AddString( uint32 hash, const char* pString, int nLength, bool* created ) { if ( m_MemBlocks.Count() >= m_nElementLimit ) { @@ -277,29 +272,25 @@ inline CUtlSymbolLarge CUtlSymbolTableLargeBase< CASEINSENSITIVE, PAGE_SIZE, MUT CUtlSymbolTableLargeBaseTreeEntry_t *entry = (CUtlSymbolTableLargeBaseTreeEntry_t *)m_MemBlockAllocator.GetBlock( block ); - entry->m_Hash = hash; - char *pText = (char *)&entry->m_String[ 0 ]; - memcpy( pText, pString, nLength ); - pText[ nLength ] = '\0'; + entry->Replace( hash, pString, nLength ); - UtlSymLargeId_t id = m_MemBlocks.AddToTail( block + sizeof( LargeSymbolTableHashDecoration_t ) ); + UtlSymLargeElm_t elem = m_MemBlocks.AddToTail( block + sizeof( LargeSymbolTableHashDecoration_t ) ); - empty_t empty; - m_HashTable.Insert( id, empty, hash ); + m_HashTable.Insert( elem, empty_t(), hash ); return entry->ToSymbol(); } template < bool CASEINSENSITIVE, size_t PAGE_SIZE, class MUTEX_TYPE > -inline const char* CUtlSymbolTableLargeBase< CASEINSENSITIVE, PAGE_SIZE, MUTEX_TYPE >::String( UtlSymLargeId_t id ) const +inline const char* CUtlSymbolTableLargeBase< CASEINSENSITIVE, PAGE_SIZE, MUTEX_TYPE >::String( UtlSymLargeElm_t elem ) const { - return ( const char* )m_MemBlockAllocator.GetBlock( m_MemBlocks[ id ] ); + return ( const char* )m_MemBlockAllocator.GetBlock( m_MemBlocks[ elem ] ); } template < bool CASEINSENSITIVE, size_t PAGE_SIZE, class MUTEX_TYPE > -inline unsigned int CUtlSymbolTableLargeBase< CASEINSENSITIVE, PAGE_SIZE, MUTEX_TYPE >::HashValue( UtlSymLargeId_t id ) const +inline uint32 CUtlSymbolTableLargeBase< CASEINSENSITIVE, PAGE_SIZE, MUTEX_TYPE >::Hash( UtlSymLargeElm_t elem ) const { - CUtlSymbolTableLargeBaseTreeEntry_t *entry = (CUtlSymbolTableLargeBaseTreeEntry_t *)m_MemBlockAllocator.GetBlock( m_MemBlocks[ id ] - sizeof( LargeSymbolTableHashDecoration_t ) ); + CUtlSymbolTableLargeBaseTreeEntry_t *entry = (CUtlSymbolTableLargeBaseTreeEntry_t *)m_MemBlockAllocator.GetBlock( m_MemBlocks[ elem ] - sizeof( LargeSymbolTableHashDecoration_t ) ); return entry->HashValue(); } @@ -311,7 +302,7 @@ inline CUtlSymbolLarge CUtlSymbolTableLargeBase< CASEINSENSITIVE, PAGE_SIZE, MUT if ( pString && nLength > 0 && *pString ) { - unsigned int hash = CUtlSymbolLarge_Hash( CASEINSENSITIVE, pString, nLength ); + uint32 hash = CUtlSymbolLarge::Hash< CASEINSENSITIVE >( pString, nLength ); AUTO_LOCK( m_Mutex ); @@ -337,7 +328,7 @@ inline CUtlSymbolLarge CUtlSymbolTableLargeBase< CASEINSENSITIVE, PAGE_SIZE, MUT if ( pString && nLength > 0 && *pString ) { - unsigned int hash = CUtlSymbolLarge_Hash( CASEINSENSITIVE, pString, nLength ); + uint32 hash = CUtlSymbolLarge::Hash< CASEINSENSITIVE >( pString, nLength ); AUTO_LOCK( m_Mutex ); diff --git a/tier1/keyvalues3.cpp b/tier1/keyvalues3.cpp index 49788cf21..3445c93d7 100644 --- a/tier1/keyvalues3.cpp +++ b/tier1/keyvalues3.cpp @@ -10,7 +10,7 @@ #endif KeyValues3::KeyValues3( KV3TypeEx_t type, KV3SubType_t subtype ) : - KeyValues3( -1, type, subtype ) + KeyValues3( KV3_INVALID_CLUSTER_ELEMENT, type, subtype ) { } @@ -19,7 +19,7 @@ KeyValues3::KeyValues3( int cluster_elem, KV3TypeEx_t type, KV3SubType_t subtype m_TypeEx( type ), m_SubType( subtype ), m_nFlags( 0 ), - m_nClusterElement( (uint16)-1 ), + m_nClusterElement( (uint16)KV3_INVALID_CLUSTER_ELEMENT ), m_nNumArrayElements( 0 ), m_nReserved( 0 ) { @@ -39,9 +39,9 @@ void KeyValues3::Alloc( int initial_size, Data_t data, int preallocated_size, bo { case KV3_TYPEEX_ARRAY: { - if(preallocated_size <= 0) + if ( preallocated_size <= 0 ) { - m_Data.m_pArray = AllocArray(); + m_Data.m_Array.m_pRoot = AllocArray(); m_bFreeArrayMemory = true; } else @@ -53,7 +53,7 @@ void KeyValues3::Alloc( int initial_size, Data_t data, int preallocated_size, bo } case KV3_TYPEEX_TABLE: { - if(preallocated_size <= 0) + if ( preallocated_size <= 0 ) { m_Data.m_pTable = AllocTable(); m_bFreeArrayMemory = true; @@ -86,15 +86,15 @@ void KeyValues3::AllocArrayInPlace( int initial_size, Data_t data, int prealloca { int bytes_needed = MAX( CKeyValues3Array::TotalSizeOf( 0 ), CKeyValues3Array::TotalSizeOf( initial_size ) ); - if(bytes_needed > preallocated_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 ); + Construct( m_Data.m_Array.m_pRoot, KV3_INVALID_CLUSTER_ELEMENT, initial_size ); - m_Data.m_pArray = data.m_pArray; + m_Data.m_Array.m_pRoot = data.m_Array.m_pRoot; m_bFreeArrayMemory = should_free; } @@ -102,13 +102,13 @@ void KeyValues3::AllocTableInPlace( int initial_size, Data_t data, int prealloca { int bytes_needed = MAX( CKeyValues3Array::TotalSizeOf( 0 ), CKeyValues3Array::TotalSizeOf( initial_size ) ); - if(bytes_needed > preallocated_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 ); + Construct( data.m_pTable, KV3_INVALID_CLUSTER_ELEMENT, initial_size ); m_Data.m_pTable = data.m_pTable; m_bFreeArrayMemory = should_free; @@ -118,11 +118,11 @@ CKeyValues3Array *KeyValues3::AllocArray( int initial_size ) { auto context = GetContext(); - if(context) + if ( context ) { auto arr = context->AllocArray( initial_size ); - if(arr) + if ( arr ) return arr; } @@ -133,11 +133,11 @@ CKeyValues3Table* KeyValues3::AllocTable( int initial_size ) { auto context = GetContext(); - if(context) + if ( context ) { auto table = context->AllocTable( initial_size ); - if(table) + if ( table ) return table; } @@ -146,27 +146,27 @@ CKeyValues3Table* KeyValues3::AllocTable( int initial_size ) void KeyValues3::FreeArray( CKeyValues3Array *element, bool clearing_context ) { - if(!element) + if ( !element ) return; element->PurgeContent( this, clearing_context ); - if(!m_bFreeArrayMemory) + if ( !m_bFreeArrayMemory ) { Destruct( element ); } else { auto context = GetContext(); - bool raw_allocated = context && context->IsArrayRawAllocated( element ); + bool raw_allocated = context && context->IsArrayAllocated( element ); - if(!raw_allocated && element->GetClusterElement() < 0) + if ( !raw_allocated && element->GetClusterElement() < 0 ) { FreeOnHeap( element ); } - else if(!clearing_context) + else if ( !clearing_context ) { - if(!raw_allocated) + if ( !raw_allocated ) context->FreeArray( element ); else Destruct( element ); @@ -176,30 +176,30 @@ void KeyValues3::FreeArray( CKeyValues3Array *element, bool clearing_context ) void KeyValues3::FreeTable( CKeyValues3Table *element, bool clearing_context ) { - if(!element) + if ( !element ) return; element->PurgeContent( this, clearing_context ); - if(!m_bFreeArrayMemory) + if ( !m_bFreeArrayMemory ) { Destruct( element ); } else { auto context = GetContext(); - bool raw_allocated = context && context->IsTableRawAllocated( element ); + bool raw_allocated = context && context->IsTableAllocated( element ); - if(!raw_allocated && element->GetClusterElement() < 0) + if ( !raw_allocated && element->GetClusterElement() < 0 ) { FreeOnHeap( element ); } - else if(!clearing_context) + else if ( !clearing_context ) { - if(!raw_allocated) - context->FreeTable( element ); - else + if ( raw_allocated ) Destruct( element ); + else + context->FreeTable( element ); } } } @@ -208,17 +208,14 @@ 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 ); + return context ? context->AllocKV( type, subtype ) : new KeyValues3( type, subtype ); } void KeyValues3::FreeMember( KeyValues3 *member ) { auto context = GetContext(); - if(context) + if ( context ) { auto cluster = member->GetCluster(); @@ -261,10 +258,10 @@ void KeyValues3::Free( bool bClearingContext ) } case KV3_TYPEEX_ARRAY: { - FreeArray( m_Data.m_pArray, bClearingContext ); + FreeArray( m_Data.m_Array.m_pRoot, bClearingContext ); m_bFreeArrayMemory = false; - m_Data.m_pArray = nullptr; + m_Data.m_Array.m_pRoot = nullptr; break; } @@ -336,7 +333,7 @@ void KeyValues3::ResolveUnspecified() } } -void KeyValues3::PrepareForType( KV3TypeEx_t type, KV3SubType_t subtype ) +void KeyValues3::PrepareForType( KV3TypeEx_t type, KV3SubType_t subtype, int initial_size, Data_t data, int bytes_available, bool should_free ) { if ( GetTypeEx() == type ) { @@ -363,7 +360,7 @@ void KeyValues3::PrepareForType( KV3TypeEx_t type, KV3SubType_t subtype ) { Free(); m_TypeEx = type; - Alloc(); + Alloc( initial_size, data, bytes_available, should_free ); } m_SubType = subtype; @@ -381,10 +378,10 @@ CKeyValues3Context* KeyValues3::GetContext() const { CKeyValues3Cluster* cluster = GetCluster(); - if ( cluster ) - return cluster->GetContext(); - else + if ( !cluster ) return nullptr; + + return cluster->GetContext(); } KV3MetaData_t* KeyValues3::GetMetaData( CKeyValues3Context** ppCtx ) const @@ -533,62 +530,71 @@ void KeyValues3::SetColor( const Color &color ) int KeyValues3::GetArrayElementCount() const { - if ( GetType() != KV3_TYPE_ARRAY ) - return 0; + if ( IsArray() ) + { + const CKeyValues3Array *pArray = GetKV3Array(); - if ( GetTypeEx() == KV3_TYPEEX_ARRAY ) - return m_Data.m_pArray->Count(); - else - return m_nNumArrayElements; + if ( !pArray ) + return m_nNumArrayElements; + + return pArray->Count(); + } + + return -1; } KeyValues3** KeyValues3::GetArrayBase() { - if ( GetTypeEx() != KV3_TYPEEX_ARRAY ) + CKeyValues3Array *pArray = GetKV3Array(); + + if ( !pArray ) return nullptr; - return m_Data.m_pArray->Base(); + return pArray->Base(); } KeyValues3* KeyValues3::GetArrayElement( int elem ) { - if ( GetTypeEx() != KV3_TYPEEX_ARRAY ) - return nullptr; + CKeyValues3Array *pArray = GetKV3Array(); - if ( elem < 0 || elem >= m_Data.m_pArray->Count() ) + if ( !pArray || elem < 0 || elem >= pArray->Count() ) return nullptr; - return m_Data.m_pArray->Element( elem ); + return pArray->Element( elem ); } KeyValues3* KeyValues3::ArrayInsertElementBefore( int elem ) { - if ( GetTypeEx() != KV3_TYPEEX_ARRAY ) - PrepareForType( KV3_TYPEEX_ARRAY, KV3_SUBTYPE_ARRAY ); + if ( !IsKV3Array() ) + SetToEmptyKV3Array(); - return *m_Data.m_pArray->InsertMultipleBefore( this, elem, 1 ); + return *GetKV3Array()->InsertMultipleBefore( this, elem, 1 ); } KeyValues3* KeyValues3::ArrayAddElementToTail() { - if ( GetTypeEx() != KV3_TYPEEX_ARRAY ) - PrepareForType( KV3_TYPEEX_ARRAY, KV3_SUBTYPE_ARRAY ); + if ( !IsArray() ) + SetToEmptyKV3Array(); + + CKeyValues3Array *pArray = GetKV3Array(); - return *m_Data.m_pArray->InsertMultipleBefore( this, m_Data.m_pArray->Count(), 1 ); + return *pArray->InsertMultipleBefore( this, pArray->Count(), 1 ); } void KeyValues3::ArraySwapItems( int idx1, int idx2 ) { - if(GetTypeEx() != KV3_TYPEEX_ARRAY) + CKeyValues3Array *pArray = GetKV3Array(); + + if ( !pArray ) return; - if(idx1 < 0 || idx1 >= m_Data.m_pArray->Count()) + if ( idx1 < 0 || idx1 >= pArray->Count() ) return; - if(idx2 < 0 || idx2 >= m_Data.m_pArray->Count()) + if ( idx2 < 0 || idx2 >= pArray->Count() ) return; - auto base = GetArrayBase(); + auto base = pArray->Base(); auto temp = base[idx1]; base[idx1] = base[idx2]; @@ -597,18 +603,20 @@ void KeyValues3::ArraySwapItems( int idx1, int idx2 ) void KeyValues3::SetArrayElementCount( int count, KV3TypeEx_t type, KV3SubType_t subtype ) { - if ( GetTypeEx() != KV3_TYPEEX_ARRAY ) - PrepareForType( KV3_TYPEEX_ARRAY, KV3_SUBTYPE_ARRAY ); + if ( !IsKV3Array() ) + SetToEmptyKV3Array(); - m_Data.m_pArray->SetCount( this, count, type, subtype ); + GetKV3Array()->SetCount( this, count, type, subtype ); } void KeyValues3::ArrayRemoveElements( int elem, int num ) { - if ( GetTypeEx() != KV3_TYPEEX_ARRAY ) + CKeyValues3Array *pArray = GetKV3Array(); + + if ( !pArray ) return; - m_Data.m_pArray->RemoveMultiple( this, elem, num ); + pArray->RemoveMultiple( this, elem, num ); } void KeyValues3::NormalizeArray() @@ -658,7 +666,7 @@ bool KeyValues3::ReadArrayInt32( int dest_size, int32* data ) const { int src_size = 0; - if ( GetType() == KV3_TYPE_STRING ) + if ( IsString() ) { CSplitString values( GetString(), " " ); src_size = values.Count(); @@ -672,9 +680,11 @@ bool KeyValues3::ReadArrayInt32( int dest_size, int32* data ) const { case KV3_TYPEEX_ARRAY: { - src_size = m_Data.m_pArray->Count(); + CKeyValues3Array *pArray = m_Data.m_Array.m_pRoot; + + src_size = pArray->Count(); int count = MIN( src_size, dest_size ); - KeyValues3** arr = m_Data.m_pArray->Base(); + KeyValues3** arr = pArray->Base(); for ( int i = 0; i < count; ++i ) data[ i ] = arr[ i ]->GetInt(); break; @@ -725,7 +735,7 @@ bool KeyValues3::ReadArrayFloat32( int dest_size, float32* data ) const { int src_size = 0; - if ( GetType() == KV3_TYPE_STRING ) + if ( IsString() ) { CSplitString values( GetString(), " " ); src_size = values.Count(); @@ -739,9 +749,11 @@ bool KeyValues3::ReadArrayFloat32( int dest_size, float32* data ) const { case KV3_TYPEEX_ARRAY: { - src_size = m_Data.m_pArray->Count(); + CKeyValues3Array *pArray = m_Data.m_Array.m_pRoot; + + src_size = pArray->Count(); int count = MIN( src_size, dest_size ); - KeyValues3** arr = m_Data.m_pArray->Base(); + KeyValues3** arr = pArray->Base(); for ( int i = 0; i < count; ++i ) data[ i ] = arr[ i ]->GetFloat(); break; @@ -761,7 +773,7 @@ bool KeyValues3::ReadArrayFloat32( int dest_size, float32* data ) const data[ i ] = ( float32 )m_Data.m_Array.m_f64[ i ]; break; } - default: + default: break; } } @@ -772,80 +784,92 @@ bool KeyValues3::ReadArrayFloat32( int dest_size, float32* data ) const return ( src_size == dest_size ); } -int KeyValues3::GetMemberCount() const +void KeyValues3::SetToEmptyTable() { - if ( GetType() != KV3_TYPE_TABLE ) - return 0; - - return m_Data.m_pTable->GetMemberCount(); + PrepareForType( KV3_TYPEEX_TABLE, KV3_SUBTYPE_TABLE ); + GetTable()->RemoveAll( this ); } -CKeyValues3Table *KeyValues3::GetTableRaw() +int KeyValues3::GetMemberCount() const { - if(GetType() != KV3_TYPE_TABLE) - return nullptr; + const CKeyValues3Table *pTable = GetTable(); - return m_Data.m_pTable; + if ( pTable ) + return pTable->GetMemberCount(); + + return KV3_INVALID_MEMBER; } KeyValues3* KeyValues3::GetMember( KV3MemberId_t id ) { - if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_Data.m_pTable->GetMemberCount() ) + CKeyValues3Table *pTable = GetTable(); + + if ( !pTable || id < 0 || id >= pTable->GetMemberCount() ) return nullptr; - return m_Data.m_pTable->GetMember( id ); + return pTable->GetMember( id ); } const char* KeyValues3::GetMemberName( KV3MemberId_t id ) const { - if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_Data.m_pTable->GetMemberCount() ) + const CKeyValues3Table *pTable = GetTable(); + + if ( !pTable || id < 0 || id >= pTable->GetMemberCount() ) return nullptr; - return m_Data.m_pTable->GetMemberName( id ); + return pTable->GetMemberName( id ); } -CKV3MemberName KeyValues3::GetMemberNameEx( KV3MemberId_t id ) const +CKV3MemberName KeyValues3::GetKV3MemberName( KV3MemberId_t id ) const { - if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_Data.m_pTable->GetMemberCount() ) + const CKeyValues3Table *pTable = GetTable(); + + if ( !pTable || id < 0 || id >= pTable->GetMemberCount() ) return CKV3MemberName(); - return CKV3MemberName( m_Data.m_pTable->GetMemberHash( id ), m_Data.m_pTable->GetMemberName( id ) ); + return CKV3MemberName( pTable->GetMemberHash( id ), pTable->GetMemberName( id ) ); } -CUtlStringToken KeyValues3::GetMemberHash( KV3MemberId_t id ) const +CKV3MemberHash KeyValues3::GetMemberHash( KV3MemberId_t id ) const { - if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_Data.m_pTable->GetMemberCount() ) - return CUtlStringToken(); + const CKeyValues3Table *pTable = GetTable(); + + if ( !pTable || id < 0 || id >= pTable->GetMemberCount() ) + return CKV3MemberHash(); - return m_Data.m_pTable->GetMemberHash( id ); + return pTable->GetMemberHash( id ); } -KeyValues3* KeyValues3::FindMember( const CKV3MemberName &name, KeyValues3* defaultValue ) +KeyValues3* KeyValues3::Internal_FindMember( const CKV3MemberName &name, KV3MemberId_t &next, KeyValues3* defaultValue ) { - if ( GetType() != KV3_TYPE_TABLE ) + CKeyValues3Table *pTable = GetTable(); + + if ( !pTable ) return defaultValue; - KV3MemberId_t id = m_Data.m_pTable->FindMember( name ); + KV3MemberId_t id = pTable->Internal_FindMember( name, next ); if ( id == KV3_INVALID_MEMBER ) return defaultValue; - return m_Data.m_pTable->GetMember( id ); + return pTable->GetMember( id ); } KeyValues3* KeyValues3::FindOrCreateMember( const CKV3MemberName &name, bool *pCreated ) { - if ( GetType() != KV3_TYPE_TABLE ) - PrepareForType( KV3_TYPEEX_TABLE, KV3_SUBTYPE_TABLE ); + if ( !IsTable() ) + SetToEmptyTable(); - KV3MemberId_t id = m_Data.m_pTable->FindMember( name ); + CKeyValues3Table *pTable = GetTable(); + + KV3MemberId_t id = pTable->FindMember( name ); if ( id == KV3_INVALID_MEMBER ) { if ( pCreated ) *pCreated = true; - id = m_Data.m_pTable->CreateMember( this, name ); + id = pTable->CreateMember( this, name ); } else { @@ -853,55 +877,95 @@ KeyValues3* KeyValues3::FindOrCreateMember( const CKV3MemberName &name, bool *pC *pCreated = false; } - return m_Data.m_pTable->GetMember( id ); + return pTable->GetMember( id ); } -void KeyValues3::SetToEmptyTable() +KeyValues3* KeyValues3::RenameMember( const CKV3MemberName &name, const CKV3MemberName &newName ) { - PrepareForType( KV3_TYPEEX_TABLE, KV3_SUBTYPE_TABLE ); - m_Data.m_pTable->RemoveAll( this ); + if ( newName.IsEmpty() ) + return nullptr; + + CKeyValues3Table *pTable = GetTable(); + + if ( !pTable ) + return nullptr; + + KV3MemberId_t id = pTable->FindMember( name ); + + if ( id == KV3_INVALID_MEMBER ) + return nullptr; + + pTable->RenameMember( this, id, newName ); + + return pTable->GetMember( id ); } bool KeyValues3::RemoveMember( KV3MemberId_t id ) { - if ( GetType() != KV3_TYPE_TABLE || id < 0 || id >= m_Data.m_pTable->GetMemberCount() ) + CKeyValues3Table *pTable = GetTable(); + + if ( !pTable || id < 0 || id >= pTable->GetMemberCount() ) return false; - m_Data.m_pTable->RemoveMember( this, id ); + pTable->RemoveMember( this, id ); return true; } bool KeyValues3::RemoveMember( const KeyValues3* kv ) { - if ( GetType() != KV3_TYPE_TABLE ) + CKeyValues3Table *pTable = GetTable(); + + if ( !pTable ) return false; - KV3MemberId_t id = m_Data.m_pTable->FindMember( kv ); + KV3MemberId_t id = pTable->FindMember( kv ); if ( id == KV3_INVALID_MEMBER ) return false; - m_Data.m_pTable->RemoveMember( this, id ); + pTable->RemoveMember( this, id ); return true; } bool KeyValues3::RemoveMember( const CKV3MemberName &name ) { - if ( GetType() != KV3_TYPE_TABLE ) + CKeyValues3Table *pTable = GetTable(); + + if ( !pTable ) return false; - KV3MemberId_t id = m_Data.m_pTable->FindMember( name ); + KV3MemberId_t id = pTable->FindMember( name ); if ( id == KV3_INVALID_MEMBER ) return false; - m_Data.m_pTable->RemoveMember( this, id ); + pTable->RemoveMember( this, id ); return true; } +bool KeyValues3::HasInvalidMemberNames() const +{ + const CKeyValues3Table *pTable = GetTable(); + + if ( !pTable ) + return false; + + return pTable->HasInvalidMemberNames(); +} + +void KeyValues3::SetHasInvalidMemberNames( bool bValue ) +{ + CKeyValues3Table *pTable = GetTable(); + + if ( !pTable ) + return; + + pTable->SetHasInvalidMemberNames( bValue ); +} + const char* KeyValues3::GetTypeAsString() const { static const char* s_Types[] = @@ -1065,7 +1129,7 @@ const char* KeyValues3::ToString( CBufferString& buff, uint flags ) const bool unprintable = false; CBufferStringN<128> temp; - CKeyValues3Array::Element_t* arr = m_Data.m_pArray->Base(); + CKeyValues3Array::Element_t* arr = m_Data.m_Array.m_pRoot->Base(); for ( int i = 0; i < elements; ++i ) { switch ( arr[i]->GetType() ) @@ -1191,7 +1255,7 @@ const char* KeyValues3::ToString( CBufferString& buff, uint flags ) const void KeyValues3::CopyFrom( const KeyValues3* pSrc ) { - if(this == pSrc) + if ( this == pSrc ) return; SetToNull(); @@ -1237,8 +1301,8 @@ void KeyValues3::CopyFrom( const KeyValues3* pSrc ) { case KV3_TYPEEX_ARRAY: { - PrepareForType( KV3_TYPEEX_ARRAY, KV3_SUBTYPE_ARRAY ); - m_Data.m_pArray->CopyFrom( this, pSrc->m_Data.m_pArray ); + SetToEmptyKV3Array(); + m_Data.m_Array.m_pRoot->CopyFrom( this, pSrc->m_Data.m_Array.m_pRoot ); break; } case KV3_TYPEEX_ARRAY_FLOAT32: @@ -1267,7 +1331,7 @@ void KeyValues3::CopyFrom( const KeyValues3* pSrc ) case KV3_TYPE_TABLE: { SetToEmptyTable(); - m_Data.m_pTable->CopyFrom( this, pSrc->m_Data.m_pTable ); + GetTable()->CopyFrom( this, pSrc->GetTable() ); break; } default: @@ -1278,6 +1342,36 @@ void KeyValues3::CopyFrom( const KeyValues3* pSrc ) m_nFlags = pSrc->m_nFlags; } +void KeyValues3::OverlayKeysFrom( KeyValues3 *parent, bool depth ) +{ + if ( !IsTable() ) + SetToNull(); + + CKeyValues3Table *pParentTable = parent->GetTable(); + + if ( !pParentTable ) + return; + + auto parent_hashes = pParentTable->HashesBase(); + auto parent_members = pParentTable->MembersBase(); + auto parent_names = pParentTable->NamesBase(); + + FOR_EACH_KV3_TABLE( *pParentTable, id ) + { + KeyValues3 *kv = FindOrCreateMember( CKV3MemberName( parent_hashes[id], parent_names[id] ) ); + KeyValues3 *parent_kv = parent_members[id]; + + if ( depth && kv->IsTable() && parent_kv->IsTable() ) + { + OverlayKeysFrom( parent_kv, true ); + } + else + { + CopyFrom( parent_kv ); + } + } +} + KeyValues3& KeyValues3::operator=( const KeyValues3& src ) { if ( this == &src ) @@ -1292,26 +1386,28 @@ void CKeyValues3Iterator::Init( KeyValues3 *kv ) { m_Stack.Purge(); - if(kv) + if ( kv ) { auto entry = m_Stack.AddToTailGetPtr(); entry->m_nIndex = -1; entry->m_pKV = kv; + + Advance(); } } void CKeyValues3Iterator::Advance() { - while(m_Stack.Count() > 0) + while ( m_Stack.Count() > 0 ) { auto &entry = m_Stack[m_Stack.Count() - 1]; auto kv = entry.m_pKV; - if(kv->GetType() == KV3_TYPE_ARRAY) + if ( kv->GetType() == KV3_TYPE_ARRAY ) { entry.m_nIndex++; - if(entry.m_nIndex < kv->GetArrayElementCount()) + if ( entry.m_nIndex < kv->GetArrayElementCount() ) { auto new_entry = m_Stack.AddToTailGetPtr(); new_entry->m_nIndex = -1; @@ -1319,11 +1415,11 @@ void CKeyValues3Iterator::Advance() return; } } - else if(kv->GetType() == KV3_TYPE_TABLE) + else if ( kv->GetType() == KV3_TYPE_TABLE ) { entry.m_nIndex++; - if(entry.m_nIndex < kv->GetMemberCount()) + if ( entry.m_nIndex < kv->GetMemberCount() ) { auto new_entry = m_Stack.AddToTailGetPtr(); new_entry->m_nIndex = -1; @@ -1350,7 +1446,7 @@ CKeyValues3Array::CKeyValues3Array( int cluster_elem, int alloc_size ) : CKeyValues3ArrayCluster* CKeyValues3Array::GetCluster() const { - if ( m_nClusterElement == -1 ) + if ( !HasCluster() ) return nullptr; return GET_OUTER( CKeyValues3ArrayCluster, m_Values[ m_nClusterElement ] ); @@ -1360,10 +1456,10 @@ CKeyValues3Context* CKeyValues3Array::GetContext() const { CKeyValues3ArrayCluster* cluster = GetCluster(); - if ( cluster ) - return cluster->GetContext(); - else + if ( !cluster ) return nullptr; + + return cluster->GetContext(); } KeyValues3* CKeyValues3Array::Element( int i ) @@ -1375,10 +1471,10 @@ KeyValues3* CKeyValues3Array::Element( int i ) void CKeyValues3Array::EnsureElementCapacity( int count, bool force, bool dont_move ) { - if(count <= m_nAllocatedChunks) + if ( count <= m_nAllocatedChunks ) return; - if(count > ALLOC_KV3ARRAY_MAX) + if ( count > ALLOC_KV3ARRAY_MAX ) { Plat_FatalErrorFunc( "%s: element count overflow (%u)\n", __FUNCTION__, count ); DebuggerBreak(); @@ -1389,7 +1485,7 @@ void CKeyValues3Array::EnsureElementCapacity( int count, bool force, bool dont_m Element_t *new_base = nullptr; - if(m_bIsDynamicallySized) + if ( m_bIsDynamicallySized ) { new_base = (Element_t *)realloc( m_pDynamicElements, new_byte_size ); } @@ -1397,7 +1493,7 @@ void CKeyValues3Array::EnsureElementCapacity( int count, bool force, bool dont_m { new_base = (Element_t *)malloc( new_byte_size ); - if(m_nCount > 0 && !dont_move) + if ( m_nCount > 0 && !dont_move ) { memmove( new_base, Base(), sizeof( Element_t ) * m_nCount ); } @@ -1411,7 +1507,8 @@ void CKeyValues3Array::EnsureElementCapacity( int count, bool force, bool dont_m 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++) + + for ( int i = count; i < m_nCount; i++ ) { parent->FreeMember( elements_base[i] ); } @@ -1419,7 +1516,7 @@ void CKeyValues3Array::SetCount( KeyValues3 *parent, int count, KV3TypeEx_t type EnsureElementCapacity( count ); elements_base = Base(); - for(int i = m_nCount; i < count; i++) + for ( int i = m_nCount; i < count; i++ ) { elements_base[i] = parent->AllocMember( type, subtype ); } @@ -1429,23 +1526,25 @@ void CKeyValues3Array::SetCount( KeyValues3 *parent, int count, KV3TypeEx_t type CKeyValues3Array::Element_t* CKeyValues3Array::InsertMultipleBefore( KeyValues3 *parent, int from, int num ) { - if(from < 0 || from > m_nCount) + 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) + 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) + + if ( from < m_nCount ) { memmove( (void *)base[from + num], (void *)base[from], sizeof(Element_t) * (m_nCount - from) ); } @@ -1487,7 +1586,7 @@ void CKeyValues3Array::RemoveMultiple( KeyValues3 *parent, int from, int num ) void CKeyValues3Array::PurgeBuffers() { - if(m_bIsDynamicallySized) + if ( m_bIsDynamicallySized ) { free( m_pDynamicElements ); m_nAllocatedChunks = m_nInitialSize; @@ -1499,7 +1598,7 @@ void CKeyValues3Array::PurgeBuffers() void CKeyValues3Array::PurgeContent( KeyValues3 *parent, bool clearing_context ) { - if(!clearing_context && parent) + if ( !clearing_context && parent ) { auto elements_base = Base(); @@ -1517,7 +1616,7 @@ CKeyValues3Table::CKeyValues3Table( int cluster_elem, int alloc_size ) : m_nCount( 0 ), m_nInitialSize( MIN( alloc_size, 255 ) ), m_bIsDynamicallySized( false ), - m_unk001( false ), + m_bHasInvalidMemberNames( false ), m_unk002( false ), m_pDynamicBuffer( nullptr ) { @@ -1525,7 +1624,7 @@ CKeyValues3Table::CKeyValues3Table( int cluster_elem, int alloc_size ) : CKeyValues3TableCluster* CKeyValues3Table::GetCluster() const { - if ( m_nClusterElement == -1 ) + if ( !HasCluster() ) return nullptr; return GET_OUTER( CKeyValues3TableCluster, m_Values[ m_nClusterElement ] ); @@ -1535,10 +1634,10 @@ CKeyValues3Context* CKeyValues3Table::GetContext() const { CKeyValues3TableCluster* cluster = GetCluster(); - if ( cluster ) - return cluster->GetContext(); - else + if ( !cluster ) return nullptr; + + return cluster->GetContext(); } KeyValues3* CKeyValues3Table::GetMember( KV3MemberId_t id ) @@ -1573,7 +1672,7 @@ void CKeyValues3Table::EnableFastSearch() for ( int i = 0; i < m_nCount; ++i ) { - m_pFastSearch->m_member_ids.Insert( pHashes[i].GetHashCode(), i ); + m_pFastSearch->m_member_ids.Insert( pHashes[i], i ); } m_pFastSearch->m_ignore = false; @@ -1582,10 +1681,10 @@ void CKeyValues3Table::EnableFastSearch() void CKeyValues3Table::EnsureMemberCapacity( int count, bool force, bool dont_move ) { - if(count <= m_nAllocatedChunks) + if ( count <= m_nAllocatedChunks ) return; - if(count > ALLOC_KV3TABLE_MAX) + if ( count > ALLOC_KV3TABLE_MAX ) { Plat_FatalErrorFunc( "%s member count overflow (%u)\n", __FUNCTION__, count ); DebuggerBreak(); @@ -1596,7 +1695,7 @@ void CKeyValues3Table::EnsureMemberCapacity( int count, bool force, bool dont_mo void *new_base = nullptr; - if(m_bIsDynamicallySized) + if ( m_bIsDynamicallySized ) { new_base = realloc( m_pDynamicBuffer, new_byte_size ); @@ -1608,7 +1707,7 @@ void CKeyValues3Table::EnsureMemberCapacity( int count, bool force, bool dont_mo { new_base = malloc( new_byte_size ); - if(m_nCount > 0 && !dont_move) + 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 ) ); @@ -1622,20 +1721,7 @@ void CKeyValues3Table::EnsureMemberCapacity( int count, bool force, bool dont_mo 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 ) +KV3MemberId_t CKeyValues3Table::Internal_FindMember( const CKV3MemberName &name, KV3MemberId_t &next ) { bool bFastSearch = false; @@ -1650,32 +1736,49 @@ KV3MemberId_t CKeyValues3Table::FindMember( const CKV3MemberName &name ) } } else - { bFastSearch = true; - } } if ( bFastSearch ) { - UtlHashHandle_t h = m_pFastSearch->m_member_ids.Find( name.GetHashCode() ); + UtlHashHandle_t h = m_pFastSearch->m_member_ids.Find( name ); if ( h != m_pFastSearch->m_member_ids.InvalidHandle() ) - return m_pFastSearch->m_member_ids[ h ]; + { + KV3MemberId_t res = m_pFastSearch->m_member_ids[ h ]; + + next = res + 1; + + return res; + } } else { const Hash_t* pHashes = HashesBase(); - for ( int i = 0; i < m_nCount; ++i ) - { - if ( pHashes[i] == name.GetHashCode() ) + for ( KV3MemberId_t i = 0; i < m_nCount; ++i ) + if ( pHashes[i] == name ) + { + next = i + 1; + return i; - } + } } return KV3_INVALID_MEMBER; } +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::CreateMember( KeyValues3 *parent, const CKV3MemberName &name, bool name_external ) { if ( GetMemberCount() >= 128 && !m_pFastSearch ) @@ -1693,25 +1796,25 @@ KV3MemberId_t CKeyValues3Table::CreateMember( KeyValues3 *parent, const CKV3Memb members_base[curr] = parent->AllocMember(); hashes_base[curr] = name.GetHashCode(); - Flags_t flags = 0; - if(name_external) + auto &curr_name = names_base[curr]; + auto &flags = flags_base[curr]; + + if ( name_external ) { - names_base[curr] = name.GetString(); + curr_name = name.GetString(); flags |= MEMBER_FLAG_EXTERNAL_NAME; } else { auto context = parent->GetContext(); - if(context) - names_base[curr] = context->AllocString( name.GetString() ); + if ( context ) + curr_name = context->AllocString( name.GetString() ); else - names_base[curr] = strdup( name.GetString() ); + curr_name = strdup( name.GetString() ); } - flags_base[curr] = flags; - if ( m_pFastSearch && !m_pFastSearch->m_ignore ) m_pFastSearch->m_member_ids.Insert( name.GetHashCode(), curr ); @@ -1741,11 +1844,11 @@ void CKeyValues3Table::CopyFrom( KeyValues3 *parent, const CKeyValues3Table* src memmove( hashes_base, src_hashes_base, sizeof(Hash_t) * new_size ); - for(int i = 0; i < new_size; i++) + for ( int i = 0; i < new_size; i++ ) { flags_base[i] = src_flags_base[i] & ~MEMBER_FLAG_EXTERNAL_NAME; - if(context) + if ( context ) names_base[i] = context->AllocString( src_names_base[i] ); else names_base[i] = strdup( src_names_base[i] ); @@ -1758,6 +1861,40 @@ void CKeyValues3Table::CopyFrom( KeyValues3 *parent, const CKeyValues3Table* src EnableFastSearch(); } +void CKeyValues3Table::RenameMember( KeyValues3 *parent, KV3MemberId_t id, const CKV3MemberName &newName ) +{ + Hash_t* hashes_base = HashesBase(); + Name_t* names_base = NamesBase(); + Flags_t* flags_base = FlagsBase(); + + auto &name = names_base[id]; + auto &flags = flags_base[id]; + + hashes_base[id] = newName; + + auto context = parent->GetContext(); + + if ( context ) + { + name = context->AllocString( newName.GetString() ); + } + else + { + if ( flags & MEMBER_FLAG_EXTERNAL_NAME ) + flags &= ~MEMBER_FLAG_EXTERNAL_NAME; + else if ( name ) + free( (void *)name ); + + name = strdup( newName.GetString() ); + } + + if ( m_pFastSearch ) + { + m_pFastSearch->m_ignore = true; + m_pFastSearch->m_ignores_counter = 1; + } +} + void CKeyValues3Table::RemoveMember( KeyValues3 *parent, KV3MemberId_t id ) { m_nCount--; @@ -1769,7 +1906,7 @@ void CKeyValues3Table::RemoveMember( KeyValues3 *parent, KV3MemberId_t id ) parent->FreeMember( members_base[id] ); - if((flags_base[id] & MEMBER_FLAG_EXTERNAL_NAME) == 0 && !parent->GetContext() && names_base[id]) + if ( ( flags_base[id] & MEMBER_FLAG_EXTERNAL_NAME ) == 0 && !parent->GetContext() && names_base[id] ) { free( (void *)names_base[id] ); } @@ -1802,19 +1939,19 @@ void CKeyValues3Table::RemoveAll( KeyValues3 *parent, int new_size ) { parent->FreeMember( members_base[i] ); - if((flags_base[i] & MEMBER_FLAG_EXTERNAL_NAME) == 0 && !parent->GetContext() && names_base[i]) + if ( ( flags_base[i] & MEMBER_FLAG_EXTERNAL_NAME ) == 0 && !parent->GetContext() && names_base[i] ) { free( (void *)names_base[i] ); } } m_nCount = 0; - if(new_size > 0) + if ( new_size > 0 ) { EnsureMemberCapacity( new_size, true, true ); } - if(new_size < 128) + if ( new_size < 128 ) { PurgeFastSearch(); } @@ -1841,12 +1978,12 @@ void CKeyValues3Table::PurgeContent( KeyValues3 *parent, bool bClearingContext ) for ( int i = 0; i < m_nCount; ++i ) { - if(!bClearingContext && parent) + if ( !bClearingContext && parent ) { parent->FreeMember( members_base[i] ); } - if((flags_base[i] & MEMBER_FLAG_EXTERNAL_NAME) == 0 && parent && !parent->GetContext() && names_base[i]) + if ( ( flags_base[i] & MEMBER_FLAG_EXTERNAL_NAME ) == 0 && parent && !parent->GetContext() && names_base[i] ) { free( (void *)names_base[i] ); } @@ -2014,4 +2151,4 @@ void CKeyValues3Context::FreeKV( KeyValues3* kv ) // Free( kv, &m_KV3BaseCluster, m_pKV3FreeCluster ); } -#include "tier0/memdbgoff.h" \ No newline at end of file +#include "tier0/memdbgoff.h"