diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /external/vpc/vstdlib/keyvaluessystem.cpp | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'external/vpc/vstdlib/keyvaluessystem.cpp')
| -rw-r--r-- | external/vpc/vstdlib/keyvaluessystem.cpp | 620 |
1 files changed, 620 insertions, 0 deletions
diff --git a/external/vpc/vstdlib/keyvaluessystem.cpp b/external/vpc/vstdlib/keyvaluessystem.cpp new file mode 100644 index 0000000..44c8e6d --- /dev/null +++ b/external/vpc/vstdlib/keyvaluessystem.cpp @@ -0,0 +1,620 @@ +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include <vstdlib/ikeyvaluessystem.h> +#include <keyvalues.h> +#include "tier1/mempool.h" +#include "utlsymbol.h" +#include "utlmap.h" +#include "tier0/threadtools.h" +#include "tier1/memstack.h" +#include "tier1/convar.h" + +#ifdef _PS3 +#include "ps3/ps3_core.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +#ifdef NO_SBH // no need to pool if using tier0 small block heap +#define KEYVALUES_USE_POOL 1 +#endif + +// +// Defines platform-endian-specific macros: +// MEM_4BYTES_AS_0_AND_3BYTES : present a 4 byte uint32 as a memory +// layout where first memory byte is zero +// and the other 3 bytes represent value +// MEM_4BYTES_FROM_0_AND_3BYTES: unpack from memory with first zero byte +// and 3 value bytes the original uint32 value +// +// used for efficiently reading/writing storing 3 byte values into memory +// region immediately following a null-byte-terminated string, essentially +// sharing the null-byte-terminator with the first memory byte +// +#if defined( PLAT_LITTLE_ENDIAN ) +// Number in memory has lowest-byte in front, use shifts to make it zero +#define MEM_4BYTES_AS_0_AND_3BYTES( x4bytes ) ( ( (uint32) (x4bytes) ) << 8 ) +#define MEM_4BYTES_FROM_0_AND_3BYTES( x03bytes ) ( ( (uint32) (x03bytes) ) >> 8 ) +#endif +#if defined( PLAT_BIG_ENDIAN ) +// Number in memory has highest-byte in front, use masking to make it zero +#define MEM_4BYTES_AS_0_AND_3BYTES( x4bytes ) ( ( (uint32) (x4bytes) ) & 0x00FFFFFF ) +#define MEM_4BYTES_FROM_0_AND_3BYTES( x03bytes ) ( ( (uint32) (x03bytes) ) & 0x00FFFFFF ) +#endif + +//----------------------------------------------------------------------------- +// Purpose: Central storage point for KeyValues memory and symbols +//----------------------------------------------------------------------------- +class CKeyValuesSystem : public IKeyValuesSystem +{ +public: + CKeyValuesSystem(); + ~CKeyValuesSystem(); + + // registers the size of the KeyValues in the specified instance + // so it can build a properly sized memory pool for the KeyValues objects + // the sizes will usually never differ but this is for versioning safety + void RegisterSizeofKeyValues(int size); + + // allocates/frees a KeyValues object from the shared mempool + void *AllocKeyValuesMemory(int size); + void FreeKeyValuesMemory(void *pMem); + + // symbol table access (used for key names) + HKeySymbol GetSymbolForString( const char *name, bool bCreate ); + const char *GetStringForSymbol(HKeySymbol symbol); + + // returns the wide version of ansi, also does the lookup on #'d strings + void GetLocalizedFromANSI( const char *ansi, wchar_t *outBuf, int unicodeBufferSizeInBytes); + void GetANSIFromLocalized( const wchar_t *wchar, char *outBuf, int ansiBufferSizeInBytes ); + + // for debugging, adds KeyValues record into global list so we can track memory leaks + virtual void AddKeyValuesToMemoryLeakList(void *pMem, HKeySymbol name); + virtual void RemoveKeyValuesFromMemoryLeakList(void *pMem); + + // set/get a value for keyvalues resolution symbol + // e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE] + virtual void SetKeyValuesExpressionSymbol( const char *name, bool bValue ); + virtual bool GetKeyValuesExpressionSymbol( const char *name ); + + // symbol table access from code with case-preserving requirements (used for key names) + virtual HKeySymbol GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *name, bool bCreate = true ); + +private: +#ifdef KEYVALUES_USE_POOL + CUtlMemoryPool *m_pMemPool; +#endif + int m_iMaxKeyValuesSize; + + // string hash table + /* + Here's the way key values system data structures are laid out: + hash table with 2047 hash buckets: + [0] { hash_item_t } + [1] + [2] + ... + each hash_item_t's stringIndex is an offset in m_Strings memory + at that offset we store the actual null-terminated string followed + by another 3 bytes for an alternative capitalization. + These 3 trailing bytes are set to 0 if no alternative capitalization + variants are present in the dictionary. + These trailing 3 bytes are interpreted as stringIndex into m_Strings + memory for the next alternative capitalization + + Getting a string value by HKeySymbol : constant time access at the + string memory represented by stringIndex + + Getting a symbol for a string value: + 1) compute the hash + 2) start walking the hash-bucket using special version of stricmp + until a case insensitive match is found + 3a) for case-insensitive lookup return the found stringIndex + 3b) for case-sensitive lookup keep walking the list of alternative + capitalizations using strcmp until exact case match is found + */ + CMemoryStack m_Strings; + struct hash_item_t + { + int stringIndex; + hash_item_t *next; + }; + CUtlMemoryPool m_HashItemMemPool; + CUtlVector<hash_item_t> m_HashTable; + int CaseInsensitiveHash(const char *string, int iBounds); + + struct MemoryLeakTracker_t + { + int nameIndex; + void *pMem; + }; + static bool MemoryLeakTrackerLessFunc( const MemoryLeakTracker_t &lhs, const MemoryLeakTracker_t &rhs ) + { + return lhs.pMem < rhs.pMem; + } + CUtlRBTree<MemoryLeakTracker_t, int> m_KeyValuesTrackingList; + + CUtlMap< HKeySymbol, bool > m_KvConditionalSymbolTable; + + CThreadFastMutex m_mutex; +}; + +// EXPOSE_SINGLE_INTERFACE(CKeyValuesSystem, IKeyValuesSystem, KEYVALUES_INTERFACE_VERSION); + +//----------------------------------------------------------------------------- +// Instance singleton and expose interface to rest of code +//----------------------------------------------------------------------------- +static CKeyValuesSystem g_KeyValuesSystem; + +IKeyValuesSystem *KeyValuesSystem() +{ + return &g_KeyValuesSystem; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CKeyValuesSystem::CKeyValuesSystem() : + m_HashItemMemPool(sizeof(hash_item_t), 64, CUtlMemoryPool::GROW_FAST, "CKeyValuesSystem::m_HashItemMemPool"), + m_KeyValuesTrackingList(0, 0, MemoryLeakTrackerLessFunc), + m_KvConditionalSymbolTable( DefLessFunc( HKeySymbol ) ) +{ + MEM_ALLOC_CREDIT(); + // initialize hash table + m_HashTable.AddMultipleToTail(2047); + for (int i = 0; i < m_HashTable.Count(); i++) + { + m_HashTable[i].stringIndex = 0; + m_HashTable[i].next = NULL; + } + + m_Strings.Init( "CKeyValuesSystem::m_Strings", 4*1024*1024, 64*1024, 0, 4 ); + // Make 0 stringIndex to never be returned, by allocating + // and wasting minimal number of alignment bytes now: + char *pszEmpty = ((char *)m_Strings.Alloc(1)); + *pszEmpty = 0; + +#ifdef KEYVALUES_USE_POOL + m_pMemPool = NULL; +#endif + m_iMaxKeyValuesSize = sizeof(KeyValues); +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CKeyValuesSystem::~CKeyValuesSystem() +{ +#ifdef KEYVALUES_USE_POOL +#ifdef _DEBUG + // display any memory leaks + if (m_pMemPool && m_pMemPool->Count() > 0) + { + DevMsg("Leaked KeyValues blocks: %d\n", m_pMemPool->Count()); + } + + // iterate all the existing keyvalues displaying their names + for (int i = 0; i < m_KeyValuesTrackingList.MaxElement(); i++) + { + if (m_KeyValuesTrackingList.IsValidIndex(i)) + { + DevMsg("\tleaked KeyValues(%s)\n", &m_Strings[m_KeyValuesTrackingList[i].nameIndex]); + } + } +#endif + + delete m_pMemPool; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: registers the size of the KeyValues in the specified instance +// so it can build a properly sized memory pool for the KeyValues objects +// the sizes will usually never differ but this is for versioning safety +//----------------------------------------------------------------------------- +void CKeyValuesSystem::RegisterSizeofKeyValues(int size) +{ + if (size > m_iMaxKeyValuesSize) + { + m_iMaxKeyValuesSize = size; + } +} + +static void KVLeak( char const *fmt, ... ) +{ + va_list argptr; + char data[1024]; + + va_start(argptr, fmt); + V_vsnprintf(data, sizeof( data ), fmt, argptr); + va_end(argptr); + + Msg( data ); +} + +//----------------------------------------------------------------------------- +// Purpose: allocates a KeyValues object from the shared mempool +//----------------------------------------------------------------------------- +void *CKeyValuesSystem::AllocKeyValuesMemory(int size) +{ +#ifdef KEYVALUES_USE_POOL + // allocate, if we don't have one yet + if (!m_pMemPool) + { + m_pMemPool = new CUtlMemoryPool(m_iMaxKeyValuesSize, 1024, CUtlMemoryPool::GROW_FAST, "CKeyValuesSystem::m_pMemPool" ); + m_pMemPool->SetErrorReportFunc( KVLeak ); + } + + return m_pMemPool->Alloc(size); +#else + return malloc( size ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: frees a KeyValues object from the shared mempool +//----------------------------------------------------------------------------- +void CKeyValuesSystem::FreeKeyValuesMemory(void *pMem) +{ +#ifdef KEYVALUES_USE_POOL + m_pMemPool->Free(pMem); +#else + free( pMem ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: symbol table access (used for key names) +//----------------------------------------------------------------------------- +HKeySymbol CKeyValuesSystem::GetSymbolForString( const char *name, bool bCreate ) +{ + if ( !name ) + { + return (-1); + } + + AUTO_LOCK( m_mutex ); + MEM_ALLOC_CREDIT(); + + int hash = CaseInsensitiveHash(name, m_HashTable.Count()); + int i = 0; + hash_item_t *item = &m_HashTable[hash]; + while (1) + { + if (!stricmp(name, (char *)m_Strings.GetBase() + item->stringIndex )) + { + return (HKeySymbol)item->stringIndex; + } + + i++; + + if (item->next == NULL) + { + if ( !bCreate ) + { + // not found + return -1; + } + + // we're not in the table + if (item->stringIndex != 0) + { + // first item is used, an new item + item->next = (hash_item_t *)m_HashItemMemPool.Alloc(sizeof(hash_item_t)); + item = item->next; + } + + // build up the new item + item->next = NULL; + int numStringBytes = strlen(name); + char *pString = (char *)m_Strings.Alloc( numStringBytes + 1 + 3 ); + if ( !pString ) + { + Error( "Out of keyvalue string space" ); + return -1; + } + item->stringIndex = pString - (char *)m_Strings.GetBase(); + V_memcpy( pString, name, numStringBytes ); + * reinterpret_cast< uint32 * >( pString + numStringBytes ) = 0; // string null-terminator + 3 alternative spelling bytes + return (HKeySymbol)item->stringIndex; + } + + item = item->next; + } + + // shouldn't be able to get here + Assert(0); + return (-1); +} + +//----------------------------------------------------------------------------- +// Purpose: symbol table access (used for key names) +//----------------------------------------------------------------------------- +HKeySymbol CKeyValuesSystem::GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *name, bool bCreate ) +{ + if ( !name ) + { + return (-1); + } + + AUTO_LOCK( m_mutex ); + MEM_ALLOC_CREDIT(); + + int hash = CaseInsensitiveHash(name, m_HashTable.Count()); + int numNameStringBytes = -1; + int i = 0; + hash_item_t *item = &m_HashTable[hash]; + while (1) + { + char *pCompareString = (char *)m_Strings.GetBase() + item->stringIndex; + int iResult = _V_stricmp_NegativeForUnequal( name, pCompareString ); + if ( iResult == 0 ) + { + // strings are exactly equal matching every letter's case + hCaseInsensitiveSymbol = (HKeySymbol)item->stringIndex; + return (HKeySymbol)item->stringIndex; + } + else if ( iResult > 0 ) + { + // strings are equal in a case-insensitive compare, but have different case for some letters + // Need to walk the case-resolving chain + numNameStringBytes = V_strlen( pCompareString ); + uint32 *pnCaseResolveIndex = reinterpret_cast< uint32 * >( pCompareString + numNameStringBytes ); + hCaseInsensitiveSymbol = (HKeySymbol)item->stringIndex; + while ( int nAlternativeStringIndex = MEM_4BYTES_FROM_0_AND_3BYTES( *pnCaseResolveIndex ) ) + { + pCompareString = (char *)m_Strings.GetBase() + nAlternativeStringIndex; + int iResult = strcmp( name, pCompareString ); + if ( !iResult ) + { + // found an exact match + return (HKeySymbol)nAlternativeStringIndex; + } + // Keep traversing alternative case-resolving chain + pnCaseResolveIndex = reinterpret_cast< uint32 * >( pCompareString + numNameStringBytes ); + } + // Reached the end of alternative case-resolving chain, pnCaseResolveIndex is pointing at 0 bytes + // indicating no further alternative stringIndex + if ( !bCreate ) + { + // If we aren't interested in creating the actual string index, + // then return symbol with default capitalization + // NOTE: this is not correct value, but it cannot be used to create a new value anyway, + // only for locating a pre-existing value and lookups are case-insensitive + return (HKeySymbol)item->stringIndex; + } + else + { + char *pString = (char *)m_Strings.Alloc( numNameStringBytes + 1 + 3 ); + if ( !pString ) + { + Error( "Out of keyvalue string space" ); + return -1; + } + int nNewAlternativeStringIndex = pString - (char *)m_Strings.GetBase(); + V_memcpy( pString, name, numNameStringBytes ); + * reinterpret_cast< uint32 * >( pString + numNameStringBytes ) = 0; // string null-terminator + 3 alternative spelling bytes + *pnCaseResolveIndex = MEM_4BYTES_AS_0_AND_3BYTES( nNewAlternativeStringIndex ); // link previous spelling entry to the new entry + return (HKeySymbol)nNewAlternativeStringIndex; + } + } + + i++; + + if (item->next == NULL) + { + if ( !bCreate ) + { + // not found + return -1; + } + + // we're not in the table + if (item->stringIndex != 0) + { + // first item is used, an new item + item->next = (hash_item_t *)m_HashItemMemPool.Alloc(sizeof(hash_item_t)); + item = item->next; + } + + // build up the new item + item->next = NULL; + int numStringBytes = strlen(name); + char *pString = (char *)m_Strings.Alloc( numStringBytes + 1 + 3 ); + if ( !pString ) + { + Error( "Out of keyvalue string space" ); + return -1; + } + item->stringIndex = pString - (char *)m_Strings.GetBase(); + V_memcpy( pString, name, numStringBytes ); + * reinterpret_cast< uint32 * >( pString + numStringBytes ) = 0; // string null-terminator + 3 alternative spelling bytes + hCaseInsensitiveSymbol = (HKeySymbol)item->stringIndex; + return (HKeySymbol)item->stringIndex; + } + + item = item->next; + } + + // shouldn't be able to get here + Assert(0); + return (-1); +} + +//----------------------------------------------------------------------------- +// Purpose: symbol table access +//----------------------------------------------------------------------------- +const char *CKeyValuesSystem::GetStringForSymbol(HKeySymbol symbol) +{ + if ( symbol == -1 ) + { + return ""; + } + return ((char *)m_Strings.GetBase() + (size_t)symbol); +} + +//----------------------------------------------------------------------------- +// Purpose: adds KeyValues record into global list so we can track memory leaks +//----------------------------------------------------------------------------- +void CKeyValuesSystem::AddKeyValuesToMemoryLeakList(void *pMem, HKeySymbol name) +{ +#ifdef _DEBUG + // only track the memory leaks in debug builds + MemoryLeakTracker_t item = { name, pMem }; + m_KeyValuesTrackingList.Insert(item); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: used to track memory leaks +//----------------------------------------------------------------------------- +void CKeyValuesSystem::RemoveKeyValuesFromMemoryLeakList(void *pMem) +{ +#ifdef _DEBUG + // only track the memory leaks in debug builds + MemoryLeakTracker_t item = { 0, pMem }; + int index = m_KeyValuesTrackingList.Find(item); + m_KeyValuesTrackingList.RemoveAt(index); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: generates a simple hash value for a string +//----------------------------------------------------------------------------- +int CKeyValuesSystem::CaseInsensitiveHash(const char *string, int iBounds) +{ + unsigned int hash = 0; + + for ( ; *string != 0; string++ ) + { + if (*string >= 'A' && *string <= 'Z') + { + hash = (hash << 1) + (*string - 'A' + 'a'); + } + else + { + hash = (hash << 1) + *string; + } + } + + return hash % iBounds; +} + +//----------------------------------------------------------------------------- +// Purpose: set/get a value for keyvalues resolution symbol +// e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE] +//----------------------------------------------------------------------------- +void CKeyValuesSystem::SetKeyValuesExpressionSymbol( const char *name, bool bValue ) +{ + if ( !name ) + return; + + if ( name[0] == '$' ) + ++ name; + + HKeySymbol hSym = GetSymbolForString( name, true ); // find or create symbol + + { + AUTO_LOCK( m_mutex ); + m_KvConditionalSymbolTable.InsertOrReplace( hSym, bValue ); + } +} + +bool CKeyValuesSystem::GetKeyValuesExpressionSymbol( const char *name ) +{ + if ( !name ) + return false; + + if ( name[0] == '$' ) + ++ name; + + HKeySymbol hSym = GetSymbolForString( name, false ); // find or create symbol + if ( hSym != -1 ) + { + AUTO_LOCK( m_mutex ); + CUtlMap< HKeySymbol, bool >::IndexType_t idx = m_KvConditionalSymbolTable.Find( hSym ); + if ( idx != m_KvConditionalSymbolTable.InvalidIndex() ) + { + // Found the symbol value in conditional symbol table + return m_KvConditionalSymbolTable.Element( idx ); + } + } + + // + // Fallback conditionals + // + + if ( !V_stricmp( name, "GAMECONSOLESPLITSCREEN" ) ) + { +#if defined( _GAMECONSOLE ) + return ( XBX_GetNumGameUsers() > 1 ); +#else + return false; +#endif + } + + if ( !V_stricmp( name, "GAMECONSOLEGUEST" ) ) + { +#if defined( _GAMECONSOLE ) + return ( XBX_GetPrimaryUserIsGuest() != 0 ); +#else + return false; +#endif + } + + if ( !V_stricmp( name, "ENGLISH" ) || + !V_stricmp( name, "JAPANESE" ) || + !V_stricmp( name, "GERMAN" ) || + !V_stricmp( name, "FRENCH" ) || + !V_stricmp( name, "SPANISH" ) || + !V_stricmp( name, "ITALIAN" ) || + !V_stricmp( name, "KOREAN" ) || + !V_stricmp( name, "TCHINESE" ) || + !V_stricmp( name, "PORTUGUESE" ) || + !V_stricmp( name, "SCHINESE" ) || + !V_stricmp( name, "POLISH" ) || + !V_stricmp( name, "RUSSIAN" ) || + !V_stricmp( name, "TURKISH" ) ) + { + // the language symbols are true if we are in that language + // english is assumed when no language is present + const char *pLanguageString; +#ifdef _GAMECONSOLE + pLanguageString = XBX_GetLanguageString(); +#else + static ConVarRef cl_language( "cl_language" ); + pLanguageString = cl_language.GetString(); +#endif + if ( !pLanguageString || !pLanguageString[0] ) + { + pLanguageString = "english"; + } + if ( !V_stricmp( name, pLanguageString ) ) + { + return true; + } + else + { + return false; + } + } + + // very expensive, back door for DLC updates + if ( !V_strnicmp( name, "CVAR_", 5 ) ) + { + ConVarRef cvRef( name + 5 ); + if ( cvRef.IsValid() ) + return cvRef.GetBool(); + } + + // purposely warn on these to prevent syntax errors + // need to get these fixed asap, otherwise unintended false behavior + Warning( "KV Conditional: Unknown symbol %s\n", name ); + return false; +} |