/** * @file XTPCustomHeap.h * * @copyright * (c) 1998-2025 Codejock Software, All Rights Reserved. * * This source file is the property of Codejock Software and must not be * redistributed by any means without the explicit written permission of * Codejock Software. * * The use of this source code is governed by the terms and conditions specified * in the Toolkit Pro license agreement. Codejock Software grants you, as a * single software developer, the limited right to use this software on one * computer only. * * Contact Information: * support@codejock.com * http://www.codejock.com * */ /** @cond */ #if !defined(_XTPCUSTOMHEAP_H__) # define _XTPCUSTOMHEAP_H__ # if _MSC_VER > 1000 # pragma once # endif // _MSC_VER > 1000 /** @endcond */ # include "Common/Base/Diagnostic/XTPDisableNoisyWarnings.h" /** @cond */ # define XTP_EXPORT_PARAMS_NO typedef enum _XTP_HEAP_INFORMATION_CLASS { xtpHeapCompatibilityInformation } XTP_HEAP_INFORMATION_CLASS; /** @endcond */ /** * @brief * This function enables features for a specified heap. * @param hHeapHandle [in] Handle to the heap where information is to be set. * This handle is returned by either the HeapCreate or * GetProcessHeap function. * * @param pHeapInformation [in] Heap information buffer. The format of this data * depends on the HeapCompatibilityInformation class. * * @param nHeapInformationLength [in] Size of the pHeapInformation buffer, in bytes. * @details * This function is a wrapper to HeapSetInformation windows API function. * If HeapSetInformation is not supported by windows, this function exits * without any actions. * @return * If the function succeeds, the return value is nonzero * If the function fails, the return value is 0 (zero). To get extended * error information, call GetLastError. * If HeapSetInformation is not supported by windows return value is also TRUE. * @see * HeapSetInformation, XTPHeapSetLowFragmentation */ _XTP_EXT_CLASS BOOL AFX_CDECL XTPHeapSetCompatibilityInformation(HANDLE hHeapHandle, PVOID pHeapInformation, ULONG_PTR nHeapInformationLength); /** * @brief * Call this member to enable "Low-fragmentation Heap" (LFH) feature for * a given heap. * @param hHeapHandle [in] Handle to the heap where information is to be set. * This handle is returned by either the HeapCreate or * GetProcessHeap function. * @details * This function is a wrapper to HeapSetInformation windows API function. * If HeapSetInformation is not supported by windows, this function exits * without any actions. * @return * If the function succeeds, the return value is nonzero * If the function fails, the return value is 0 (zero). To get extended * error information, call GetLastError. * If HeapSetInformation is not supported by windows return value is also TRUE. * @see * HeapSetInformation, XTPHeapSetCompatibilityInformation */ _XTP_EXT_CLASS BOOL AFX_CDECL XTPHeapSetLowFragmentation(HANDLE hHeapHandle); /** * @brief * This template class used as a base class for allocators which can use * a custom (separate) heap. Each allocator use own heap or standard heap. * To use custom heap set ms_bUseCustomHeap member to TRUE. * @param _TData This class must contain following static members for allocator: * * struct allocatorClassData * { * static HANDLE ms_hCustomHeap; // handle to the custom heap * static LONG ms_dwRefs; // allocated blocks count; * static BOOL ms_bLFHEnabled; // report is LFH enabled for custom heap; * static BOOL ms_bUseCustomHeap; // Define does allocator use custom heap or * default heap; * }; * @see * XTP_DECLARE_HEAP_ALLOCATOR, XTP_IMPLEMENT_HEAP_ALLOCATOR, * CXTPHeapObjectT */ template class CXTPHeapAllocatorT : public _TData { public: /** @cond */ typedef _TData TData; using _TData::ms_bLFHEnabled; using _TData::ms_bUseCustomHeap; using _TData::ms_dwRefs; using _TData::ms_hCustomHeap; /** @endcond */ // Allocator Interface /** * @brief * Allocate memory block of nBytes size. * @param nBytes An integer that specifies the number of bytes to be read or written. * @return * A pointer to allocated block or NULL. * @see * Free_mem */ static void* AFX_CDECL Alloc_mem(size_t nBytes) { InterlockedIncrement(&ms_dwRefs); // ms_dwRefs++; if (ms_bUseCustomHeap) { CreateHeapIfNeed(); return ::HeapAlloc(ms_hCustomHeap, 0, nBytes); } else { # ifdef _DEBUG return DEBUG_NEW char[nBytes]; # else return new char[nBytes]; # endif } }; /** * @brief * Free memory block previously allocated by Alloc_mem. * @param p [in] Pointer to a memory block. * @return * @see * Alloc_mem */ static void AFX_CDECL Free_mem(void* p) { LONG nRefs = 0; if (ms_dwRefs) nRefs = InterlockedDecrement(&ms_dwRefs); // ms_dwRefs--; if (ms_bUseCustomHeap) { VERIFY(::HeapFree(ms_hCustomHeap, 0, p)); if (nRefs == 0) ClearHeap(); } else { delete p; } }; // Implementation /** @cond */ CXTPHeapAllocatorT(){}; virtual ~CXTPHeapAllocatorT() { // When other static objects use this allocator, // they may be destroyed later (and free memory later) // Each static object, which use this allocator, // should check ms_dwRefs and destroy heap if no more refs. if (ms_dwRefs == 0) ClearHeap(); }; static void AFX_CDECL CreateHeapIfNeed() { if (!ms_hCustomHeap) { ms_hCustomHeap = ::HeapCreate(0, 0, 0); _ASSERTE(ms_hCustomHeap); ms_bLFHEnabled = XTPHeapSetLowFragmentation(ms_hCustomHeap); } } static void AFX_CDECL ClearHeap() { _ASSERTE(ms_dwRefs == 0); if (ms_hCustomHeap) VERIFY(::HeapDestroy(ms_hCustomHeap)); ms_hCustomHeap = NULL; } /** @endcond */ }; /** @cond */ # define XTP_DECLARE_HEAP_ALLOCATOR_(allocatorClass, EXPORT_PARAMS) \ struct EXPORT_PARAMS allocatorClass##Data \ { \ static HANDLE ms_hCustomHeap; \ static LONG ms_dwRefs; \ static BOOL ms_bLFHEnabled; \ static BOOL ms_bUseCustomHeap; \ }; \ class EXPORT_PARAMS allocatorClass : public CXTPHeapAllocatorT \ { \ }; /** @endcond */ /** * @brief * This macros used to declare allocator class derived from CXTPHeapAllocatorT * which can use default or custom (separate) heap. * Such allocator can be used as parameter for CXTPHeapObjectT template or * for other cases. * @param allocatorClass [in] Name of the allocator class. * @details * Used together with XTP_IMPLEMENT_HEAP_ALLOCATOR macro. * * Example: *
 *
 * // probably in header (*.h) file:
 * XTP_DECLARE_HEAP_ALLOCATOR(CXTPGridRowAllocator)
 *
 * // in implementation (*.cpp) file:
 * XTP_IMPLEMENT_HEAP_ALLOCATOR(CXTPGridRowAllocator)
 *
 * 
