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.

402 lines
12 KiB

  1. //===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
  2. //
  3. //
  4. //
  5. //==================================================================================================
  6. #include "tier1/bitbuf.h"
  7. #include "serializedentity.h"
  8. // NOTE: This has to be the last file included!
  9. #include "tier0/memdbgon.h"
  10. //-----------------------------------------------------------------------------
  11. DEFINE_FIXEDSIZE_ALLOCATOR_MT( CSerializedEntity, 2048, CUtlMemoryPool::GROW_FAST );
  12. //-----------------------------------------------------------------------------
  13. CSerializedEntity::CSerializedEntity()
  14. : m_pFields( NULL )
  15. , m_pFieldDataOffsets( NULL )
  16. , m_nFieldCount( 0 )
  17. , m_nNumReservedFields( 0 )
  18. , m_nFieldDataBits( 0 )
  19. , m_pFieldData( NULL )
  20. {
  21. }
  22. CSerializedEntity::~CSerializedEntity()
  23. {
  24. Clear();
  25. }
  26. void CSerializedEntity::SetupPackMemory( int nNumFields, int nDataBits )
  27. {
  28. AssertMsg( !IsPacked(), "Attempted to pack the memory inside of a Serialized entity that was already allocated. This is a memory leak." );
  29. //determine the total size for the buffer
  30. int fieldSize = PAD_NUMBER( nNumFields * sizeof( *m_pFields ), 4 );
  31. int dataOffsetsSize = PAD_NUMBER( nNumFields * sizeof( *m_pFieldDataOffsets ), 4 );
  32. int fieldDataSize = PAD_NUMBER( Bits2Bytes( nDataBits ), 4 );
  33. int size = fieldSize + dataOffsetsSize + fieldDataSize;
  34. if ( size )
  35. {
  36. //allocate a block of memory for it
  37. uint8* pMemBlock = (uint8*) g_pMemAlloc->Alloc( size );
  38. //and setup our pointers
  39. m_pFields = ( short * ) pMemBlock;
  40. m_pFieldDataOffsets = ( uint32 * ) ( pMemBlock + fieldSize );
  41. m_pFieldData = ( uint8 * )( pMemBlock + fieldSize + dataOffsetsSize );
  42. }
  43. else
  44. {
  45. // NOTE: We can't rely on the Source 1 XBox allocator returning NULL for a zero byte allocation
  46. m_pFields = NULL;
  47. m_pFieldDataOffsets = NULL;
  48. m_pFieldData = NULL;
  49. }
  50. m_nFieldCount = nNumFields;
  51. m_nNumReservedFields = knPacked;
  52. m_nFieldDataBits = nDataBits;
  53. }
  54. void CSerializedEntity::Pack( short *pFields, uint32 *pFieldDataOffsets, int fieldCount, uint32 nFieldDataBits, uint8 *pFieldData )
  55. {
  56. //setup the memory appropriately
  57. SetupPackMemory( fieldCount, nFieldDataBits );
  58. memcpy( m_pFields, pFields, m_nFieldCount * sizeof( *m_pFields ) );
  59. memcpy( m_pFieldDataOffsets, pFieldDataOffsets, m_nFieldCount * sizeof( *m_pFieldDataOffsets ) );
  60. memcpy( m_pFieldData, pFieldData, Bits2Bytes( nFieldDataBits ) );
  61. }
  62. void CSerializedEntity::Copy( const CSerializedEntity &other )
  63. {
  64. if( this != &other )
  65. {
  66. Clear();
  67. Pack( other.m_pFields, other.m_pFieldDataOffsets, other.m_nFieldCount, other.m_nFieldDataBits, other.m_pFieldData );
  68. }
  69. }
  70. void CSerializedEntity::Swap( CSerializedEntity& other )
  71. {
  72. //just swap all the pointers between the two fields
  73. V_swap(m_pFields, other.m_pFields);
  74. V_swap(m_pFieldDataOffsets, other.m_pFieldDataOffsets);
  75. V_swap(m_nFieldDataBits, other.m_nFieldDataBits);
  76. V_swap(m_pFieldData, other.m_pFieldData);
  77. V_swap(m_nFieldCount, other.m_nFieldCount);
  78. V_swap(m_nNumReservedFields, other.m_nNumReservedFields);
  79. #ifdef PARANOID_SERIALIZEDENTITY
  80. V_swap(m_File, other.m_File);
  81. V_swap(m_Line, other.m_Line);
  82. #endif
  83. }
  84. void CSerializedEntity::Clear()
  85. {
  86. if ( IsPacked() )
  87. {
  88. g_pMemAlloc->Free( m_pFields );
  89. //the other two were allocated contiguously after the fields
  90. }
  91. else
  92. {
  93. //fields and data offsets are always allocated in a block
  94. g_pMemAlloc->Free( m_pFields );
  95. delete[] m_pFieldData;
  96. }
  97. m_nFieldDataBits = 0;
  98. m_pFields = NULL;
  99. m_pFieldDataOffsets = NULL;
  100. m_pFieldData = NULL;
  101. m_nFieldCount = 0;
  102. m_nNumReservedFields = 0;
  103. }
  104. void CSerializedEntity::ReservePathAndOffsetMemory( uint32 nNumElements )
  105. {
  106. Assert( !IsPacked() );
  107. //don't allow them to reserve less memory than what we have already allocated
  108. m_nNumReservedFields = MAX( nNumElements, m_nFieldCount );
  109. //save our old pointers so we can copy from them
  110. short* pOldFields = m_pFields;
  111. uint32* pOldOffsets = m_pFieldDataOffsets;
  112. //allocate the new block
  113. uint32 nFieldSize = PAD_NUMBER( sizeof( *m_pFields ) * m_nNumReservedFields, 4 );
  114. uint32 nOffsetSize = sizeof( uint32 ) * m_nNumReservedFields;
  115. int nSize = nFieldSize + nOffsetSize;
  116. if ( nSize )
  117. {
  118. uint8* pMem = ( uint8* )g_pMemAlloc->Alloc( nSize );
  119. //update our pointers
  120. m_pFields = ( short* )pMem;
  121. m_pFieldDataOffsets = ( uint32* )( pMem + nFieldSize);
  122. //copy over the old data
  123. if( pOldFields )
  124. {
  125. memcpy( m_pFields, pOldFields, sizeof( short ) * m_nFieldCount );
  126. memcpy( m_pFieldDataOffsets, pOldOffsets, sizeof( uint32 ) * m_nFieldCount );
  127. }
  128. }
  129. else
  130. {
  131. // NOTE: We can't rely on the Source 1 XBox allocator returning NULL for a zero byte allocation
  132. m_pFields = NULL;
  133. m_pFieldDataOffsets = NULL;
  134. }
  135. //cleanup the old (which is over allocated)
  136. g_pMemAlloc->Free( pOldFields );
  137. }
  138. void CSerializedEntity::Grow()
  139. {
  140. Assert( !IsPacked() );
  141. //determine our updated size (start at 2, grow in multiples of 2)
  142. if ( !m_pFields )
  143. ReservePathAndOffsetMemory( 2 );
  144. else
  145. ReservePathAndOffsetMemory( m_nNumReservedFields * 2 );
  146. }
  147. // pvecFieldPathBits is used by DTI mode
  148. bool CSerializedEntity::ReadFieldPaths( bf_read *pBuf, CUtlVector< int > *pvecFieldPathBits /*= NULL*/ )
  149. {
  150. Assert( !IsPacked() );
  151. CDeltaBitsReader reader( pBuf );
  152. int iProp;
  153. while ( -1 != (iProp = reader.ReadNextPropIndex()) )
  154. {
  155. if ( m_nFieldCount == m_nNumReservedFields )
  156. {
  157. Grow();
  158. }
  159. m_pFields[m_nFieldCount++] = iProp;
  160. if ( pvecFieldPathBits )
  161. {
  162. pvecFieldPathBits->AddToTail( reader.GetFieldPathBits() );
  163. }
  164. }
  165. return true;
  166. }
  167. //given a data chunk, this will make sure that the resulting bits in the last byte are cleared to zero to ensure that they are consistent across compares
  168. void ClearRemainingBits( uint32 nNumBits, uint8* pBitData )
  169. {
  170. //determine how many bits beyond the last byte we need to keep
  171. const uint32 nRemainingBits = nNumBits % 8;
  172. //if the remaining bits is zero, we are byte aligned, and accessing that byte can be beyond the array
  173. if( nRemainingBits > 0 )
  174. {
  175. //determine the byte to index into
  176. const uint32 nByte = nNumBits / 8;
  177. const uint32 nKeepMask = ( 1 << nRemainingBits ) - 1;
  178. pBitData[ nByte ] &= nKeepMask;
  179. }
  180. }
  181. void CSerializedEntity::PackWithFieldData( void *pData, int nDataBits )
  182. {
  183. Assert( !IsPacked() );
  184. Assert( m_pFieldData == NULL );
  185. short *pStartFields = m_pFields;
  186. uint32 *pStartDataOffsets = m_pFieldDataOffsets;
  187. //setup the memory appropriately
  188. SetupPackMemory( m_nFieldCount, nDataBits );
  189. memcpy( m_pFields, pStartFields, m_nFieldCount * sizeof( *m_pFields ) );
  190. memcpy( m_pFieldDataOffsets, pStartDataOffsets, m_nFieldCount * sizeof( *m_pFieldDataOffsets ) );
  191. memcpy( m_pFieldData, pData, Bits2Bytes( nDataBits ) );
  192. ClearRemainingBits( nDataBits, m_pFieldData );
  193. //free our original memory
  194. g_pMemAlloc->Free( pStartFields );
  195. }
  196. void CSerializedEntity::PackWithFieldData( bf_read &buf, int nDataBits )
  197. {
  198. Assert( !IsPacked() );
  199. Assert( m_pFieldData == NULL );
  200. short *pStartFields = m_pFields;
  201. uint32 *pStartDataOffsets = m_pFieldDataOffsets;
  202. //setup the memory appropriately
  203. SetupPackMemory( m_nFieldCount, nDataBits );
  204. memcpy( m_pFields, pStartFields, m_nFieldCount * sizeof( *m_pFields ) );
  205. memcpy( m_pFieldDataOffsets, pStartDataOffsets, m_nFieldCount * sizeof( *m_pFieldDataOffsets ) );
  206. buf.ReadBits( m_pFieldData, nDataBits );
  207. ClearRemainingBits( nDataBits, m_pFieldData );
  208. //free our original memory
  209. g_pMemAlloc->Free( pStartFields );
  210. }
  211. void CSerializedEntity::StartReading( bf_read& bitReader ) const
  212. {
  213. bitReader.StartReading( m_pFieldData, PAD_NUMBER( Bits2Bytes( m_nFieldDataBits ), 4 ), 0, m_nFieldDataBits );
  214. }
  215. void CSerializedEntity::StartWriting( bf_write& bitWriter )
  216. {
  217. bitWriter.StartWriting( m_pFieldData, PAD_NUMBER( Bits2Bytes( m_nFieldDataBits ), 4 ), 0, m_nFieldDataBits );
  218. }
  219. void CSerializedEntity::DumpMemInfo()
  220. {
  221. CUtlMemoryPool &pool = s_Allocator;
  222. Msg("Pool: Count,Peak,Size,Total\n");
  223. Msg(" %d,%d,%d,%d\n", pool.Count(), pool.PeakCount(), pool.BlockSize(), pool.Size() );
  224. }
  225. class CSerializedEntities : public ISerializedEntities
  226. {
  227. public:
  228. #ifdef PARANOID_SERIALIZEDENTITY
  229. CUtlRBTree< CSerializedEntity *, int > m_Current;
  230. CThreadFastMutex m_Mutex;
  231. CInterlockedInt m_Allocated;
  232. void Report()
  233. {
  234. if ( m_Allocated > 0 )
  235. {
  236. // If you see this, uncomment the #define PARANOID above and run again to see what's causing leaks
  237. Msg( "%d CSerializedEntity object leaked!!!\n", (int)m_Allocated );
  238. }
  239. }
  240. #endif //PARANOID_SERIALIZEDENTITY
  241. CSerializedEntities()
  242. #ifdef PARANOID_SERIALIZEDENTITY
  243. : m_Current( 0, 0, DefLessFunc( CSerializedEntity * ) )
  244. , m_Allocated( 0 )
  245. #endif
  246. {
  247. }
  248. ~CSerializedEntities()
  249. {
  250. #ifdef PARANOID_SERIALIZEDENTITY
  251. Report();
  252. FOR_EACH_UTLRBTREE( m_Current, i )
  253. {
  254. Msg( "Leak %s:%d\n", m_Current[ i ]->m_File, m_Current[ i ]->m_Line );
  255. }
  256. #endif
  257. }
  258. virtual SerializedEntityHandle_t AllocateSerializedEntity(char const *pFile, int nLine )
  259. {
  260. CSerializedEntity *pEntity = new CSerializedEntity();
  261. #ifdef PARANOID_SERIALIZEDENTITY
  262. pEntity->m_File = pFile;
  263. pEntity->m_Line = nLine;
  264. m_Allocated++;
  265. {
  266. AUTO_LOCK_FM( m_Mutex );
  267. m_Current.Insert( pEntity );
  268. }
  269. #endif
  270. return reinterpret_cast< SerializedEntityHandle_t >( pEntity );
  271. }
  272. virtual void ReleaseSerializedEntity( SerializedEntityHandle_t handle )
  273. {
  274. if ( handle == SERIALIZED_ENTITY_HANDLE_INVALID )
  275. return;
  276. CSerializedEntity *pEntity = reinterpret_cast< CSerializedEntity * >( handle );
  277. #ifdef PARANOID_SERIALIZEDENTITY
  278. m_Allocated--;
  279. {
  280. AUTO_LOCK_FM( m_Mutex );
  281. m_Current.Remove( pEntity );
  282. }
  283. #endif
  284. delete pEntity;
  285. }
  286. virtual SerializedEntityHandle_t CopySerializedEntity( SerializedEntityHandle_t handle, char const *pFile, int nLine )
  287. {
  288. if ( handle == SERIALIZED_ENTITY_HANDLE_INVALID )
  289. {
  290. Assert( 0 );
  291. return SERIALIZED_ENTITY_HANDLE_INVALID;
  292. }
  293. CSerializedEntity *pSrc = reinterpret_cast< CSerializedEntity * >( handle );
  294. Assert( pSrc );
  295. CSerializedEntity *pDest = reinterpret_cast< CSerializedEntity * >( AllocateSerializedEntity(pFile, nLine) );
  296. pDest->Copy( *pSrc );
  297. return reinterpret_cast< SerializedEntityHandle_t >( pDest );
  298. }
  299. };
  300. static CSerializedEntities g_SerializedEntities;
  301. ISerializedEntities *g_pSerializedEntities = &g_SerializedEntities;
  302. CON_COMMAND( sv_dump_serialized_entities_mem, "Dump serialized entity allocations stats." )
  303. {
  304. CSerializedEntity::DumpMemInfo();
  305. #ifdef PARANOID_SERIALIZEDENTITY
  306. // Use int64 to avoid integer overflow when we do the calculations incorrectly.
  307. int64 totalBytes = 0;
  308. FOR_EACH_UTLRBTREE( g_SerializedEntities.m_Current, i )
  309. {
  310. CSerializedEntity *pEntity = g_SerializedEntities.m_Current[ i ];
  311. // m_nNumReservedFields indicates whether the entity is packed, which then determines how
  312. // we retrieve the size.
  313. int numFields = pEntity->IsPacked() ? pEntity->m_nFieldCount : pEntity->m_nNumReservedFields;
  314. int fieldSize = PAD_NUMBER( numFields * sizeof( *pEntity->m_pFields ), 4 );
  315. int dataOffsetsSize = PAD_NUMBER( numFields * sizeof( *pEntity->m_pFieldDataOffsets ), 4 );
  316. int fieldDataSize = PAD_NUMBER( Bits2Bytes( pEntity->m_nFieldDataBits ), 4 );
  317. int size = fieldSize + dataOffsetsSize + fieldDataSize;
  318. totalBytes += size;
  319. }
  320. double totalKB = totalBytes / 1024.0;
  321. unsigned int count = g_SerializedEntities.m_Current.Count();
  322. Msg( "SerializedEntity: %u, %1.1f KB, %1.1f KB/CSerializedEntity,\n", count, totalKB, totalKB / count );
  323. #endif
  324. }
  325. #ifdef PARANOID_SERIALIZEDENTITY
  326. CON_COMMAND( sv_dump_serialized_entities, "Dump serialized entity allocations." )
  327. {
  328. FOR_EACH_UTLRBTREE( g_SerializedEntities.m_Current, i )
  329. {
  330. Msg( "SerializedEntity from %s:%d: %d fields, %d bits\n",
  331. g_SerializedEntities.m_Current[ i ]->m_File,
  332. g_SerializedEntities.m_Current[ i ]->m_Line,
  333. g_SerializedEntities.m_Current[ i ]->m_nFieldCount,
  334. g_SerializedEntities.m_Current[ i ]->m_nFieldDataBits );
  335. }
  336. }
  337. #endif