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.

426 lines
10 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "convar.h"
  8. #include "tier0/dbg.h"
  9. #include "stringpool.h"
  10. #include "tier1/strtools.h"
  11. #include "generichash.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. //-----------------------------------------------------------------------------
  15. // Purpose: Comparison function for string sorted associative data structures
  16. //-----------------------------------------------------------------------------
  17. bool StrLessInsensitive( const char * const &pszLeft, const char * const &pszRight )
  18. {
  19. return ( V_stricmp( pszLeft, pszRight) < 0 );
  20. }
  21. bool StrLessSensitive( const char * const &pszLeft, const char * const &pszRight )
  22. {
  23. return ( V_strcmp( pszLeft, pszRight) < 0 );
  24. }
  25. //-----------------------------------------------------------------------------
  26. //-----------------------------------------------------------------------------
  27. CStringPool::CStringPool( StringPoolCase_t caseSensitivity )
  28. : m_Strings( 32, 256, caseSensitivity == StringPoolCaseInsensitive ? StrLessInsensitive : StrLessSensitive )
  29. {
  30. }
  31. //-----------------------------------------------------------------------------
  32. //-----------------------------------------------------------------------------
  33. CStringPool::~CStringPool()
  34. {
  35. FreeAll();
  36. }
  37. //-----------------------------------------------------------------------------
  38. //-----------------------------------------------------------------------------
  39. unsigned int CStringPool::Count() const
  40. {
  41. return m_Strings.Count();
  42. }
  43. //-----------------------------------------------------------------------------
  44. //-----------------------------------------------------------------------------
  45. const char * CStringPool::Find( const char *pszValue )
  46. {
  47. unsigned short i = m_Strings.Find(pszValue);
  48. if ( m_Strings.IsValidIndex(i) )
  49. return m_Strings[i];
  50. return NULL;
  51. }
  52. const char * CStringPool::Allocate( const char *pszValue )
  53. {
  54. char *pszNew;
  55. unsigned short i = m_Strings.Find(pszValue);
  56. bool bNew = (i == m_Strings.InvalidIndex());
  57. if ( !bNew )
  58. return m_Strings[i];
  59. pszNew = strdup( pszValue );
  60. m_Strings.Insert( pszNew );
  61. return pszNew;
  62. }
  63. //-----------------------------------------------------------------------------
  64. //-----------------------------------------------------------------------------
  65. void CStringPool::FreeAll()
  66. {
  67. unsigned short i = m_Strings.FirstInorder();
  68. while ( i != m_Strings.InvalidIndex() )
  69. {
  70. free( (void *)m_Strings[i] );
  71. i = m_Strings.NextInorder(i);
  72. }
  73. m_Strings.RemoveAll();
  74. }
  75. //-----------------------------------------------------------------------------
  76. //-----------------------------------------------------------------------------
  77. CCountedStringPool::CCountedStringPool( StringPoolCase_t caseSensitivity )
  78. {
  79. MEM_ALLOC_CREDIT();
  80. m_HashTable.EnsureCount(HASH_TABLE_SIZE);
  81. for( int i = 0; i < m_HashTable.Count(); i++ )
  82. {
  83. m_HashTable[i] = INVALID_ELEMENT;
  84. }
  85. m_FreeListStart = INVALID_ELEMENT;
  86. m_Elements.AddToTail();
  87. m_Elements[0].pString = NULL;
  88. m_Elements[0].nReferenceCount = 0;
  89. m_Elements[0].nNextElement = INVALID_ELEMENT;
  90. m_caseSensitivity = caseSensitivity;
  91. }
  92. CCountedStringPool::~CCountedStringPool()
  93. {
  94. FreeAll();
  95. }
  96. void CCountedStringPool::FreeAll()
  97. {
  98. int i;
  99. // Reset the hash table:
  100. for( i = 0; i < m_HashTable.Count(); i++ )
  101. {
  102. m_HashTable[i] = INVALID_ELEMENT;
  103. }
  104. // Blow away the free list:
  105. m_FreeListStart = INVALID_ELEMENT;
  106. for( i = 0; i < m_Elements.Count(); i++ )
  107. {
  108. if( m_Elements[i].pString )
  109. {
  110. delete [] m_Elements[i].pString;
  111. m_Elements[i].pString = NULL;
  112. m_Elements[i].nReferenceCount = 0;
  113. m_Elements[i].nNextElement = INVALID_ELEMENT;
  114. }
  115. }
  116. // Remove all but the invalid element:
  117. m_Elements.RemoveAll();
  118. m_Elements.AddToTail();
  119. m_Elements[0].pString = NULL;
  120. m_Elements[0].nReferenceCount = 0;
  121. m_Elements[0].nNextElement = INVALID_ELEMENT;
  122. }
  123. unsigned CCountedStringPool::Hash( const char *pszKey )
  124. {
  125. if ( m_caseSensitivity == StringPoolCaseInsensitive )
  126. {
  127. return HashStringCaseless( pszKey );
  128. }
  129. return HashString( pszKey );
  130. }
  131. unsigned short CCountedStringPool::FindStringHandle( const char* pIntrinsic )
  132. {
  133. if( pIntrinsic == NULL )
  134. return INVALID_ELEMENT;
  135. unsigned short nHashBucketIndex = ( Hash( pIntrinsic ) %HASH_TABLE_SIZE);
  136. unsigned short nCurrentBucket = m_HashTable[ nHashBucketIndex ];
  137. // Does the bucket already exist?
  138. if( nCurrentBucket != INVALID_ELEMENT )
  139. {
  140. for( ; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement )
  141. {
  142. if( !V_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) )
  143. {
  144. return nCurrentBucket;
  145. }
  146. }
  147. }
  148. return 0;
  149. }
  150. char* CCountedStringPool::FindString( const char* pIntrinsic )
  151. {
  152. if( pIntrinsic == NULL )
  153. return NULL;
  154. // Yes, this will be NULL on failure.
  155. return m_Elements[FindStringHandle(pIntrinsic)].pString;
  156. }
  157. unsigned short CCountedStringPool::ReferenceStringHandle( const char* pIntrinsic )
  158. {
  159. if( pIntrinsic == NULL )
  160. return INVALID_ELEMENT;
  161. unsigned short nHashBucketIndex = ( Hash( pIntrinsic ) % HASH_TABLE_SIZE);
  162. unsigned short nCurrentBucket = m_HashTable[ nHashBucketIndex ];
  163. // Does the bucket already exist?
  164. if( nCurrentBucket != INVALID_ELEMENT )
  165. {
  166. for( ; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement )
  167. {
  168. if( !V_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) )
  169. {
  170. // Anyone who hits 65k references is permanant
  171. if( m_Elements[nCurrentBucket].nReferenceCount < MAX_REFERENCE )
  172. {
  173. m_Elements[nCurrentBucket].nReferenceCount ++ ;
  174. }
  175. return nCurrentBucket;
  176. }
  177. }
  178. }
  179. if( m_FreeListStart != INVALID_ELEMENT )
  180. {
  181. nCurrentBucket = m_FreeListStart;
  182. m_FreeListStart = m_Elements[nCurrentBucket].nNextElement;
  183. }
  184. else
  185. {
  186. nCurrentBucket = m_Elements.AddToTail();
  187. }
  188. m_Elements[nCurrentBucket].nReferenceCount = 1;
  189. // Insert at the beginning of the bucket:
  190. m_Elements[nCurrentBucket].nNextElement = m_HashTable[ nHashBucketIndex ];
  191. m_HashTable[ nHashBucketIndex ] = nCurrentBucket;
  192. m_Elements[nCurrentBucket].pString = new char[V_strlen( pIntrinsic ) + 1];
  193. V_strcpy( m_Elements[nCurrentBucket].pString, pIntrinsic );
  194. return nCurrentBucket;
  195. }
  196. char* CCountedStringPool::ReferenceString( const char* pIntrinsic )
  197. {
  198. if(!pIntrinsic)
  199. return NULL;
  200. return m_Elements[ReferenceStringHandle( pIntrinsic)].pString;
  201. }
  202. void CCountedStringPool::DereferenceString( const char* pIntrinsic )
  203. {
  204. // If we get a NULL pointer, just return
  205. if (!pIntrinsic)
  206. return;
  207. unsigned short nHashBucketIndex = (Hash( pIntrinsic ) % m_HashTable.Count());
  208. unsigned short nCurrentBucket = m_HashTable[ nHashBucketIndex ];
  209. // If there isn't anything in the bucket, just return.
  210. if ( nCurrentBucket == INVALID_ELEMENT )
  211. return;
  212. for( unsigned short previous = INVALID_ELEMENT; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement )
  213. {
  214. if( !V_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) )
  215. {
  216. // Anyone who hits 65k references is permanant
  217. if( m_Elements[nCurrentBucket].nReferenceCount < MAX_REFERENCE )
  218. {
  219. m_Elements[nCurrentBucket].nReferenceCount --;
  220. }
  221. if( m_Elements[nCurrentBucket].nReferenceCount == 0 )
  222. {
  223. if( previous == INVALID_ELEMENT )
  224. {
  225. m_HashTable[nHashBucketIndex] = m_Elements[nCurrentBucket].nNextElement;
  226. }
  227. else
  228. {
  229. m_Elements[previous].nNextElement = m_Elements[nCurrentBucket].nNextElement;
  230. }
  231. delete [] m_Elements[nCurrentBucket].pString;
  232. m_Elements[nCurrentBucket].pString = NULL;
  233. m_Elements[nCurrentBucket].nReferenceCount = 0;
  234. m_Elements[nCurrentBucket].nNextElement = m_FreeListStart;
  235. m_FreeListStart = nCurrentBucket;
  236. break;
  237. }
  238. }
  239. previous = nCurrentBucket;
  240. }
  241. }
  242. char* CCountedStringPool::HandleToString( unsigned short handle )
  243. {
  244. return m_Elements[handle].pString;
  245. }
  246. void CCountedStringPool::SpewStrings()
  247. {
  248. int i;
  249. for ( i = 0; i < m_Elements.Count(); i++ )
  250. {
  251. char* string;
  252. string = m_Elements[i].pString;
  253. Msg("String %d: ref:%d %s\n", i, m_Elements[i].nReferenceCount, string == NULL? "EMPTY - ok for slot zero only!" : string);
  254. }
  255. Msg("\n%d total counted strings.", m_Elements.Count());
  256. }
  257. #ifdef _DEBUG
  258. CON_COMMAND( test_stringpool, "Tests the class CStringPool" )
  259. {
  260. CStringPool pool;
  261. Assert(pool.Count() == 0);
  262. pool.Allocate("test");
  263. Assert(pool.Count() == 1);
  264. pool.Allocate("test");
  265. Assert(pool.Count() == 1);
  266. pool.Allocate("test2");
  267. Assert(pool.Count() == 2);
  268. Assert( pool.Find("test2") != NULL );
  269. Assert( pool.Find("TEST") != NULL );
  270. Assert( pool.Find("Test2") != NULL );
  271. Assert( pool.Find("test") != NULL );
  272. pool.FreeAll();
  273. Assert(pool.Count() == 0);
  274. Msg("Pass.");
  275. }
  276. #endif
  277. #define STRING_POOL_VERSION MAKEID( 'C', 'S', 'P', '1' )
  278. #define MAX_STRING_SAVE 1024
  279. bool CCountedStringPool::SaveToBuffer( CUtlBuffer &buffer )
  280. {
  281. if ( m_Elements.Count() <= 1 )
  282. {
  283. // pool is empty, saving nothing
  284. // caller can check put position of buffer to detect
  285. return true;
  286. }
  287. // signature/version
  288. buffer.PutInt( STRING_POOL_VERSION );
  289. buffer.PutUnsignedShort( m_FreeListStart );
  290. buffer.PutInt( m_HashTable.Count() );
  291. for ( int i = 0; i < m_HashTable.Count(); i++ )
  292. {
  293. buffer.PutUnsignedShort( m_HashTable[i] );
  294. }
  295. buffer.PutInt( m_Elements.Count() );
  296. for ( int i = 1; i < m_Elements.Count(); i++ )
  297. {
  298. buffer.PutUnsignedShort( m_Elements[i].nNextElement );
  299. buffer.PutUnsignedChar( m_Elements[i].nReferenceCount );
  300. const char *pString = m_Elements[i].pString;
  301. if ( strlen( pString ) >= MAX_STRING_SAVE )
  302. {
  303. return false;
  304. }
  305. buffer.PutString( pString ? pString : "" );
  306. }
  307. return buffer.IsValid();
  308. }
  309. bool CCountedStringPool::RestoreFromBuffer( CUtlBuffer &buffer )
  310. {
  311. int signature = buffer.GetInt();
  312. if ( signature != STRING_POOL_VERSION )
  313. {
  314. // wrong version
  315. return false;
  316. }
  317. FreeAll();
  318. m_FreeListStart = buffer.GetUnsignedShort();
  319. int hashCount = buffer.GetInt();
  320. m_HashTable.SetCount( hashCount );
  321. for ( int i = 0; i < hashCount; i++ )
  322. {
  323. m_HashTable[i] = buffer.GetUnsignedShort();
  324. }
  325. int tableCount = buffer.GetInt();
  326. if ( tableCount > 1 )
  327. {
  328. m_Elements.AddMultipleToTail( tableCount-1 );
  329. }
  330. char tempString[MAX_STRING_SAVE];
  331. for ( int i = 1; i < tableCount; i++ )
  332. {
  333. m_Elements[i].nNextElement = buffer.GetUnsignedShort();
  334. m_Elements[i].nReferenceCount = buffer.GetUnsignedChar();
  335. buffer.GetString( tempString, sizeof( tempString ) );
  336. m_Elements[i].pString = strdup( tempString );
  337. }
  338. return buffer.IsValid();
  339. }