Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

416 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <vstdlib/IKeyValuesSystem.h>
  8. #include <KeyValues.h>
  9. #include "mempool.h"
  10. #include "utlsymbol.h"
  11. #include "tier0/threadtools.h"
  12. #include "tier1/memstack.h"
  13. #include "tier1/utlmap.h"
  14. #include "tier1/utlstring.h"
  15. #include "tier1/fmtstr.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include <tier0/memdbgon.h>
  18. #ifdef NO_SBH // no need to pool if using tier0 small block heap
  19. #define KEYVALUES_USE_POOL 1
  20. #endif
  21. //-----------------------------------------------------------------------------
  22. // Purpose: Central storage point for KeyValues memory and symbols
  23. //-----------------------------------------------------------------------------
  24. class CKeyValuesSystem : public IKeyValuesSystem
  25. {
  26. public:
  27. CKeyValuesSystem();
  28. ~CKeyValuesSystem();
  29. // registers the size of the KeyValues in the specified instance
  30. // so it can build a properly sized memory pool for the KeyValues objects
  31. // the sizes will usually never differ but this is for versioning safety
  32. void RegisterSizeofKeyValues(int size);
  33. // allocates/frees a KeyValues object from the shared mempool
  34. void *AllocKeyValuesMemory(int size);
  35. void FreeKeyValuesMemory(void *pMem);
  36. // symbol table access (used for key names)
  37. HKeySymbol GetSymbolForString( const char *name, bool bCreate );
  38. const char *GetStringForSymbol(HKeySymbol symbol);
  39. // returns the wide version of ansi, also does the lookup on #'d strings
  40. void GetLocalizedFromANSI( const char *ansi, wchar_t *outBuf, int unicodeBufferSizeInBytes);
  41. void GetANSIFromLocalized( const wchar_t *wchar, char *outBuf, int ansiBufferSizeInBytes );
  42. // for debugging, adds KeyValues record into global list so we can track memory leaks
  43. virtual void AddKeyValuesToMemoryLeakList(void *pMem, HKeySymbol name);
  44. virtual void RemoveKeyValuesFromMemoryLeakList(void *pMem);
  45. // maintain a cache of KeyValues we load from disk. This saves us quite a lot of time on app startup.
  46. virtual void AddFileKeyValuesToCache( const KeyValues* _kv, const char *resourceName, const char *pathID );
  47. virtual bool LoadFileKeyValuesFromCache( KeyValues* outKv, const char *resourceName, const char *pathID, IBaseFileSystem *filesystem ) const;
  48. virtual void InvalidateCache();
  49. virtual void InvalidateCacheForFile( const char *resourceName, const char *pathID );
  50. private:
  51. #ifdef KEYVALUES_USE_POOL
  52. CUtlMemoryPool *m_pMemPool;
  53. #endif
  54. int m_iMaxKeyValuesSize;
  55. // string hash table
  56. CMemoryStack m_Strings;
  57. struct hash_item_t
  58. {
  59. int stringIndex;
  60. hash_item_t *next;
  61. };
  62. CUtlMemoryPool m_HashItemMemPool;
  63. CUtlVector<hash_item_t> m_HashTable;
  64. int CaseInsensitiveHash(const char *string, int iBounds);
  65. void DoInvalidateCache();
  66. struct MemoryLeakTracker_t
  67. {
  68. int nameIndex;
  69. void *pMem;
  70. };
  71. static bool MemoryLeakTrackerLessFunc( const MemoryLeakTracker_t &lhs, const MemoryLeakTracker_t &rhs )
  72. {
  73. return lhs.pMem < rhs.pMem;
  74. }
  75. CUtlRBTree<MemoryLeakTracker_t, int> m_KeyValuesTrackingList;
  76. CThreadFastMutex m_mutex;
  77. CUtlMap<CUtlString, KeyValues*> m_KeyValueCache;
  78. };
  79. // EXPOSE_SINGLE_INTERFACE(CKeyValuesSystem, IKeyValuesSystem, KEYVALUES_INTERFACE_VERSION);
  80. //-----------------------------------------------------------------------------
  81. // Instance singleton and expose interface to rest of code
  82. //-----------------------------------------------------------------------------
  83. static CKeyValuesSystem g_KeyValuesSystem;
  84. IKeyValuesSystem *KeyValuesSystem()
  85. {
  86. return &g_KeyValuesSystem;
  87. }
  88. //-----------------------------------------------------------------------------
  89. // Purpose: Constructor
  90. //-----------------------------------------------------------------------------
  91. CKeyValuesSystem::CKeyValuesSystem()
  92. : m_HashItemMemPool(sizeof(hash_item_t), 64, UTLMEMORYPOOL_GROW_FAST, "CKeyValuesSystem::m_HashItemMemPool")
  93. , m_KeyValuesTrackingList(0, 0, MemoryLeakTrackerLessFunc)
  94. , m_KeyValueCache( UtlStringLessFunc )
  95. {
  96. // initialize hash table
  97. m_HashTable.AddMultipleToTail(2047);
  98. for (int i = 0; i < m_HashTable.Count(); i++)
  99. {
  100. m_HashTable[i].stringIndex = 0;
  101. m_HashTable[i].next = NULL;
  102. }
  103. m_Strings.Init( 4*1024*1024, 64*1024, 0, 4 );
  104. char *pszEmpty = ((char *)m_Strings.Alloc(1));
  105. *pszEmpty = 0;
  106. #ifdef KEYVALUES_USE_POOL
  107. m_pMemPool = NULL;
  108. #endif
  109. m_iMaxKeyValuesSize = sizeof(KeyValues);
  110. }
  111. //-----------------------------------------------------------------------------
  112. // Purpose: Destructor
  113. //-----------------------------------------------------------------------------
  114. CKeyValuesSystem::~CKeyValuesSystem()
  115. {
  116. #ifdef KEYVALUES_USE_POOL
  117. #ifdef _DEBUG
  118. // display any memory leaks
  119. if (m_pMemPool && m_pMemPool->Count() > 0)
  120. {
  121. DevMsg("Leaked KeyValues blocks: %d\n", m_pMemPool->Count());
  122. }
  123. // iterate all the existing keyvalues displaying their names
  124. for (int i = 0; i < m_KeyValuesTrackingList.MaxElement(); i++)
  125. {
  126. if (m_KeyValuesTrackingList.IsValidIndex(i))
  127. {
  128. DevMsg("\tleaked KeyValues(%s)\n", &m_Strings[m_KeyValuesTrackingList[i].nameIndex]);
  129. }
  130. }
  131. #endif
  132. delete m_pMemPool;
  133. #endif
  134. DoInvalidateCache();
  135. }
  136. //-----------------------------------------------------------------------------
  137. // Purpose: registers the size of the KeyValues in the specified instance
  138. // so it can build a properly sized memory pool for the KeyValues objects
  139. // the sizes will usually never differ but this is for versioning safety
  140. //-----------------------------------------------------------------------------
  141. void CKeyValuesSystem::RegisterSizeofKeyValues(int size)
  142. {
  143. if (size > m_iMaxKeyValuesSize)
  144. {
  145. m_iMaxKeyValuesSize = size;
  146. }
  147. }
  148. #ifdef KEYVALUES_USE_POOL
  149. static void KVLeak( char const *fmt, ... )
  150. {
  151. va_list argptr;
  152. char data[1024];
  153. va_start(argptr, fmt);
  154. Q_vsnprintf(data, sizeof( data ), fmt, argptr);
  155. va_end(argptr);
  156. Msg( "%s", data );
  157. }
  158. #endif
  159. //-----------------------------------------------------------------------------
  160. // Purpose: allocates a KeyValues object from the shared mempool
  161. //-----------------------------------------------------------------------------
  162. void *CKeyValuesSystem::AllocKeyValuesMemory(int size)
  163. {
  164. #ifdef KEYVALUES_USE_POOL
  165. // allocate, if we don't have one yet
  166. if (!m_pMemPool)
  167. {
  168. m_pMemPool = new CUtlMemoryPool(m_iMaxKeyValuesSize, 1024, UTLMEMORYPOOL_GROW_FAST, "CKeyValuesSystem::m_pMemPool" );
  169. m_pMemPool->SetErrorReportFunc( KVLeak );
  170. }
  171. return m_pMemPool->Alloc(size);
  172. #else
  173. return malloc( size );
  174. #endif
  175. }
  176. //-----------------------------------------------------------------------------
  177. // Purpose: frees a KeyValues object from the shared mempool
  178. //-----------------------------------------------------------------------------
  179. void CKeyValuesSystem::FreeKeyValuesMemory(void *pMem)
  180. {
  181. #ifdef KEYVALUES_USE_POOL
  182. m_pMemPool->Free(pMem);
  183. #else
  184. free( pMem );
  185. #endif
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Purpose: symbol table access (used for key names)
  189. //-----------------------------------------------------------------------------
  190. HKeySymbol CKeyValuesSystem::GetSymbolForString( const char *name, bool bCreate )
  191. {
  192. if ( !name )
  193. {
  194. return (-1);
  195. }
  196. AUTO_LOCK( m_mutex );
  197. int hash = CaseInsensitiveHash(name, m_HashTable.Count());
  198. int i = 0;
  199. hash_item_t *item = &m_HashTable[hash];
  200. while (1)
  201. {
  202. if (!stricmp(name, (char *)m_Strings.GetBase() + item->stringIndex ))
  203. {
  204. return (HKeySymbol)item->stringIndex;
  205. }
  206. i++;
  207. if (item->next == NULL)
  208. {
  209. if ( !bCreate )
  210. {
  211. // not found
  212. return -1;
  213. }
  214. // we're not in the table
  215. if (item->stringIndex != 0)
  216. {
  217. // first item is used, an new item
  218. item->next = (hash_item_t *)m_HashItemMemPool.Alloc(sizeof(hash_item_t));
  219. item = item->next;
  220. }
  221. // build up the new item
  222. item->next = NULL;
  223. char *pString = (char *)m_Strings.Alloc( V_strlen(name) + 1 );
  224. if ( !pString )
  225. {
  226. Error( "Out of keyvalue string space" );
  227. return -1;
  228. }
  229. item->stringIndex = pString - (char *)m_Strings.GetBase();
  230. strcpy(pString, name);
  231. return (HKeySymbol)item->stringIndex;
  232. }
  233. item = item->next;
  234. }
  235. // shouldn't be able to get here
  236. Assert(0);
  237. return (-1);
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Purpose: symbol table access
  241. //-----------------------------------------------------------------------------
  242. const char *CKeyValuesSystem::GetStringForSymbol(HKeySymbol symbol)
  243. {
  244. if ( symbol == -1 )
  245. {
  246. return "";
  247. }
  248. return ((char *)m_Strings.GetBase() + (size_t)symbol);
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Purpose: adds KeyValues record into global list so we can track memory leaks
  252. //-----------------------------------------------------------------------------
  253. void CKeyValuesSystem::AddKeyValuesToMemoryLeakList(void *pMem, HKeySymbol name)
  254. {
  255. #ifdef _DEBUG
  256. // only track the memory leaks in debug builds
  257. MemoryLeakTracker_t item = { name, pMem };
  258. m_KeyValuesTrackingList.Insert(item);
  259. #endif
  260. }
  261. //-----------------------------------------------------------------------------
  262. // Purpose: used to track memory leaks
  263. //-----------------------------------------------------------------------------
  264. void CKeyValuesSystem::RemoveKeyValuesFromMemoryLeakList(void *pMem)
  265. {
  266. #ifdef _DEBUG
  267. // only track the memory leaks in debug builds
  268. MemoryLeakTracker_t item = { 0, pMem };
  269. int index = m_KeyValuesTrackingList.Find(item);
  270. m_KeyValuesTrackingList.RemoveAt(index);
  271. #endif
  272. }
  273. //-----------------------------------------------------------------------------
  274. // Purpose: Removes a particular key value file (from a particular source) from the cache if present.
  275. //-----------------------------------------------------------------------------
  276. void CKeyValuesSystem::InvalidateCacheForFile(const char *resourceName, const char *pathID)
  277. {
  278. CUtlString identString( CFmtStr( "%s::%s", resourceName ? resourceName : "", pathID ? pathID : "" ) );
  279. CUtlMap<CUtlString, KeyValues*>::IndexType_t index = m_KeyValueCache.Find( identString );
  280. if ( m_KeyValueCache.IsValidIndex( index ) )
  281. {
  282. m_KeyValueCache[ index ]->deleteThis();
  283. m_KeyValueCache.RemoveAt( index );
  284. }
  285. }
  286. //-----------------------------------------------------------------------------
  287. // Purpose: Adds a particular key value file (from a particular source) to the cache if not already present.
  288. //-----------------------------------------------------------------------------
  289. void CKeyValuesSystem::AddFileKeyValuesToCache(const KeyValues* _kv, const char *resourceName, const char *pathID)
  290. {
  291. CUtlString identString( CFmtStr( "%s::%s", resourceName ? resourceName : "", pathID ? pathID : "" ) );
  292. // Some files actually have multiple roots, and if you use regular MakeCopy (without passing true), those
  293. // will be missed. This caused a bug in soundscapes on dedicated servers.
  294. m_KeyValueCache.Insert( identString, _kv->MakeCopy( true ) );
  295. }
  296. //-----------------------------------------------------------------------------
  297. // Purpose: Fetches a particular keyvalue from the cache, and copies into _outKv (clearing anything there already).
  298. //-----------------------------------------------------------------------------
  299. bool CKeyValuesSystem::LoadFileKeyValuesFromCache(KeyValues* outKv, const char *resourceName, const char *pathID, IBaseFileSystem *filesystem) const
  300. {
  301. Assert( outKv );
  302. Assert( resourceName );
  303. COM_TimestampedLog("CKeyValuesSystem::LoadFileKeyValuesFromCache(%s%s%s): Begin", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "");
  304. CUtlString identString(CFmtStr("%s::%s", resourceName ? resourceName : "", pathID ? pathID : ""));
  305. CUtlMap<CUtlString, KeyValues*>::IndexType_t index = m_KeyValueCache.Find( identString );
  306. if ( m_KeyValueCache.IsValidIndex( index ) ) {
  307. (*outKv) = ( *m_KeyValueCache[ index ] );
  308. COM_TimestampedLog("CKeyValuesSystem::LoadFileKeyValuesFromCache(%s%s%s): End / Hit", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "");
  309. return true;
  310. }
  311. COM_TimestampedLog("CKeyValuesSystem::LoadFileKeyValuesFromCache(%s%s%s): End / Miss", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "");
  312. return false;
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose: Evicts everything from the cache, cleans up the memory used.
  316. //-----------------------------------------------------------------------------
  317. void CKeyValuesSystem::InvalidateCache()
  318. {
  319. DoInvalidateCache();
  320. }
  321. //-----------------------------------------------------------------------------
  322. // Purpose: generates a simple hash value for a string
  323. //-----------------------------------------------------------------------------
  324. int CKeyValuesSystem::CaseInsensitiveHash(const char *string, int iBounds)
  325. {
  326. unsigned int hash = 0;
  327. for ( ; *string != 0; string++ )
  328. {
  329. if (*string >= 'A' && *string <= 'Z')
  330. {
  331. hash = (hash << 1) + (*string - 'A' + 'a');
  332. }
  333. else
  334. {
  335. hash = (hash << 1) + *string;
  336. }
  337. }
  338. return hash % iBounds;
  339. }
  340. //-----------------------------------------------------------------------------
  341. // Purpose: Evicts everything from the cache, cleans up the memory used.
  342. //-----------------------------------------------------------------------------
  343. void CKeyValuesSystem::DoInvalidateCache()
  344. {
  345. // Cleanup the cache.
  346. FOR_EACH_MAP_FAST( m_KeyValueCache, mapIndex )
  347. {
  348. m_KeyValueCache[mapIndex]->deleteThis();
  349. }
  350. // Apparently you cannot call RemoveAll on a map without also purging the contents because... ?
  351. // If you do and you continue to use the map, you will eventually wind up in a case where you
  352. // have an empty map but it still iterates over elements. Awesome?
  353. m_KeyValueCache.Purge();
  354. }