|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* $/Plasma20/Sources/Plasma/CoreLibExe/hsExeMalloc.cpp
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include "Pch.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
*
|
|
|
|
* Local constants
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
#if defined(NO_MEM_TRACKER) || !defined(HS_FIND_MEM_LEAKS) || !defined(HS_BUILD_FOR_WIN32) || !defined(_MSC_VER)
|
|
|
|
// no mem debugging
|
|
|
|
#else
|
|
|
|
# undef MEM_DEBUG
|
|
|
|
# define MEM_DEBUG
|
|
|
|
#endif
|
|
|
|
|
|
|
|
const unsigned kMemReallocInPlaceOnly = 1<<0;
|
|
|
|
const unsigned kMemZero = 1<<1;
|
|
|
|
const unsigned kMemIgnoreBlock = 1<<2; // don't track this allocation
|
|
|
|
|
|
|
|
#ifndef MEM_DEBUG
|
|
|
|
|
|
|
|
# define _malloc_dbg(s, t, f, l) malloc(s)
|
|
|
|
# define _calloc_dbg(c, s, t, f, l) calloc(c, s)
|
|
|
|
# define _realloc_dbg(p, s, t, f, l) realloc(p, s)
|
|
|
|
# define _expand_dbg(p, s, t, f, l) _expand(p, s)
|
|
|
|
# define _free_dbg(p, t) free(p)
|
|
|
|
# define _msize_dbg(p, t) _msize(p)
|
|
|
|
|
|
|
|
# ifndef _CLIENT_BLOCK
|
|
|
|
# define _CLIENT_BLOCK 0
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# ifndef _IGNORE_BLOCK
|
|
|
|
# define _IGNORE_BLOCK 0
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# ifndef _CRTDBG_ALLOC_MEM_DF
|
|
|
|
# define _CRTDBG_ALLOC_MEM_DF 0
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# define SET_CRT_DEBUG_FIELD(a)
|
|
|
|
# define CLEAR_CRT_DEBUG_FIELD(a)
|
|
|
|
|
|
|
|
#endif // !MEM_DEBUG
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Private data
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
#define SET_CRT_DEBUG_FIELD(a) \
|
|
|
|
_CrtSetDbgFlag((a) | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
|
|
|
|
#define CLEAR_CRT_DEBUG_FIELD(a) \
|
|
|
|
_CrtSetDbgFlag(~(a) & _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
|
|
|
|
|
|
|
|
// From dbgint.h
|
|
|
|
#define nNoMansLandSize 4
|
|
|
|
typedef struct _CrtMemBlockHeader
|
|
|
|
{
|
|
|
|
struct _CrtMemBlockHeader * pBlockHeaderNext;
|
|
|
|
struct _CrtMemBlockHeader * pBlockHeaderPrev;
|
|
|
|
char * szFileName;
|
|
|
|
int nLine;
|
|
|
|
#ifdef _WIN64
|
|
|
|
/* These items are reversed on Win64 to eliminate gaps in the struct
|
|
|
|
* and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
|
|
|
|
* maintained in the debug heap.
|
|
|
|
*/
|
|
|
|
int nBlockUse;
|
|
|
|
size_t nDataSize;
|
|
|
|
#else /* _WIN64 */
|
|
|
|
size_t nDataSize;
|
|
|
|
int nBlockUse;
|
|
|
|
#endif /* _WIN64 */
|
|
|
|
long lRequest;
|
|
|
|
unsigned char gap[nNoMansLandSize];
|
|
|
|
/* followed by:
|
|
|
|
* unsigned char data[nDataSize];
|
|
|
|
* unsigned char anotherGap[nNoMansLandSize];
|
|
|
|
*/
|
|
|
|
} _CrtMemBlockHeader;
|
|
|
|
#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))
|
|
|
|
#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
|
|
|
|
|
|
|
|
|
|
|
|
enum EMemFile {
|
|
|
|
kMemErr,
|
|
|
|
kMemLeaks,
|
|
|
|
kMemUsage,
|
|
|
|
kMemAllocs,
|
|
|
|
kNumMemFiles
|
|
|
|
};
|
|
|
|
|
|
|
|
static char * s_memFilename[kNumMemFiles] = {
|
|
|
|
"MemErr.log",
|
|
|
|
"MemLeaks.log",
|
|
|
|
"MemUsage.log",
|
|
|
|
"MemAllocs.log",
|
|
|
|
};
|
|
|
|
|
|
|
|
static char * s_memDlgTitle[kNumMemFiles] = {
|
|
|
|
"Memory error",
|
|
|
|
"Memory leak",
|
|
|
|
nil,
|
|
|
|
nil,
|
|
|
|
};
|
|
|
|
|
|
|
|
static HANDLE s_memFile[kNumMemFiles] = {
|
|
|
|
INVALID_HANDLE_VALUE,
|
|
|
|
INVALID_HANDLE_VALUE,
|
|
|
|
INVALID_HANDLE_VALUE,
|
|
|
|
INVALID_HANDLE_VALUE,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct MemDumpParam {
|
|
|
|
EMemFile file;
|
|
|
|
bool showDialog;
|
|
|
|
};
|
|
|
|
|
|
|
|
static unsigned s_memColor;
|
|
|
|
static unsigned s_memCheckOff;
|
|
|
|
|
|
|
|
static CCritSect * s_critsect;
|
|
|
|
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace ExeMalloc {
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Internal functions
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
static void ConvertFilename (
|
|
|
|
const char src[],
|
|
|
|
unsigned chars,
|
|
|
|
char * dst
|
|
|
|
) {
|
|
|
|
|
|
|
|
// Because the filename field may point into a DLL that has been
|
|
|
|
// unloaded, this code validates and converts the string into a
|
|
|
|
// reasonable value
|
|
|
|
__try {
|
|
|
|
unsigned pos = 0;
|
|
|
|
for (;;) {
|
|
|
|
// If the file name is too long then assume it is bogus
|
|
|
|
if (pos >= chars) {
|
|
|
|
pos = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the next character
|
|
|
|
unsigned chr = src[pos];
|
|
|
|
if (!chr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// If the character isn't valid low-ASCII
|
|
|
|
// then assume that the name is bogus
|
|
|
|
if ((chr < 32) || (chr > 127)) {
|
|
|
|
pos = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store character
|
|
|
|
dst[pos++] = (char) chr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that name is terminated
|
|
|
|
dst[pos] = 0;
|
|
|
|
}
|
|
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
dst[0] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print the address of the filename; it may be of some
|
|
|
|
// use given the load address and the map file of the DLL
|
|
|
|
if (!dst[0])
|
|
|
|
snprintf(dst, chars, "@%p", src);
|
|
|
|
}
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
static void OpenErrorFile (EMemFile file) {
|
|
|
|
ASSERT(INVALID_HANDLE_VALUE == s_memFile[file]);
|
|
|
|
s_memFile[file] = CreateFile(
|
|
|
|
s_memFilename[file],
|
|
|
|
GENERIC_WRITE,
|
|
|
|
FILE_SHARE_READ,
|
|
|
|
(LPSECURITY_ATTRIBUTES) NULL,
|
|
|
|
CREATE_ALWAYS,
|
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
}
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
static void __cdecl ReportMem (EMemFile file, bool showDialog, const char fmt[], ...) {
|
|
|
|
|
|
|
|
if (s_memFile[file] == INVALID_HANDLE_VALUE) {
|
|
|
|
DebugBreakIfDebuggerPresent();
|
|
|
|
OpenErrorFile(file);
|
|
|
|
ErrorMinimizeAppWindow();
|
|
|
|
}
|
|
|
|
|
|
|
|
char buffer[512];
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
DWORD length = hsVsnprintf(buffer, arrsize(buffer), fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
#ifdef HS_BUILD_FOR_WIN32
|
|
|
|
|
|
|
|
OutputDebugStringA(buffer);
|
|
|
|
|
|
|
|
if (s_memFile[file] != INVALID_HANDLE_VALUE)
|
|
|
|
WriteFile(s_memFile[file], buffer, length, &length, NULL);
|
|
|
|
|
|
|
|
static bool s_skip;
|
|
|
|
if (showDialog && !s_skip && !ErrorGetOption(kErrOptNonGuiAsserts)) {
|
|
|
|
s_skip = IDOK != MessageBox(
|
|
|
|
NULL,
|
|
|
|
buffer,
|
|
|
|
s_memDlgTitle[file],
|
|
|
|
MB_ICONSTOP | MB_SETFOREGROUND | MB_TASKMODAL | MB_OKCANCEL
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
fputs(buffer, stderr);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
static void __cdecl MemDumpCallback (void * mem, void * param) {
|
|
|
|
REF(MemDumpCallback);
|
|
|
|
|
|
|
|
const _CrtMemBlockHeader * pHead = pHdr(mem);
|
|
|
|
MemDumpParam * dumpParam = (MemDumpParam *) param;
|
|
|
|
|
|
|
|
char filename[MAX_PATH];
|
|
|
|
ConvertFilename(
|
|
|
|
pHead->szFileName,
|
|
|
|
arrsize(filename),
|
|
|
|
filename
|
|
|
|
);
|
|
|
|
|
|
|
|
// HACK: Don't report array memory leaks since these underly the hash
|
|
|
|
// table type and may not be cleaned up until after the mem leak
|
|
|
|
// checker runs. =(
|
|
|
|
if (strstr(filename, "pnUtArray"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
ReportMem(
|
|
|
|
dumpParam->file,
|
|
|
|
dumpParam->showDialog,
|
|
|
|
"Offset %p size %u at %s:%d\r\n",
|
|
|
|
mem,
|
|
|
|
pHead->nDataSize,
|
|
|
|
filename,
|
|
|
|
pHead->nLine
|
|
|
|
);
|
|
|
|
}
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
static void __cdecl OnExitMemDumpCallback (void * mem, size_t) {
|
|
|
|
static MemDumpParam param = { kMemLeaks, true };
|
|
|
|
if (!ErrorGetOption(kErrOptDisableMemLeakChecking))
|
|
|
|
MemDumpCallback(mem, ¶m);
|
|
|
|
}
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
static void __cdecl CheckLeaksOnExit () {
|
|
|
|
REF(CheckLeaksOnExit);
|
|
|
|
if (!ErrorGetOption(kErrOptDisableMemLeakChecking)) {
|
|
|
|
MemDumpParam param;
|
|
|
|
param.file = kMemLeaks;
|
|
|
|
param.showDialog = true;
|
|
|
|
_CrtDoForAllClientObjects(MemDumpCallback, ¶m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
static int __cdecl CrtAllocHook (
|
|
|
|
int method,
|
|
|
|
void * pUserData,
|
|
|
|
size_t nSize,
|
|
|
|
int nBlockUse,
|
|
|
|
long lRequest,
|
|
|
|
const unsigned char * szFileName,
|
|
|
|
int nLine
|
|
|
|
) {
|
|
|
|
REF(method);
|
|
|
|
REF(pUserData);
|
|
|
|
REF(nSize);
|
|
|
|
REF(nBlockUse);
|
|
|
|
REF(lRequest);
|
|
|
|
REF(szFileName);
|
|
|
|
REF(nLine);
|
|
|
|
|
|
|
|
if (nBlockUse == _NORMAL_BLOCK) {
|
|
|
|
int xx = 0;
|
|
|
|
REF(xx);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
AUTO_INIT_FUNC(hsExeMallocInit) {
|
|
|
|
// The critical section has to be initialized
|
|
|
|
// before program startup and never freed
|
|
|
|
static byte rawMemory[sizeof CCritSect];
|
|
|
|
s_critsect = new(rawMemory) CCritSect;
|
|
|
|
SET_CRT_DEBUG_FIELD(_CRTDBG_LEAK_CHECK_DF);
|
|
|
|
_CrtSetAllocHook(CrtAllocHook);
|
|
|
|
_CrtSetDumpClient(OnExitMemDumpCallback);
|
|
|
|
// atexit(CheckLeaksOnExit);
|
|
|
|
}
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Module functions
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void MemSetLeakChecking (bool on) {
|
|
|
|
if (on)
|
|
|
|
SET_CRT_DEBUG_FIELD(_CRTDBG_LEAK_CHECK_DF);
|
|
|
|
else
|
|
|
|
CLEAR_CRT_DEBUG_FIELD(_CRTDBG_LEAK_CHECK_DF);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} using namespace ExeMalloc;
|
|
|
|
/****************************************************************************
|
|
|
|
*
|
|
|
|
* Exports
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void MemDumpAllocReport () {
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
MemDumpParam param;
|
|
|
|
param.file = kMemAllocs;
|
|
|
|
param.showDialog = true;
|
|
|
|
_CrtDoForAllClientObjects(MemDumpCallback, ¶m);
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void MemDumpUsageReport () {
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void MemValidateNow () {
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void MemSetValidation (unsigned on) {
|
|
|
|
REF(on);
|
|
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void MemPushDisableTracking () {
|
|
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
++s_memCheckOff;
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void MemPopDisableTracking () {
|
|
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
ASSERT(s_memCheckOff);
|
|
|
|
--s_memCheckOff;
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void MemSetColor (unsigned short color) {
|
|
|
|
REF(color);
|
|
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
s_memColor = color & 0xFFFF;
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void * MemAlloc (unsigned bytes, unsigned flags, const char file[], int line) {
|
|
|
|
|
|
|
|
REF(file);
|
|
|
|
REF(line);
|
|
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
unsigned block;
|
|
|
|
if (flags & kMemIgnoreBlock || s_memCheckOff)
|
|
|
|
block = _IGNORE_BLOCK;
|
|
|
|
else
|
|
|
|
block = _CLIENT_BLOCK | (s_memColor << 16);
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
if (s_critsect)
|
|
|
|
s_critsect->Enter();
|
|
|
|
if (block == _IGNORE_BLOCK)
|
|
|
|
CLEAR_CRT_DEBUG_FIELD(_CRTDBG_ALLOC_MEM_DF);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void * ptr = (flags & kMemZero)
|
|
|
|
? _calloc_dbg(bytes, 1, block, file, line)
|
|
|
|
: _malloc_dbg(bytes, block, file, line);
|
|
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
if (block == _IGNORE_BLOCK)
|
|
|
|
SET_CRT_DEBUG_FIELD(_CRTDBG_ALLOC_MEM_DF);
|
|
|
|
if (s_critsect)
|
|
|
|
s_critsect->Leave();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!ptr)
|
|
|
|
ErrorFatal(__LINE__, __FILE__, "Out of memory");
|
|
|
|
|
|
|
|
// In debug mode ensure that memory is initialized to some freaky value
|
|
|
|
#ifdef HS_DEBUGGING
|
|
|
|
if (! (flags & kMemZero))
|
|
|
|
MemSet(ptr, (byte) ((unsigned_ptr)ptr >> 4), bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
// Compiler specific:
|
|
|
|
// Adding this line causes MSVC to stop assuming that memory allocation
|
|
|
|
// can fail thus producing more efficient assembler code.
|
|
|
|
__assume(ptr);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// return the allocated buffer
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
void MemFree (void * ptr, unsigned flags) {
|
|
|
|
REF(flags);
|
|
|
|
|
|
|
|
if (!ptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
const _CrtMemBlockHeader * pHead = pHdr(ptr);
|
|
|
|
unsigned block = pHead->nBlockUse;
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
|
|
|
|
_free_dbg(ptr, block);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void * MemRealloc (void * ptr, unsigned bytes, unsigned flags, const char file[], int line) {
|
|
|
|
REF(file);
|
|
|
|
REF(line);
|
|
|
|
|
|
|
|
#ifdef HS_DEBUGGING
|
|
|
|
unsigned oldBytes = ptr ? MemSize(ptr) : 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
unsigned block;
|
|
|
|
if (flags & kMemIgnoreBlock || s_memCheckOff)
|
|
|
|
block = _IGNORE_BLOCK;
|
|
|
|
else
|
|
|
|
block = _CLIENT_BLOCK | (s_memColor << 16);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void * newPtr = nil;
|
|
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
if (s_critsect)
|
|
|
|
s_critsect->Enter();
|
|
|
|
if (block == _IGNORE_BLOCK)
|
|
|
|
CLEAR_CRT_DEBUG_FIELD(_CRTDBG_ALLOC_MEM_DF);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if (flags & kMemReallocInPlaceOnly) {
|
|
|
|
#ifndef MEM_DEBUG
|
|
|
|
break;
|
|
|
|
#else
|
|
|
|
newPtr = _expand_dbg(ptr, bytes, block, file, line);
|
|
|
|
|
|
|
|
// expand can succeed without making the block big enough -- check for this case!
|
|
|
|
if (!newPtr || _msize_dbg(newPtr, block) < bytes)
|
|
|
|
break;
|
|
|
|
#endif // MEM_DEBUG
|
|
|
|
}
|
|
|
|
else if (!bytes) {
|
|
|
|
newPtr = _malloc_dbg(0, block, file, line);
|
|
|
|
_free_dbg(ptr, block);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
newPtr = _realloc_dbg(ptr, bytes, block, file, line);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!newPtr)
|
|
|
|
ErrorFatal(__LINE__, __FILE__, "Out of memory");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
if (block == _IGNORE_BLOCK)
|
|
|
|
SET_CRT_DEBUG_FIELD(_CRTDBG_ALLOC_MEM_DF);
|
|
|
|
if (s_critsect)
|
|
|
|
s_critsect->Leave();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* This code doesn't work because the memory manager may have "rounded" the size
|
|
|
|
* of a previous allocation upward to keep it aligned. Therefore, the tail of
|
|
|
|
* the memory block may be initialized with garbage instead of zeroes, and the
|
|
|
|
* realloc call actually copied that memory.
|
|
|
|
if ((bytes > oldBytes) && (flags & kMemZero))
|
|
|
|
MemZero((byte *)newPtr + oldBytes, bytes - oldBytes);
|
|
|
|
*/
|
|
|
|
ASSERT(!(flags & kMemZero));
|
|
|
|
|
|
|
|
// In debug mode ensure that memory is initialized to some freaky value
|
|
|
|
#ifdef HS_DEBUGGING
|
|
|
|
if ((bytes > oldBytes) && !(flags & kMemZero))
|
|
|
|
MemSet((byte *)newPtr + oldBytes, (byte) ((unsigned_ptr) newPtr >> 4), bytes - oldBytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return newPtr;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
unsigned MemSize (void * ptr) {
|
|
|
|
ASSERT(ptr);
|
|
|
|
unsigned result;
|
|
|
|
|
|
|
|
#ifdef MEM_DEBUG
|
|
|
|
const _CrtMemBlockHeader * pHead = pHdr(ptr);
|
|
|
|
unsigned block = pHead->nBlockUse;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
result = (unsigned)_msize_dbg(ptr, block);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
int MemCmp (const void * buf1, const void * buf2, unsigned bytes) {
|
|
|
|
return memcmp(buf1, buf2, bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void MemCopy (void * dest, const void * source, unsigned bytes) {
|
|
|
|
memcpy(dest, source, bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void MemMove (void * dest, const void * source, unsigned bytes) {
|
|
|
|
memmove(dest, source, bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void MemSet (void * dest, unsigned value, unsigned bytes) {
|
|
|
|
memset(dest, value, bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void MemZero (void * dest, unsigned bytes) {
|
|
|
|
memset(dest, 0, bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void * MemDup (const void * ptr, unsigned bytes, unsigned flags, const char file[], int line) {
|
|
|
|
void * dst = MemAlloc(bytes, flags, file, line);
|
|
|
|
MemCopy(dst, ptr, bytes);
|
|
|
|
return dst;
|
|
|
|
}
|