Windows2003-3790/inetcore/outlookexpress/wabw/wabapi/gllocal.c
2020-09-30 16:53:55 +02:00

1211 lines
25 KiB
C

/*
* gllocal.c
*
* Implementation of global and local heaps
*
* Copyright (C) 1994 Microsoft Corporation
*/
#include "_apipch.h"
#define _GLLOCAL_C
#ifdef MAC
#include "ole2ui.h"
#include <utilmac.h>
#include <mapiprof.h>
#ifdef GetPrivateProfileInt
#undef GetPrivateProfileInt
#undef GetPrivateProfileString
#endif
#define GetPrivateProfileInt MAPIGetPrivateProfileInt
#define GetPrivateProfileString MAPIGetPrivateProfileString
#endif // MAC
// #include "glheap.h"
#ifdef MAC
#pragma code_seg("glheap", "fixed, preload")
#else
#ifdef OLD_STUFF
#pragma SEGMENT(glheap)
#endif // OLD_STUFF
#endif
#ifdef DEBUG
#define STATIC
#else
#define STATIC static
#endif
// Local Heap Debug Implementation --------------------------------------------
#ifdef DEBUG
static TCHAR szDebugIni[] = TEXT("WABDBG.INI");
static TCHAR szSectionHeap[] = TEXT("Memory Management");
static TCHAR szKeyUseVirtual[] = TEXT("VirtualMemory");
static TCHAR szKeyAssertLeaks[] = TEXT("AssertLeaks");
static TCHAR szKeyDumpLeaks[] = TEXT("DumpLeaks");
static TCHAR szKeyFillMem[] = TEXT("FillMemory");
static TCHAR szKeyFillByte[] = TEXT("FillByte");
// Artificial Errors for local heaps
BOOL FForceFailure(HLH hlh, UINT cb);
static TCHAR szAESectionHeap[] = TEXT("Local Heap Failures");
static TCHAR szAEKeyFailStart[] = TEXT("AllocsToFirstFailure");
static TCHAR szAEKeyFailInterval[] = TEXT("FailureInterval");
static TCHAR szAEKeyFailBufSize[] = TEXT("FailureSize");
#ifdef HEAPMON
static TCHAR szKeyHeapMon[] = TEXT("MonitorHeap");
#ifdef MAC
static TCHAR szHeapMonDLL[] = TEXT("GLHM");
#else
static TCHAR szHeapMonDLL[] = TEXT("GLHMON32.DLL");
#endif
static char szHeapMonEntry[] = "HeapMonitor";
static char szGetSymNameEntry[] = "GetSymbolName";
#endif
// Virtual Memory Support --------------------------------------------
//
// The VM Allocators do not currently work on:
// AMD64
// MAC
//
#if defined(MAC) || defined(_AMD64_) || defined(_IA64_)
#define VMAlloc(cb) 0
#define VMAllocEx(cb, ul) 0
#define VMRealloc(pv, cb) 0
#define VMReallocEx(pv, cb, ul) 0
#define VMFree(pv)
#define VMFreeEx(pv, ul)
#define VMGetSize(pv) 0
#define VMGetSizeEx(pv, ul) 0
#endif
#if defined(WIN32) && !defined(MAC)
#define LH_EnterCriticalSection(hlh) EnterCriticalSection(&hlh->cs)
#define LH_LeaveCriticalSection(hlh) LeaveCriticalSection(&hlh->cs)
#else
#define LH_EnterCriticalSection(hlh)
#define LH_LeaveCriticalSection(hlh)
#endif
#ifdef HEAPMON
/*
- FRegisterHeap
-
* Purpose:
* If the user wants to monitor the Heap, then load the DLL with
* the HeapMonitor UI.
*/
BOOL FRegisterHeap(PLH plh)
{
HINSTANCE hInst;
LPHEAPMONPROC pfnHeapMon;
LPGETSYMNAMEPROC pfnGetSymName;
plh->hInstHeapMon = 0;
plh->pfnGetSymName = NULL;
hInst = LoadLibrary(szHeapMonDLL);
if (!hInst)
{
DebugTrace(TEXT("FRegisterHeap: Failed to LoadLibrary GLHMON32.DLL.\n"));
goto ret;
}
pfnHeapMon = (LPHEAPMONPROC)GetProcAddress(hInst, szHeapMonEntry);
if (!pfnHeapMon)
{
DebugTrace(TEXT("FRegisterHeap: Failed to GetProcAddress of HeapMonitor.\n"));
FreeLibrary(hInst);
goto ret;
}
pfnGetSymName = (LPGETSYMNAMEPROC)GetProcAddress(hInst, szGetSymNameEntry);
if (!pfnGetSymName)
{
DebugTrace(TEXT("FRegisterHeap: Failed to GetProcAddress of GetSymName.\n"));
}
plh->hInstHeapMon = hInst;
if (!pfnHeapMon(plh, HEAPMON_LOAD))
{
DebugTrace(TEXT("FRegisterHeap: Call to HeapMonitor failed.\n"));
plh->hInstHeapMon = 0;
goto ret;
}
plh->pfnHeapMon = pfnHeapMon;
plh->pfnGetSymName = pfnGetSymName;
ret:
return (plh->hInstHeapMon ? TRUE : FALSE);
}
void UnRegisterHeap(HLH hlh)
{
if (hlh->pfnHeapMon)
hlh->pfnHeapMon(hlh, HEAPMON_UNLOAD);
}
#endif // HEAPMON
/*
- LH_ReportLeak
-
* Purpose:
* To report individual memory leaks through DebugTrace and the
* LH_LeakHook breakpoint function.
*/
void LH_ReportLeak(HLH hlh, PLHBLK plhblk)
{
DebugTrace(TEXT("Memory leak '%s' in %s @ %08lX, Allocation #%ld, Size: %ld\n"),
plhblk->szName[0] ? plhblk->szName : TEXT("NONAME"),
hlh->szHeapName, PlhblkToPv(plhblk),
plhblk->ulAllocNum, CbPlhblkClient(plhblk));
#if defined(WIN32) && defined(_X86_) && defined(LEAK_TEST)
{
int i;
for (i = 0; i < NCALLERS && plhblk->pfnCallers[i]; i++)
{
char szSymbol[256];
char szModule[64];
DWORD dwDisp;
BOOL fGotSym = FALSE;
szSymbol[0] = 0;
szModule[0] = 0;
if (hlh->pfnGetSymName)
if (hlh->pfnGetSymName((DWORD) plhblk->pfnCallers[i], szModule,
szSymbol, &dwDisp))
fGotSym = TRUE;
if (fGotSym)
{
DebugTrace(TEXT("[%d] %s %s"), i, szModule, szSymbol);
if (dwDisp)
DebugTrace(TEXT("+%ld"), dwDisp);
DebugTrace(TEXT("\n"));
}
else
DebugTrace(TEXT("[%d] %s %08lX \n"), i, szModule, plhblk->pfnCallers[i]);
DBGMEM_LeakHook(plhblk->pfnCallers[i]);
}
}
#endif
}
/*
- LH_DumpLeaks
-
* Purpose:
* Gets called at LH_Close time to report any memory leaks against
* this heap. There are 3 reporting fascilities used by this routine:
*
* => Breakpoint hooking (via LH_LeakHook)
* => Asserts (via TrapSz)
* => Debug trace tags (via DebugTrace)
*
* The Debug Trace is the default method if no others are specified
* or if the others are in-appropriate for the given platform.
*/
void LH_DumpLeaks(HLH hlh)
{
PLHBLK plhblk;
BOOL fDump = !!(hlh->ulFlags & HEAP_DUMP_LEAKS);
BOOL fAssert = !!(hlh->ulFlags & HEAP_ASSERT_LEAKS);
int cLeaks = 0;
for (plhblk = hlh->plhblkHead; plhblk; plhblk = plhblk->plhblkNext)
{
if (fDump)
LH_ReportLeak(hlh, plhblk);
cLeaks++;
}
if (cLeaks)
{
#if defined(WIN16) || (defined(WIN32) && defined(_X86_))
if (fAssert)
{
TrapSz3( TEXT("GLHEAP detected %d memory leak%s in Heap: %s"),
cLeaks, (cLeaks == 1 ? szEmpty : TEXT("s")), hlh->szHeapName);
}
else
DebugTrace(TEXT("GLHEAP detected %d memory leak%s in Heap: %s\n"),
cLeaks, (cLeaks == 1 ? szEmpty : TEXT("s")), hlh->szHeapName);
#else
DebugTrace(TEXT("GLHEAP detected %d memory leak%s in Heap: %s\n"),
cLeaks, (cLeaks == 1 ? szEmpty : TEXT("s")), hlh->szHeapName);
#endif
}
}
BOOL LH_ValidatePlhblk(HLH hlh, PLHBLK plhblk, char ** pszReason)
{
if (IsBadWritePtr(plhblk, sizeof(LHBLK)))
{
*pszReason = "Block header cannot be written to";
goto err;
}
if (plhblk->hlh != hlh)
{
*pszReason = "Block header does not have correct pointer back to heap";
goto err;
}
if (plhblk->plhblkNext)
{
if (IsBadWritePtr(plhblk->plhblkNext, sizeof(LHBLK)))
{
*pszReason = "Block header has invalid next link pointer";
goto err;
}
if (plhblk->plhblkNext->plhblkPrev != plhblk)
{
*pszReason = "Block header points to a next block which doesn't "
"point back to it";
goto err;
}
}
if (plhblk->plhblkPrev)
{
if (IsBadWritePtr(plhblk->plhblkPrev, sizeof(LHBLK))) {
*pszReason = "Block header has invalid prev link pointer";
goto err;
}
if (plhblk->plhblkPrev->plhblkNext != plhblk)
{
*pszReason = "Block header points to a prev block which doesn't "
"point back to it";
goto err;
}
}
else if (hlh->plhblkHead != plhblk)
{
*pszReason = "Block header has a zero prev link but the heap doesn't "
"believe it is the first block";
goto err;
}
if (plhblk->ulAllocNum > hlh->ulAllocNum)
{
*pszReason = "Block header has an invalid internal allocation number";
goto err;
}
return TRUE;
err:
return FALSE;
}
// $MAC - Need WINAPI
BOOL
#ifdef MAC
WINAPI
#endif
LH_DidAlloc(HLH hlh, LPVOID pv)
{
PLHBLK plhblk;
char * pszReason;
BOOL fDidAlloc = FALSE;
for (plhblk = hlh->plhblkHead; plhblk; plhblk = plhblk->plhblkNext)
{
AssertSz2(LH_ValidatePlhblk(hlh, plhblk, &pszReason),
TEXT("Block header (plhblk=%08lX) is invalid\n%s"),
plhblk, pszReason);
if (PlhblkToPv(plhblk) == pv)
{
fDidAlloc = TRUE;
break;
}
}
return fDidAlloc;
}
BOOL LH_ValidatePv(HLH hlh, LPVOID pv, char * pszFunc)
{
PLHBLK plhblk;
char * pszReason;
plhblk = PvToPlhblk(hlh, pv);
if (!plhblk)
{
TrapSz3( TEXT("%s detected a memory block (%08lX) which was either not ")
TEXT("allocated in heap '%s' or has already been freed."),
pszFunc, pv, hlh->szHeapName);
return(FALSE);
}
if (LH_ValidatePlhblk(hlh, plhblk, &pszReason))
return(TRUE);
TrapSz4( TEXT("%s detected an invalid memory block (%08lX) in heap '%s'. %s."),
pszFunc, pv, hlh->szHeapName, pszReason);
return FALSE;
}
/*
- PlhblkEnqueue
-
* Purpose:
* To add a newly allocated block to the allocation list hanging
* off the heap. We do an InsertSorted because the HeapMonitor
* will need to reference the allocations ordered by their
* location in the heap. Since the monitor will walk the heap
* often, it is more efficient to do the sort up front.
*/
void PlhblkEnqueue(PLHBLK plhblk)
{
PLHBLK plhblkCurr = NULL;
PLHBLK plhblkNext = plhblk->hlh->plhblkHead;
while (plhblkNext)
{
if (plhblkNext > plhblk)
break;
plhblkCurr = plhblkNext;
plhblkNext = plhblkCurr->plhblkNext;
}
if (plhblkNext)
{
plhblk->plhblkNext = plhblkNext;
plhblk->plhblkPrev = plhblkCurr;
plhblkNext->plhblkPrev = plhblk;
}
else
{
plhblk->plhblkNext = NULL;
plhblk->plhblkPrev = plhblkCurr;
}
if (plhblkCurr)
plhblkCurr->plhblkNext = plhblk;
else
plhblk->hlh->plhblkHead = plhblk;
}
/*
- PlhblkDequeue
-
* Purpose:
* To remove a freed block from the list of allocations hanging
* off the heap.
*/
void PlhblkDequeue(PLHBLK plhblk)
{
if (plhblk->plhblkNext)
plhblk->plhblkNext->plhblkPrev = plhblk->plhblkPrev;
if (plhblk->plhblkPrev)
plhblk->plhblkPrev->plhblkNext = plhblk->plhblkNext;
else
plhblk->hlh->plhblkHead = plhblk->plhblkNext;
}
/*
- HexByteToBin
-
* Purpose:
* Takes a hex string and converts the 2 msd's to a byte, ignoring
* the remaining digits. This function assumes the string is
* formatted as: 0xnn, otherwise it simply returns 0x00.
*/
BYTE HexByteToBin(LPSTR sz)
{
int i, n[2], nT;
if (*sz++ != '0')
return 0x00;
nT = *sz++;
if (nT != 'x' && nT != 'X')
return 0x00;
for (i = 0; i < 2; i++)
{
nT = *sz++;
if (nT >= '0' && nT <= '9')
n[i] = nT - '0';
else if (nT >= 'A' && nT <= 'F')
n[i] = nT - 'A' + 10;
else if (nT >= 'a' && nT <= 'f')
n[i] = nT - 'a' + 10;
else
return (BYTE)0x00;
}
n[0] <<= 4;
return (BYTE)((BYTE)n[0] | (BYTE)n[1]);
}
HLH WINAPI LH_Open(DWORD dwMaxHeap)
{
_HLH _hlhData = 0;
_HLH _hlhBlks = 0;
PLH plh = NULL;
UINT cch = 0;
UINT uiT = 0;
TCHAR szFillByte[8];
LPSTR lpFillByte = NULL;
ULONG cbVirtual = 0;
// The first thing we must do is create a heap that we will
// allocate our Allocation Blocks on. We also allocate our
// debug Heap object on this heap.
_hlhBlks = _LH_Open(dwMaxHeap);
if (!_hlhBlks)
{
DebugTrace(TEXT("LH_Open: Failed to create new heap!\n"));
goto ret;
}
// Allocate the thing we hand back to the caller on this new heap.
plh = _LH_Alloc(_hlhBlks, sizeof(LH));
if (!plh)
{
DebugTrace(TEXT("LH_Alloc: Failed to allocate heap handle!\n"));
_LH_Close(_hlhBlks);
_hlhBlks = 0;
goto ret;
}
// Initialize all the goodies we store in this thing.
// Hook this heap into the global list of heaps we've
// created in this context.
memset(plh, 0, sizeof(LH));
plh->pfnSetName = (LPLHSETNAME)LH_SetNameFn;
plh->_hlhBlks = _hlhBlks;
plh->ulFlags = HEAP_LOCAL;
#if defined(WIN32) && !defined(MAC)
InitializeCriticalSection(&plh->cs);
#endif
// VirtualMemory default is FALSE
cbVirtual = GetPrivateProfileInt(szSectionHeap, szKeyUseVirtual, 0,
szDebugIni);
if (cbVirtual)
{
plh->ulFlags |= HEAP_USE_VIRTUAL;
// We always want virtual allocations on MIPS and PPC to be 4-byte
// aligned, because all our code assumes that the beginning of an
// allocation is aligned on machine word boundaries. On other
// platforms, changing this behavior is non-fatal, but on MIPS and
// PPC we'll get alignment faults everywhere.
#if !defined(_MIPS_) && !defined(_PPC_)
if (cbVirtual == 4)
#endif
plh->ulFlags |= HEAP_USE_VIRTUAL_4;
}
// DumpLeaks default is TRUE
if (GetPrivateProfileInt(szSectionHeap, szKeyDumpLeaks, 1, szDebugIni))
plh->ulFlags |= HEAP_DUMP_LEAKS;
// AssertLeaks default is FALSE
if (GetPrivateProfileInt(szSectionHeap, szKeyAssertLeaks, 0, szDebugIni))
plh->ulFlags |= HEAP_ASSERT_LEAKS;
// FillMem default is TRUE
if (GetPrivateProfileInt(szSectionHeap, szKeyFillMem, 1, szDebugIni))
plh->ulFlags |= HEAP_FILL_MEM;
if (plh->ulFlags & HEAP_FILL_MEM)
{
cch = GetPrivateProfileString(
szSectionHeap,
szKeyFillByte,
szEmpty,
szFillByte,
CharSizeOf(szFillByte)-1,
szDebugIni);
}
// Set the memory fill character.
lpFillByte = ConvertWtoA(szFillByte);
plh->chFill = (BYTE)(cch ? HexByteToBin(lpFillByte) : chDefaultFill);
LocalFreeAndNull(&lpFillByte);
//
// Set up artificial failures. If anything is set in our ini file, then
// HEAP_FAILURES_ENABLED gets set.
//
uiT = GetPrivateProfileInt(szAESectionHeap, szAEKeyFailStart, 0, szDebugIni);
if (uiT)
{
plh->ulFlags |= HEAP_FAILURES_ENABLED;
plh->ulFailStart = (ULONG) uiT;
plh->ulFailInterval =
(ULONG) GetPrivateProfileInt(szAESectionHeap, szAEKeyFailInterval, 0, szDebugIni);
plh->uiFailBufSize =
GetPrivateProfileInt(szAESectionHeap, szAEKeyFailBufSize, 0, szDebugIni);
}
#ifdef HEAPMON
// If the user wants Heap Monitor UI, the spin a thread to manage a
// DialogBox that can display the status of the heap at all times.
if (GetPrivateProfileInt(szSectionHeap, szKeyHeapMon, 0, szDebugIni))
if (FRegisterHeap(plh))
plh->ulFlags |= HEAP_HEAP_MONITOR;
#endif
// If we are not using virtual memory allocators, then we
// create another heap to allocate the users data in.
if (!(plh->ulFlags & HEAP_USE_VIRTUAL))
{
_hlhData = _LH_Open(dwMaxHeap);
if (!_hlhData)
{
DebugTrace(TEXT("LH_Alloc: Failed to allocate heap handle!\n"));
_LH_Close(_hlhBlks);
plh = NULL;
goto ret;
}
plh->_hlhData = _hlhData;
}
#ifndef _WIN64
LH_SetHeapName1(plh, TEXT("LH %08lX"), plh);
#else
LH_SetHeapName1(plh, TEXT("LH %p"), plh);
#endif // _WIN64
ret:
return (HLH)plh;
}
void WINAPI LH_Close(HLH hlh)
{
_HLH _hlhData = hlh->_hlhData;
_HLH _hlhBlks = hlh->_hlhBlks;
// Dump memory leaks if we're supposed to.
if (hlh->ulFlags & HEAP_DUMP_LEAKS)
LH_DumpLeaks(hlh);
// Destroy the HeapMonitor thread and un-load the DLL
#ifdef HEAPMON
UnRegisterHeap(hlh);
if ((hlh->ulFlags & HEAP_HEAP_MONITOR) && hlh->hInstHeapMon)
FreeLibrary(hlh->hInstHeapMon);
#endif
#if defined(WIN32) && !defined(MAC)
DeleteCriticalSection(&hlh->cs);
#endif
// Clean-up and leave. Closing frees leaks, so we're cool!
if (!(hlh->ulFlags & HEAP_USE_VIRTUAL) && _hlhData)
_LH_Close(_hlhData);
if (_hlhBlks)
{
_LH_Free (_hlhBlks, hlh);
_LH_Close(_hlhBlks);
}
}
LPVOID WINAPI LH_Alloc(HLH hlh, UINT cb)
{
PLHBLK plhblk = NULL;
LPVOID pvAlloc = NULL;
// Note: To be consistent with other (e.g. system) allocators,
// we have to return a valid allocation if cb == 0. So, we
// allow a cb of 0 to actually be allocated. (See bug 3556 in
// the sqlguest:exchange database.)
LH_EnterCriticalSection(hlh);
if (hlh->ulFlags & HEAP_FAILURES_ENABLED)
{
if (FForceFailure(hlh, cb))
{
DebugTrace(TEXT("LH_Alloc: Artificial Failure\n"));
pvAlloc = NULL;
hlh->ulAllocNum++;
goto out;
}
}
if (hlh->ulFlags & HEAP_USE_VIRTUAL_4)
pvAlloc = VMAllocEx(cb, 4);
else if (hlh->ulFlags & HEAP_USE_VIRTUAL)
pvAlloc = VMAllocEx(cb, 1);
else if (cb > UINT_MAX)
plhblk = 0;
else
#ifndef _WIN64
pvAlloc = _LH_Alloc(hlh->_hlhData, (UINT)cb);
#else
{
Assert(hlh->_hlhData);
Assert(cb);
Assert(HeapValidate(hlh->_hlhData, 0, NULL));
pvAlloc = _LH_Alloc(hlh->_hlhData, (UINT)cb);
}
#endif
if (pvAlloc)
{
plhblk = (PLHBLK)_LH_Alloc(hlh->_hlhBlks, sizeof(LHBLK));
if (plhblk)
{
plhblk->hlh = hlh;
plhblk->szName[0] = 0;
plhblk->ulSize = cb;
plhblk->ulAllocNum = ++hlh->ulAllocNum;
plhblk->pv = pvAlloc;
PlhblkEnqueue(plhblk);
#if defined(WIN32) && defined(_X86_) && defined(LEAK_TEST)
GetCallStack((DWORD *)plhblk->pfnCallers, 0, NCALLERS);
#endif
if (hlh->ulFlags & HEAP_FILL_MEM)
memset(pvAlloc, hlh->chFill, (size_t)cb);
}
else
{
if (hlh->ulFlags & HEAP_USE_VIRTUAL_4)
VMFreeEx(pvAlloc, 4);
else if (hlh->ulFlags & HEAP_USE_VIRTUAL)
VMFreeEx(pvAlloc, 1);
else
_LH_Free(hlh->_hlhData, pvAlloc);
pvAlloc = NULL;
}
}
out:
LH_LeaveCriticalSection(hlh);
return pvAlloc;
}
LPVOID WINAPI LH_Realloc(HLH hlh, LPVOID pv, UINT cb)
{
LPVOID pvNew = NULL;
LH_EnterCriticalSection(hlh);
if (pv == 0)
pvNew = LH_Alloc(hlh, cb);
else if (cb == 0)
LH_Free(hlh, pv);
else if (LH_ValidatePv(hlh, pv, "LH_Realloc"))
{
PLHBLK plhblk = PvToPlhblk(hlh, pv);
UINT cbOld = (UINT)CbPlhblkClient(plhblk);
PlhblkDequeue(plhblk);
if (cb > cbOld &&
((hlh->ulFlags & HEAP_FAILURES_ENABLED) && FForceFailure(hlh, cb)))
{
hlh->ulAllocNum++;
pvNew = 0;
DebugTrace(TEXT("LH_Realloc: Artificial Failure\n"));
} else if (hlh->ulFlags & HEAP_USE_VIRTUAL_4)
pvNew = VMReallocEx(pv, cb, 4);
else if (hlh->ulFlags & HEAP_USE_VIRTUAL)
pvNew = VMReallocEx(pv, cb, 1);
else if (cb > UINT_MAX)
pvNew = 0;
else
pvNew = _LH_Realloc(hlh->_hlhData, pv, (UINT)cb);
PlhblkEnqueue(plhblk);
if (pvNew)
{
hlh->ulAllocNum++;
plhblk->pv = pvNew;
plhblk->ulSize = cb;
if (cb > cbOld)
memset((LPBYTE)pvNew + cbOld, hlh->chFill, cb - cbOld);
}
}
LH_LeaveCriticalSection(hlh);
return pvNew;
}
void WINAPI LH_Free(HLH hlh, LPVOID pv)
{
PLHBLK plhblk;
LH_EnterCriticalSection(hlh);
if (pv && LH_ValidatePv(hlh, pv, "LH_Free"))
{
plhblk = PvToPlhblk(hlh, pv);
PlhblkDequeue(plhblk);
memset(pv, 0xDC, (size_t)CbPlhblkClient(plhblk));
if (hlh->ulFlags & HEAP_USE_VIRTUAL_4)
VMFreeEx(pv, 4);
else if (hlh->ulFlags & HEAP_USE_VIRTUAL)
VMFreeEx(pv, 1);
else
_LH_Free(hlh->_hlhData, pv);
_LH_Free(hlh->_hlhBlks, plhblk);
}
LH_LeaveCriticalSection(hlh);
}
UINT WINAPI LH_GetSize(HLH hlh, LPVOID pv)
{
UINT cb = 0;
LH_EnterCriticalSection(hlh);
if (LH_ValidatePv(hlh, pv, "LH_GetSize"))
{
if (hlh->ulFlags & HEAP_USE_VIRTUAL_4)
cb = (UINT)VMGetSizeEx(pv, 4);
else if (hlh->ulFlags & HEAP_USE_VIRTUAL)
cb = (UINT)VMGetSizeEx(pv, 1);
else
cb = (UINT) _LH_GetSize(hlh->_hlhData, pv);
}
LH_LeaveCriticalSection(hlh);
return cb;
}
void __cdecl LH_SetHeapNameFn(HLH hlh, TCHAR *pszFormat, ...)
{
TCHAR sz[512];
va_list vl;
va_start(vl, pszFormat);
wvnsprintf(sz, ARRAYSIZE(sz), pszFormat, vl);
va_end(vl);
StrCpyN(hlh->szHeapName,
sz,
CharSizeOf(hlh->szHeapName));
}
void __cdecl EXPORT_16 LH_SetNameFn(HLH hlh, LPVOID pv, TCHAR *pszFormat, ...)
{
TCHAR sz[512];
PLHBLK plhblk;
va_list vl;
plhblk = PvToPlhblk(hlh, pv);
if (plhblk)
{
va_start(vl, pszFormat);
wvnsprintf(sz, ARRAYSIZE(sz), pszFormat, vl);
va_end(vl);
StrCpyN(plhblk->szName, sz, CharSizeOf(plhblk->szName));
}
}
// $MAC - Need WINAPI
TCHAR *
#ifdef MAC
WINAPI
#endif
LH_GetName(HLH hlh, LPVOID pv)
{
PLHBLK plhblk;
plhblk = PvToPlhblk(hlh, pv);
if (plhblk)
return(plhblk->szName);
return(szEmpty);
}
BOOL FForceFailure(HLH hlh, UINT cb)
{
//
// First, see if we're past our start of failures point
//
if (hlh->ulFailStart && (hlh->ulFailStart <= hlh->ulAllocNum))
{
//
// If so, then are we at an interval where we should return errors?
//
if ((hlh->ulFailInterval)
&& ((hlh->ulAllocNum - hlh->ulFailStart)%hlh->ulFailInterval) == 0)
{
//
// return that we should fail here
//
return TRUE;
}
//
// Check to see if the alloc size is greater than allowed
//
if (hlh->uiFailBufSize && cb >= hlh->uiFailBufSize)
return TRUE;
}
//
// Otherwise, no error is returned for this alloc
//
return FALSE;
}
/*
- PvToPlhblk
-
* Purpose:
* Finds the LHBLK for this allocation in the heap's active list.
*/
PLHBLK PvToPlhblk(HLH hlh, LPVOID pv)
{
PLHBLK plhblk;
LH_EnterCriticalSection(hlh);
plhblk = hlh->plhblkHead;
while (plhblk)
{
if (plhblk->pv == pv)
break;
plhblk = plhblk->plhblkNext;
}
LH_LeaveCriticalSection(hlh);
return plhblk;
}
#endif /* DEBUG */
#ifdef MAC // MAC!!
#if defined(DEBUG)
static TCHAR stMemErr[] = TEXT("\pHad a memory error. See above for details");
#endif
LPVOID WINAPI _LH_Open(DWORD dwMaxHeap)
{
Ptr lp;
lp = NewPtrClear(sizeof(LHeap));
if (lp == NULL)
{
#if defined(DEBUG)
DebugTrace(TEXT("_LH_Open had an error. MemError = %d"), MemError());
DebugStr(stMemErr);
#endif /* DEBUG */
return NULL;
}
return (LPVOID)lp;
}
void WINAPI _LH_Close(LPVOID plh)
{
LBlkPtr plb, plbNext;
#if defined(DEBUG)
short idx = 0;
#endif
if (plh == NULL)
return;
// Walk the block list throwing out remaining mem as we go along.
plb = ((LHeapPtr)plh)->plb;
while (plb)
{
plbNext = plb->next;
DisposePtr((Ptr)plb);
#if defined(DEBUG)
if (MemError())
{
DebugTrace(TEXT("_LH_Close: Had a memory error."));
DebugTrace(TEXT("Error number = %d"), MemError());
DebugStr(stMemErr);
}
idx ++;
#endif
plb = plbNext;
}
// Throw out the heap header.
DisposePtr((Ptr)plh);
#if defined(DEBUG)
if (MemError())
{
DebugTrace(TEXT("_LH_Close: Had error throwing out heap head."));
DebugTrace(TEXT("MemError = %d"), MemError());
DebugStr(stMemErr);
}
if (idx)
DebugTrace(TEXT("Threw out %d left over local memory blocks\n"), idx);
#endif /* DEBUG */
}
LPVOID WINAPI _LH_Alloc(LPVOID plh, UINT cb)
{
LBlkPtr plbNew, plb;
Ptr lp;
if (plh == NULL)
return NULL;
// Get memory for the linked list element. Mem requests are stored in a
// linked list off a 'heap' head because real heap management is such a
// pain on the Mac.
plbNew = (LBlkPtr)NewPtr(sizeof(LBlock));
if (plbNew == NULL)
goto trouble;
// Memory for the actual request.
lp = NewPtrClear(cb);
if (lp == NULL)
{
DisposePtr((Ptr)plbNew);
goto trouble;
}
// All members of LBlock are filled in so there's no need to call
// NewPtrclear() above.
plbNew->ptr = lp;
plbNew->next = NULL;
// Find the end of the linked list and link this element in.
if (plb = ((LHeapPtr)plh)->plb)
{
while (plb->next)
plb = plb->next;
plb->next = plbNew;
}
else
((LHeapPtr)plh)->plb = plbNew;
// Return the successfully allocated memory.
return lp;
trouble:
{
#if defined(DEBUG)
DebugTrace(TEXT("_LH_Alloc failed. MemError = %d"), MemError());
DebugTrace(TEXT("The number of requested bytes = %d"), cb);
DebugStr(stMemErr);
#endif /* DEBUG */
}
return NULL;
}
UINT WINAPI _LH_GetSize(LPVOID plh, LPVOID pv)
{
long cb;
cb = GetPtrSize((Ptr)pv);
if (MemError())
{
#if defined(DEBUG)
DebugTrace(TEXT("_LH_GetSize had an error. MemError = %d"), MemError());
DebugStr(stMemErr);
#endif /* DEBUG */
return 0;
}
return cb;
}
LPVOID WINAPI _LH_Realloc(LPVOID plh, LPVOID pv, UINT cb)
{
Ptr lp;
UINT cbOld;
// Get rid of schizo cases.
if (pv == NULL)
{
lp = _LH_Alloc(plh, cb);
if (lp == NULL)
goto err;
return lp;
}
else if (cb == 0)
{
_LH_Free(plh, pv);
return NULL;
}
// Get the size of the block the old ptr pointed to.
cbOld = _LH_GetSize(plh, pv);
if (cbOld == 0)
goto err;
// Get memory for the new pointer.
lp = _LH_Alloc(plh, cb);
if (lp == NULL)
goto err;
// Copy the old info into the new pointer, throw out the old mem and
// return the result.
BlockMove(pv, lp, cbOld <= cb ? cbOld : cb);
_LH_Free(plh, pv);
return lp;
err:
#if defined(DEBUG)
DebugStr("\p_LH_Realloc failed");
#endif /* DEBUG */
return 0;
}
void WINAPI _LH_Free(LPVOID plh, LPVOID pv)
{
LBlkPtr plb, plbPrev = NULL;
if (pv == NULL)
return;
// Remove the memory from the linked list.
plb = ((LHeapPtr)plh)->plb;
while (plb)
{
if (plb->ptr == pv)
break;
plbPrev = plb;
plb = plb->next;
}
if (plb)
{
if (plbPrev)
plbPrev->next = plb->next;
else
((LHeapPtr)plh)->plb = plb->next;
}
else
{
#if defined(DEBUG)
DebugStr("\p_LH_Free: Did not find requested <plb> in linked list");
#endif /* DEBUG */
return;
}
// Throw out the linked list element.
DisposePtr((Ptr)plb);
#if defined(DEBUG)
if (MemError())
goto err;
#endif /* DEBUG */
// Throw out the memory itself.
DisposePtr((Ptr)pv);
#if defined(DEBUG)
if (MemError())
err:
{
DebugTrace(TEXT("_LH_Free: Error disposing ptr. MemError = %d"), MemError());
DebugStr(stMemErr);
}
#endif /* DEBUG */
}
#endif /* MAC */