* @see * XTP_IMPLEMENT_HEAP_ALLOCATOR, CXTPHeapAllocatorT, CXTPHeapObjectT */ # define XTP_DECLARE_HEAP_ALLOCATOR(allocatorClass) \ XTP_DECLARE_HEAP_ALLOCATOR_(allocatorClass, XTP_EXPORT_PARAMS_NO) /** * @brief * This macros used to implement allocator class previously declared by * XTP_DECLARE_HEAP_ALLOCATOR macro. * Such allocator can be used as parameter for CXTPHeapObjectT template or * for other cases. * @param allocatorClass [in] Name of the allocator class. * @param bUseCustomHeap [in] Set as TRUE to enable custom heap by default for this allocator and * FALSE to disable. * @details * Used together with XTP_DECLARE_HEAP_ALLOCATOR macro. * * Example: *
 *
 * // probably in header (*.h) file:
 * XTP_DECLARE_HEAP_ALLOCATOR(CXTPGridRowAllocator)
 *
 * // in implementation (*.cpp) file:
 * XTP_IMPLEMENT_HEAP_ALLOCATOR(CXTPGridRowAllocator)
 *
 * 
* @see * XTP_DECLARE_HEAP_ALLOCATOR, CXTPHeapAllocatorT, CXTPHeapObjectT */ # define XTP_IMPLEMENT_HEAP_ALLOCATOR(allocatorClass, bUseCustomHeap) \ HANDLE allocatorClass##Data::ms_hCustomHeap = NULL; \ LONG allocatorClass##Data::ms_dwRefs = 0; \ BOOL allocatorClass##Data::ms_bLFHEnabled = FALSE; \ BOOL allocatorClass##Data::ms_bUseCustomHeap = bUseCustomHeap; \ allocatorClass g_obj##allocatorClass; /** * @brief * This template class used as a helper class to override new/delete * operators and use custom heap allocators inside them. * @param _TObject A base class; * @param _TAllocator An allocator class name; * * * Example: *
 * // Allocator must be declared (and implemented)
 * // probably in header (*.h) file:
 * XTP_DECLARE_HEAP_ALLOCATOR(CMyCustomHeapAllocator)
 *
 * // in implementation (*.cpp) file:
 * XTP_IMPLEMENT_HEAP_ALLOCATOR(CMyCustomHeapAllocator, TRUE)
 *
 * // To enable custom heap allocations use second parameter in
 * // XTP_IMPLEMENT_HEAP_ALLOCATOR macro or set corresponding flag
 * // on initialization, before any allocations:
 * //
 * CMyCustomHeapAllocator::ms_bUseCustomHeap = TRUE;
 *
 * // One way to use:
 * class CMyClass : public CXTPHeapObjectT
 * {
 *     // ...
 * };
 * CMyClass* pMyClassObj = new CMyClass();
 *
 *
 * // Other way to use:
 * class CMyClass : public CMyClassBase
 * {
 *     // ...
 * };
 *
 * class CMyClass_heap : public CXTPHeapObjectT
 * {
 *     // ...
 * };
 * CMyClass* pMyClassObj = new CMyClass_heap();
 * 
