From 3bf9df6b2785fa6d951086978a3e66f49427166a Mon Sep 17 00:00:00 2001 From: FluorescentCIAAfricanAmerican <0934gj3049fk@protonmail.com> Date: Wed, 22 Apr 2020 12:56:21 -0400 Subject: 1 --- vstdlib/KeyValuesSystem.cpp | 416 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100644 vstdlib/KeyValuesSystem.cpp (limited to 'vstdlib/KeyValuesSystem.cpp') diff --git a/vstdlib/KeyValuesSystem.cpp b/vstdlib/KeyValuesSystem.cpp new file mode 100644 index 0000000..0665d94 --- /dev/null +++ b/vstdlib/KeyValuesSystem.cpp @@ -0,0 +1,416 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include +#include +#include "mempool.h" +#include "utlsymbol.h" +#include "tier0/threadtools.h" +#include "tier1/memstack.h" +#include "tier1/utlmap.h" +#include "tier1/utlstring.h" +#include "tier1/fmtstr.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +#ifdef NO_SBH // no need to pool if using tier0 small block heap +#define KEYVALUES_USE_POOL 1 +#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); + + // maintain a cache of KeyValues we load from disk. This saves us quite a lot of time on app startup. + virtual void AddFileKeyValuesToCache( const KeyValues* _kv, const char *resourceName, const char *pathID ); + virtual bool LoadFileKeyValuesFromCache( KeyValues* outKv, const char *resourceName, const char *pathID, IBaseFileSystem *filesystem ) const; + virtual void InvalidateCache(); + virtual void InvalidateCacheForFile( const char *resourceName, const char *pathID ); + +private: +#ifdef KEYVALUES_USE_POOL + CUtlMemoryPool *m_pMemPool; +#endif + int m_iMaxKeyValuesSize; + + // string hash table + CMemoryStack m_Strings; + struct hash_item_t + { + int stringIndex; + hash_item_t *next; + }; + CUtlMemoryPool m_HashItemMemPool; + CUtlVector m_HashTable; + int CaseInsensitiveHash(const char *string, int iBounds); + + void DoInvalidateCache(); + + struct MemoryLeakTracker_t + { + int nameIndex; + void *pMem; + }; + static bool MemoryLeakTrackerLessFunc( const MemoryLeakTracker_t &lhs, const MemoryLeakTracker_t &rhs ) + { + return lhs.pMem < rhs.pMem; + } + CUtlRBTree m_KeyValuesTrackingList; + + CThreadFastMutex m_mutex; + + CUtlMap m_KeyValueCache; +}; + +// 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, UTLMEMORYPOOL_GROW_FAST, "CKeyValuesSystem::m_HashItemMemPool") +, m_KeyValuesTrackingList(0, 0, MemoryLeakTrackerLessFunc) +, m_KeyValueCache( UtlStringLessFunc ) +{ + // 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( 4*1024*1024, 64*1024, 0, 4 ); + 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 + + DoInvalidateCache(); +} + +//----------------------------------------------------------------------------- +// 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; + } +} + +#ifdef KEYVALUES_USE_POOL +static void KVLeak( char const *fmt, ... ) +{ + va_list argptr; + char data[1024]; + + va_start(argptr, fmt); + Q_vsnprintf(data, sizeof( data ), fmt, argptr); + va_end(argptr); + + Msg( "%s", data ); +} +#endif + +//----------------------------------------------------------------------------- +// 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, UTLMEMORYPOOL_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 ); + + 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; + char *pString = (char *)m_Strings.Alloc( V_strlen(name) + 1 ); + if ( !pString ) + { + Error( "Out of keyvalue string space" ); + return -1; + } + item->stringIndex = pString - (char *)m_Strings.GetBase(); + strcpy(pString, name); + 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: Removes a particular key value file (from a particular source) from the cache if present. +//----------------------------------------------------------------------------- +void CKeyValuesSystem::InvalidateCacheForFile(const char *resourceName, const char *pathID) +{ + CUtlString identString( CFmtStr( "%s::%s", resourceName ? resourceName : "", pathID ? pathID : "" ) ); + + CUtlMap::IndexType_t index = m_KeyValueCache.Find( identString ); + if ( m_KeyValueCache.IsValidIndex( index ) ) + { + m_KeyValueCache[ index ]->deleteThis(); + m_KeyValueCache.RemoveAt( index ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Adds a particular key value file (from a particular source) to the cache if not already present. +//----------------------------------------------------------------------------- +void CKeyValuesSystem::AddFileKeyValuesToCache(const KeyValues* _kv, const char *resourceName, const char *pathID) +{ + CUtlString identString( CFmtStr( "%s::%s", resourceName ? resourceName : "", pathID ? pathID : "" ) ); + // Some files actually have multiple roots, and if you use regular MakeCopy (without passing true), those + // will be missed. This caused a bug in soundscapes on dedicated servers. + m_KeyValueCache.Insert( identString, _kv->MakeCopy( true ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Fetches a particular keyvalue from the cache, and copies into _outKv (clearing anything there already). +//----------------------------------------------------------------------------- +bool CKeyValuesSystem::LoadFileKeyValuesFromCache(KeyValues* outKv, const char *resourceName, const char *pathID, IBaseFileSystem *filesystem) const +{ + Assert( outKv ); + Assert( resourceName ); + + COM_TimestampedLog("CKeyValuesSystem::LoadFileKeyValuesFromCache(%s%s%s): Begin", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : ""); + + CUtlString identString(CFmtStr("%s::%s", resourceName ? resourceName : "", pathID ? pathID : "")); + + CUtlMap::IndexType_t index = m_KeyValueCache.Find( identString ); + + if ( m_KeyValueCache.IsValidIndex( index ) ) { + (*outKv) = ( *m_KeyValueCache[ index ] ); + COM_TimestampedLog("CKeyValuesSystem::LoadFileKeyValuesFromCache(%s%s%s): End / Hit", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : ""); + return true; + } + + COM_TimestampedLog("CKeyValuesSystem::LoadFileKeyValuesFromCache(%s%s%s): End / Miss", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : ""); + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Evicts everything from the cache, cleans up the memory used. +//----------------------------------------------------------------------------- +void CKeyValuesSystem::InvalidateCache() +{ + DoInvalidateCache(); +} + +//----------------------------------------------------------------------------- +// 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: Evicts everything from the cache, cleans up the memory used. +//----------------------------------------------------------------------------- +void CKeyValuesSystem::DoInvalidateCache() +{ + // Cleanup the cache. + FOR_EACH_MAP_FAST( m_KeyValueCache, mapIndex ) + { + m_KeyValueCache[mapIndex]->deleteThis(); + } + + // Apparently you cannot call RemoveAll on a map without also purging the contents because... ? + // If you do and you continue to use the map, you will eventually wind up in a case where you + // have an empty map but it still iterates over elements. Awesome? + m_KeyValueCache.Purge(); +} + -- cgit v1.2.3