VstdLib: reimplement CKeyValuesSystem

NOTE: reimplementation is for tools only, the game and SDK still uses the engine's implementation.
This commit is contained in:
Kawe Mazidjatari 2024-01-12 00:32:17 +01:00
parent b2ce189ba1
commit 51986ec1c6
3 changed files with 549 additions and 82 deletions

View File

@ -26,29 +26,31 @@ public:
// 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
virtual void RegisterSizeofKeyValues( ssize_t nSize ) = 0;
virtual void RegisterSizeofKeyValues( const ssize_t nSize ) = 0;
// allocates/frees a KeyValues object from the shared mempool
virtual void *AllocKeyValuesMemory( ssize_t nSize ) = 0;
virtual void FreeKeyValuesMemory( void *pMem ) = 0;
virtual void *AllocKeyValuesMemory( const ssize_t nSize ) = 0;
virtual void FreeKeyValuesMemory( void *const pMem ) = 0;
// symbol table access (used for key names)
virtual HKeySymbol GetSymbolForString( const char *pName, bool bCreate = true ) = 0;
virtual const char *GetStringForSymbol( HKeySymbol symbol ) = 0;
virtual HKeySymbol GetSymbolForString( const char *const pName, const bool bCreate = true ) = 0;
virtual const char *GetStringForSymbol( const HKeySymbol symbol ) = 0;
// for debugging, adds KeyValues record into global list so we can track memory leaks
virtual void AddKeyValuesToMemoryLeakList( void *pMem, HKeySymbol name ) = 0;
virtual void RemoveKeyValuesFromMemoryLeakList( void *pMem ) = 0;
virtual void AddKeyValuesToMemoryLeakList( const void *const pMem, const HKeySymbol name ) = 0;
virtual void RemoveKeyValuesFromMemoryLeakList( const void *const pMem ) = 0;
// Returns the globally allocated keyvalues memory pool.
virtual void *GetKeyValuesMemory( void ) = 0;
// set/get a value for keyvalues resolution symbol
// e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE]
virtual void SetKeyValuesExpressionSymbol( const char *pName, bool bValue ) = 0;
virtual bool GetKeyValuesExpressionSymbol( const char *pName ) = 0;
virtual void SetKeyValuesExpressionSymbol( const char *const pName, const bool bValue ) = 0;
virtual bool GetKeyValuesExpressionSymbol( const char *const pName ) = 0;
// symbol table access from code with case-preserving requirements (used for key names)
virtual HKeySymbol GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *pName, bool bCreate = true ) = 0;
virtual HKeySymbol GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *const pName, const bool bCreate = true ) = 0;
};
IKeyValuesSystem* KeyValuesSystem();
#endif // VSTDLIB_IKEYVALUESSYSTEM_H

View File