* * @see * XTP_DECLARE_HEAP_ALLOCATOR, XTP_IMPLEMENT_HEAP_ALLOCATOR, */ template class CXTPHeapObjectT : public _TObject { public: /** @cond */ typedef _TObject TObject; typedef _TAllocator TAllocator; /** @endcond */ /** * @brief * Allocate memory block of nSize bytes. * @param nSize Requested block size. * @return * A pointer to allocated block or NULL. * @see * operator delete */ void* PASCAL operator new(size_t nSize) { if (TAllocator::ms_bUseCustomHeap) return TAllocator::Alloc_mem(nSize); else { InterlockedIncrement(&TAllocator::ms_dwRefs); // TAllocator::ms_dwRefs++; return ::operator new(nSize); } } /** * @brief * Free memory block previously allocated by operator new. * @param p [in] Pointer to a memory block. * @return * @see * operator new */ void PASCAL operator delete(void* p) { if (TAllocator::ms_bUseCustomHeap) TAllocator::Free_mem(p); else { InterlockedDecrement(&TAllocator::ms_dwRefs); // TAllocator::ms_dwRefs--; ::operator delete(p); } } /** @cond */ // void* PASCAL operator new(size_t, void* p) {return p;} // default is fine as is # if _MSC_VER >= 1200 void PASCAL operator delete(void* p, void* pPlace) { if (TAllocator::ms_bUseCustomHeap) operator delete(p); else { InterlockedDecrement(&TAllocator::ms_dwRefs); // TAllocator::ms_dwRefs--; ::operator delete(p, pPlace); } } # endif # if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT) // for file name/line number tracking using DEBUG_NEW void* PASCAL operator new(size_t nSize, LPCSTR lpszFileName, int nLine) { if (TAllocator::ms_bUseCustomHeap) return operator new(nSize); else { InterlockedIncrement(&TAllocator::ms_dwRefs); // TAllocator::ms_dwRefs++; return ::operator new(nSize, lpszFileName, nLine); } } # if _MSC_VER >= 1200 void PASCAL operator delete(void* p, LPCSTR lpszFileName, int nLine) { if (TAllocator::ms_bUseCustomHeap) operator delete(p); else { InterlockedDecrement(&TAllocator::ms_dwRefs); // TAllocator::ms_dwRefs--; ::operator delete(p, lpszFileName, nLine); } } # endif # endif /** @endcond */ }; /** * @brief * This class used to store strings. It automatically allocate/deallocate * memory for string data. * @details * Override _AllocStringData, _FreeStringData to change default memory allocation. * @see * CXTPHeapStringT, XTP_DECLARE_HEAP_ALLOCATOR, CXTPHeapAllocatorT */ class _XTP_EXT_CLASS CXTPHeapString { public: /** * @brief * Default object constructor. * @see * ~CXTPHeapString */ CXTPHeapString(); /** * @brief * Object constructor. * @param pcszString [in] Pointer to a source string. * @see * ~CXTPHeapString */ CXTPHeapString(LPCTSTR pcszString); /** * @brief * Default object destructor. * @see * CXTPHeapString */ virtual ~CXTPHeapString(); /** * @brief * Use this function to determine is string empty. * @return * TRUE if stored string is empty, FALSE otherwise. */ virtual BOOL IsEmpty() const; /** * @brief * Assign new string value. * @param pcszString [in] Pointer to a source string. * @return * Stored string value. */ LPCTSTR operator=(LPCTSTR pcszString); /** * @brief * Determine are strings equal. * @param pcszString [in] Pointer to a string to compare. * @return * TRUE if strings are equal, FALSE otherwise. */ BOOL operator==(LPCTSTR pcszString) const; /** * @brief * Determine are strings different. * @param pcszString [in] Pointer to a string to compare. * @return * TRUE if strings are different, FALSE otherwise. */ BOOL operator!=(LPCTSTR pcszString) const; /** * @brief * operator LPCTSTR. * @return * Pointer to a stored string. */ operator LPCTSTR() const; /** * @brief * operator CString. * @return * A CString object which contains stored string. */ operator CString() const; /** * @brief * Allocates a BSTR from stored string data. * @return * The newly allocated string. */ virtual BSTR AllocSysString() const; /** * @brief * Use this method to set new string value. * @param pcszString [in] Pointer to a source string. */ virtual void SetString(LPCTSTR pcszString); protected: /** * @brief * This method is used to allocate string data buffer. * @param nNewStrLen [in] New string length without null character. */ virtual void _AllocStringData(int nNewStrLen); /** * @brief * This method is used to free string data buffer previously allocated * in _AllocStringData. */ virtual void _FreeStringData(); int m_nStrLen; /**< Stored string length (without null character). */ LPTSTR m_pcszString; /**< Pointer to a stored string (or NULL). */ }; /** * @brief * This template used to store strings using different allocators. * @details * String data stored in default or separate (custom) heap. * Specially useful for VC 6.0 instead of CString, because * CString Release implementation allocates data using some cache * and data is not deallocated when CString destroyed. * @see * CXTPHeapString, XTP_DECLARE_HEAP_ALLOCATOR, CXTPHeapAllocatorT */ template class CXTPHeapStringT : public CXTPHeapString { public: /** @cond */ typedef _TAllocator TAllocator; /** @endcond */ /** * @brief * Default object constructor. * @param pcszString [in] Pointer to a source string. * @see * ~CXTPHeapStringT */ CXTPHeapStringT(LPCTSTR pcszString = _T("")) { SetString(pcszString); } /** * @brief * Default object destructor. * @see * CXTPHeapString */ virtual ~CXTPHeapStringT() { _FreeStringData(); } /** * @brief * Assign new string value. * @param pcszString [in] Pointer to a source string. * @return * Stored string value. */ LPCTSTR operator=(LPCTSTR pcszString) { SetString(pcszString); return m_pcszString ? m_pcszString : _T(""); } /** * @brief * Determine are strings equal. * @param pcszString [in] Pointer to a string to compare. * @return * TRUE if strings are equal, FALSE otherwise. */ BOOL operator==(LPCTSTR pcszString) const { return 0 == _tcscmp((LPCTSTR) * this, pcszString); } /** * @brief * Determine are strings different. * @param pcszString [in] Pointer to a string to compare. * @return * TRUE if strings are different, FALSE otherwise. */ BOOL operator!=(LPCTSTR pcszString) const { return 0 != _tcscmp((LPCTSTR) * this, pcszString); } /** * @brief * operator LPCTSTR. * @return * Pointer to a stored string. */ operator LPCTSTR() const { return m_pcszString ? m_pcszString : _T(""); } /** * @brief * operator CString. * @return * A CString object which contains stored string. */ operator CString() const { return CString(m_pcszString ? m_pcszString : _T("")); } protected: /** * @brief * This method is used to allocate string data buffer. * @param nNewStrLen [in] New string length without null character. */ virtual void _AllocStringData(int nNewStrLen) { _ASSERTE(m_pcszString == NULL); m_nStrLen = nNewStrLen; m_pcszString = (LPTSTR)TAllocator::Alloc_mem((nNewStrLen + 1) * sizeof(TCHAR)); } /** * @brief * This method is used to free string data buffer previously allocated * in _AllocStringData. */ virtual void _FreeStringData() { if (m_pcszString) TAllocator::Free_mem(m_pcszString); m_pcszString = NULL; m_nStrLen = 0; } }; /** @cond */ struct XTP_BATCHALLOC_OBJ_HEADER; struct XTP_BATCHALLOC_BLOCK_HEADER { LONG m_dwRefs; # ifdef _DEBUG DWORD m_dwObjCount; DWORD m_dwObjSize; // for debug # endif XTP_BATCHALLOC_OBJ_HEADER* pFreeList; XTP_BATCHALLOC_BLOCK_HEADER* pPrev; XTP_BATCHALLOC_BLOCK_HEADER* pNext; }; struct XTP_BATCHALLOC_OBJ_HEADER { XTP_BATCHALLOC_BLOCK_HEADER* pBlockHeader; XTP_BATCHALLOC_OBJ_HEADER* pNextFree; void* GetData() { BYTE* pData = (BYTE*)this; pData += sizeof(XTP_BATCHALLOC_OBJ_HEADER); return pData; } static XTP_BATCHALLOC_OBJ_HEADER* AFX_CDECL GetHeader(void* pData) { BYTE* pBlockData = (BYTE*)pData; pBlockData -= sizeof(XTP_BATCHALLOC_OBJ_HEADER); return (XTP_BATCHALLOC_OBJ_HEADER*)pBlockData; } }; template class CXTPBatchAllocManagerT : public _TBatchAllocData { using _TBatchAllocData::m_bDestroyEmptyBlocksOnFree; using _TBatchAllocData::m_bDestroyLastEmptyBlockOnFree; using _TBatchAllocData::m_dwAllocatedObjects; using _TBatchAllocData::m_nBlockSize; using _TBatchAllocData::m_pBusyBlocks; using _TBatchAllocData::m_pFreeBlocks; public: typedef _TBatchAllocData TBatchAllocData; typedef _TAllocator TAllocator; virtual ~CXTPBatchAllocManagerT() { // all data must be deallocated before _ASSERTE(m_pBusyBlocks == NULL); FreeExtraData(); _ASSERTE(m_pFreeBlocks == NULL); // This static object destructor may be called later than allocator destructor. if (TAllocator::ms_dwRefs == 0) TAllocator::ClearHeap(); } AFX_INLINE static int AFX_CDECL _Round4(int nSize) { int nDiv = nSize / 4; int nMod = nSize % 4; int nSizeQ4 = (nDiv + (nMod ? 1 : 0)) * 4; return nSizeQ4; } static void* AFX_CDECL AllocData(size_t nSize) { if (!m_pFreeBlocks) { int nObjSize = _Round4(XTPToIntChecked(nSize) + XTPToIntChecked(sizeof(XTP_BATCHALLOC_OBJ_HEADER))); int nBlockDataSize = XTPToIntChecked(sizeof(XTP_BATCHALLOC_BLOCK_HEADER) + m_nBlockSize * nObjSize); BYTE* pBlockData = (BYTE*)_TAllocator::Alloc_mem(XTPToSizeTChecked(nBlockDataSize)); if (!pBlockData) return NULL; XTP_BATCHALLOC_BLOCK_HEADER* pBlockHdr = (XTP_BATCHALLOC_BLOCK_HEADER*)pBlockData; ZeroMemory(pBlockHdr, sizeof(XTP_BATCHALLOC_BLOCK_HEADER)); pBlockHdr->m_dwRefs = 0; # ifdef _DEBUG pBlockHdr->m_dwObjCount = XTPToDWORDChecked(m_nBlockSize); pBlockHdr->m_dwObjSize = (DWORD)nSize; # endif //--------- m_pFreeBlocks = pBlockHdr; XTP_BATCHALLOC_OBJ_HEADER* pObjHdr = NULL; pBlockData = pBlockData + sizeof(XTP_BATCHALLOC_BLOCK_HEADER); pBlockHdr->pFreeList = (XTP_BATCHALLOC_OBJ_HEADER*)pBlockData; for (int i = 0; i < m_nBlockSize; i++) { pObjHdr = (XTP_BATCHALLOC_OBJ_HEADER*)pBlockData; // ZeroMemory(pObjHdr, sizeof(XTP_BATCHALLOC_OBJ_HEADER)); pBlockData += nObjSize; pObjHdr->pBlockHeader = pBlockHdr; pObjHdr->pNextFree = (XTP_BATCHALLOC_OBJ_HEADER*)pBlockData; } if (pObjHdr) pObjHdr->pNextFree = NULL; } _ASSERTE(m_pFreeBlocks && m_pFreeBlocks->pFreeList); if (!m_pFreeBlocks || !m_pFreeBlocks->pFreeList) return NULL; _ASSERTE(m_pFreeBlocks->m_dwObjSize == (DWORD)nSize); void* pData = m_pFreeBlocks->pFreeList->GetData(); m_pFreeBlocks->m_dwRefs++; m_dwAllocatedObjects++; XTP_BATCHALLOC_OBJ_HEADER* pNextFree = m_pFreeBlocks->pFreeList->pNextFree; m_pFreeBlocks->pFreeList->pNextFree = NULL; m_pFreeBlocks->pFreeList = pNextFree; // no more free objects, move to busy blocks if (!pNextFree) { XTP_BATCHALLOC_BLOCK_HEADER* pNewBusyBlock = m_pFreeBlocks; m_pFreeBlocks = m_pFreeBlocks->pPrev ? m_pFreeBlocks->pPrev : m_pFreeBlocks->pNext; if (m_pFreeBlocks) m_pFreeBlocks->pPrev = pNewBusyBlock->pPrev; pNewBusyBlock->pNext = m_pBusyBlocks; pNewBusyBlock->pPrev = m_pBusyBlocks ? m_pBusyBlocks->pPrev : NULL; if (m_pBusyBlocks) m_pBusyBlocks->pPrev = pNewBusyBlock; m_pBusyBlocks = pNewBusyBlock; } return pData; } static void AFX_CDECL FreeData(void* pObj) { if (!pObj) return; XTP_BATCHALLOC_OBJ_HEADER* pObjHdr = XTP_BATCHALLOC_OBJ_HEADER::GetHeader(pObj); pObjHdr->pNextFree = pObjHdr->pBlockHeader->pFreeList; pObjHdr->pBlockHeader->pFreeList = pObjHdr; pObjHdr->pBlockHeader->m_dwRefs--; m_dwAllocatedObjects--; // was busy block, move to free list if (pObjHdr->pNextFree == NULL) { XTP_BATCHALLOC_BLOCK_HEADER* pNewFreeBlock = pObjHdr->pBlockHeader; if (pNewFreeBlock->pPrev) pNewFreeBlock->pPrev->pNext = pNewFreeBlock->pNext; if (pNewFreeBlock->pNext) pNewFreeBlock->pNext->pPrev = pNewFreeBlock->pPrev; if (m_pBusyBlocks == pNewFreeBlock) m_pBusyBlocks = pNewFreeBlock->pPrev ? pNewFreeBlock->pPrev : pNewFreeBlock->pNext; pNewFreeBlock->pNext = m_pFreeBlocks; pNewFreeBlock->pPrev = m_pFreeBlocks ? m_pFreeBlocks->pPrev : NULL; if (m_pFreeBlocks) m_pFreeBlocks->pPrev = pNewFreeBlock; m_pFreeBlocks = pNewFreeBlock; } // block is totally free, destroy block if (pObjHdr->pBlockHeader->m_dwRefs == 0 && m_bDestroyEmptyBlocksOnFree) { XTP_BATCHALLOC_BLOCK_HEADER* pEmptyBlock = pObjHdr->pBlockHeader; XTP_BATCHALLOC_BLOCK_HEADER* pIsNextFree = pEmptyBlock->pPrev ? pEmptyBlock->pPrev : pEmptyBlock->pNext; BOOL bLast = (m_pFreeBlocks == pEmptyBlock) && pIsNextFree; // do not destroy last free block if (!bLast || m_bDestroyLastEmptyBlockOnFree) { if (pEmptyBlock->pPrev) pEmptyBlock->pPrev->pNext = pEmptyBlock->pNext; if (pEmptyBlock->pNext) pEmptyBlock->pNext->pPrev = pEmptyBlock->pPrev; if (m_pFreeBlocks == pEmptyBlock) m_pFreeBlocks = pEmptyBlock->pPrev ? pEmptyBlock->pPrev : pEmptyBlock->pNext; _TAllocator::Free_mem(pEmptyBlock); } } // if (m_dwAllocatedObjects == 0) // FreeExtraData(); } public: static void AFX_CDECL FreeExtraData() { XTP_BATCHALLOC_BLOCK_HEADER* pBlock = m_pFreeBlocks; while (pBlock && pBlock->pPrev) { pBlock = pBlock->pPrev; } while (pBlock) { // block is totally free, destroy block if (pBlock->m_dwRefs == 0) { XTP_BATCHALLOC_BLOCK_HEADER* pEmptyBlock = pBlock; if (pEmptyBlock->pPrev) pEmptyBlock->pPrev->pNext = pEmptyBlock->pNext; if (pEmptyBlock->pNext) pEmptyBlock->pNext->pPrev = pEmptyBlock->pPrev; if (m_pFreeBlocks == pEmptyBlock) m_pFreeBlocks = pEmptyBlock->pPrev ? pEmptyBlock->pPrev : pEmptyBlock->pNext; pBlock = pBlock->pNext; TAllocator::Free_mem(pEmptyBlock); } else { pBlock = pBlock->pNext; } } } }; /** @endcond */ /** * @brief * This template class used as a helper class to override new/delete * operators and use batch allocation inside them. * Batch allocation means that memory allocated not for one object only, * but for many objects at one time (for 1024 objects by default). * Next allocations take memory from this big block. New blocks allocated * when necessary. This increase performance and reduce heap fragmentation. * Batch allocation mechanism responsible for allocation/deallocation * blocks of memory from heap and internally organize free/busy lists of * memory pieces. When object deleted, its memory stored in free list and * used for new objects. * When all memory pieces from block free, it may be deallocated from * heap automatically (this depends on options in _TBatchAllocData) * or by FreeExtraData call, * * @param _TObject A base class; * @param _TAllocator An allocator class name; by default _TObject::TAllocator is used. * @param _TBatchAllocData This class must contain following static members for allocator: * * struct BatchAllocData * { * static BOOL m_bEnableBatchAllocation; // Define is Batch * Allocation enabled; static LONG m_dwAllocatedObjects; // * allocated blocks count; * * @param static BOOL m_bDestroyEmptyBlocksOnFree; // if TRUE completely * free blocks will be deallocated on free objects, otherwise they will * stay in free list. static BOOL m_bDestroyLastEmptyBlockOnFree; // if * @param TRUE last completely free block will be deallocated on free * objects, otherwise it will stay in free list. * * static int m_nBlockSize; // Count of objects in * block. * protected: * static XTP_BATCHALLOC_BLOCK_HEADER* m_pFreeBlocks; // List of * blocks which have free pieces. static XTP_BATCHALLOC_BLOCK_HEADER* * m_pBusyBlocks; // List of blocks which have not free pieces. * }; * * * Example: *
 *
 * // Batch data must be declared (and implemented)
 * // probably in header (*.h) file:
 * XTP_DECLARE_BATCH_ALLOC_OBJ_DATA(CBatchGridRecord_Data);
 * class CBatchGridRecord : public CXTPBatchAllocObjT
 * {
 *    // ...
 * };
 *
 * // in implementation (*.cpp) file:
 * XTP_IMPLEMENT_BATCH_ALLOC_OBJ_DATA(CBatchGridRecord_Data, CBatchGridRecord, TRUE);
 *
 * // To enable Batch allocations use second parameter in
 * // XTP_IMPLEMENT_BATCH_ALLOC_OBJ_DATA macro or set corresponding flag
 * // on initialization, before any allocations:
 * //
 * CMyCustomHeapAllocator::ms_bUseCustomHeap = TRUE;
 *
 * // How to use:
 * CBatchGridRecord* pMyClassObj = new CBatchGridRecord();
 *
 * 
* * @see * XTP_DECLARE_BATCH_ALLOC_OBJ_DATA, XTP_IMPLEMENT_BATCH_ALLOC_OBJ_DATA, */ template class CXTPBatchAllocObjT : public _TObject { public: /** @cond */ typedef _TObject TObject; typedef _TBatchAllocData TBatchAllocData; typedef _TAllocator TAllocator; typedef CXTPBatchAllocManagerT<_TAllocator, _TBatchAllocData> TBlockMan; /** @endcond */ public: /** * @brief * This member function check all blocks and deallocate which are * completely free. * @return * @see * _TBatchAllocData */ static void AFX_CDECL FreeExtraData() { TBlockMan::FreeExtraData(); } /** * @brief * Allocate memory block of nSize bytes. * @param nSize Requested block size. * @return * A pointer to allocated block or NULL. * @see * operator delete */ void* PASCAL operator new(size_t nSize) { if (TBlockMan::m_bBatchAllocationEnabled) return TBlockMan::AllocData(nSize); else return _TObject::operator new(nSize); } /** * @brief * Free memory block previously allocated by operator new. * @param p [in] Pointer to a memory block. * @return * @see * operator new */ void PASCAL operator delete(void* p) { if (TBlockMan::m_bBatchAllocationEnabled) TBlockMan::FreeData(p); else _TObject::operator delete(p); } /** @cond */ // void* PASCAL operator new(size_t, void* p) {return p; /**< } // default is fine as is */ # if _MSC_VER >= 1200 void PASCAL operator delete(void* p, void* pPlace) { if (TBlockMan::m_bBatchAllocationEnabled) TBlockMan::FreeData(p); else _TObject::operator delete(p, pPlace); } # endif # if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT) // for file name/line number tracking using DEBUG_NEW void* PASCAL operator new(size_t nSize, LPCSTR lpszFileName, int nLine) { if (TBlockMan::m_bBatchAllocationEnabled) return operator new(nSize); else return _TObject::operator new(nSize, lpszFileName, nLine); } # if _MSC_VER >= 1200 void PASCAL operator delete(void* p, LPCSTR lpszFileName, int nLine) { if (TBlockMan::m_bBatchAllocationEnabled) operator delete(p); else _TObject::operator delete(p, lpszFileName, nLine); } # endif # endif /** @endcond */ }; /** @cond */ # define XTP_DECLARE_BATCH_ALLOC_OBJ_DATA_(dataClass, EXPORT_PARAMS) \ struct EXPORT_PARAMS dataClass \ { \ static BOOL m_bBatchAllocationEnabled; \ static LONG m_dwAllocatedObjects; \ static BOOL m_bDestroyEmptyBlocksOnFree; \ static BOOL m_bDestroyLastEmptyBlockOnFree; \ static int m_nBlockSize; \ \ static BOOL AFX_CDECL IsDataEmpty() \ { \ return !m_pFreeBlocks && !m_pBusyBlocks; \ }; \ \ protected: \ static XTP_BATCHALLOC_BLOCK_HEADER* m_pFreeBlocks; \ static XTP_BATCHALLOC_BLOCK_HEADER* m_pBusyBlocks; \ }; /** @endcond */ /** * @brief * This macros used to declare Batch allocation data which used with * CXTPBatchAllocObjT template. * @param dataClass [in] Name of the Batch allocation data class. * @details * Used together with XTP_IMPLEMENT_BATCH_ALLOC_OBJ_DATA macro. * * Example: *
 *
 * XTP_DECLARE_BATCH_ALLOC_OBJ_DATA(CBatchGridRecord_Data);
 * class CBatchGridRecord : public CXTPBatchAllocObjT
 * {
 *    // ...
 * };
 *
 * // in implementation (*.cpp) file:
 * XTP_IMPLEMENT_BATCH_ALLOC_OBJ_DATA(CBatchGridRecord_Data, CBatchGridRecord, TRUE);
 *
 * 
* @see * XTP_IMPLEMENT_HEAP_ALLOCATOR, CXTPHeapAllocatorT, CXTPBatchAllocObjT */ # define XTP_DECLARE_BATCH_ALLOC_OBJ_DATA(dataClass) \ XTP_DECLARE_BATCH_ALLOC_OBJ_DATA_(dataClass, XTP_EXPORT_PARAMS_NO) /** * @brief * This macros used to declare Batch allocation data which used with * CXTPBatchAllocObjT template. * @param dataClass [in] Name of the Batch allocation data class. * @param objClass [in] Name of the object class for Batch allocation. * @param batchAllocEnabled [in] Set as TRUE to enable Batch allocation by default for this object * and FALSE to disable. * @details * Used together with XTP_DECLARE_BATCH_ALLOC_OBJ_DATA macro. * * Example: *
 *
 * XTP_DECLARE_BATCH_ALLOC_OBJ_DATA(CBatchGridRecord_Data);
 * class CBatchGridRecord : public CXTPBatchAllocObjT
 * {
 *    // ...
 * };
 *
 * // in implementation (*.cpp) file:
 * XTP_IMPLEMENT_BATCH_ALLOC_OBJ_DATA(CBatchGridRecord_Data, CBatchGridRecord, TRUE);
 *
 * 
* @see * XTP_DECLARE_BATCH_ALLOC_OBJ_DATA, CXTPHeapAllocatorT, CXTPBatchAllocObjT */ # define XTP_IMPLEMENT_BATCH_ALLOC_OBJ_DATA(dataClass, objClass, batchAllocEnabled) \ BOOL dataClass::m_bBatchAllocationEnabled = batchAllocEnabled; \ LONG dataClass::m_dwAllocatedObjects = 0; \ int dataClass::m_nBlockSize = 1024; \ BOOL dataClass::m_bDestroyEmptyBlocksOnFree = FALSE; \ BOOL dataClass::m_bDestroyLastEmptyBlockOnFree = FALSE; \ XTP_BATCHALLOC_BLOCK_HEADER* dataClass::m_pFreeBlocks = NULL; \ XTP_BATCHALLOC_BLOCK_HEADER* dataClass::m_pBusyBlocks = NULL; \ objClass::TBlockMan gs_##objClass##_BlocksManager; # include "Common/Base/Diagnostic/XTPEnableNoisyWarnings.h" /** @cond */ #endif // !defined(_XTPCUSTOMHEAP_H__) /** @endcond */