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.

334 lines
8.3 KiB

  1. //========= Copyright 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 StrLess( const char * const &pszLeft, const char * const &pszRight )
  18. {
  19. return ( Q_stricmp( pszLeft, pszRight) < 0 );
  20. }
  21. //-----------------------------------------------------------------------------
  22. //-----------------------------------------------------------------------------
  23. CStringPool::CStringPool()
  24. : m_Strings( 32, 256, StrLess )
  25. {
  26. }
  27. //-----------------------------------------------------------------------------
  28. //-----------------------------------------------------------------------------
  29. CStringPool::~CStringPool()
  30. {
  31. FreeAll();
  32. }
  33. //-----------------------------------------------------------------------------
  34. //-----------------------------------------------------------------------------
  35. unsigned int CStringPool::Count() const
  36. {
  37. return m_Strings.Count();
  38. }
  39. //-----------------------------------------------------------------------------
  40. //-----------------------------------------------------------------------------
  41. const char * CStringPool::Find( const char *pszValue )
  42. {
  43. unsigned short i = m_Strings.Find(pszValue);
  44. if ( m_Strings.IsValidIndex(i) )
  45. return m_Strings[i];
  46. return NULL;
  47. }
  48. const char * CStringPool::Allocate( const char *pszValue )
  49. {
  50. char *pszNew;
  51. unsigned short i = m_Strings.Find(pszValue);
  52. bool bNew = (i == m_Strings.InvalidIndex());
  53. if ( !bNew )
  54. return m_Strings[i];
  55. pszNew = strdup( pszValue );
  56. if ( bNew )
  57. m_Strings.Insert( pszNew );
  58. return pszNew;
  59. }
  60. //-----------------------------------------------------------------------------
  61. //-----------------------------------------------------------------------------
  62. void CStringPool::FreeAll()
  63. {
  64. unsigned short i = m_Strings.FirstInorder();
  65. while ( i != m_Strings.InvalidIndex() )
  66. {
  67. free( (void *)m_Strings[i] );
  68. i = m_Strings.NextInorder(i);
  69. }
  70. m_Strings.RemoveAll();
  71. }
  72. //-----------------------------------------------------------------------------
  73. //-----------------------------------------------------------------------------
  74. CCountedStringPool::CCountedStringPool()
  75. {
  76. MEM_ALLOC_CREDIT();
  77. m_HashTable.EnsureCount(HASH_TABLE_SIZE);
  78. for( int i = 0; i < m_HashTable.Count(); i++ )
  79. {
  80. m_HashTable[i] = INVALID_ELEMENT;
  81. }
  82. m_FreeListStart = INVALID_ELEMENT;
  83. m_Elements.AddToTail();
  84. m_Elements[0].pString = NULL;
  85. m_Elements[0].nReferenceCount = 0;
  86. m_Elements[0].nNextElement = INVALID_ELEMENT;
  87. }
  88. CCountedStringPool::~CCountedStringPool()
  89. {
  90. FreeAll();
  91. }
  92. void CCountedStringPool::FreeAll()
  93. {
  94. int i;
  95. // Reset the hash table:
  96. for( i = 0; i < m_HashTable.Count(); i++ )
  97. {
  98. m_HashTable[i] = INVALID_ELEMENT;
  99. }
  100. // Blow away the free list:
  101. m_FreeListStart = INVALID_ELEMENT;
  102. for( i = 0; i < m_Elements.Count(); i++ )
  103. {
  104. if( m_Elements[i].pString )
  105. {
  106. delete [] m_Elements[i].pString;
  107. m_Elements[i].pString = NULL;
  108. m_Elements[i].nReferenceCount = 0;
  109. m_Elements[i].nNextElement = INVALID_ELEMENT;
  110. }
  111. }
  112. // Remove all but the invalid element:
  113. m_Elements.RemoveAll();
  114. m_Elements.AddToTail();
  115. m_Elements[0].pString = NULL;
  116. m_Elements[0].nReferenceCount = 0;
  117. m_Elements[0].nNextElement = INVALID_ELEMENT;
  118. }
  119. unsigned short CCountedStringPool::FindStringHandle( const char* pIntrinsic )
  120. {
  121. if( pIntrinsic == NULL )
  122. return INVALID_ELEMENT;
  123. unsigned short nHashBucketIndex = (HashStringCaseless(pIntrinsic ) %HASH_TABLE_SIZE);
  124. unsigned short nCurrentBucket = m_HashTable[ nHashBucketIndex ];
  125. // Does the bucket already exist?
  126. if( nCurrentBucket != INVALID_ELEMENT )
  127. {
  128. for( ; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement )
  129. {
  130. if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) )
  131. {
  132. return nCurrentBucket;
  133. }
  134. }
  135. }
  136. return 0;
  137. }
  138. char* CCountedStringPool::FindString( const char* pIntrinsic )
  139. {
  140. if( pIntrinsic == NULL )
  141. return NULL;
  142. // Yes, this will be NULL on failure.
  143. return m_Elements[FindStringHandle(pIntrinsic)].pString;
  144. }
  145. unsigned short CCountedStringPool::ReferenceStringHandle( const char* pIntrinsic )
  146. {
  147. if( pIntrinsic == NULL )
  148. return INVALID_ELEMENT;
  149. unsigned short nHashBucketIndex = (HashStringCaseless( pIntrinsic ) % HASH_TABLE_SIZE);
  150. unsigned short nCurrentBucket = m_HashTable[ nHashBucketIndex ];
  151. // Does the bucket already exist?
  152. if( nCurrentBucket != INVALID_ELEMENT )
  153. {
  154. for( ; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement )
  155. {
  156. if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) )
  157. {
  158. // Anyone who hits 65k references is permanant
  159. if( m_Elements[nCurrentBucket].nReferenceCount < MAX_REFERENCE )
  160. {
  161. m_Elements[nCurrentBucket].nReferenceCount ++ ;
  162. }
  163. return nCurrentBucket;
  164. }
  165. }
  166. }
  167. if( m_FreeListStart != INVALID_ELEMENT )
  168. {
  169. nCurrentBucket = m_FreeListStart;
  170. m_FreeListStart = m_Elements[nCurrentBucket].nNextElement;
  171. }
  172. else
  173. {
  174. nCurrentBucket = m_Elements.AddToTail();
  175. }
  176. m_Elements[nCurrentBucket].nReferenceCount = 1;
  177. // Insert at the beginning of the bucket:
  178. m_Elements[nCurrentBucket].nNextElement = m_HashTable[ nHashBucketIndex ];
  179. m_HashTable[ nHashBucketIndex ] = nCurrentBucket;
  180. m_Elements[nCurrentBucket].pString = new char[Q_strlen( pIntrinsic ) + 1];
  181. Q_strcpy( m_Elements[nCurrentBucket].pString, pIntrinsic );
  182. return nCurrentBucket;
  183. }
  184. char* CCountedStringPool::ReferenceString( const char* pIntrinsic )
  185. {
  186. if(!pIntrinsic)
  187. return NULL;
  188. return m_Elements[ReferenceStringHandle( pIntrinsic)].pString;
  189. }
  190. void CCountedStringPool::DereferenceString( const char* pIntrinsic )
  191. {
  192. // If we get a NULL pointer, just return
  193. if (!pIntrinsic)
  194. return;
  195. unsigned short nHashBucketIndex = (HashStringCaseless( pIntrinsic ) % m_HashTable.Count());
  196. unsigned short nCurrentBucket = m_HashTable[ nHashBucketIndex ];
  197. // If there isn't anything in the bucket, just return.
  198. if ( nCurrentBucket == INVALID_ELEMENT )
  199. return;
  200. for( unsigned short previous = INVALID_ELEMENT; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement )
  201. {
  202. if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) )
  203. {
  204. // Anyone who hits 65k references is permanant
  205. if( m_Elements[nCurrentBucket].nReferenceCount < MAX_REFERENCE )
  206. {
  207. m_Elements[nCurrentBucket].nReferenceCount --;
  208. }
  209. if( m_Elements[nCurrentBucket].nReferenceCount == 0 )
  210. {
  211. if( previous == INVALID_ELEMENT )
  212. {
  213. m_HashTable[nHashBucketIndex] = m_Elements[nCurrentBucket].nNextElement;
  214. }
  215. else
  216. {
  217. m_Elements[previous].nNextElement = m_Elements[nCurrentBucket].nNextElement;
  218. }
  219. delete [] m_Elements[nCurrentBucket].pString;
  220. m_Elements[nCurrentBucket].pString = NULL;
  221. m_Elements[nCurrentBucket].nReferenceCount = 0;
  222. m_Elements[nCurrentBucket].nNextElement = m_FreeListStart;
  223. m_FreeListStart = nCurrentBucket;
  224. break;
  225. }
  226. }
  227. previous = nCurrentBucket;
  228. }
  229. }
  230. char* CCountedStringPool::HandleToString( unsigned short handle )
  231. {
  232. return m_Elements[handle].pString;
  233. }
  234. void CCountedStringPool::SpewStrings()
  235. {
  236. int i;
  237. for ( i = 0; i < m_Elements.Count(); i++ )
  238. {
  239. char* string = m_Elements[i].pString;
  240. Msg("String %d: ref:%d %s", i, m_Elements[i].nReferenceCount, string == NULL? "EMPTY - ok for slot zero only!" : string);
  241. }
  242. Msg("\n%d total counted strings.", m_Elements.Count());
  243. }
  244. #ifdef _DEBUG
  245. CON_COMMAND( test_stringpool, "Tests the class CStringPool" )
  246. {
  247. CStringPool pool;
  248. Assert(pool.Count() == 0);
  249. pool.Allocate("test");
  250. Assert(pool.Count() == 1);
  251. pool.Allocate("test");
  252. Assert(pool.Count() == 1);
  253. pool.Allocate("test2");
  254. Assert(pool.Count() == 2);
  255. Assert( pool.Find("test2") != NULL );
  256. Assert( pool.Find("TEST") != NULL );
  257. Assert( pool.Find("Test2") != NULL );
  258. Assert( pool.Find("test") != NULL );
  259. pool.FreeAll();
  260. Assert(pool.Count() == 0);
  261. Msg("Pass.");
  262. }
  263. #endif