|
|
//+------------------------------------------------------------------------
//
// File: memutil.cxx
//
// Contents: Memory utilities
//
// History: Stolen from Trident
//
//-------------------------------------------------------------------------
#include "headers.hxx"
EXTERN_C HANDLE g_hProcessHeap;
#define SMALLBLOCKHEAP 0
void ClearInterfaceFn(IUnknown **ppUnk) { IUnknown * pUnk;
pUnk = *ppUnk; *ppUnk = NULL; if (pUnk) pUnk->Release(); }
//+------------------------------------------------------------------------
// Allocation functions not implemented in this file:
//
// CDUTIL.HXX
// operator new
// operator delete
//
// OLE's OBJBASE.H
// CoTaskMemAlloc, CoTaskMemFree
//
//-------------------------------------------------------------------------
#if SMALLBLOCKHEAP
DeclareTag(tagSmallBlockHeap, "!Memory", "Check small block heap every time") DeclareTag(tagSmallBlockHeapDisable, "!Memory", "Disable small block heap");
#define _CRTBLD 1
#include "winheap.h"
EXTERN_C CRITICAL_SECTION g_csHeap;
#if DBG == 1
#define CHECKSBH if (IsTagEnabled(tagSmallBlockHeap)) {Assert(CheckSmallBlockHeap() && "Small block heap corrupt");};
BOOL IsSmallBlockHeapEnabled() { static int g_fSmallBlockHeap = -1; if (g_fSmallBlockHeap == -1) g_fSmallBlockHeap = IsTagEnabled(tagSmallBlockHeapDisable) ? 0 : 1; return(g_fSmallBlockHeap == 1); } BOOL CheckSmallBlockHeap() { if (IsSmallBlockHeapEnabled()) { EnterCriticalSection(&g_csHeap); BOOL f = __sbh_heap_check() >= 0; LeaveCriticalSection(&g_csHeap); return f; } return TRUE; } #else
#define CHECKSBH
#endif
#else
#if DBG == 1
BOOL CheckSmallBlockHeap() { return TRUE; } #endif
#endif SMALLBLOCKHEAP
//+------------------------------------------------------------------------
//
// Function: _MemGetSize
//
// Synopsis: Get size of block allocated with MemAlloc/MemRealloc.
//
// Note that MemAlloc/MemRealloc can allocate more than
// the requested number of bytes. Therefore the size returned
// from this function is possibly greater than the size
// passed to MemAlloc/Realloc.
//
// Arguments: [pv] - Return size of this block.
//
// Returns: The size of the block, or zero of pv == NULL.
//
//-------------------------------------------------------------------------
ULONG _MemGetSize(void *pv) { if (pv == NULL) return 0;
Assert(g_hProcessHeap);
#if SMALLBLOCKHEAP
#if DBG==1
if (IsSmallBlockHeapEnabled()) #endif
{ __sbh_region_t * preg; __sbh_page_t * ppage; __map_t * pmap;
EnterCriticalSection(&g_csHeap); if ((pmap = __sbh_find_block(DbgPreGetSize(pv), &preg, &ppage)) != NULL ) { size_t s = DbgPostGetSize(((size_t)(*pmap)) << _PARASHIFT); LeaveCriticalSection(&g_csHeap); return s; } LeaveCriticalSection(&g_csHeap); } #endif
return DbgPostGetSize(HeapSize(g_hProcessHeap, 0, DbgPreGetSize(pv))); }
//+------------------------------------------------------------------------
//
// Function: _MemAlloc
//
// Synopsis: Allocate block of memory.
//
// The contents of the block are undefined. If the requested size
// is zero, this function returns a valid pointer. The returned
// pointer is guaranteed to be suitably aligned for storage of any
// object type.
//
// Arguments: [cb] - Number of bytes to allocate.
//
// Returns: Pointer to the allocated block, or NULL on error.
//
//-------------------------------------------------------------------------
void * _MemAlloc(ULONG cb) { AssertSz (cb, "Requesting zero sized block.");
// The small-block heap will lose its mind if this ever happens, so we
// protect against the possibility.
if (cb == 0) cb = 1;
Assert(g_hProcessHeap);
#if SMALLBLOCKHEAP
#if DBG==1
if (IsSmallBlockHeapEnabled()) #endif
{ /* round up to the nearest paragraph */ size_t cbr = (DbgPreAlloc(cb) + _PARASIZE - 1) & ~(_PARASIZE - 1);
if (cbr < __sbh_threshold) { CHECKSBH; EnterCriticalSection(&g_csHeap); void * pv = DbgPostAlloc(__sbh_alloc_block(cbr >> _PARASHIFT)); LeaveCriticalSection(&g_csHeap); if (pv) return pv; } } #endif
return DbgPostAlloc(HeapAlloc(g_hProcessHeap, 0, DbgPreAlloc(cb))); }
//+------------------------------------------------------------------------
// Function: _MemAllocClear
//
// Synopsis: Allocate a zero filled block of memory.
//
// If the requested size is zero, this function returns a valid
// pointer. The returned pointer is guaranteed to be suitably
// aligned for storage of any object type.
//
// Arguments: [cb] - Number of bytes to allocate.
//
// Returns: Pointer to the allocated block, or NULL on error.
//
//-------------------------------------------------------------------------
void * _MemAllocClear(ULONG cb) { AssertSz (cb, "Allocating zero sized block.");
// The small-block heap will lose its mind if this ever happens, so we
// protect against the possibility.
if (cb == 0) cb = 1;
void * pv;
Assert(g_hProcessHeap);
#if SMALLBLOCKHEAP
#if DBG==1
if (IsSmallBlockHeapEnabled()) #endif
{ /* round up to the nearest paragraph */ size_t cbr = (DbgPreAlloc(cb) + _PARASIZE - 1) & ~(_PARASIZE - 1);
if (cbr < __sbh_threshold) { CHECKSBH; EnterCriticalSection(&g_csHeap); pv = DbgPostAlloc(__sbh_alloc_block(cbr >> _PARASHIFT)); LeaveCriticalSection(&g_csHeap); if (pv) { memset(pv, 0, cb); return pv; } } } #endif
pv = DbgPostAlloc(HeapAlloc(g_hProcessHeap, HEAP_ZERO_MEMORY, DbgPreAlloc(cb)));
// In debug, DbgPostAlloc set the memory so we need to clear it again.
#if DBG==1
if (pv) { memset(pv, 0, cb); } #endif
return pv; }
//+------------------------------------------------------------------------
//
// Function: _MemFree
//
// Synopsis: Free a block of memory allocated with MemAlloc,
// MemAllocFree or MemRealloc.
//
// Arguments: [pv] - Pointer to block to free. A value of zero is
// is ignored.
//
//-------------------------------------------------------------------------
void _MemFree(void *pv) { // The null check is required for HeapFree.
if (pv == NULL) return;
Assert(g_hProcessHeap);
#if DBG == 1
pv = DbgPreFree(pv); #endif
#if SMALLBLOCKHEAP
#if DBG==1
if (IsSmallBlockHeapEnabled()) #endif
{ __sbh_region_t *preg; __sbh_page_t * ppage; __map_t * pmap;
CHECKSBH; EnterCriticalSection(&g_csHeap); if ( (pmap = __sbh_find_block(pv, &preg, &ppage)) != NULL ) { __sbh_free_block(preg, ppage, pmap); LeaveCriticalSection(&g_csHeap); DbgPostFree(); return; } LeaveCriticalSection(&g_csHeap); } #endif
HeapFree(g_hProcessHeap, 0, pv); DbgPostFree(); }
//+------------------------------------------------------------------------
// Function: _MemRealloc
//
// Synopsis: Change the size of an existing block of memory, allocate a
// block of memory, or free a block of memory depending on the
// arguments.
//
// If cb is zero, this function always frees the block of memory
// and *ppv is set to zero.
//
// If cb is not zero and *ppv is zero, then this function allocates
// cb bytes.
//
// If cb is not zero and *ppv is non-zero, then this function
// changes the size of the block, possibly by moving it.
//
// On error, *ppv is left unchanged. The block contents remains
// unchanged up to the smaller of the new and old sizes. The
// contents of the block beyond the old size is undefined.
// The returned pointer is guaranteed to be suitably aligned for
// storage of any object type.
//
// The signature of this function is different than thy typical
// realloc-like function to avoid the following common error:
// pv = realloc(pv, cb);
// If realloc fails, then null is returned and the pointer to the
// original block of memory is leaked.
//
// Arguments: [cb] - Requested size in bytes. A value of zero always frees
// the block.
// [ppv] - On input, pointer to existing block pointer or null.
// On output, pointer to new block pointer.
//
// Returns: HRESULT
//
//-------------------------------------------------------------------------
HRESULT _MemRealloc(void **ppv, ULONG cb) { void *pv;
Assert(g_hProcessHeap);
if (cb == 0) { _MemFree(*ppv); *ppv = 0; } else if (*ppv == NULL) { *ppv = _MemAlloc(cb); if (*ppv == NULL) return E_OUTOFMEMORY; } else { #if DBG == 1
cb = DbgPreRealloc(*ppv, cb, &pv); #else
pv = *ppv; #endif
#if SMALLBLOCKHEAP
#if DBG==1
if (IsSmallBlockHeapEnabled()) #endif
{ __sbh_region_t *preg; __sbh_page_t * ppage; __map_t * pmap; ULONG cbr; void * pvNew;
cbr = (cb + _PARASIZE - 1) & ~(_PARASIZE - 1);
CHECKSBH; EnterCriticalSection(&g_csHeap); if ( (pmap = __sbh_find_block(pv, &preg, &ppage)) != NULL ) { pvNew = NULL; /*
* If the new size falls below __sbh_threshold, try to * carry out the reallocation within the small-block * heap. */ if ( cbr < __sbh_threshold ) { if ( __sbh_resize_block(preg, ppage, pmap, cbr >> _PARASHIFT)) { pvNew = pv; } else if ((pvNew = __sbh_alloc_block(cbr >> _PARASHIFT)) != NULL) { ULONG cbOld = ((size_t)(*pmap)) << _PARASHIFT; memcpy(pvNew, pv, min(cbOld, cb)); __sbh_free_block(preg, ppage, pmap); } }
/*
* If the reallocation has not been (successfully) * performed in the small-block heap, try to allocate a * new block with HeapAlloc. */ if ((pvNew == NULL) && ((pvNew = HeapAlloc(g_hProcessHeap, 0, cb)) != NULL)) { ULONG cbOld = ((size_t)(*pmap)) << _PARASHIFT; memcpy(pvNew, pv, min(cbOld, cb)); __sbh_free_block(preg, ppage, pmap); } LeaveCriticalSection(&g_csHeap); *ppv = DbgPostRealloc(pvNew); if (*ppv) { return S_OK; } else { return E_OUTOFMEMORY; } } else { LeaveCriticalSection(&g_csHeap); } } #endif
pv = DbgPostRealloc(HeapReAlloc(g_hProcessHeap, 0, pv, cb));
if (pv == NULL) return E_OUTOFMEMORY; *ppv = pv; }
return S_OK; }
// MEMGUARD -------------------------------------------------------------------
#if defined(MEMGUARD)
#define MGGUARDDATA 0xF0F0BAAD
struct MGGUARD { MGGUARD *pNext; DWORD dw; };
MGGUARD * g_pMemList = NULL;
void _MgMemValidate() { EnterCriticalSection(&g_csHeap);
MGGUARD *pg = g_pMemList;
while (pg) { if (pg->dw != MGGUARDDATA) { DebugBreak(); }
pg = pg->pNext; }
LeaveCriticalSection(&g_csHeap); }
void _MgRemove(MGGUARD *pmg) { if (!pmg) return;
EnterCriticalSection(&g_csHeap);
MGGUARD *pg = g_pMemList;
if (pmg == pg) { g_pMemList = pg->pNext; goto Cleanup; }
while (pg) { if (pg->pNext == pmg) { pg->pNext = pg->pNext->pNext; break; }
pg = pg->pNext; }
Cleanup: LeaveCriticalSection(&g_csHeap);
}
void _MgAdd(MGGUARD *pmg) { EnterCriticalSection(&g_csHeap);
pmg->pNext = g_pMemList; g_pMemList = pmg;
LeaveCriticalSection(&g_csHeap); }
void * _MgMemAlloc(ULONG cb) { _MgMemValidate();
MGGUARD * pmg = (MGGUARD *)_MemAlloc(sizeof(MGGUARD) + cb);
if (pmg) { pmg->dw = MGGUARDDATA;
_MgAdd(pmg);
return(pmg + 1); } else { return(NULL); }
}
void * _MgMemAllocClear(ULONG cb) { _MgMemValidate();
MGGUARD * pmg = (MGGUARD *)_MemAllocClear(sizeof(MGGUARD) + cb);
if (pmg) { pmg->dw = MGGUARDDATA;
_MgAdd(pmg);
return(pmg + 1); } else { return(NULL); } }
HRESULT _MgMemRealloc(void ** ppv, ULONG cb) { _MgMemValidate();
if (cb == 0) { _MgMemFree(*ppv); *ppv = 0; return(S_OK); }
if (*ppv == NULL) { *ppv = _MgMemAlloc(cb); return(*ppv ? S_OK : E_OUTOFMEMORY); }
MGGUARD * pmg = (MGGUARD *)*ppv - 1;
_MgRemove(pmg);
HRESULT hr = _MemRealloc((void **)&pmg, sizeof(MGGUARD) + cb);
if (hr == S_OK) { pmg->dw = MGGUARDDATA;
_MgAdd(pmg);
*ppv = pmg + 1; }
return(hr); }
ULONG _MgMemGetSize(void * pv) { _MgMemValidate();
if (pv == NULL) return(0); else return(_MemGetSize((MGGUARD *)pv - 1) - sizeof(MGGUARD)); }
void _MgMemFree(void * pv) { _MgMemValidate();
if (pv) { MGGUARD * pmg = (MGGUARD *)pv - 1; if (pmg->dw != MGGUARDDATA) { // The memory guard DWORD was overwritten! Bogus!
#ifdef _M_IX86
_asm int 3 // To get a proper stacktrace.
#else
DebugBreak(); #endif
} _MgRemove(pmg);
_MemFree(pmg); } }
HRESULT _MgMemAllocString(LPCTSTR pchSrc, LPTSTR * ppchDst) { TCHAR *pch; size_t cb;
cb = (_tcsclen(pchSrc) + 1) * sizeof(TCHAR); *ppchDst = pch = (TCHAR *)_MgMemAlloc(cb); if (!pch) return E_OUTOFMEMORY; else { memcpy(pch, pchSrc, cb); return S_OK; } }
HRESULT _MgMemAllocString(ULONG cch, const TCHAR *pchSrc, TCHAR **ppchDest) { TCHAR *pch; size_t cb = cch * sizeof(TCHAR);
*ppchDest = pch = (TCHAR *)_MgMemAlloc(cb + sizeof(TCHAR)); if (!pch) return E_OUTOFMEMORY; else { memcpy(pch, pchSrc, cb); pch[cch] = 0; return S_OK; } }
HRESULT _MgMemReplaceString(const TCHAR *pchSrc, TCHAR **ppchDest) { HRESULT hr; TCHAR *pch;
if (pchSrc) { hr = THR(_MgMemAllocString(pchSrc, &pch)); if (hr) RRETURN(hr); } else { pch = NULL; }
_MgMemFreeString(*ppchDest); *ppchDest = pch;
return S_OK; }
#endif // MEMGUARD
|