Counter Strike : Global Offensive Source Code
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.

523 lines
15 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Defines a symbol table
  4. //
  5. // $Header: $
  6. // $NoKeywords: $
  7. //=============================================================================//
  8. #pragma warning (disable:4514)
  9. #include "utlsymbol.h"
  10. #include "tier0/threadtools.h"
  11. #include "stringpool.h"
  12. #include "generichash.h"
  13. #include "tier0/vprof.h"
  14. #include <stddef.h>
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. #define INVALID_STRING_INDEX CStringPoolIndex( 0xFFFF, 0xFFFF )
  18. #define MIN_STRING_POOL_SIZE 2048
  19. //-----------------------------------------------------------------------------
  20. // globals
  21. //-----------------------------------------------------------------------------
  22. CUtlSymbolTableMT* CUtlSymbol::s_pSymbolTable = 0;
  23. bool CUtlSymbol::s_bAllowStaticSymbolTable = true;
  24. //-----------------------------------------------------------------------------
  25. // symbol methods
  26. //-----------------------------------------------------------------------------
  27. void CUtlSymbol::Initialize()
  28. {
  29. // If this assert fails, then the module that this call is in has chosen to disallow
  30. // use of the static symbol table. Usually, it's to prevent confusion because it's easy
  31. // to accidentally use the global symbol table when you really want to use a specific one.
  32. Assert( s_bAllowStaticSymbolTable );
  33. // necessary to allow us to create global symbols
  34. static bool symbolsInitialized = false;
  35. if (!symbolsInitialized)
  36. {
  37. s_pSymbolTable = new CUtlSymbolTableMT;
  38. symbolsInitialized = true;
  39. }
  40. }
  41. void CUtlSymbol::LockTableForRead()
  42. {
  43. Initialize();
  44. s_pSymbolTable->LockForRead();
  45. }
  46. void CUtlSymbol::UnlockTableForRead()
  47. {
  48. s_pSymbolTable->UnlockForRead();
  49. }
  50. //-----------------------------------------------------------------------------
  51. // Purpose: Singleton to delete table on exit from module
  52. //-----------------------------------------------------------------------------
  53. class CCleanupUtlSymbolTable
  54. {
  55. public:
  56. ~CCleanupUtlSymbolTable()
  57. {
  58. delete CUtlSymbol::s_pSymbolTable;
  59. CUtlSymbol::s_pSymbolTable = NULL;
  60. }
  61. };
  62. static CCleanupUtlSymbolTable g_CleanupSymbolTable;
  63. CUtlSymbolTableMT* CUtlSymbol::CurrTable()
  64. {
  65. Initialize();
  66. return s_pSymbolTable;
  67. }
  68. //-----------------------------------------------------------------------------
  69. // string->symbol->string
  70. //-----------------------------------------------------------------------------
  71. CUtlSymbol::CUtlSymbol( const char* pStr )
  72. {
  73. m_Id = CurrTable()->AddString( pStr );
  74. }
  75. const char* CUtlSymbol::String( ) const
  76. {
  77. return CurrTable()->String(m_Id);
  78. }
  79. const char* CUtlSymbol::StringNoLock( ) const
  80. {
  81. return CurrTable()->StringNoLock(m_Id);
  82. }
  83. void CUtlSymbol::DisableStaticSymbolTable()
  84. {
  85. s_bAllowStaticSymbolTable = false;
  86. }
  87. //-----------------------------------------------------------------------------
  88. // checks if the symbol matches a string
  89. //-----------------------------------------------------------------------------
  90. bool CUtlSymbol::operator==( const char* pStr ) const
  91. {
  92. if (m_Id == UTL_INVAL_SYMBOL)
  93. return false;
  94. return strcmp( String(), pStr ) == 0;
  95. }
  96. //-----------------------------------------------------------------------------
  97. // symbol table stuff
  98. //-----------------------------------------------------------------------------
  99. inline const char* CUtlSymbolTable::DecoratedStringFromIndex( const CStringPoolIndex &index ) const
  100. {
  101. Assert( index.m_iPool < m_StringPools.Count() );
  102. Assert( index.m_iOffset < m_StringPools[index.m_iPool]->m_TotalLen );
  103. // step over the hash decorating the beginning of the string
  104. return (&m_StringPools[index.m_iPool]->m_Data[index.m_iOffset]);
  105. }
  106. inline const char* CUtlSymbolTable::StringFromIndex( const CStringPoolIndex &index ) const
  107. {
  108. // step over the hash decorating the beginning of the string
  109. return DecoratedStringFromIndex(index)+sizeof(hashDecoration_t);
  110. }
  111. // The first two bytes of each string in the pool are actually the hash for that string.
  112. // Thus we compare hashes rather than entire strings for a significant perf benefit.
  113. // However since there is a high rate of hash collision we must still compare strings
  114. // if the hashes match.
  115. bool CUtlSymbolTable::CLess::operator()( const CStringPoolIndex &i1, const CStringPoolIndex &i2 ) const
  116. {
  117. // Need to do pointer math because CUtlSymbolTable is used in CUtlVectors, and hence
  118. // can be arbitrarily moved in memory on a realloc. Yes, this is portable. In reality,
  119. // right now at least, because m_LessFunc is the first member of CUtlRBTree, and m_Lookup
  120. // is the first member of CUtlSymbolTabke, this == pTable
  121. CUtlSymbolTable *pTable = (CUtlSymbolTable *)( (byte *)this - offsetof(CUtlSymbolTable::CTree, m_LessFunc) ) - offsetof(CUtlSymbolTable, m_Lookup );
  122. #if 1 // using the hashes
  123. const char *str1, *str2;
  124. hashDecoration_t hash1, hash2;
  125. if (i1 == INVALID_STRING_INDEX)
  126. {
  127. str1 = pTable->m_pUserSearchString;
  128. hash1 = pTable->m_nUserSearchStringHash;
  129. }
  130. else
  131. {
  132. str1 = pTable->DecoratedStringFromIndex( i1 );
  133. hashDecoration_t storedHash = *reinterpret_cast<const hashDecoration_t *>(str1);
  134. str1 += sizeof(hashDecoration_t);
  135. AssertMsg2( storedHash == ( !pTable->m_bInsensitive ? HashString(str1) : HashStringCaseless(str1) ),
  136. "The stored hash (%d) for symbol %s is not correct.", storedHash, str1 );
  137. hash1 = storedHash;
  138. }
  139. if (i2 == INVALID_STRING_INDEX)
  140. {
  141. str2 = pTable->m_pUserSearchString;
  142. hash2 = pTable->m_nUserSearchStringHash;
  143. }
  144. else
  145. {
  146. str2 = pTable->DecoratedStringFromIndex( i2 );
  147. hashDecoration_t storedHash = *reinterpret_cast<const hashDecoration_t *>(str2);
  148. str2 += sizeof(hashDecoration_t);
  149. AssertMsg2( storedHash == ( !pTable->m_bInsensitive ? HashString(str2) : HashStringCaseless(str2) ),
  150. "The stored hash (%d) for symbol '%s' is not correct.", storedHash, str2 );
  151. hash2 = storedHash;
  152. }
  153. // compare the hashes
  154. if ( hash1 == hash2 )
  155. {
  156. if ( !str1 && str2 )
  157. return 1;
  158. if ( !str2 && str1 )
  159. return -1;
  160. if ( !str1 && !str2 )
  161. return 0;
  162. // if the hashes match compare the strings
  163. if ( !pTable->m_bInsensitive )
  164. return strcmp( str1, str2 ) < 0;
  165. else
  166. return V_stricmp( str1, str2 ) < 0;
  167. }
  168. else
  169. {
  170. return hash1 < hash2;
  171. }
  172. #else // not using the hashes, just comparing strings
  173. const char* str1 = (i1 == INVALID_STRING_INDEX) ? pTable->m_pUserSearchString :
  174. pTable->StringFromIndex( i1 );
  175. const char* str2 = (i2 == INVALID_STRING_INDEX) ? pTable->m_pUserSearchString :
  176. pTable->StringFromIndex( i2 );
  177. if ( !str1 && str2 )
  178. return 1;
  179. if ( !str2 && str1 )
  180. return -1;
  181. if ( !str1 && !str2 )
  182. return 0;
  183. if ( !pTable->m_bInsensitive )
  184. return strcmp( str1, str2 ) < 0;
  185. else
  186. return strcmpi( str1, str2 ) < 0;
  187. #endif
  188. }
  189. //-----------------------------------------------------------------------------
  190. // constructor, destructor
  191. //-----------------------------------------------------------------------------
  192. CUtlSymbolTable::CUtlSymbolTable( int growSize, int initSize, bool caseInsensitive ) :
  193. m_Lookup( growSize, initSize ), m_bInsensitive( caseInsensitive ), m_StringPools( 8 )
  194. {
  195. }
  196. CUtlSymbolTable::~CUtlSymbolTable()
  197. {
  198. // Release the stringpool string data
  199. RemoveAll();
  200. }
  201. CUtlSymbol CUtlSymbolTable::Find( const char* pString ) const
  202. {
  203. VPROF( "CUtlSymbol::Find" );
  204. if (!pString)
  205. return CUtlSymbol();
  206. // Store a special context used to help with insertion
  207. m_pUserSearchString = pString;
  208. m_nUserSearchStringHash = m_bInsensitive ? HashStringCaseless(pString) : HashString(pString) ;
  209. // Passing this special invalid symbol makes the comparison function
  210. // use the string passed in the context
  211. UtlSymId_t idx = m_Lookup.Find( INVALID_STRING_INDEX );
  212. #ifdef _DEBUG
  213. m_pUserSearchString = NULL;
  214. m_nUserSearchStringHash = 0;
  215. #endif
  216. return CUtlSymbol( idx );
  217. }
  218. int CUtlSymbolTable::FindPoolWithSpace( int len ) const
  219. {
  220. for ( int i=0; i < m_StringPools.Count(); i++ )
  221. {
  222. StringPool_t *pPool = m_StringPools[i];
  223. if ( (pPool->m_TotalLen - pPool->m_SpaceUsed) >= len )
  224. {
  225. return i;
  226. }
  227. }
  228. return -1;
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Finds and/or creates a symbol based on the string
  232. //-----------------------------------------------------------------------------
  233. CUtlSymbol CUtlSymbolTable::AddString( const char* pString )
  234. {
  235. VPROF("CUtlSymbol::AddString");
  236. if (!pString)
  237. return CUtlSymbol( UTL_INVAL_SYMBOL );
  238. CUtlSymbol id = Find( pString );
  239. if (id.IsValid())
  240. return id;
  241. int lenString = strlen(pString) + 1; // length of just the string
  242. int lenDecorated = lenString + sizeof(hashDecoration_t); // and with its hash decoration
  243. // make sure that all strings are aligned on 2-byte boundaries so the hashes will read correctly
  244. COMPILE_TIME_ASSERT(sizeof(hashDecoration_t) == 2);
  245. lenDecorated = (lenDecorated + 1) & (~0x01); // round up to nearest multiple of 2
  246. // Find a pool with space for this string, or allocate a new one.
  247. int iPool = FindPoolWithSpace( lenDecorated );
  248. if ( iPool == -1 )
  249. {
  250. // Add a new pool.
  251. int newPoolSize = MAX( lenDecorated + sizeof( StringPool_t ), MIN_STRING_POOL_SIZE );
  252. StringPool_t *pPool = (StringPool_t*)malloc( newPoolSize );
  253. pPool->m_TotalLen = newPoolSize - sizeof( StringPool_t );
  254. pPool->m_SpaceUsed = 0;
  255. iPool = m_StringPools.AddToTail( pPool );
  256. }
  257. // Compute a hash
  258. hashDecoration_t hash = m_bInsensitive ? HashStringCaseless(pString) : HashString(pString) ;
  259. // Copy the string in.
  260. StringPool_t *pPool = m_StringPools[iPool];
  261. Assert( pPool->m_SpaceUsed < 0xFFFF ); // This should never happen, because if we had a string > 64k, it
  262. // would have been given its entire own pool.
  263. unsigned short iStringOffset = pPool->m_SpaceUsed;
  264. const char *startingAddr = &pPool->m_Data[pPool->m_SpaceUsed];
  265. // store the hash at the head of the string
  266. *((hashDecoration_t *)(startingAddr)) = hash;
  267. // and then the string's data
  268. memcpy( (void *)(startingAddr + sizeof(hashDecoration_t)), pString, lenString );
  269. pPool->m_SpaceUsed += lenDecorated;
  270. // insert the string into the vector.
  271. CStringPoolIndex index;
  272. index.m_iPool = iPool;
  273. index.m_iOffset = iStringOffset;
  274. MEM_ALLOC_CREDIT();
  275. UtlSymId_t idx = m_Lookup.Insert( index );
  276. return CUtlSymbol( idx );
  277. }
  278. //-----------------------------------------------------------------------------
  279. // Look up the string associated with a particular symbol
  280. //-----------------------------------------------------------------------------
  281. const char* CUtlSymbolTable::String( CUtlSymbol id ) const
  282. {
  283. if (!id.IsValid())
  284. return "";
  285. Assert( m_Lookup.IsValidIndex((UtlSymId_t)id) );
  286. return StringFromIndex( m_Lookup[id] );
  287. }
  288. //-----------------------------------------------------------------------------
  289. // Remove all symbols in the table.
  290. //-----------------------------------------------------------------------------
  291. void CUtlSymbolTable::RemoveAll()
  292. {
  293. m_Lookup.Purge();
  294. for ( int i=0; i < m_StringPools.Count(); i++ )
  295. free( m_StringPools[i] );
  296. m_StringPools.RemoveAll();
  297. }
  298. //-----------------------------------------------------------------------------
  299. // Purpose:
  300. // Input : *pFileName -
  301. // Output : FileNameHandle_t
  302. //-----------------------------------------------------------------------------
  303. FileNameHandle_t CUtlFilenameSymbolTable::FindOrAddFileName( const char *pFileName )
  304. {
  305. if ( !pFileName )
  306. {
  307. return NULL;
  308. }
  309. // find first
  310. FileNameHandle_t hFileName = FindFileName( pFileName );
  311. if ( hFileName )
  312. {
  313. return hFileName;
  314. }
  315. // Fix slashes+dotslashes and make lower case first..
  316. char fn[ MAX_PATH ];
  317. Q_strncpy( fn, pFileName, sizeof( fn ) );
  318. Q_RemoveDotSlashes( fn );
  319. #ifdef _WIN32
  320. strlwr( fn );
  321. #endif
  322. // Split the filename into constituent parts
  323. char basepath[ MAX_PATH ];
  324. Q_ExtractFilePath( fn, basepath, sizeof( basepath ) );
  325. char filename[ MAX_PATH ];
  326. Q_strncpy( filename, fn + Q_strlen( basepath ), sizeof( filename ) );
  327. // not found, lock and look again
  328. FileNameHandleInternal_t handle;
  329. m_lock.LockForWrite();
  330. handle.SetPath( m_PathStringPool.FindStringHandle( basepath ) );
  331. handle.SetFile( m_FileStringPool.FindStringHandle( filename ) );
  332. if ( handle.GetPath() && handle.GetFile() )
  333. {
  334. // found
  335. m_lock.UnlockWrite();
  336. return *( FileNameHandle_t * )( &handle );
  337. }
  338. // safely add it
  339. handle.SetPath( m_PathStringPool.ReferenceStringHandle( basepath ) );
  340. handle.SetFile( m_FileStringPool.ReferenceStringHandle( filename ) );
  341. m_lock.UnlockWrite();
  342. return *( FileNameHandle_t * )( &handle );
  343. }
  344. FileNameHandle_t CUtlFilenameSymbolTable::FindFileName( const char *pFileName )
  345. {
  346. if ( !pFileName )
  347. {
  348. return NULL;
  349. }
  350. // Fix slashes+dotslashes and make lower case first..
  351. char fn[ MAX_PATH ];
  352. Q_strncpy( fn, pFileName, sizeof( fn ) );
  353. Q_RemoveDotSlashes( fn );
  354. #ifdef _WIN32
  355. strlwr( fn );
  356. #endif
  357. // Split the filename into constituent parts
  358. char basepath[ MAX_PATH ];
  359. Q_ExtractFilePath( fn, basepath, sizeof( basepath ) );
  360. char filename[ MAX_PATH ];
  361. Q_strncpy( filename, fn + Q_strlen( basepath ), sizeof( filename ) );
  362. FileNameHandleInternal_t handle;
  363. m_lock.LockForRead();
  364. handle.SetPath( m_PathStringPool.FindStringHandle( basepath ) );
  365. handle.SetFile( m_FileStringPool.FindStringHandle( filename ) );
  366. m_lock.UnlockRead();
  367. if ( ( handle.GetPath() == 0 ) || ( handle.GetFile() == 0 ) )
  368. return NULL;
  369. return *( FileNameHandle_t * )( &handle );
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Purpose:
  373. // Input : handle -
  374. // Output : const char
  375. //-----------------------------------------------------------------------------
  376. bool CUtlFilenameSymbolTable::String( const FileNameHandle_t& handle, char *buf, int buflen )
  377. {
  378. buf[ 0 ] = 0;
  379. FileNameHandleInternal_t *internalFileHandle = ( FileNameHandleInternal_t * )&handle;
  380. if ( !internalFileHandle )
  381. {
  382. return false;
  383. }
  384. m_lock.LockForRead();
  385. const char *path = m_PathStringPool.HandleToString( internalFileHandle->GetPath() );
  386. const char *fn = m_FileStringPool.HandleToString( internalFileHandle->GetFile() );
  387. m_lock.UnlockRead();
  388. if ( !path || !fn )
  389. {
  390. return false;
  391. }
  392. Q_strncpy( buf, path, buflen );
  393. Q_strncat( buf, fn, buflen, COPY_ALL_CHARACTERS );
  394. return true;
  395. }
  396. void CUtlFilenameSymbolTable::RemoveAll()
  397. {
  398. m_PathStringPool.FreeAll();
  399. m_FileStringPool.FreeAll();
  400. }
  401. void CUtlFilenameSymbolTable::SpewStrings()
  402. {
  403. m_lock.LockForRead();
  404. m_PathStringPool.SpewStrings();
  405. m_FileStringPool.SpewStrings();
  406. m_lock.UnlockRead();
  407. }
  408. bool CUtlFilenameSymbolTable::SaveToBuffer( CUtlBuffer &buffer )
  409. {
  410. m_lock.LockForRead();
  411. bool bResult = m_PathStringPool.SaveToBuffer( buffer );
  412. bResult = bResult && m_FileStringPool.SaveToBuffer( buffer );
  413. m_lock.UnlockRead();
  414. return bResult;
  415. }
  416. bool CUtlFilenameSymbolTable::RestoreFromBuffer( CUtlBuffer &buffer )
  417. {
  418. m_lock.LockForWrite();
  419. bool bResult = m_PathStringPool.RestoreFromBuffer( buffer );
  420. bResult = bResult && m_FileStringPool.RestoreFromBuffer( buffer );
  421. m_lock.UnlockWrite();
  422. return bResult;
  423. }