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.

436 lines
12 KiB

  1. //========= Copyright 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 "KeyValues.h"
  11. #include "tier0/threadtools.h"
  12. #include "tier0/memdbgon.h"
  13. #include "stringpool.h"
  14. #include "utlhashtable.h"
  15. #include "utlstring.h"
  16. // Ensure that everybody has the right compiler version installed. The version
  17. // number can be obtained by looking at the compiler output when you type 'cl'
  18. // and removing the last two digits and the periods: 16.00.40219.01 becomes 160040219
  19. #ifdef _MSC_FULL_VER
  20. #if _MSC_FULL_VER > 160000000
  21. // VS 2010
  22. #if _MSC_FULL_VER < 160040219
  23. #error You must install VS 2010 SP1
  24. #endif
  25. #else
  26. // VS 2005
  27. #if _MSC_FULL_VER < 140050727
  28. #error You must install VS 2005 SP1
  29. #endif
  30. #endif
  31. #endif
  32. // memdbgon must be the last include file in a .cpp file!!!
  33. #include "tier0/memdbgon.h"
  34. #define INVALID_STRING_INDEX CStringPoolIndex( 0xFFFF, 0xFFFF )
  35. #define MIN_STRING_POOL_SIZE 2048
  36. //-----------------------------------------------------------------------------
  37. // globals
  38. //-----------------------------------------------------------------------------
  39. CUtlSymbolTableMT* CUtlSymbol::s_pSymbolTable = 0;
  40. bool CUtlSymbol::s_bAllowStaticSymbolTable = true;
  41. //-----------------------------------------------------------------------------
  42. // symbol methods
  43. //-----------------------------------------------------------------------------
  44. void CUtlSymbol::Initialize()
  45. {
  46. // If this assert fails, then the module that this call is in has chosen to disallow
  47. // use of the static symbol table. Usually, it's to prevent confusion because it's easy
  48. // to accidentally use the global symbol table when you really want to use a specific one.
  49. Assert( s_bAllowStaticSymbolTable );
  50. // necessary to allow us to create global symbols
  51. static bool symbolsInitialized = false;
  52. if (!symbolsInitialized)
  53. {
  54. s_pSymbolTable = new CUtlSymbolTableMT;
  55. symbolsInitialized = true;
  56. }
  57. }
  58. //-----------------------------------------------------------------------------
  59. // Purpose: Singleton to delete table on exit from module
  60. //-----------------------------------------------------------------------------
  61. class CCleanupUtlSymbolTable
  62. {
  63. public:
  64. ~CCleanupUtlSymbolTable()
  65. {
  66. delete CUtlSymbol::s_pSymbolTable;
  67. CUtlSymbol::s_pSymbolTable = NULL;
  68. }
  69. };
  70. static CCleanupUtlSymbolTable g_CleanupSymbolTable;
  71. CUtlSymbolTableMT* CUtlSymbol::CurrTable()
  72. {
  73. Initialize();
  74. return s_pSymbolTable;
  75. }
  76. //-----------------------------------------------------------------------------
  77. // string->symbol->string
  78. //-----------------------------------------------------------------------------
  79. CUtlSymbol::CUtlSymbol( const char* pStr )
  80. {
  81. m_Id = CurrTable()->AddString( pStr );
  82. }
  83. const char* CUtlSymbol::String( ) const
  84. {
  85. return CurrTable()->String(m_Id);
  86. }
  87. void CUtlSymbol::DisableStaticSymbolTable()
  88. {
  89. s_bAllowStaticSymbolTable = false;
  90. }
  91. //-----------------------------------------------------------------------------
  92. // checks if the symbol matches a string
  93. //-----------------------------------------------------------------------------
  94. bool CUtlSymbol::operator==( const char* pStr ) const
  95. {
  96. if (m_Id == UTL_INVAL_SYMBOL)
  97. return false;
  98. return strcmp( String(), pStr ) == 0;
  99. }
  100. //-----------------------------------------------------------------------------
  101. // symbol table stuff
  102. //-----------------------------------------------------------------------------
  103. inline const char* CUtlSymbolTable::StringFromIndex( const CStringPoolIndex &index ) const
  104. {
  105. Assert( index.m_iPool < m_StringPools.Count() );
  106. Assert( index.m_iOffset < m_StringPools[index.m_iPool]->m_TotalLen );
  107. return &m_StringPools[index.m_iPool]->m_Data[index.m_iOffset];
  108. }
  109. bool CUtlSymbolTable::CLess::operator()( const CStringPoolIndex &i1, const CStringPoolIndex &i2 ) const
  110. {
  111. // Need to do pointer math because CUtlSymbolTable is used in CUtlVectors, and hence
  112. // can be arbitrarily moved in memory on a realloc. Yes, this is portable. In reality,
  113. // right now at least, because m_LessFunc is the first member of CUtlRBTree, and m_Lookup
  114. // is the first member of CUtlSymbolTabke, this == pTable
  115. CUtlSymbolTable *pTable = (CUtlSymbolTable *)( (byte *)this - offsetof(CUtlSymbolTable::CTree, m_LessFunc) ) - offsetof(CUtlSymbolTable, m_Lookup );
  116. const char* str1 = (i1 == INVALID_STRING_INDEX) ? pTable->m_pUserSearchString :
  117. pTable->StringFromIndex( i1 );
  118. const char* str2 = (i2 == INVALID_STRING_INDEX) ? pTable->m_pUserSearchString :
  119. pTable->StringFromIndex( i2 );
  120. if ( !str1 && str2 )
  121. return false;
  122. if ( !str2 && str1 )
  123. return true;
  124. if ( !str1 && !str2 )
  125. return false;
  126. if ( !pTable->m_bInsensitive )
  127. return V_strcmp( str1, str2 ) < 0;
  128. else
  129. return V_stricmp( str1, str2 ) < 0;
  130. }
  131. //-----------------------------------------------------------------------------
  132. // constructor, destructor
  133. //-----------------------------------------------------------------------------
  134. CUtlSymbolTable::CUtlSymbolTable( int growSize, int initSize, bool caseInsensitive ) :
  135. m_Lookup( growSize, initSize ), m_bInsensitive( caseInsensitive ), m_StringPools( 8 )
  136. {
  137. }
  138. CUtlSymbolTable::~CUtlSymbolTable()
  139. {
  140. // Release the stringpool string data
  141. RemoveAll();
  142. }
  143. CUtlSymbol CUtlSymbolTable::Find( const char* pString ) const
  144. {
  145. if (!pString)
  146. return CUtlSymbol();
  147. // Store a special context used to help with insertion
  148. m_pUserSearchString = pString;
  149. // Passing this special invalid symbol makes the comparison function
  150. // use the string passed in the context
  151. UtlSymId_t idx = m_Lookup.Find( INVALID_STRING_INDEX );
  152. #ifdef _DEBUG
  153. m_pUserSearchString = NULL;
  154. #endif
  155. return CUtlSymbol( idx );
  156. }
  157. int CUtlSymbolTable::FindPoolWithSpace( int len ) const
  158. {
  159. for ( int i=0; i < m_StringPools.Count(); i++ )
  160. {
  161. StringPool_t *pPool = m_StringPools[i];
  162. if ( (pPool->m_TotalLen - pPool->m_SpaceUsed) >= len )
  163. {
  164. return i;
  165. }
  166. }
  167. return -1;
  168. }
  169. //-----------------------------------------------------------------------------
  170. // Finds and/or creates a symbol based on the string
  171. //-----------------------------------------------------------------------------
  172. CUtlSymbol CUtlSymbolTable::AddString( const char* pString )
  173. {
  174. if (!pString)
  175. return CUtlSymbol( UTL_INVAL_SYMBOL );
  176. CUtlSymbol id = Find( pString );
  177. if (id.IsValid())
  178. return id;
  179. int len = V_strlen(pString) + 1;
  180. // Find a pool with space for this string, or allocate a new one.
  181. int iPool = FindPoolWithSpace( len );
  182. if ( iPool == -1 )
  183. {
  184. // Add a new pool.
  185. int newPoolSize = max( len, MIN_STRING_POOL_SIZE );
  186. StringPool_t *pPool = (StringPool_t*)malloc( sizeof( StringPool_t ) + newPoolSize - 1 );
  187. pPool->m_TotalLen = newPoolSize;
  188. pPool->m_SpaceUsed = 0;
  189. iPool = m_StringPools.AddToTail( pPool );
  190. }
  191. // Copy the string in.
  192. StringPool_t *pPool = m_StringPools[iPool];
  193. Assert( pPool->m_SpaceUsed < 0xFFFF ); // This should never happen, because if we had a string > 64k, it
  194. // would have been given its entire own pool.
  195. unsigned short iStringOffset = pPool->m_SpaceUsed;
  196. memcpy( &pPool->m_Data[pPool->m_SpaceUsed], pString, len );
  197. pPool->m_SpaceUsed += len;
  198. // didn't find, insert the string into the vector.
  199. CStringPoolIndex index;
  200. index.m_iPool = iPool;
  201. index.m_iOffset = iStringOffset;
  202. UtlSymId_t idx = m_Lookup.Insert( index );
  203. return CUtlSymbol( idx );
  204. }
  205. //-----------------------------------------------------------------------------
  206. // Look up the string associated with a particular symbol
  207. //-----------------------------------------------------------------------------
  208. const char* CUtlSymbolTable::String( CUtlSymbol id ) const
  209. {
  210. if (!id.IsValid())
  211. return "";
  212. Assert( m_Lookup.IsValidIndex((UtlSymId_t)id) );
  213. return StringFromIndex( m_Lookup[id] );
  214. }
  215. //-----------------------------------------------------------------------------
  216. // Remove all symbols in the table.
  217. //-----------------------------------------------------------------------------
  218. void CUtlSymbolTable::RemoveAll()
  219. {
  220. m_Lookup.Purge();
  221. for ( int i=0; i < m_StringPools.Count(); i++ )
  222. free( m_StringPools[i] );
  223. m_StringPools.RemoveAll();
  224. }
  225. class CUtlFilenameSymbolTable::HashTable : public CUtlStableHashtable<CUtlConstString>
  226. {
  227. };
  228. CUtlFilenameSymbolTable::CUtlFilenameSymbolTable()
  229. {
  230. m_Strings = new HashTable;
  231. }
  232. CUtlFilenameSymbolTable::~CUtlFilenameSymbolTable()
  233. {
  234. delete m_Strings;
  235. }
  236. //-----------------------------------------------------------------------------
  237. // Purpose:
  238. // Input : *pFileName -
  239. // Output : FileNameHandle_t
  240. //-----------------------------------------------------------------------------
  241. FileNameHandle_t CUtlFilenameSymbolTable::FindOrAddFileName( const char *pFileName )
  242. {
  243. if ( !pFileName )
  244. {
  245. return NULL;
  246. }
  247. // find first
  248. FileNameHandle_t hFileName = FindFileName( pFileName );
  249. if ( hFileName )
  250. {
  251. return hFileName;
  252. }
  253. // Fix slashes+dotslashes and make lower case first..
  254. char fn[ MAX_PATH ];
  255. Q_strncpy( fn, pFileName, sizeof( fn ) );
  256. Q_RemoveDotSlashes( fn );
  257. #ifdef _WIN32
  258. Q_strlower( fn );
  259. #endif
  260. // Split the filename into constituent parts
  261. char basepath[ MAX_PATH ];
  262. Q_ExtractFilePath( fn, basepath, sizeof( basepath ) );
  263. char filename[ MAX_PATH ];
  264. Q_strncpy( filename, fn + Q_strlen( basepath ), sizeof( filename ) );
  265. // not found, lock and look again
  266. FileNameHandleInternal_t handle;
  267. m_lock.LockForWrite();
  268. handle.path = m_Strings->Insert( basepath ) + 1;
  269. handle.file = m_Strings->Insert( filename ) + 1;
  270. //handle.path = m_StringPool.FindStringHandle( basepath );
  271. //handle.file = m_StringPool.FindStringHandle( filename );
  272. //if ( handle.path != m_Strings.InvalidHandle() && handle.file )
  273. //{
  274. // found
  275. // m_lock.UnlockWrite();
  276. // return *( FileNameHandle_t * )( &handle );
  277. //}
  278. // safely add it
  279. //handle.path = m_StringPool.ReferenceStringHandle( basepath );
  280. //handle.file = m_StringPool.ReferenceStringHandle( filename );
  281. m_lock.UnlockWrite();
  282. return *( FileNameHandle_t * )( &handle );
  283. }
  284. FileNameHandle_t CUtlFilenameSymbolTable::FindFileName( const char *pFileName )
  285. {
  286. if ( !pFileName )
  287. {
  288. return NULL;
  289. }
  290. // Fix slashes+dotslashes and make lower case first..
  291. char fn[ MAX_PATH ];
  292. Q_strncpy( fn, pFileName, sizeof( fn ) );
  293. Q_RemoveDotSlashes( fn );
  294. #ifdef _WIN32
  295. Q_strlower( fn );
  296. #endif
  297. // Split the filename into constituent parts
  298. char basepath[ MAX_PATH ];
  299. Q_ExtractFilePath( fn, basepath, sizeof( basepath ) );
  300. char filename[ MAX_PATH ];
  301. Q_strncpy( filename, fn + Q_strlen( basepath ), sizeof( filename ) );
  302. FileNameHandleInternal_t handle;
  303. Assert( (uint16)(m_Strings->InvalidHandle() + 1) == 0 );
  304. m_lock.LockForRead();
  305. handle.path = m_Strings->Find(basepath) + 1;
  306. handle.file = m_Strings->Find(filename) + 1;
  307. //handle.path = m_StringPool.FindStringHandle(basepath);
  308. //handle.file = m_StringPool.FindStringHandle(filename);
  309. m_lock.UnlockRead();
  310. if ( handle.path == 0 || handle.file == 0 )
  311. return NULL;
  312. return *( FileNameHandle_t * )( &handle );
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose:
  316. // Input : handle -
  317. // Output : const char
  318. //-----------------------------------------------------------------------------
  319. bool CUtlFilenameSymbolTable::String( const FileNameHandle_t& handle, char *buf, int buflen )
  320. {
  321. buf[ 0 ] = 0;
  322. FileNameHandleInternal_t *internal = ( FileNameHandleInternal_t * )&handle;
  323. if ( !internal || !internal->file || !internal->path )
  324. {
  325. return false;
  326. }
  327. m_lock.LockForRead();
  328. //const char *path = m_StringPool.HandleToString(internal->path);
  329. //const char *fn = m_StringPool.HandleToString(internal->file);
  330. const char *path = (*m_Strings)[ internal->path - 1 ].Get();
  331. const char *fn = (*m_Strings)[ internal->file - 1].Get();
  332. m_lock.UnlockRead();
  333. if ( !path || !fn )
  334. {
  335. return false;
  336. }
  337. Q_strncpy( buf, path, buflen );
  338. Q_strncat( buf, fn, buflen, COPY_ALL_CHARACTERS );
  339. return true;
  340. }
  341. void CUtlFilenameSymbolTable::RemoveAll()
  342. {
  343. m_Strings->Purge();
  344. }