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.

511 lines
13 KiB

  1. //========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #ifndef STRINGPOOL_H
  8. #define STRINGPOOL_H
  9. #if defined( _WIN32 )
  10. #pragma once
  11. #endif
  12. #include "utlrbtree.h"
  13. #include "utlvector.h"
  14. #include "utlbuffer.h"
  15. #include "generichash.h"
  16. //-----------------------------------------------------------------------------
  17. // Purpose: Allocates memory for strings, checking for duplicates first,
  18. // reusing exising strings if duplicate found.
  19. //-----------------------------------------------------------------------------
  20. enum StringPoolCase_t
  21. {
  22. StringPoolCaseInsensitive,
  23. StringPoolCaseSensitive
  24. };
  25. class CStringPool
  26. {
  27. public:
  28. CStringPool( StringPoolCase_t caseSensitivity = StringPoolCaseInsensitive );
  29. ~CStringPool();
  30. unsigned int Count() const;
  31. const char * Allocate( const char *pszValue );
  32. // This feature is deliberately not supported because it's pretty dangerous
  33. // given current uses of CStringPool, which assume they can copy string pointers without
  34. // any refcounts.
  35. //void Free( const char *pszValue );
  36. void FreeAll();
  37. // searches for a string already in the pool
  38. const char * Find( const char *pszValue );
  39. protected:
  40. typedef CUtlRBTree<const char *, unsigned short> CStrSet;
  41. CStrSet m_Strings;
  42. };
  43. //-----------------------------------------------------------------------------
  44. // Purpose: A reference counted string pool.
  45. //
  46. // Elements are stored more efficiently than in the conventional string pool,
  47. // quicker to look up, and storage is tracked via reference counts.
  48. //
  49. // At some point this should replace CStringPool
  50. //-----------------------------------------------------------------------------
  51. template<class T>
  52. class CCountedStringPoolBase
  53. {
  54. public: // HACK, hash_item_t structure should not be public.
  55. struct hash_item_t
  56. {
  57. char* pString;
  58. T nNextElement;
  59. unsigned char nReferenceCount;
  60. unsigned char pad;
  61. };
  62. enum
  63. {
  64. INVALID_ELEMENT = 0,
  65. MAX_REFERENCE = 0xFF,
  66. HASH_TABLE_SIZE = 1024
  67. };
  68. CUtlVector<T> m_HashTable; // Points to each element
  69. CUtlVector<hash_item_t> m_Elements;
  70. T m_FreeListStart;
  71. StringPoolCase_t m_caseSensitivity;
  72. public:
  73. CCountedStringPoolBase( StringPoolCase_t caseSensitivity = StringPoolCaseInsensitive );
  74. virtual ~CCountedStringPoolBase();
  75. void FreeAll();
  76. char *FindString( const char* pIntrinsic );
  77. char *ReferenceString( const char* pIntrinsic );
  78. void DereferenceString( const char* pIntrinsic );
  79. // These are only reliable if there are less than 64k strings in your string pool
  80. T FindStringHandle( const char* pIntrinsic );
  81. T ReferenceStringHandle( const char* pIntrinsic );
  82. char *HandleToString( T handle );
  83. void SpewStrings();
  84. unsigned Hash( const char *pszKey );
  85. bool SaveToBuffer( CUtlBuffer &buffer );
  86. bool RestoreFromBuffer( CUtlBuffer &buffer );
  87. // Debug helper method to validate that we didn't overflow
  88. void VerifyNotOverflowed( unsigned int value );
  89. };
  90. typedef CCountedStringPoolBase<unsigned short> CCountedStringPool;
  91. template<class T>
  92. inline CCountedStringPoolBase<T>::CCountedStringPoolBase( StringPoolCase_t caseSensitivity )
  93. {
  94. MEM_ALLOC_CREDIT();
  95. m_HashTable.EnsureCount(HASH_TABLE_SIZE);
  96. for( int i = 0; i < m_HashTable.Count(); i++ )
  97. {
  98. m_HashTable[i] = INVALID_ELEMENT;
  99. }
  100. m_FreeListStart = INVALID_ELEMENT;
  101. m_Elements.AddToTail();
  102. m_Elements[0].pString = NULL;
  103. m_Elements[0].nReferenceCount = 0;
  104. m_Elements[0].nNextElement = INVALID_ELEMENT;
  105. m_caseSensitivity = caseSensitivity;
  106. }
  107. template<class T>
  108. inline CCountedStringPoolBase<T>::~CCountedStringPoolBase()
  109. {
  110. FreeAll();
  111. }
  112. template<class T>
  113. inline void CCountedStringPoolBase<T>::FreeAll()
  114. {
  115. int i;
  116. // Reset the hash table:
  117. for( i = 0; i < m_HashTable.Count(); i++ )
  118. {
  119. m_HashTable[i] = INVALID_ELEMENT;
  120. }
  121. // Blow away the free list:
  122. m_FreeListStart = INVALID_ELEMENT;
  123. for( i = 0; i < m_Elements.Count(); i++ )
  124. {
  125. if( m_Elements[i].pString )
  126. {
  127. delete [] m_Elements[i].pString;
  128. m_Elements[i].pString = NULL;
  129. m_Elements[i].nReferenceCount = 0;
  130. m_Elements[i].nNextElement = INVALID_ELEMENT;
  131. }
  132. }
  133. // Remove all but the invalid element:
  134. m_Elements.RemoveAll();
  135. m_Elements.AddToTail();
  136. m_Elements[0].pString = NULL;
  137. m_Elements[0].nReferenceCount = 0;
  138. m_Elements[0].nNextElement = INVALID_ELEMENT;
  139. }
  140. template<class T>
  141. inline unsigned CCountedStringPoolBase<T>::Hash( const char *pszKey )
  142. {
  143. if ( m_caseSensitivity == StringPoolCaseInsensitive )
  144. {
  145. return HashStringCaseless( pszKey );
  146. }
  147. return HashString( pszKey );
  148. }
  149. template<class T>
  150. inline T CCountedStringPoolBase<T>::FindStringHandle( const char* pIntrinsic )
  151. {
  152. if( pIntrinsic == NULL )
  153. return INVALID_ELEMENT;
  154. T nHashBucketIndex = ( Hash( pIntrinsic ) %HASH_TABLE_SIZE);
  155. T nCurrentBucket = m_HashTable[ nHashBucketIndex ];
  156. // Does the bucket already exist?
  157. if( nCurrentBucket != INVALID_ELEMENT )
  158. {
  159. for( ; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement )
  160. {
  161. if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) )
  162. {
  163. return nCurrentBucket;
  164. }
  165. }
  166. }
  167. return 0;
  168. }
  169. template<class T>
  170. inline char* CCountedStringPoolBase<T>::FindString( const char* pIntrinsic )
  171. {
  172. if( pIntrinsic == NULL )
  173. return NULL;
  174. // Yes, this will be NULL on failure.
  175. return m_Elements[FindStringHandle(pIntrinsic)].pString;
  176. }
  177. template<class T>
  178. inline T CCountedStringPoolBase<T>::ReferenceStringHandle( const char* pIntrinsic )
  179. {
  180. if( pIntrinsic == NULL )
  181. return INVALID_ELEMENT;
  182. T nHashBucketIndex = ( Hash( pIntrinsic ) % HASH_TABLE_SIZE);
  183. T nCurrentBucket = m_HashTable[ nHashBucketIndex ];
  184. // Does the bucket already exist?
  185. if( nCurrentBucket != INVALID_ELEMENT )
  186. {
  187. for( ; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement )
  188. {
  189. if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) )
  190. {
  191. // Anyone who hits 65k references is permanant
  192. if( m_Elements[nCurrentBucket].nReferenceCount < MAX_REFERENCE )
  193. {
  194. m_Elements[nCurrentBucket].nReferenceCount ++ ;
  195. }
  196. return nCurrentBucket;
  197. }
  198. }
  199. }
  200. if( m_FreeListStart != INVALID_ELEMENT )
  201. {
  202. nCurrentBucket = m_FreeListStart;
  203. m_FreeListStart = m_Elements[nCurrentBucket].nNextElement;
  204. }
  205. else
  206. {
  207. unsigned int newElement = m_Elements.AddToTail();
  208. VerifyNotOverflowed( newElement );
  209. nCurrentBucket = newElement;
  210. }
  211. m_Elements[nCurrentBucket].nReferenceCount = 1;
  212. // Insert at the beginning of the bucket:
  213. m_Elements[nCurrentBucket].nNextElement = m_HashTable[ nHashBucketIndex ];
  214. m_HashTable[ nHashBucketIndex ] = nCurrentBucket;
  215. m_Elements[nCurrentBucket].pString = new char[Q_strlen( pIntrinsic ) + 1];
  216. Q_strcpy( m_Elements[nCurrentBucket].pString, pIntrinsic );
  217. return nCurrentBucket;
  218. }
  219. template<>
  220. inline void CCountedStringPoolBase<unsigned short>::VerifyNotOverflowed( unsigned int value ) { Assert( value < 0xffff ); }
  221. template<>
  222. inline void CCountedStringPoolBase<unsigned int>::VerifyNotOverflowed( unsigned int value ) {}
  223. template<class T>
  224. inline char* CCountedStringPoolBase<T>::ReferenceString( const char* pIntrinsic )
  225. {
  226. if(!pIntrinsic)
  227. return NULL;
  228. return m_Elements[ReferenceStringHandle( pIntrinsic)].pString;
  229. }
  230. template<class T>
  231. inline void CCountedStringPoolBase<T>::DereferenceString( const char* pIntrinsic )
  232. {
  233. // If we get a NULL pointer, just return
  234. if (!pIntrinsic)
  235. return;
  236. T nHashBucketIndex = (Hash( pIntrinsic ) % m_HashTable.Count());
  237. T nCurrentBucket = m_HashTable[ nHashBucketIndex ];
  238. // If there isn't anything in the bucket, just return.
  239. if ( nCurrentBucket == INVALID_ELEMENT )
  240. return;
  241. for( T previous = INVALID_ELEMENT; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement )
  242. {
  243. if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) )
  244. {
  245. // Anyone who hits 65k references is permanant
  246. if( m_Elements[nCurrentBucket].nReferenceCount < MAX_REFERENCE )
  247. {
  248. m_Elements[nCurrentBucket].nReferenceCount --;
  249. }
  250. if( m_Elements[nCurrentBucket].nReferenceCount == 0 )
  251. {
  252. if( previous == INVALID_ELEMENT )
  253. {
  254. m_HashTable[nHashBucketIndex] = m_Elements[nCurrentBucket].nNextElement;
  255. }
  256. else
  257. {
  258. m_Elements[previous].nNextElement = m_Elements[nCurrentBucket].nNextElement;
  259. }
  260. delete [] m_Elements[nCurrentBucket].pString;
  261. m_Elements[nCurrentBucket].pString = NULL;
  262. m_Elements[nCurrentBucket].nReferenceCount = 0;
  263. m_Elements[nCurrentBucket].nNextElement = m_FreeListStart;
  264. m_FreeListStart = nCurrentBucket;
  265. break;
  266. }
  267. }
  268. previous = nCurrentBucket;
  269. }
  270. }
  271. template<class T>
  272. inline char* CCountedStringPoolBase<T>::HandleToString( T handle )
  273. {
  274. return m_Elements[handle].pString;
  275. }
  276. template<class T>
  277. inline void CCountedStringPoolBase<T>::SpewStrings()
  278. {
  279. int i;
  280. for ( i = 0; i < m_Elements.Count(); i++ )
  281. {
  282. char* string = m_Elements[i].pString;
  283. Msg("String %d: ref:%d %s\n", i, m_Elements[i].nReferenceCount, string == NULL? "EMPTY - ok for slot zero only!" : string);
  284. }
  285. Msg("\n%d total counted strings.", m_Elements.Count());
  286. }
  287. #define STRING_POOL_VERSION MAKEID( 'C', 'S', 'P', '1' )
  288. #define MAX_STRING_SAVE 1024
  289. template<>
  290. inline bool CCountedStringPoolBase<unsigned short>::SaveToBuffer( CUtlBuffer &buffer )
  291. {
  292. if ( m_Elements.Count() <= 1 )
  293. {
  294. // pool is empty, saving nothing
  295. // caller can check put position of buffer to detect
  296. return true;
  297. }
  298. // signature/version
  299. buffer.PutInt( STRING_POOL_VERSION );
  300. buffer.PutUnsignedShort( m_FreeListStart );
  301. buffer.PutInt( m_HashTable.Count() );
  302. for ( int i = 0; i < m_HashTable.Count(); i++ )
  303. {
  304. buffer.PutUnsignedShort( m_HashTable[i] );
  305. }
  306. buffer.PutInt( m_Elements.Count() );
  307. for ( int i = 1; i < m_Elements.Count(); i++ )
  308. {
  309. buffer.PutUnsignedShort( m_Elements[i].nNextElement );
  310. buffer.PutUnsignedChar( m_Elements[i].nReferenceCount );
  311. const char *pString = m_Elements[i].pString;
  312. if ( strlen( pString ) >= MAX_STRING_SAVE )
  313. {
  314. return false;
  315. }
  316. buffer.PutString( pString ? pString : "" );
  317. }
  318. return buffer.IsValid();
  319. }
  320. template<>
  321. inline bool CCountedStringPoolBase<unsigned short>::RestoreFromBuffer( CUtlBuffer &buffer )
  322. {
  323. int signature = buffer.GetInt();
  324. if ( signature != STRING_POOL_VERSION )
  325. {
  326. // wrong version
  327. return false;
  328. }
  329. FreeAll();
  330. m_FreeListStart = buffer.GetUnsignedShort();
  331. int hashCount = buffer.GetInt();
  332. m_HashTable.SetCount( hashCount );
  333. for ( int i = 0; i < hashCount; i++ )
  334. {
  335. m_HashTable[i] = buffer.GetUnsignedShort();
  336. }
  337. int tableCount = buffer.GetInt();
  338. if ( tableCount > 1 )
  339. {
  340. m_Elements.AddMultipleToTail( tableCount-1 );
  341. }
  342. char tempString[MAX_STRING_SAVE];
  343. for ( int i = 1; i < tableCount; i++ )
  344. {
  345. m_Elements[i].nNextElement = buffer.GetUnsignedShort();
  346. m_Elements[i].nReferenceCount = buffer.GetUnsignedChar();
  347. buffer.GetString( tempString, sizeof( tempString ) );
  348. m_Elements[i].pString = strdup( tempString );
  349. }
  350. return buffer.IsValid();
  351. }
  352. template<>
  353. inline bool CCountedStringPoolBase<unsigned int>::SaveToBuffer( CUtlBuffer &buffer )
  354. {
  355. if ( m_Elements.Count() <= 1 )
  356. {
  357. // pool is empty, saving nothing
  358. // caller can check put position of buffer to detect
  359. return true;
  360. }
  361. // signature/version
  362. buffer.PutInt( STRING_POOL_VERSION );
  363. buffer.PutUnsignedInt( m_FreeListStart );
  364. buffer.PutInt( m_HashTable.Count() );
  365. for ( int i = 0; i < m_HashTable.Count(); i++ )
  366. {
  367. buffer.PutUnsignedInt( m_HashTable[i] );
  368. }
  369. buffer.PutInt( m_Elements.Count() );
  370. for ( int i = 1; i < m_Elements.Count(); i++ )
  371. {
  372. buffer.PutUnsignedInt( m_Elements[i].nNextElement );
  373. buffer.PutUnsignedChar( m_Elements[i].nReferenceCount );
  374. const char *pString = m_Elements[i].pString;
  375. if ( strlen( pString ) >= MAX_STRING_SAVE )
  376. {
  377. return false;
  378. }
  379. buffer.PutString( pString ? pString : "" );
  380. }
  381. return buffer.IsValid();
  382. }
  383. template<>
  384. inline bool CCountedStringPoolBase<unsigned int>::RestoreFromBuffer( CUtlBuffer &buffer )
  385. {
  386. int signature = buffer.GetInt();
  387. if ( signature != STRING_POOL_VERSION )
  388. {
  389. // wrong version
  390. return false;
  391. }
  392. FreeAll();
  393. m_FreeListStart = buffer.GetUnsignedInt();
  394. int hashCount = buffer.GetInt();
  395. m_HashTable.SetCount( hashCount );
  396. for ( int i = 0; i < hashCount; i++ )
  397. {
  398. m_HashTable[i] = buffer.GetUnsignedInt();
  399. }
  400. int tableCount = buffer.GetInt();
  401. if ( tableCount > 1 )
  402. {
  403. m_Elements.AddMultipleToTail( tableCount-1 );
  404. }
  405. char tempString[MAX_STRING_SAVE];
  406. for ( int i = 1; i < tableCount; i++ )
  407. {
  408. m_Elements[i].nNextElement = buffer.GetUnsignedInt();
  409. m_Elements[i].nReferenceCount = buffer.GetUnsignedChar();
  410. buffer.GetString( tempString, sizeof( tempString ) );
  411. m_Elements[i].pString = strdup( tempString );
  412. }
  413. return buffer.IsValid();
  414. }
  415. #endif // STRINGPOOL_H