@ -1,110 +1,521 @@
//=============================================================================//
//====== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. =======//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "core/stdafx.h"
#include "vstdlib/keyvaluessystem.h"
#include <ikeyvaluessystem.h>
#include "keyvaluessystem.h"
#include "tier0/threadtools.h"
#include "tier1/keyvalues.h"
#include "tier1/mempool.h"
#include "tier1/utlsymbol.h"
#include "tier1/utlmap.h"
#include "tier1/memstack.h"
#include "tier1/convar.h"
#include "tier1/strtools.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: 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
// Input : nSize -
//-----------------------------------------------------------------------------
void CKeyValuesSystem::RegisterSizeofKeyValues(int64_t nSize)
void CKeyValuesSystem::RegisterSizeofKeyValues(const ssize_t size)
{
const static int index = 0;
CallVFunc<void>(index, this, nSize);
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(eDLL_T::COMMON, "%s", data);
}
#endif
//-----------------------------------------------------------------------------
// Purpose: allocates a KeyValues object from the shared mempool
// Input : nSize -
// Output : pointer to allocated pool
//-----------------------------------------------------------------------------
void* CKeyValuesSystem::AllocKeyValuesMemory(int64_t nSize)
void* CKeyValuesSystem::AllocKeyValuesMemory(const ssize_t size)
{
const static int index = 1;
return CallVFunc<void*>(index, this, nSize);
#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
// Input : *pMem -
//-----------------------------------------------------------------------------
void CKeyValuesSystem::FreeKeyValuesMemory(void* pMem)
void CKeyValuesSystem::FreeKeyValuesMemory(void* const pMem)
{
const static int index = 2;
CallVFunc<void>(index, this, pMem);
#ifdef KEYVALUES_USE_POOL
m_pMemPool->Free(pMem);
#else
free(pMem);
#endif
}
//-----------------------------------------------------------------------------
// Purpose: symbol table access (used for key names)
// Input : *szName -
// bCreate -
// Output : handle to KeyValue symbol on success, -1 on failure
//-----------------------------------------------------------------------------
HKeySymbol CKeyValuesSystem::GetSymbolForString(const char* szName, bool bCreate)
HKeySymbol CKeyValuesSystem::GetSymbolForString(const char* const name, const bool bCreate)
{
const static int index = 3;
return CallVFunc<HKeySymbol>(index, this, szName, 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;
const size_t numStringBytes = strlen(name);
char* pString = (char*)m_Strings.Alloc(numStringBytes + 1 + 3);
if (!pString)
{
Error(eDLL_T::COMMON, EXIT_FAILURE, "Out of keyvalue string space");
return -1;
}
item->stringIndex = pString - (char*)m_Strings.GetBase();
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* const name, const bool bCreate)
{
if (!name)
{
return (-1);
}
AUTO_LOCK(m_Mutex);
MEM_ALLOC_CREDIT();
const int hash = CaseInsensitiveHash(name, m_HashTable.Count());
ssize_t numNameStringBytes = -1;
ssize_t i = 0;
hash_item_t* item = &m_HashTable[hash];
while (1)
{
char* pCompareString = (char*)m_Strings.GetBase() + item->stringIndex;
const int iResultNegative = _V_stricmp_NegativeForUnequal(name, pCompareString);
if (iResultNegative == 0)
{
// strings are exactly equal matching every letter's case
hCaseInsensitiveSymbol = (HKeySymbol)item->stringIndex;
return (HKeySymbol)item->stringIndex;
}
else if (iResultNegative > 0)
{
// strings are equal in a case-insensitive compare, but have different case for some letters
// Need to walk the case-resolving chain
numNameStringBytes = Q_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;
const 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(eDLL_T::COMMON, EXIT_FAILURE, "Out of keyvalue string space");
return -1;
}
int64_t nNewAlternativeStringIndex = pString - (char*)m_Strings.GetBase();
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;
size_t numStringBytes = strlen(name);
char* pString = (char*)m_Strings.Alloc(numStringBytes + 1 + 3);
if (!pString)
{
Error(eDLL_T::COMMON, EXIT_FAILURE, "Out of keyvalue string space");
return -1;
}
item->stringIndex = pString - (char*)m_Strings.GetBase();
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
// Input : symbol -
// Output : symbol string if found, empty if not found
//-----------------------------------------------------------------------------
const char* CKeyValuesSystem::GetStringForSymbol(HKeySymbol symbol)
const char* CKeyValuesSystem::GetStringForSymbol(const HKeySymbol symbol)
{
const static int index = 4;
return CallVFunc<const char*>(index, this, symbol);
if (symbol == -1)
{
return "";
}
return ((char*)m_Strings.GetBase() + (size_t)symbol);
}
//-----------------------------------------------------------------------------
// Purpose: gets the global KeyValues memory pool
// Output : *g_pKeyValuesMemPool -
// Purpose: adds KeyValues record into global list so we can track memory leaks
//-----------------------------------------------------------------------------
void* CKeyValuesSystem::GetMemPool(void)
void CKeyValuesSystem::AddKeyValuesToMemoryLeakList(const void* const pMem, const HKeySymbol name)
{
const static int index = 7;
return CallVFunc<void*>(index, this);
#ifdef _DEBUG
// only track the memory leaks in debug builds
MemoryLeakTracker_t item = { name, pMem };
m_KeyValuesTrackingList.Insert(item);
#endif
}
//-----------------------------------------------------------------------------
// Purpose: set a value for keyvalues resolution symbol
// Purpose: used to track memory leaks
//-----------------------------------------------------------------------------
void CKeyValuesSystem::RemoveKeyValuesFromMemoryLeakList(const void* const 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* const string, const int iBounds)
{
unsigned int hash = 0;
const char* iter = string;
for (; *iter != 0; iter++)
{
if (*iter >= 'A' && *iter <= 'Z')
{
hash = (hash << 1) + (*iter - 'A' + 'a');
}
else
{
hash = (hash << 1) + *iter;
}
}
return hash % iBounds;
}
//-----------------------------------------------------------------------------
// Purpose: set/get a value for keyvalues resolution symbol
// e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE]
// Input : *szName -
// bValue -
//-----------------------------------------------------------------------------
void CKeyValuesSystem::SetKeyValuesExpressionSymbol(const char* szName, bool bValue)
void CKeyValuesSystem::SetKeyValuesExpressionSymbol(const char* name, const bool bValue)
{
const static int index = 8;
CallVFunc<void>(index, this, szName, bValue);
if (!name)
return;
const char* pName = name;
if (pName[0] == '$')
++pName;
HKeySymbol hSym = GetSymbolForString(pName, true); // find or create symbol
{
AUTO_LOCK(m_Mutex);
m_KvConditionalSymbolTable.InsertOrReplace(hSym, bValue);
}
}
//-----------------------------------------------------------------------------
// Purpose: get a value for keyvalues resolution symbol
// Input : *szName -
//-----------------------------------------------------------------------------
bool CKeyValuesSystem::GetKeyValuesExpressionSymbol(const char* szName)
bool CKeyValuesSystem::GetKeyValuesExpressionSymbol(const char* const name)
{
const static int index = 9;
return CallVFunc<bool>(index, this, szName);
}
if (!name)
return false;
//-----------------------------------------------------------------------------
// Purpose: symbol table access (used for key names)
// Input : *hCaseInsensitiveSymbol -
// *szName -
// bCreate -
// Output : handle to KeyValue symbol on success, INVALID_KEY_SYMBOL on failure
//-----------------------------------------------------------------------------
HKeySymbol CKeyValuesSystem::GetSymbolForStringCaseSensitive(HKeySymbol& hCaseInsensitiveSymbol, const char* szName, bool bCreate)
{
const static int index = 10;
return CallVFunc<HKeySymbol>(index, this, hCaseInsensitiveSymbol, szName, bCreate);
const char* pName = name;
if (pName[0] == '$')
++pName;
const HKeySymbol hSym = GetSymbolForString(pName, 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(pName, "GAMECONSOLESPLITSCREEN"))
// {
//#if defined( _GAMECONSOLE )
// return (XBX_GetNumGameUsers() > 1);
//#else
// return false;
//#endif
// }
//
// if (!V_stricmp(pName, "GAMECONSOLEGUEST"))
// {
//#if defined( _GAMECONSOLE )
// return (XBX_GetPrimaryUserIsGuest() != 0);
//#else
// return false;
//#endif
// }
//
// if (!V_stricmp(pName, "ENGLISH") ||
// !V_stricmp(pName, "JAPANESE") ||
// !V_stricmp(pName, "GERMAN") ||
// !V_stricmp(pName, "FRENCH") ||
// !V_stricmp(pName, "SPANISH") ||
// !V_stricmp(pName, "ITALIAN") ||
// !V_stricmp(pName, "KOREAN") ||
// !V_stricmp(pName, "TCHINESE") ||
// !V_stricmp(pName, "PORTUGUESE") ||
// !V_stricmp(pName, "SCHINESE") ||
// !V_stricmp(pName, "POLISH") ||
// !V_stricmp(pName, "RUSSIAN") ||
// !V_stricmp(pName, "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(pName, pLanguageString))
// {
// return true;
// }
// else
// {
// return false;
// }
// }
//
// // very expensive, back door for DLC updates
// if (!V_strnicmp(pName, "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(eDLL_T::COMMON, "KV Conditional: Unknown symbol %s\n", pName);
return false;
}

View File

@ -6,38 +6,93 @@
#include "tier1/utlvector.h"
#include "tier1/utlmap.h"
inline void* g_pKeyValuesMemPool = nullptr;
class CKeyValuesSystem : public IKeyValuesSystem// VTABLE @ 0x1413AA1E8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM
{
public:
void RegisterSizeofKeyValues(int64_t nSize);
void* AllocKeyValuesMemory(int64_t nSize);
void FreeKeyValuesMemory(void* pMem);
HKeySymbol GetSymbolForString(const char* szName, bool bCreate = true);
const char* GetStringForSymbol(HKeySymbol symbol);
CKeyValuesSystem();
~CKeyValuesSystem();
void* GetMemPool(void); // GetMemPool returns a global variable called m_pMemPool, it gets modified by AllocKeyValuesMemory and with FreeKeyValuesMemory you can see where to find it in FreeKeyValuesMemory.
void SetKeyValuesExpressionSymbol(const char* szName, bool bValue);
bool GetKeyValuesExpressionSymbol(const char* szName);
HKeySymbol GetSymbolForStringCaseSensitive(HKeySymbol& hCaseInsensitiveSymbol, const char* szName, bool bCreate = true);
// 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
virtual void RegisterSizeofKeyValues(const ssize_t nSize);
// allocates/frees a KeyValues object from the shared mempool
virtual void* AllocKeyValuesMemory(const ssize_t nSize);
virtual void FreeKeyValuesMemory(void*const pMem);
// symbol table access (used for key names)
virtual HKeySymbol GetSymbolForString(const char*const szName, const bool bCreate = true);
virtual const char* GetStringForSymbol(const HKeySymbol symbol);
// for debugging, adds KeyValues record into global list so we can track memory leaks
virtual void AddKeyValuesToMemoryLeakList(const void*const pMem, const HKeySymbol name);
virtual void RemoveKeyValuesFromMemoryLeakList(const void*const pMem);
// GetMemPool returns a global variable called m_pMemPool, it gets modified by AllocKeyValuesMemory and with FreeKeyValuesMemory you can see where to find it in FreeKeyValuesMemory.
virtual void* GetKeyValuesMemory(void) { return g_pKeyValuesMemPool; }
// set/get a value for keyvalues resolution symbol
// e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE]
virtual void SetKeyValuesExpressionSymbol(const char*const szName, const bool bValue);
virtual bool GetKeyValuesExpressionSymbol(const char*const szName);
// symbol table access from code with case-preserving requirements (used for key names)
virtual HKeySymbol GetSymbolForStringCaseSensitive(HKeySymbol& hCaseInsensitiveSymbol, const char*const szName, const bool bCreate = true);
private:
int64 m_iMaxKeyValuesSize;
ssize_t 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;
int64_t stringIndex;
hash_item_t* next;
};
CUtlMemoryPool m_HashItemMemPool;
CUtlVector<hash_item_t> m_HashTable;
int CaseInsensitiveHash(const char *const string, const int iBounds);
struct MemoryLeakTracker_t
{
int nameIndex;
void* pMem;
int64_t nameIndex;
const void* pMem;
};
static bool MemoryLeakTrackerLessFunc(const MemoryLeakTracker_t& lhs, const MemoryLeakTracker_t& rhs)
{
return lhs.pMem < rhs.pMem;
}
// Unknown less func.
void* m_pCompareFunc;
@ -49,13 +104,12 @@ private:
};
/* ==== KEYVALUESSYSTEM ================================================================================================================================================= */
inline void* g_pKeyValuesMemPool = nullptr;
inline CKeyValuesSystem* g_pKeyValuesSystem = nullptr;
//-----------------------------------------------------------------------------
// Instance singleton and expose interface to rest of code
//-----------------------------------------------------------------------------
FORCEINLINE CKeyValuesSystem* KeyValuesSystem()
FORCEINLINE IKeyValuesSystem* KeyValuesSystem()
{
return g_pKeyValuesSystem;
}