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.

649 lines
17 KiB

  1. //===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. //
  8. //-----------------------------------------------------------------------------
  9. // $Log: $
  10. //
  11. // $NoKeywords: $
  12. //===========================================================================//
  13. #ifndef MEMPOOL_H
  14. #define MEMPOOL_H
  15. #ifdef _WIN32
  16. #pragma once
  17. #endif
  18. #include "tier0/memalloc.h"
  19. #include "tier0/tslist.h"
  20. #include "tier0/platform.h"
  21. #include "tier1/utlvector.h"
  22. #include "tier1/utlrbtree.h"
  23. //-----------------------------------------------------------------------------
  24. // Purpose: Optimized pool memory allocator
  25. //-----------------------------------------------------------------------------
  26. typedef void (*MemoryPoolReportFunc_t)( char const* pMsg, ... );
  27. class CUtlMemoryPool
  28. {
  29. public:
  30. // Ways the memory pool can grow when it needs to make a new blob.
  31. enum MemoryPoolGrowType_t
  32. {
  33. GROW_NONE=0, // Don't allow new blobs.
  34. GROW_FAST=1, // New blob size is numElements * (i+1) (ie: the blocks it allocates
  35. // get larger and larger each time it allocates one).
  36. GROW_SLOW=2 // New blob size is numElements.
  37. };
  38. CUtlMemoryPool( int blockSize, int numElements, int growMode = GROW_FAST, const char *pszAllocOwner = NULL, int nAlignment = 0 );
  39. ~CUtlMemoryPool();
  40. void* Alloc(); // Allocate the element size you specified in the constructor.
  41. void* Alloc( size_t amount );
  42. void* AllocZero(); // Allocate the element size you specified in the constructor, zero the memory before construction
  43. void* AllocZero( size_t amount );
  44. void Free(void *pMem);
  45. // Frees everything
  46. void Clear();
  47. // Error reporting...
  48. static void SetErrorReportFunc( MemoryPoolReportFunc_t func );
  49. // returns number of allocated blocks
  50. int Count() const { return m_BlocksAllocated; }
  51. int PeakCount() const { return m_PeakAlloc; }
  52. int BlockSize() const { return m_BlockSize; }
  53. int Size() const { return m_NumBlobs * m_BlocksPerBlob * m_BlockSize; }
  54. bool IsAllocationWithinPool( void *pMem ) const;
  55. protected:
  56. class CBlob
  57. {
  58. public:
  59. CBlob *m_pPrev, *m_pNext;
  60. int m_NumBytes; // Number of bytes in this blob.
  61. char m_Data[1];
  62. char m_Padding[3]; // to int align the struct
  63. };
  64. // Resets the pool
  65. void Init();
  66. void AddNewBlob();
  67. void ReportLeaks();
  68. int m_BlockSize;
  69. int m_BlocksPerBlob;
  70. int m_GrowMode; // GROW_ enum.
  71. // FIXME: Change m_ppMemBlob into a growable array?
  72. void *m_pHeadOfFreeList;
  73. int m_BlocksAllocated;
  74. int m_PeakAlloc;
  75. unsigned short m_nAlignment;
  76. unsigned short m_NumBlobs;
  77. const char * m_pszAllocOwner;
  78. // CBlob could be not a multiple of 4 bytes so stuff it at the end here to keep us otherwise aligned
  79. CBlob m_BlobHead;
  80. static MemoryPoolReportFunc_t g_ReportFunc;
  81. };
  82. //-----------------------------------------------------------------------------
  83. // Multi-thread/Thread Safe Memory Class
  84. //-----------------------------------------------------------------------------
  85. class CMemoryPoolMT : public CUtlMemoryPool
  86. {
  87. public:
  88. CMemoryPoolMT( int blockSize, int numElements, int growMode = GROW_FAST, const char *pszAllocOwner = NULL, int nAlignment = 0) : CUtlMemoryPool( blockSize, numElements, growMode, pszAllocOwner, nAlignment ) {}
  89. void* Alloc() { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::Alloc(); }
  90. void* Alloc( size_t amount ) { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::Alloc( amount ); }
  91. void* AllocZero() { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::AllocZero(); }
  92. void* AllocZero( size_t amount ) { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::AllocZero( amount ); }
  93. void Free(void *pMem) { AUTO_LOCK( m_mutex ); CUtlMemoryPool::Free( pMem ); }
  94. // Frees everything
  95. void Clear() { AUTO_LOCK( m_mutex ); return CUtlMemoryPool::Clear(); }
  96. private:
  97. CThreadFastMutex m_mutex; // @TODO: Rework to use tslist (toml 7/6/2007)
  98. };
  99. //-----------------------------------------------------------------------------
  100. // Wrapper macro to make an allocator that returns particular typed allocations
  101. // and construction and destruction of objects.
  102. //-----------------------------------------------------------------------------
  103. template< class T >
  104. class CClassMemoryPool : public CUtlMemoryPool
  105. {
  106. public:
  107. CClassMemoryPool(int numElements, int growMode = GROW_FAST, int nAlignment = 0 ) :
  108. CUtlMemoryPool( sizeof(T), numElements, growMode, MEM_ALLOC_CLASSNAME(T), nAlignment ) {}
  109. T* Alloc();
  110. T* AllocZero();
  111. void Free( T *pMem );
  112. void Clear();
  113. };
  114. //-----------------------------------------------------------------------------
  115. // Specialized pool for aligned data management (e.g., Xbox textures)
  116. //-----------------------------------------------------------------------------
  117. template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, bool GROWMODE = false, int COMPACT_THRESHOLD = 4 >
  118. class CAlignedMemPool
  119. {
  120. enum
  121. {
  122. BLOCK_SIZE = COMPILETIME_MAX( ALIGN_VALUE( ITEM_SIZE, ALIGNMENT ), 8 ),
  123. };
  124. public:
  125. CAlignedMemPool();
  126. void *Alloc();
  127. void Free( void *p );
  128. static int __cdecl CompareChunk( void * const *ppLeft, void * const *ppRight );
  129. void Compact();
  130. int NumTotal() { AUTO_LOCK( m_mutex ); return m_Chunks.Count() * ( CHUNK_SIZE / BLOCK_SIZE ); }
  131. int NumAllocated() { AUTO_LOCK( m_mutex ); return NumTotal() - m_nFree; }
  132. int NumFree() { AUTO_LOCK( m_mutex ); return m_nFree; }
  133. int BytesTotal() { AUTO_LOCK( m_mutex ); return NumTotal() * BLOCK_SIZE; }
  134. int BytesAllocated() { AUTO_LOCK( m_mutex ); return NumAllocated() * BLOCK_SIZE; }
  135. int BytesFree() { AUTO_LOCK( m_mutex ); return NumFree() * BLOCK_SIZE; }
  136. int ItemSize() { return ITEM_SIZE; }
  137. int BlockSize() { return BLOCK_SIZE; }
  138. int ChunkSize() { return CHUNK_SIZE; }
  139. private:
  140. struct FreeBlock_t
  141. {
  142. FreeBlock_t *pNext;
  143. byte reserved[ BLOCK_SIZE - sizeof( FreeBlock_t *) ];
  144. };
  145. CUtlVector<void *> m_Chunks; // Chunks are tracked outside blocks (unlike CUtlMemoryPool) to simplify alignment issues
  146. FreeBlock_t * m_pFirstFree;
  147. int m_nFree;
  148. CAllocator m_Allocator;
  149. double m_TimeLastCompact;
  150. CThreadFastMutex m_mutex;
  151. };
  152. //-----------------------------------------------------------------------------
  153. // Pool variant using standard allocation
  154. //-----------------------------------------------------------------------------
  155. template <typename T, int nInitialCount = 0, bool bDefCreateNewIfEmpty = true >
  156. class CObjectPool
  157. {
  158. public:
  159. CObjectPool()
  160. {
  161. int i = nInitialCount;
  162. while ( i-- > 0 )
  163. {
  164. m_AvailableObjects.PushItem( new T );
  165. }
  166. }
  167. ~CObjectPool()
  168. {
  169. Purge();
  170. }
  171. int NumAvailable()
  172. {
  173. return m_AvailableObjects.Count();
  174. }
  175. void Purge()
  176. {
  177. T *p = NULL;
  178. while ( m_AvailableObjects.PopItem( &p ) )
  179. {
  180. delete p;
  181. }
  182. }
  183. T *GetObject( bool bCreateNewIfEmpty = bDefCreateNewIfEmpty )
  184. {
  185. T *p = NULL;
  186. if ( !m_AvailableObjects.PopItem( &p ) )
  187. {
  188. p = ( bCreateNewIfEmpty ) ? new T : NULL;
  189. }
  190. return p;
  191. }
  192. void PutObject( T *p )
  193. {
  194. m_AvailableObjects.PushItem( p );
  195. }
  196. private:
  197. CTSList<T *> m_AvailableObjects;
  198. };
  199. //-----------------------------------------------------------------------------
  200. // Fixed budget pool with overflow to malloc
  201. //-----------------------------------------------------------------------------
  202. template <size_t PROVIDED_ITEM_SIZE, int ITEM_COUNT>
  203. class CFixedBudgetMemoryPool
  204. {
  205. public:
  206. CFixedBudgetMemoryPool()
  207. {
  208. m_pBase = m_pLimit = 0;
  209. COMPILE_TIME_ASSERT( ITEM_SIZE % 4 == 0 );
  210. }
  211. bool Owns( void *p )
  212. {
  213. return ( p >= m_pBase && p < m_pLimit );
  214. }
  215. void *Alloc()
  216. {
  217. MEM_ALLOC_CREDIT_CLASS();
  218. #ifndef USE_MEM_DEBUG
  219. if ( !m_pBase )
  220. {
  221. LOCAL_THREAD_LOCK();
  222. if ( !m_pBase )
  223. {
  224. byte *pMemory = m_pBase = (byte *)malloc( ITEM_COUNT * ITEM_SIZE );
  225. m_pLimit = m_pBase + ( ITEM_COUNT * ITEM_SIZE );
  226. for ( int i = 0; i < ITEM_COUNT; i++ )
  227. {
  228. m_freeList.Push( (TSLNodeBase_t *)pMemory );
  229. pMemory += ITEM_SIZE;
  230. }
  231. }
  232. }
  233. void *p = m_freeList.Pop();
  234. if ( p )
  235. return p;
  236. #endif
  237. return malloc( ITEM_SIZE );
  238. }
  239. void Free( void *p )
  240. {
  241. #ifndef USE_MEM_DEBUG
  242. if ( Owns( p ) )
  243. m_freeList.Push( (TSLNodeBase_t *)p );
  244. else
  245. #endif
  246. free( p );
  247. }
  248. void Clear()
  249. {
  250. #ifndef USE_MEM_DEBUG
  251. if ( m_pBase )
  252. {
  253. free( m_pBase );
  254. }
  255. m_pBase = m_pLimit = 0;
  256. Construct( &m_freeList );
  257. #endif
  258. }
  259. bool IsEmpty()
  260. {
  261. #ifndef USE_MEM_DEBUG
  262. if ( m_pBase && m_freeList.Count() != ITEM_COUNT )
  263. return false;
  264. #endif
  265. return true;
  266. }
  267. enum
  268. {
  269. ITEM_SIZE = ALIGN_VALUE( PROVIDED_ITEM_SIZE, TSLIST_NODE_ALIGNMENT )
  270. };
  271. CTSListBase m_freeList;
  272. byte *m_pBase;
  273. byte *m_pLimit;
  274. };
  275. #define BIND_TO_FIXED_BUDGET_POOL( poolName ) \
  276. inline void* operator new( size_t size ) { return poolName.Alloc(); } \
  277. inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { return poolName.Alloc(); } \
  278. inline void operator delete( void* p ) { poolName.Free(p); } \
  279. inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { poolName.Free(p); }
  280. //-----------------------------------------------------------------------------
  281. template< class T >
  282. inline T* CClassMemoryPool<T>::Alloc()
  283. {
  284. T *pRet;
  285. {
  286. MEM_ALLOC_CREDIT_CLASS();
  287. pRet = (T*)CUtlMemoryPool::Alloc();
  288. }
  289. if ( pRet )
  290. {
  291. Construct( pRet );
  292. }
  293. return pRet;
  294. }
  295. template< class T >
  296. inline T* CClassMemoryPool<T>::AllocZero()
  297. {
  298. T *pRet;
  299. {
  300. MEM_ALLOC_CREDIT_CLASS();
  301. pRet = (T*)CUtlMemoryPool::AllocZero();
  302. }
  303. if ( pRet )
  304. {
  305. Construct( pRet );
  306. }
  307. return pRet;
  308. }
  309. template< class T >
  310. inline void CClassMemoryPool<T>::Free(T *pMem)
  311. {
  312. if ( pMem )
  313. {
  314. Destruct( pMem );
  315. }
  316. CUtlMemoryPool::Free( pMem );
  317. }
  318. template< class T >
  319. inline void CClassMemoryPool<T>::Clear()
  320. {
  321. CUtlRBTree<void *> freeBlocks;
  322. SetDefLessFunc( freeBlocks );
  323. void *pCurFree = m_pHeadOfFreeList;
  324. while ( pCurFree != NULL )
  325. {
  326. freeBlocks.Insert( pCurFree );
  327. pCurFree = *((void**)pCurFree);
  328. }
  329. for( CBlob *pCur=m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur=pCur->m_pNext )
  330. {
  331. T *p = (T *)pCur->m_Data;
  332. T *pLimit = (T *)(pCur->m_Data + pCur->m_NumBytes);
  333. while ( p < pLimit )
  334. {
  335. if ( freeBlocks.Find( p ) == freeBlocks.InvalidIndex() )
  336. {
  337. Destruct( p );
  338. }
  339. p++;
  340. }
  341. }
  342. CUtlMemoryPool::Clear();
  343. }
  344. //-----------------------------------------------------------------------------
  345. // Macros that make it simple to make a class use a fixed-size allocator
  346. // Put DECLARE_FIXEDSIZE_ALLOCATOR in the private section of a class,
  347. // Put DEFINE_FIXEDSIZE_ALLOCATOR in the CPP file
  348. //-----------------------------------------------------------------------------
  349. #define DECLARE_FIXEDSIZE_ALLOCATOR( _class ) \
  350. public: \
  351. inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \
  352. inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \
  353. inline void operator delete( void* p ) { s_Allocator.Free(p); } \
  354. inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { s_Allocator.Free(p); } \
  355. private: \
  356. static CUtlMemoryPool s_Allocator
  357. #define DEFINE_FIXEDSIZE_ALLOCATOR( _class, _initsize, _grow ) \
  358. CUtlMemoryPool _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool")
  359. #define DEFINE_FIXEDSIZE_ALLOCATOR_ALIGNED( _class, _initsize, _grow, _alignment ) \
  360. CUtlMemoryPool _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool", _alignment )
  361. #define DECLARE_FIXEDSIZE_ALLOCATOR_MT( _class ) \
  362. public: \
  363. inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \
  364. inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \
  365. inline void operator delete( void* p ) { s_Allocator.Free(p); } \
  366. inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { s_Allocator.Free(p); } \
  367. private: \
  368. static CMemoryPoolMT s_Allocator
  369. #define DEFINE_FIXEDSIZE_ALLOCATOR_MT( _class, _initsize, _grow ) \
  370. CMemoryPoolMT _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool")
  371. //-----------------------------------------------------------------------------
  372. // Macros that make it simple to make a class use a fixed-size allocator
  373. // This version allows us to use a memory pool which is externally defined...
  374. // Put DECLARE_FIXEDSIZE_ALLOCATOR_EXTERNAL in the private section of a class,
  375. // Put DEFINE_FIXEDSIZE_ALLOCATOR_EXTERNAL in the CPP file
  376. //-----------------------------------------------------------------------------
  377. #define DECLARE_FIXEDSIZE_ALLOCATOR_EXTERNAL( _class ) \
  378. public: \
  379. inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_pAllocator->Alloc(size); } \
  380. inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_pAllocator->Alloc(size); } \
  381. inline void operator delete( void* p ) { s_pAllocator->Free(p); } \
  382. private: \
  383. static CUtlMemoryPool* s_pAllocator
  384. #define DEFINE_FIXEDSIZE_ALLOCATOR_EXTERNAL( _class, _allocator ) \
  385. CUtlMemoryPool* _class::s_pAllocator = _allocator
  386. template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, bool GROWMODE, int COMPACT_THRESHOLD >
  387. inline CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, GROWMODE, COMPACT_THRESHOLD>::CAlignedMemPool()
  388. : m_pFirstFree( 0 ),
  389. m_nFree( 0 ),
  390. m_TimeLastCompact( 0 )
  391. {
  392. COMPILE_TIME_ASSERT( sizeof( FreeBlock_t ) >= BLOCK_SIZE );
  393. COMPILE_TIME_ASSERT( ALIGN_VALUE( sizeof( FreeBlock_t ), ALIGNMENT ) == sizeof( FreeBlock_t ) );
  394. }
  395. template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, bool GROWMODE, int COMPACT_THRESHOLD >
  396. inline void *CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, GROWMODE, COMPACT_THRESHOLD>::Alloc()
  397. {
  398. AUTO_LOCK( m_mutex );
  399. if ( !m_pFirstFree )
  400. {
  401. if ( !GROWMODE && m_Chunks.Count() )
  402. {
  403. return NULL;
  404. }
  405. FreeBlock_t *pNew = (FreeBlock_t *)m_Allocator.Alloc( CHUNK_SIZE );
  406. Assert( (unsigned)pNew % ALIGNMENT == 0 );
  407. m_Chunks.AddToTail( pNew );
  408. m_nFree = CHUNK_SIZE / BLOCK_SIZE;
  409. m_pFirstFree = pNew;
  410. for ( int i = 0; i < m_nFree - 1; i++ )
  411. {
  412. pNew->pNext = pNew + 1;
  413. pNew++;
  414. }
  415. pNew->pNext = NULL;
  416. }
  417. void *p = m_pFirstFree;
  418. m_pFirstFree = m_pFirstFree->pNext;
  419. m_nFree--;
  420. return p;
  421. }
  422. template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, bool GROWMODE, int COMPACT_THRESHOLD >
  423. inline void CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, GROWMODE, COMPACT_THRESHOLD>::Free( void *p )
  424. {
  425. AUTO_LOCK( m_mutex );
  426. // Insertion sort to encourage allocation clusters in chunks
  427. FreeBlock_t *pFree = ((FreeBlock_t *)p);
  428. FreeBlock_t *pCur = m_pFirstFree;
  429. FreeBlock_t *pPrev = NULL;
  430. while ( pCur && pFree > pCur )
  431. {
  432. pPrev = pCur;
  433. pCur = pCur->pNext;
  434. }
  435. pFree->pNext = pCur;
  436. if ( pPrev )
  437. {
  438. pPrev->pNext = pFree;
  439. }
  440. else
  441. {
  442. m_pFirstFree = pFree;
  443. }
  444. m_nFree++;
  445. if ( m_nFree >= ( CHUNK_SIZE / BLOCK_SIZE ) * COMPACT_THRESHOLD )
  446. {
  447. double time = Plat_FloatTime();
  448. double compactTime = ( m_nFree >= ( CHUNK_SIZE / BLOCK_SIZE ) * COMPACT_THRESHOLD * 4 ) ? 15.0 : 30.0;
  449. if ( m_TimeLastCompact > time || m_TimeLastCompact + compactTime < time )
  450. {
  451. Compact();
  452. m_TimeLastCompact = time;
  453. }
  454. }
  455. }
  456. template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, bool GROWMODE, int COMPACT_THRESHOLD >
  457. inline int __cdecl CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, GROWMODE, COMPACT_THRESHOLD>::CompareChunk( void * const *ppLeft, void * const *ppRight )
  458. {
  459. return ((unsigned)*ppLeft) - ((unsigned)*ppRight);
  460. }
  461. template <int ITEM_SIZE, int ALIGNMENT, int CHUNK_SIZE, class CAllocator, bool GROWMODE, int COMPACT_THRESHOLD >
  462. inline void CAlignedMemPool<ITEM_SIZE, ALIGNMENT, CHUNK_SIZE, CAllocator, GROWMODE, COMPACT_THRESHOLD>::Compact()
  463. {
  464. FreeBlock_t *pCur = m_pFirstFree;
  465. FreeBlock_t *pPrev = NULL;
  466. m_Chunks.Sort( CompareChunk );
  467. #ifdef VALIDATE_ALIGNED_MEM_POOL
  468. {
  469. FreeBlock_t *p = m_pFirstFree;
  470. while ( p )
  471. {
  472. if ( p->pNext && p > p->pNext )
  473. {
  474. __asm { int 3 }
  475. }
  476. p = p->pNext;
  477. }
  478. for ( int i = 0; i < m_Chunks.Count(); i++ )
  479. {
  480. if ( i + 1 < m_Chunks.Count() )
  481. {
  482. if ( m_Chunks[i] > m_Chunks[i + 1] )
  483. {
  484. __asm { int 3 }
  485. }
  486. }
  487. }
  488. }
  489. #endif
  490. int i;
  491. for ( i = 0; i < m_Chunks.Count(); i++ )
  492. {
  493. int nBlocksPerChunk = CHUNK_SIZE / BLOCK_SIZE;
  494. FreeBlock_t *pChunkLimit = ((FreeBlock_t *)m_Chunks[i]) + nBlocksPerChunk;
  495. int nFromChunk = 0;
  496. if ( pCur == m_Chunks[i] )
  497. {
  498. FreeBlock_t *pFirst = pCur;
  499. while ( pCur && pCur >= m_Chunks[i] && pCur < pChunkLimit )
  500. {
  501. pCur = pCur->pNext;
  502. nFromChunk++;
  503. }
  504. pCur = pFirst;
  505. }
  506. while ( pCur && pCur >= m_Chunks[i] && pCur < pChunkLimit )
  507. {
  508. if ( nFromChunk != nBlocksPerChunk )
  509. {
  510. if ( pPrev )
  511. {
  512. pPrev->pNext = pCur;
  513. }
  514. else
  515. {
  516. m_pFirstFree = pCur;
  517. }
  518. pPrev = pCur;
  519. }
  520. else if ( pPrev )
  521. {
  522. pPrev->pNext = NULL;
  523. }
  524. else
  525. {
  526. m_pFirstFree = NULL;
  527. }
  528. pCur = pCur->pNext;
  529. }
  530. if ( nFromChunk == nBlocksPerChunk )
  531. {
  532. m_Allocator.Free( m_Chunks[i] );
  533. m_nFree -= nBlocksPerChunk;
  534. m_Chunks[i] = 0;
  535. }
  536. }
  537. for ( i = m_Chunks.Count() - 1; i >= 0 ; i-- )
  538. {
  539. if ( !m_Chunks[i] )
  540. {
  541. m_Chunks.FastRemove( i );
  542. }
  543. }
  544. }
  545. #endif // MEMPOOL_H