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.

1077 lines
32 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. // A growable memory class.
  8. //===========================================================================//
  9. #ifndef UTLMEMORY_H
  10. #define UTLMEMORY_H
  11. #ifdef _WIN32
  12. #pragma once
  13. #endif
  14. #include "tier0/dbg.h"
  15. #include <string.h>
  16. #include "tier0/platform.h"
  17. #include "mathlib/mathlib.h"
  18. #include "tier0/memalloc.h"
  19. #include "tier0/memdbgon.h"
  20. #pragma warning (disable:4100)
  21. #pragma warning (disable:4514)
  22. //-----------------------------------------------------------------------------
  23. #ifdef UTLMEMORY_TRACK
  24. #define UTLMEMORY_TRACK_ALLOC() MemAlloc_RegisterAllocation( "Sum of all UtlMemory", 0, m_nAllocationCount * sizeof(T), m_nAllocationCount * sizeof(T), 0 )
  25. #define UTLMEMORY_TRACK_FREE() if ( !m_pMemory ) ; else MemAlloc_RegisterDeallocation( "Sum of all UtlMemory", 0, m_nAllocationCount * sizeof(T), m_nAllocationCount * sizeof(T), 0 )
  26. #else
  27. #define UTLMEMORY_TRACK_ALLOC() ((void)0)
  28. #define UTLMEMORY_TRACK_FREE() ((void)0)
  29. #endif
  30. //-----------------------------------------------------------------------------
  31. // The CUtlMemory class:
  32. // A growable memory class which doubles in size by default.
  33. //-----------------------------------------------------------------------------
  34. template< class T, class I = int >
  35. class CUtlMemory
  36. {
  37. public:
  38. // constructor, destructor
  39. CUtlMemory( int nGrowSize = 0, int nInitSize = 0 );
  40. CUtlMemory( T* pMemory, int numElements );
  41. CUtlMemory( const T* pMemory, int numElements );
  42. ~CUtlMemory();
  43. // Set the size by which the memory grows
  44. void Init( int nGrowSize = 0, int nInitSize = 0 );
  45. class Iterator_t
  46. {
  47. public:
  48. Iterator_t( I i ) : index( i ) {}
  49. I index;
  50. bool operator==( const Iterator_t it ) const { return index == it.index; }
  51. bool operator!=( const Iterator_t it ) const { return index != it.index; }
  52. };
  53. Iterator_t First() const { return Iterator_t( IsIdxValid( 0 ) ? 0 : InvalidIndex() ); }
  54. Iterator_t Next( const Iterator_t &it ) const { return Iterator_t( IsIdxValid( it.index + 1 ) ? it.index + 1 : InvalidIndex() ); }
  55. I GetIndex( const Iterator_t &it ) const { return it.index; }
  56. bool IsIdxAfter( I i, const Iterator_t &it ) const { return i > it.index; }
  57. bool IsValidIterator( const Iterator_t &it ) const { return IsIdxValid( it.index ); }
  58. Iterator_t InvalidIterator() const { return Iterator_t( InvalidIndex() ); }
  59. // element access
  60. T& operator[]( I i );
  61. const T& operator[]( I i ) const;
  62. T& Element( I i );
  63. const T& Element( I i ) const;
  64. // Can we use this index?
  65. bool IsIdxValid( I i ) const;
  66. // Specify the invalid ('null') index that we'll only return on failure
  67. static const I INVALID_INDEX = ( I )-1; // For use with COMPILE_TIME_ASSERT
  68. static I InvalidIndex() { return INVALID_INDEX; }
  69. // Gets the base address (can change when adding elements!)
  70. T* Base();
  71. const T* Base() const;
  72. // Attaches the buffer to external memory....
  73. void SetExternalBuffer( T* pMemory, int numElements );
  74. void SetExternalBuffer( const T* pMemory, int numElements );
  75. // Takes ownership of the passed memory, including freeing it when this buffer is destroyed.
  76. void AssumeMemory( T *pMemory, int nSize );
  77. // Fast swap
  78. void Swap( CUtlMemory< T, I > &mem );
  79. // Switches the buffer from an external memory buffer to a reallocatable buffer
  80. // Will copy the current contents of the external buffer to the reallocatable buffer
  81. void ConvertToGrowableMemory( int nGrowSize );
  82. // Size
  83. int NumAllocated() const;
  84. int Count() const;
  85. // Grows the memory, so that at least allocated + num elements are allocated
  86. void Grow( int num = 1 );
  87. // Makes sure we've got at least this much memory
  88. void EnsureCapacity( int num );
  89. // Memory deallocation
  90. void Purge();
  91. // Purge all but the given number of elements
  92. void Purge( int numElements );
  93. // is the memory externally allocated?
  94. bool IsExternallyAllocated() const;
  95. // is the memory read only?
  96. bool IsReadOnly() const;
  97. // Set the size by which the memory grows
  98. void SetGrowSize( int size );
  99. protected:
  100. void ValidateGrowSize()
  101. {
  102. #ifdef _X360
  103. if ( m_nGrowSize && m_nGrowSize != EXTERNAL_BUFFER_MARKER )
  104. {
  105. // Max grow size at 128 bytes on XBOX
  106. const int MAX_GROW = 128;
  107. if ( m_nGrowSize * sizeof(T) > MAX_GROW )
  108. {
  109. m_nGrowSize = max( 1, MAX_GROW / sizeof(T) );
  110. }
  111. }
  112. #endif
  113. }
  114. enum
  115. {
  116. EXTERNAL_BUFFER_MARKER = -1,
  117. EXTERNAL_CONST_BUFFER_MARKER = -2,
  118. };
  119. T* m_pMemory;
  120. int m_nAllocationCount;
  121. int m_nGrowSize;
  122. };
  123. //-----------------------------------------------------------------------------
  124. // The CUtlMemory class:
  125. // A growable memory class which doubles in size by default.
  126. //-----------------------------------------------------------------------------
  127. template< class T, size_t SIZE, class I = int >
  128. class CUtlMemoryFixedGrowable : public CUtlMemory< T, I >
  129. {
  130. typedef CUtlMemory< T, I > BaseClass;
  131. public:
  132. CUtlMemoryFixedGrowable( int nGrowSize = 0, int nInitSize = SIZE ) : BaseClass( m_pFixedMemory, SIZE )
  133. {
  134. Assert( nInitSize == 0 || nInitSize == SIZE );
  135. m_nMallocGrowSize = nGrowSize;
  136. }
  137. void Grow( int nCount = 1 )
  138. {
  139. if ( this->IsExternallyAllocated() )
  140. {
  141. this->ConvertToGrowableMemory( m_nMallocGrowSize );
  142. }
  143. BaseClass::Grow( nCount );
  144. }
  145. void EnsureCapacity( int num )
  146. {
  147. if ( CUtlMemory<T>::m_nAllocationCount >= num )
  148. return;
  149. if ( this->IsExternallyAllocated() )
  150. {
  151. // Can't grow a buffer whose memory was externally allocated
  152. this->ConvertToGrowableMemory( m_nMallocGrowSize );
  153. }
  154. BaseClass::EnsureCapacity( num );
  155. }
  156. private:
  157. int m_nMallocGrowSize;
  158. T m_pFixedMemory[ SIZE ];
  159. };
  160. //-----------------------------------------------------------------------------
  161. // The CUtlMemoryFixed class:
  162. // A fixed memory class
  163. //-----------------------------------------------------------------------------
  164. template< typename T, size_t SIZE, int nAlignment = 0 >
  165. class CUtlMemoryFixed
  166. {
  167. public:
  168. // constructor, destructor
  169. CUtlMemoryFixed( int nGrowSize = 0, int nInitSize = 0 ) { Assert( nInitSize == 0 || nInitSize == SIZE ); }
  170. CUtlMemoryFixed( T* pMemory, int numElements ) { Assert( 0 ); }
  171. // Can we use this index?
  172. // Use unsigned math to improve performance
  173. bool IsIdxValid( int i ) const { return (size_t)i < SIZE; }
  174. // Specify the invalid ('null') index that we'll only return on failure
  175. static const int INVALID_INDEX = -1; // For use with COMPILE_TIME_ASSERT
  176. static int InvalidIndex() { return INVALID_INDEX; }
  177. // Gets the base address
  178. T* Base() { if ( nAlignment == 0 ) return (T*)(&m_Memory[0]); else return (T*)AlignValue( &m_Memory[0], nAlignment ); }
  179. const T* Base() const { if ( nAlignment == 0 ) return (T*)(&m_Memory[0]); else return (T*)AlignValue( &m_Memory[0], nAlignment ); }
  180. // element access
  181. // Use unsigned math and inlined checks to improve performance.
  182. T& operator[]( int i ) { Assert( (size_t)i < SIZE ); return Base()[i]; }
  183. const T& operator[]( int i ) const { Assert( (size_t)i < SIZE ); return Base()[i]; }
  184. T& Element( int i ) { Assert( (size_t)i < SIZE ); return Base()[i]; }
  185. const T& Element( int i ) const { Assert( (size_t)i < SIZE ); return Base()[i]; }
  186. // Attaches the buffer to external memory....
  187. void SetExternalBuffer( T* pMemory, int numElements ) { Assert( 0 ); }
  188. // Size
  189. int NumAllocated() const { return SIZE; }
  190. int Count() const { return SIZE; }
  191. // Grows the memory, so that at least allocated + num elements are allocated
  192. void Grow( int num = 1 ) { Assert( 0 ); }
  193. // Makes sure we've got at least this much memory
  194. void EnsureCapacity( int num ) { Assert( num <= SIZE ); }
  195. // Memory deallocation
  196. void Purge() {}
  197. // Purge all but the given number of elements (NOT IMPLEMENTED IN CUtlMemoryFixed)
  198. void Purge( int numElements ) { Assert( 0 ); }
  199. // is the memory externally allocated?
  200. bool IsExternallyAllocated() const { return false; }
  201. // Set the size by which the memory grows
  202. void SetGrowSize( int size ) {}
  203. class Iterator_t
  204. {
  205. public:
  206. Iterator_t( int i ) : index( i ) {}
  207. int index;
  208. bool operator==( const Iterator_t it ) const { return index == it.index; }
  209. bool operator!=( const Iterator_t it ) const { return index != it.index; }
  210. };
  211. Iterator_t First() const { return Iterator_t( IsIdxValid( 0 ) ? 0 : InvalidIndex() ); }
  212. Iterator_t Next( const Iterator_t &it ) const { return Iterator_t( IsIdxValid( it.index + 1 ) ? it.index + 1 : InvalidIndex() ); }
  213. int GetIndex( const Iterator_t &it ) const { return it.index; }
  214. bool IsIdxAfter( int i, const Iterator_t &it ) const { return i > it.index; }
  215. bool IsValidIterator( const Iterator_t &it ) const { return IsIdxValid( it.index ); }
  216. Iterator_t InvalidIterator() const { return Iterator_t( InvalidIndex() ); }
  217. private:
  218. char m_Memory[ SIZE*sizeof(T) + nAlignment ];
  219. };
  220. #if defined(POSIX)
  221. // From Chris Green: Memory is a little fuzzy but I believe this class did
  222. // something fishy with respect to msize and alignment that was OK under our
  223. // allocator, the glibc allocator, etc but not the valgrind one (which has no
  224. // padding because it detects all forms of head/tail overwrite, including
  225. // writing 1 byte past a 1 byte allocation).
  226. #define REMEMBER_ALLOC_SIZE_FOR_VALGRIND 1
  227. #endif
  228. //-----------------------------------------------------------------------------
  229. // The CUtlMemoryConservative class:
  230. // A dynamic memory class that tries to minimize overhead (itself small, no custom grow factor)
  231. //-----------------------------------------------------------------------------
  232. template< typename T >
  233. class CUtlMemoryConservative
  234. {
  235. public:
  236. // constructor, destructor
  237. CUtlMemoryConservative( int nGrowSize = 0, int nInitSize = 0 ) : m_pMemory( NULL )
  238. {
  239. #ifdef REMEMBER_ALLOC_SIZE_FOR_VALGRIND
  240. m_nCurAllocSize = 0;
  241. #endif
  242. }
  243. CUtlMemoryConservative( T* pMemory, int numElements ) { Assert( 0 ); }
  244. ~CUtlMemoryConservative() { if ( m_pMemory ) free( m_pMemory ); }
  245. // Can we use this index?
  246. bool IsIdxValid( int i ) const { return ( IsDebug() ) ? ( i >= 0 && i < NumAllocated() ) : ( i >= 0 ); }
  247. static int InvalidIndex() { return -1; }
  248. // Gets the base address
  249. T* Base() { return m_pMemory; }
  250. const T* Base() const { return m_pMemory; }
  251. // element access
  252. T& operator[]( int i ) { Assert( IsIdxValid(i) ); return Base()[i]; }
  253. const T& operator[]( int i ) const { Assert( IsIdxValid(i) ); return Base()[i]; }
  254. T& Element( int i ) { Assert( IsIdxValid(i) ); return Base()[i]; }
  255. const T& Element( int i ) const { Assert( IsIdxValid(i) ); return Base()[i]; }
  256. // Attaches the buffer to external memory....
  257. void SetExternalBuffer( T* pMemory, int numElements ) { Assert( 0 ); }
  258. // Size
  259. FORCEINLINE void RememberAllocSize( size_t sz )
  260. {
  261. #ifdef REMEMBER_ALLOC_SIZE_FOR_VALGRIND
  262. m_nCurAllocSize = sz;
  263. #endif
  264. }
  265. size_t AllocSize( void ) const
  266. {
  267. #ifdef REMEMBER_ALLOC_SIZE_FOR_VALGRIND
  268. return m_nCurAllocSize;
  269. #else
  270. return ( m_pMemory ) ? g_pMemAlloc->GetSize( m_pMemory ) : 0;
  271. #endif
  272. }
  273. int NumAllocated() const
  274. {
  275. return AllocSize() / sizeof( T );
  276. }
  277. int Count() const
  278. {
  279. return NumAllocated();
  280. }
  281. FORCEINLINE void ReAlloc( size_t sz )
  282. {
  283. m_pMemory = (T*)realloc( m_pMemory, sz );
  284. RememberAllocSize( sz );
  285. }
  286. // Grows the memory, so that at least allocated + num elements are allocated
  287. void Grow( int num = 1 )
  288. {
  289. int nCurN = NumAllocated();
  290. ReAlloc( ( nCurN + num ) * sizeof( T ) );
  291. }
  292. // Makes sure we've got at least this much memory
  293. void EnsureCapacity( int num )
  294. {
  295. size_t nSize = sizeof( T ) * MAX( num, Count() );
  296. ReAlloc( nSize );
  297. }
  298. // Memory deallocation
  299. void Purge()
  300. {
  301. free( m_pMemory );
  302. RememberAllocSize( 0 );
  303. m_pMemory = NULL;
  304. }
  305. // Purge all but the given number of elements
  306. void Purge( int numElements ) { ReAlloc( numElements * sizeof(T) ); }
  307. // is the memory externally allocated?
  308. bool IsExternallyAllocated() const { return false; }
  309. // Set the size by which the memory grows
  310. void SetGrowSize( int size ) {}
  311. class Iterator_t
  312. {
  313. public:
  314. Iterator_t( int i, int _limit ) : index( i ), limit( _limit ) {}
  315. int index;
  316. int limit;
  317. bool operator==( const Iterator_t it ) const { return index == it.index; }
  318. bool operator!=( const Iterator_t it ) const { return index != it.index; }
  319. };
  320. Iterator_t First() const { int limit = NumAllocated(); return Iterator_t( limit ? 0 : InvalidIndex(), limit ); }
  321. Iterator_t Next( const Iterator_t &it ) const { return Iterator_t( ( it.index + 1 < it.limit ) ? it.index + 1 : InvalidIndex(), it.limit ); }
  322. int GetIndex( const Iterator_t &it ) const { return it.index; }
  323. bool IsIdxAfter( int i, const Iterator_t &it ) const { return i > it.index; }
  324. bool IsValidIterator( const Iterator_t &it ) const { return IsIdxValid( it.index ) && ( it.index < it.limit ); }
  325. Iterator_t InvalidIterator() const { return Iterator_t( InvalidIndex(), 0 ); }
  326. private:
  327. T *m_pMemory;
  328. #ifdef REMEMBER_ALLOC_SIZE_FOR_VALGRIND
  329. size_t m_nCurAllocSize;
  330. #endif
  331. };
  332. //-----------------------------------------------------------------------------
  333. // constructor, destructor
  334. //-----------------------------------------------------------------------------
  335. template< class T, class I >
  336. CUtlMemory<T,I>::CUtlMemory( int nGrowSize, int nInitAllocationCount ) : m_pMemory(0),
  337. m_nAllocationCount( nInitAllocationCount ), m_nGrowSize( nGrowSize )
  338. {
  339. ValidateGrowSize();
  340. Assert( nGrowSize >= 0 );
  341. if (m_nAllocationCount)
  342. {
  343. UTLMEMORY_TRACK_ALLOC();
  344. MEM_ALLOC_CREDIT_CLASS();
  345. m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) );
  346. }
  347. }
  348. template< class T, class I >
  349. CUtlMemory<T,I>::CUtlMemory( T* pMemory, int numElements ) : m_pMemory(pMemory),
  350. m_nAllocationCount( numElements )
  351. {
  352. // Special marker indicating externally supplied modifyable memory
  353. m_nGrowSize = EXTERNAL_BUFFER_MARKER;
  354. }
  355. template< class T, class I >
  356. CUtlMemory<T,I>::CUtlMemory( const T* pMemory, int numElements ) : m_pMemory( (T*)pMemory ),
  357. m_nAllocationCount( numElements )
  358. {
  359. // Special marker indicating externally supplied modifyable memory
  360. m_nGrowSize = EXTERNAL_CONST_BUFFER_MARKER;
  361. }
  362. template< class T, class I >
  363. CUtlMemory<T,I>::~CUtlMemory()
  364. {
  365. Purge();
  366. }
  367. template< class T, class I >
  368. void CUtlMemory<T,I>::Init( int nGrowSize /*= 0*/, int nInitSize /*= 0*/ )
  369. {
  370. Purge();
  371. m_nGrowSize = nGrowSize;
  372. m_nAllocationCount = nInitSize;
  373. ValidateGrowSize();
  374. Assert( nGrowSize >= 0 );
  375. if (m_nAllocationCount)
  376. {
  377. UTLMEMORY_TRACK_ALLOC();
  378. MEM_ALLOC_CREDIT_CLASS();
  379. m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) );
  380. }
  381. }
  382. //-----------------------------------------------------------------------------
  383. // Fast swap
  384. //-----------------------------------------------------------------------------
  385. template< class T, class I >
  386. void CUtlMemory<T,I>::Swap( CUtlMemory<T,I> &mem )
  387. {
  388. V_swap( m_nGrowSize, mem.m_nGrowSize );
  389. V_swap( m_pMemory, mem.m_pMemory );
  390. V_swap( m_nAllocationCount, mem.m_nAllocationCount );
  391. }
  392. //-----------------------------------------------------------------------------
  393. // Switches the buffer from an external memory buffer to a reallocatable buffer
  394. //-----------------------------------------------------------------------------
  395. template< class T, class I >
  396. void CUtlMemory<T,I>::ConvertToGrowableMemory( int nGrowSize )
  397. {
  398. if ( !IsExternallyAllocated() )
  399. return;
  400. m_nGrowSize = nGrowSize;
  401. if (m_nAllocationCount)
  402. {
  403. UTLMEMORY_TRACK_ALLOC();
  404. MEM_ALLOC_CREDIT_CLASS();
  405. int nNumBytes = m_nAllocationCount * sizeof(T);
  406. T *pMemory = (T*)malloc( nNumBytes );
  407. memcpy( (void*)pMemory, (void*)m_pMemory, nNumBytes );
  408. m_pMemory = pMemory;
  409. }
  410. else
  411. {
  412. m_pMemory = NULL;
  413. }
  414. }
  415. //-----------------------------------------------------------------------------
  416. // Attaches the buffer to external memory....
  417. //-----------------------------------------------------------------------------
  418. template< class T, class I >
  419. void CUtlMemory<T,I>::SetExternalBuffer( T* pMemory, int numElements )
  420. {
  421. // Blow away any existing allocated memory
  422. Purge();
  423. m_pMemory = pMemory;
  424. m_nAllocationCount = numElements;
  425. // Indicate that we don't own the memory
  426. m_nGrowSize = EXTERNAL_BUFFER_MARKER;
  427. }
  428. template< class T, class I >
  429. void CUtlMemory<T,I>::SetExternalBuffer( const T* pMemory, int numElements )
  430. {
  431. // Blow away any existing allocated memory
  432. Purge();
  433. m_pMemory = const_cast<T*>( pMemory );
  434. m_nAllocationCount = numElements;
  435. // Indicate that we don't own the memory
  436. m_nGrowSize = EXTERNAL_CONST_BUFFER_MARKER;
  437. }
  438. template< class T, class I >
  439. void CUtlMemory<T,I>::AssumeMemory( T* pMemory, int numElements )
  440. {
  441. // Blow away any existing allocated memory
  442. Purge();
  443. // Simply take the pointer but don't mark us as external
  444. m_pMemory = pMemory;
  445. m_nAllocationCount = numElements;
  446. }
  447. //-----------------------------------------------------------------------------
  448. // element access
  449. //-----------------------------------------------------------------------------
  450. template< class T, class I >
  451. inline T& CUtlMemory<T,I>::operator[]( I i )
  452. {
  453. // Avoid function calls in the asserts to improve debug build performance
  454. Assert( m_nGrowSize != EXTERNAL_CONST_BUFFER_MARKER ); //Assert( !IsReadOnly() );
  455. Assert( (uint32)i < (uint32)m_nAllocationCount );
  456. return m_pMemory[(uint32)i];
  457. }
  458. template< class T, class I >
  459. inline const T& CUtlMemory<T,I>::operator[]( I i ) const
  460. {
  461. // Avoid function calls in the asserts to improve debug build performance
  462. Assert( (uint32)i < (uint32)m_nAllocationCount );
  463. return m_pMemory[(uint32)i];
  464. }
  465. template< class T, class I >
  466. inline T& CUtlMemory<T,I>::Element( I i )
  467. {
  468. // Avoid function calls in the asserts to improve debug build performance
  469. Assert( m_nGrowSize != EXTERNAL_CONST_BUFFER_MARKER ); //Assert( !IsReadOnly() );
  470. Assert( (uint32)i < (uint32)m_nAllocationCount );
  471. return m_pMemory[(uint32)i];
  472. }
  473. template< class T, class I >
  474. inline const T& CUtlMemory<T,I>::Element( I i ) const
  475. {
  476. // Avoid function calls in the asserts to improve debug build performance
  477. Assert( (uint32)i < (uint32)m_nAllocationCount );
  478. return m_pMemory[(uint32)i];
  479. }
  480. //-----------------------------------------------------------------------------
  481. // is the memory externally allocated?
  482. //-----------------------------------------------------------------------------
  483. template< class T, class I >
  484. bool CUtlMemory<T,I>::IsExternallyAllocated() const
  485. {
  486. return (m_nGrowSize < 0);
  487. }
  488. //-----------------------------------------------------------------------------
  489. // is the memory read only?
  490. //-----------------------------------------------------------------------------
  491. template< class T, class I >
  492. bool CUtlMemory<T,I>::IsReadOnly() const
  493. {
  494. return (m_nGrowSize == EXTERNAL_CONST_BUFFER_MARKER);
  495. }
  496. template< class T, class I >
  497. void CUtlMemory<T,I>::SetGrowSize( int nSize )
  498. {
  499. Assert( !IsExternallyAllocated() );
  500. Assert( nSize >= 0 );
  501. m_nGrowSize = nSize;
  502. ValidateGrowSize();
  503. }
  504. //-----------------------------------------------------------------------------
  505. // Gets the base address (can change when adding elements!)
  506. //-----------------------------------------------------------------------------
  507. template< class T, class I >
  508. inline T* CUtlMemory<T,I>::Base()
  509. {
  510. Assert( !IsReadOnly() );
  511. return m_pMemory;
  512. }
  513. template< class T, class I >
  514. inline const T *CUtlMemory<T,I>::Base() const
  515. {
  516. return m_pMemory;
  517. }
  518. //-----------------------------------------------------------------------------
  519. // Size
  520. //-----------------------------------------------------------------------------
  521. template< class T, class I >
  522. inline int CUtlMemory<T,I>::NumAllocated() const
  523. {
  524. return m_nAllocationCount;
  525. }
  526. template< class T, class I >
  527. inline int CUtlMemory<T,I>::Count() const
  528. {
  529. return m_nAllocationCount;
  530. }
  531. //-----------------------------------------------------------------------------
  532. // Is element index valid?
  533. //-----------------------------------------------------------------------------
  534. template< class T, class I >
  535. inline bool CUtlMemory<T,I>::IsIdxValid( I i ) const
  536. {
  537. // If we always cast 'i' and 'm_nAllocationCount' to unsigned then we can
  538. // do our range checking with a single comparison instead of two. This gives
  539. // a modest speedup in debug builds.
  540. return (uint32)i < (uint32)m_nAllocationCount;
  541. }
  542. //-----------------------------------------------------------------------------
  543. // Grows the memory
  544. //-----------------------------------------------------------------------------
  545. inline int UtlMemory_CalcNewAllocationCount( int nAllocationCount, int nGrowSize, int nNewSize, int nBytesItem )
  546. {
  547. if ( nGrowSize )
  548. {
  549. nAllocationCount = ((1 + ((nNewSize - 1) / nGrowSize)) * nGrowSize);
  550. }
  551. else
  552. {
  553. if ( !nAllocationCount )
  554. {
  555. // Compute an allocation which is at least as big as a cache line...
  556. nAllocationCount = (31 + nBytesItem) / nBytesItem;
  557. }
  558. while (nAllocationCount < nNewSize)
  559. {
  560. #ifndef _X360
  561. nAllocationCount *= 2;
  562. #else
  563. int nNewAllocationCount = ( nAllocationCount * 9) / 8; // 12.5 %
  564. if ( nNewAllocationCount > nAllocationCount )
  565. nAllocationCount = nNewAllocationCount;
  566. else
  567. nAllocationCount *= 2;
  568. #endif
  569. }
  570. }
  571. return nAllocationCount;
  572. }
  573. template< class T, class I >
  574. void CUtlMemory<T,I>::Grow( int num )
  575. {
  576. Assert( num > 0 );
  577. if ( IsExternallyAllocated() )
  578. {
  579. // Can't grow a buffer whose memory was externally allocated
  580. Assert(0);
  581. return;
  582. }
  583. // Make sure we have at least numallocated + num allocations.
  584. // Use the grow rules specified for this memory (in m_nGrowSize)
  585. int nAllocationRequested = m_nAllocationCount + num;
  586. UTLMEMORY_TRACK_FREE();
  587. int nNewAllocationCount = UtlMemory_CalcNewAllocationCount( m_nAllocationCount, m_nGrowSize, nAllocationRequested, sizeof(T) );
  588. // if m_nAllocationRequested wraps index type I, recalculate
  589. if ( ( int )( I )nNewAllocationCount < nAllocationRequested )
  590. {
  591. if ( ( int )( I )nNewAllocationCount == 0 && ( int )( I )( nNewAllocationCount - 1 ) >= nAllocationRequested )
  592. {
  593. --nNewAllocationCount; // deal w/ the common case of m_nAllocationCount == MAX_USHORT + 1
  594. }
  595. else
  596. {
  597. if ( ( int )( I )nAllocationRequested != nAllocationRequested )
  598. {
  599. // we've been asked to grow memory to a size s.t. the index type can't address the requested amount of memory
  600. Assert( 0 );
  601. return;
  602. }
  603. while ( ( int )( I )nNewAllocationCount < nAllocationRequested )
  604. {
  605. nNewAllocationCount = ( nNewAllocationCount + nAllocationRequested ) / 2;
  606. }
  607. }
  608. }
  609. m_nAllocationCount = nNewAllocationCount;
  610. UTLMEMORY_TRACK_ALLOC();
  611. if (m_pMemory)
  612. {
  613. MEM_ALLOC_CREDIT_CLASS();
  614. m_pMemory = (T*)realloc( m_pMemory, m_nAllocationCount * sizeof(T) );
  615. Assert( m_pMemory );
  616. }
  617. else
  618. {
  619. MEM_ALLOC_CREDIT_CLASS();
  620. m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) );
  621. Assert( m_pMemory );
  622. }
  623. }
  624. //-----------------------------------------------------------------------------
  625. // Makes sure we've got at least this much memory
  626. //-----------------------------------------------------------------------------
  627. template< class T, class I >
  628. inline void CUtlMemory<T,I>::EnsureCapacity( int num )
  629. {
  630. if (m_nAllocationCount >= num)
  631. return;
  632. if ( IsExternallyAllocated() )
  633. {
  634. // Can't grow a buffer whose memory was externally allocated
  635. Assert(0);
  636. return;
  637. }
  638. UTLMEMORY_TRACK_FREE();
  639. m_nAllocationCount = num;
  640. UTLMEMORY_TRACK_ALLOC();
  641. if (m_pMemory)
  642. {
  643. MEM_ALLOC_CREDIT_CLASS();
  644. m_pMemory = (T*)realloc( m_pMemory, m_nAllocationCount * sizeof(T) );
  645. }
  646. else
  647. {
  648. MEM_ALLOC_CREDIT_CLASS();
  649. m_pMemory = (T*)malloc( m_nAllocationCount * sizeof(T) );
  650. }
  651. }
  652. //-----------------------------------------------------------------------------
  653. // Memory deallocation
  654. //-----------------------------------------------------------------------------
  655. template< class T, class I >
  656. void CUtlMemory<T,I>::Purge()
  657. {
  658. if ( !IsExternallyAllocated() )
  659. {
  660. if (m_pMemory)
  661. {
  662. UTLMEMORY_TRACK_FREE();
  663. free( (void*)m_pMemory );
  664. m_pMemory = 0;
  665. }
  666. m_nAllocationCount = 0;
  667. }
  668. }
  669. template< class T, class I >
  670. void CUtlMemory<T,I>::Purge( int numElements )
  671. {
  672. Assert( numElements >= 0 );
  673. if( numElements > m_nAllocationCount )
  674. {
  675. // Ensure this isn't a grow request in disguise.
  676. Assert( numElements <= m_nAllocationCount );
  677. return;
  678. }
  679. // If we have zero elements, simply do a purge:
  680. if( numElements == 0 )
  681. {
  682. Purge();
  683. return;
  684. }
  685. if ( IsExternallyAllocated() )
  686. {
  687. // Can't shrink a buffer whose memory was externally allocated, fail silently like purge
  688. return;
  689. }
  690. // If the number of elements is the same as the allocation count, we are done.
  691. if( numElements == m_nAllocationCount )
  692. {
  693. return;
  694. }
  695. if( !m_pMemory )
  696. {
  697. // Allocation count is non zero, but memory is null.
  698. Assert( m_pMemory );
  699. return;
  700. }
  701. UTLMEMORY_TRACK_FREE();
  702. m_nAllocationCount = numElements;
  703. UTLMEMORY_TRACK_ALLOC();
  704. // Allocation count > 0, shrink it down.
  705. MEM_ALLOC_CREDIT_CLASS();
  706. m_pMemory = (T*)realloc( m_pMemory, m_nAllocationCount * sizeof(T) );
  707. }
  708. //-----------------------------------------------------------------------------
  709. // The CUtlMemory class:
  710. // A growable memory class which doubles in size by default.
  711. //-----------------------------------------------------------------------------
  712. template< class T, int nAlignment >
  713. class CUtlMemoryAligned : public CUtlMemory<T>
  714. {
  715. public:
  716. // constructor, destructor
  717. CUtlMemoryAligned( int nGrowSize = 0, int nInitSize = 0 );
  718. CUtlMemoryAligned( T* pMemory, int numElements );
  719. CUtlMemoryAligned( const T* pMemory, int numElements );
  720. ~CUtlMemoryAligned();
  721. // Attaches the buffer to external memory....
  722. void SetExternalBuffer( T* pMemory, int numElements );
  723. void SetExternalBuffer( const T* pMemory, int numElements );
  724. // Grows the memory, so that at least allocated + num elements are allocated
  725. void Grow( int num = 1 );
  726. // Makes sure we've got at least this much memory
  727. void EnsureCapacity( int num );
  728. // Memory deallocation
  729. void Purge();
  730. // Purge all but the given number of elements (NOT IMPLEMENTED IN CUtlMemoryAligned)
  731. void Purge( int numElements ) { Assert( 0 ); }
  732. private:
  733. void *Align( const void *pAddr );
  734. };
  735. //-----------------------------------------------------------------------------
  736. // Aligns a pointer
  737. //-----------------------------------------------------------------------------
  738. template< class T, int nAlignment >
  739. void *CUtlMemoryAligned<T, nAlignment>::Align( const void *pAddr )
  740. {
  741. size_t nAlignmentMask = nAlignment - 1;
  742. return (void*)( ((size_t)pAddr + nAlignmentMask) & (~nAlignmentMask) );
  743. }
  744. //-----------------------------------------------------------------------------
  745. // constructor, destructor
  746. //-----------------------------------------------------------------------------
  747. template< class T, int nAlignment >
  748. CUtlMemoryAligned<T, nAlignment>::CUtlMemoryAligned( int nGrowSize, int nInitAllocationCount )
  749. {
  750. CUtlMemory<T>::m_pMemory = 0;
  751. CUtlMemory<T>::m_nAllocationCount = nInitAllocationCount;
  752. CUtlMemory<T>::m_nGrowSize = nGrowSize;
  753. this->ValidateGrowSize();
  754. // Alignment must be a power of two
  755. COMPILE_TIME_ASSERT( (nAlignment & (nAlignment-1)) == 0 );
  756. Assert( (nGrowSize >= 0) && (nGrowSize != CUtlMemory<T>::EXTERNAL_BUFFER_MARKER) );
  757. if ( CUtlMemory<T>::m_nAllocationCount )
  758. {
  759. UTLMEMORY_TRACK_ALLOC();
  760. MEM_ALLOC_CREDIT_CLASS();
  761. CUtlMemory<T>::m_pMemory = (T*)_aligned_malloc( nInitAllocationCount * sizeof(T), nAlignment );
  762. }
  763. }
  764. template< class T, int nAlignment >
  765. CUtlMemoryAligned<T, nAlignment>::CUtlMemoryAligned( T* pMemory, int numElements )
  766. {
  767. // Special marker indicating externally supplied memory
  768. CUtlMemory<T>::m_nGrowSize = CUtlMemory<T>::EXTERNAL_BUFFER_MARKER;
  769. CUtlMemory<T>::m_pMemory = (T*)Align( pMemory );
  770. CUtlMemory<T>::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory<T>::m_pMemory ) / sizeof(T);
  771. }
  772. template< class T, int nAlignment >
  773. CUtlMemoryAligned<T, nAlignment>::CUtlMemoryAligned( const T* pMemory, int numElements )
  774. {
  775. // Special marker indicating externally supplied memory
  776. CUtlMemory<T>::m_nGrowSize = CUtlMemory<T>::EXTERNAL_CONST_BUFFER_MARKER;
  777. CUtlMemory<T>::m_pMemory = (T*)Align( pMemory );
  778. CUtlMemory<T>::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory<T>::m_pMemory ) / sizeof(T);
  779. }
  780. template< class T, int nAlignment >
  781. CUtlMemoryAligned<T, nAlignment>::~CUtlMemoryAligned()
  782. {
  783. Purge();
  784. }
  785. //-----------------------------------------------------------------------------
  786. // Attaches the buffer to external memory....
  787. //-----------------------------------------------------------------------------
  788. template< class T, int nAlignment >
  789. void CUtlMemoryAligned<T, nAlignment>::SetExternalBuffer( T* pMemory, int numElements )
  790. {
  791. // Blow away any existing allocated memory
  792. Purge();
  793. CUtlMemory<T>::m_pMemory = (T*)Align( pMemory );
  794. CUtlMemory<T>::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory<T>::m_pMemory ) / sizeof(T);
  795. // Indicate that we don't own the memory
  796. CUtlMemory<T>::m_nGrowSize = CUtlMemory<T>::EXTERNAL_BUFFER_MARKER;
  797. }
  798. template< class T, int nAlignment >
  799. void CUtlMemoryAligned<T, nAlignment>::SetExternalBuffer( const T* pMemory, int numElements )
  800. {
  801. // Blow away any existing allocated memory
  802. Purge();
  803. CUtlMemory<T>::m_pMemory = (T*)Align( pMemory );
  804. CUtlMemory<T>::m_nAllocationCount = ( (int)(pMemory + numElements) - (int)CUtlMemory<T>::m_pMemory ) / sizeof(T);
  805. // Indicate that we don't own the memory
  806. CUtlMemory<T>::m_nGrowSize = CUtlMemory<T>::EXTERNAL_CONST_BUFFER_MARKER;
  807. }
  808. //-----------------------------------------------------------------------------
  809. // Grows the memory
  810. //-----------------------------------------------------------------------------
  811. template< class T, int nAlignment >
  812. void CUtlMemoryAligned<T, nAlignment>::Grow( int num )
  813. {
  814. Assert( num > 0 );
  815. if ( this->IsExternallyAllocated() )
  816. {
  817. // Can't grow a buffer whose memory was externally allocated
  818. Assert(0);
  819. return;
  820. }
  821. UTLMEMORY_TRACK_FREE();
  822. // Make sure we have at least numallocated + num allocations.
  823. // Use the grow rules specified for this memory (in m_nGrowSize)
  824. int nAllocationRequested = CUtlMemory<T>::m_nAllocationCount + num;
  825. CUtlMemory<T>::m_nAllocationCount = UtlMemory_CalcNewAllocationCount( CUtlMemory<T>::m_nAllocationCount, CUtlMemory<T>::m_nGrowSize, nAllocationRequested, sizeof(T) );
  826. UTLMEMORY_TRACK_ALLOC();
  827. if ( CUtlMemory<T>::m_pMemory )
  828. {
  829. MEM_ALLOC_CREDIT_CLASS();
  830. CUtlMemory<T>::m_pMemory = (T*)MemAlloc_ReallocAligned( CUtlMemory<T>::m_pMemory, CUtlMemory<T>::m_nAllocationCount * sizeof(T), nAlignment );
  831. Assert( CUtlMemory<T>::m_pMemory );
  832. }
  833. else
  834. {
  835. MEM_ALLOC_CREDIT_CLASS();
  836. CUtlMemory<T>::m_pMemory = (T*)MemAlloc_AllocAligned( CUtlMemory<T>::m_nAllocationCount * sizeof(T), nAlignment );
  837. Assert( CUtlMemory<T>::m_pMemory );
  838. }
  839. }
  840. //-----------------------------------------------------------------------------
  841. // Makes sure we've got at least this much memory
  842. //-----------------------------------------------------------------------------
  843. template< class T, int nAlignment >
  844. inline void CUtlMemoryAligned<T, nAlignment>::EnsureCapacity( int num )
  845. {
  846. if ( CUtlMemory<T>::m_nAllocationCount >= num )
  847. return;
  848. if ( this->IsExternallyAllocated() )
  849. {
  850. // Can't grow a buffer whose memory was externally allocated
  851. Assert(0);
  852. return;
  853. }
  854. UTLMEMORY_TRACK_FREE();
  855. CUtlMemory<T>::m_nAllocationCount = num;
  856. UTLMEMORY_TRACK_ALLOC();
  857. if ( CUtlMemory<T>::m_pMemory )
  858. {
  859. MEM_ALLOC_CREDIT_CLASS();
  860. CUtlMemory<T>::m_pMemory = (T*)MemAlloc_ReallocAligned( CUtlMemory<T>::m_pMemory, CUtlMemory<T>::m_nAllocationCount * sizeof(T), nAlignment );
  861. }
  862. else
  863. {
  864. MEM_ALLOC_CREDIT_CLASS();
  865. CUtlMemory<T>::m_pMemory = (T*)MemAlloc_AllocAligned( CUtlMemory<T>::m_nAllocationCount * sizeof(T), nAlignment );
  866. }
  867. }
  868. //-----------------------------------------------------------------------------
  869. // Memory deallocation
  870. //-----------------------------------------------------------------------------
  871. template< class T, int nAlignment >
  872. void CUtlMemoryAligned<T, nAlignment>::Purge()
  873. {
  874. if ( !this->IsExternallyAllocated() )
  875. {
  876. if ( CUtlMemory<T>::m_pMemory )
  877. {
  878. UTLMEMORY_TRACK_FREE();
  879. MemAlloc_FreeAligned( CUtlMemory<T>::m_pMemory );
  880. CUtlMemory<T>::m_pMemory = 0;
  881. }
  882. CUtlMemory<T>::m_nAllocationCount = 0;
  883. }
  884. }
  885. #include "tier0/memdbgoff.h"
  886. #endif // UTLMEMORY_H