688 lines
15 KiB
C++
688 lines
15 KiB
C++
//+------------------------------------------------------------------------
|
|
//
|
|
// 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
|