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.

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