Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

968 lines
22 KiB

/************************************************************************
* *
* LCMEM.CPP *
* *
* Copyright (C) Microsoft Corporation 1994 *
* All Rights reserved. *
* *
************************************************************************/
#ifdef SPECIFICATION
This module contains heap management code. When memory is freed, there
is an automatic check for an overwrite of the allocated memory.
Allocations are always aligned on 32-bit boundarys for Intel, and 64-bit
boundarys for MIPS. No "hole" is left that is smaller then the alignment
type (32 bytes for INTEL) in order to cut down on fragmentation.
The fsStatus has a dual purpose -- it not only indicates if the
block of memory is in use or not, but it is also used to check for heap
corruption. If someone mallocs a block of memory, and then writes over
the end of that allocation, the heap will be corrupted -- this can be
determined if the fsStatus is not one of the two valid values.
Other automatic checks are for attempting to free or reallocate a NULL
pointer, or freeing or reallocaing a pointer that is not in the range
of the heap.
Heap size is limited to 1 meg. To get more, raise MAX_HEAP. In the
_DEBUG version, the complete heap is checked for consistency when the
program exits. In both retail and debug, the entire heap is freed prior
to exiting.
Note that it is unnecessary to check return values for lcMalloc(),
lcCalloc(), and lcReAlloc(). If a problem occurs, such as a lack of
system memory, these functions simply won't return.
I haven't tested this against the 32-bit C runtime versions, but the
16-bit version of this code was 5-10 times faster then the C runtime
equivalents.
REQUIRED FUNCTIONS:
OOM(void)
The caller must supply an OOM() function that will be called if
memory allocation fails. It is assumed that the OOM() function
will not return.
AssertErrorReport(const char* szBuf, int usLine, const char* pszFile);
Retail function for reporting internal errors.
RECOMENDATION: If you're going to allocate memory that will be rarely
discarded, then either use the C runtime, or allocate
it as soon as possible. This heap manager maintains a
pointer to the first available block in the heap, so
there is no speed penalty for memory objects allocated
before any object that gets freed. Use LocalAlloc for
large objects. Use this heap management for memory which
might get corrupted, or that will be allocated and
freed.
COMMENTS: One could turn all of this into a class if you wanted multiple
heaps. The price would be the overhead of passing the class
pointer every time one of the class functions was called.
#endif
#include "stdafx.h"
#pragma hdrstop
#ifndef LCMEM_H
#include "lcmem.h"
#endif
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
enum HEAP_STATUS {
INUSE = 0x13571357, // You look at memory in hex. Make the patterns pretty
UNUSED = 0x24682468 // In Hex.
};
typedef struct _heap {
HEAP_STATUS fsStatus;
int cb;
_heap* pNext;
_heap* pPrev;
} HEAP;
// Align on 32 bits for Intel, 64 bits for MIPS
#ifdef _X86_
#ifdef _DEBUG
int ALIGNMENT = 4; // change to 1 for better checking for overwrite
#else
const int ALIGNMENT = 4;
#endif // _DEBUG
#else
const int ALIGNMENT = 8;
#endif
const int MINIMUM_LEFTOVER = ((sizeof(HEAP) + 32) / \
ALIGNMENT * ALIGNMENT + ALIGNMENT);
const int MAX_HEAP = (1024 * 1024 * 10); // maximum size of the heap = 10 Meg
enum {
HEAP_CORRUPTED,
HEAP_ALREADY_FREED,
HEAP_CHECK_FAILURE,
HEAP_INVALID_POINTER,
HEAP_NULL_POINTER,
};
const int HEAP_ALLOC_INCREMENT = (64 * 1024);
int curHeapAllocation = HEAP_ALLOC_INCREMENT;
#ifndef _DEBUG
#define pszCallersFile __FILE__
#define line __LINE__
#endif
static HEAP* pHeapBase;
static HEAP* pHeapAlloc;
static HEAP* pHeapFirstAvail;
#ifdef _DEBUG
static BOOL fCorupted;
#endif
static void STDCALL CorruptedHeap(int HeapError, const char* pszFile, int Line);
// We create a class so that the heap will be automatically initialized
// before program execution begins.
class CInitHeap
{
public:
CInitHeap();
~CInitHeap();
};
/*
* REVIEW: The advantage of initialization here is that the app linking with
* this module doesn't need to know or care about how to initialize the heap.
* The downside is that if initialization fails (typically, out of memory),
* then we can't load any strings from the resource table because we don't
* have an instance handle yet. HCW's OOM() function hard-codes an English
* message. This may not be acceptable for other apps.
*/
static CInitHeap theap;
/***************************************************************************
FUNCTION: CInitHeap
PURPOSE: Reserve memory for the heap, and initialize the first block
COMMENTS:
MODIFICATION DATES:
23-Feb-1991 [ralphw]
01-Jun-1994 [ralphw] Ported to 32-bits
***************************************************************************/
CInitHeap::CInitHeap()
{
pHeapAlloc = (HEAP*) VirtualAlloc(NULL, MAX_HEAP, MEM_RESERVE, PAGE_READWRITE);
if (!pHeapAlloc)
OOM();
if (!VirtualAlloc(pHeapAlloc, curHeapAllocation, MEM_COMMIT, PAGE_READWRITE))
OOM();
// The first structure gives information about the entire heap
pHeapAlloc->fsStatus = INUSE;
pHeapAlloc->cb = curHeapAllocation;
pHeapAlloc->pNext = (HEAP*) (((PBYTE) pHeapAlloc) + sizeof(HEAP));
pHeapAlloc->pPrev = NULL;
pHeapBase = pHeapAlloc;
pHeapBase++;
pHeapBase->fsStatus = UNUSED;
pHeapBase->cb = curHeapAllocation - (2 * sizeof(HEAP));
pHeapBase->pNext = NULL;
pHeapBase->pPrev = NULL;
pHeapFirstAvail = pHeapBase;
}
CInitHeap::~CInitHeap()
{
if (pHeapAlloc) {
#ifdef _DEBUG
if (!fCorupted)
lcHeapCheck();
#endif
// Decommit all regions and then release them
VirtualFree(pHeapAlloc, MAX_HEAP, MEM_DECOMMIT);
VirtualFree(pHeapAlloc, 0, MEM_RELEASE);
pHeapAlloc = NULL; // total paranoia
}
}
/***************************************************************************
FUNCTION: tmalloc
PURPOSE: Allocate memory. Size of memory will be rounded up to
ALIGNMENT
RETURNS:
COMMENTS:
MODIFICATION DATES:
16-Feb-1991 [ralphw]
***************************************************************************/
#ifdef _DEBUG
void* STDCALL tmalloc(int cb, int line, const char* pszCallersFile)
#else
void* STDCALL tmalloc(int cb)
#endif
{
HEAP* pNext;
#ifdef CODEVIEW
NHP pPrev;
#endif
cb += sizeof(HEAP);
int cbAligned = (cb & (ALIGNMENT - 1)) ? // aligned on a paragraph?
(cb += sizeof(HEAP)) / ALIGNMENT * ALIGNMENT + ALIGNMENT :
cb;
int cbReasonable = cbAligned + MINIMUM_LEFTOVER;
HEAP* pheap = pHeapFirstAvail;
ASSERT(pheap >= pHeapBase && (PBYTE) pheap <=
(PBYTE) pHeapAlloc + curHeapAllocation);
for (;;) {
if (pheap->fsStatus == INUSE) {
NextLink:
// Warning! we can be here due to evil goto, and fsStatus may not
// be INUSE!
if (pheap->pNext != NULL) {
pheap = pheap->pNext;
}
else {
int add;
if (pheap->fsStatus == UNUSED) {
add = 0;
do {
add += HEAP_ALLOC_INCREMENT;
} while (pheap->cb + add < cbReasonable);
}
else
add = HEAP_ALLOC_INCREMENT;
if (curHeapAllocation + add > MAX_HEAP)
OOM();
// Commit more memory, and then update curHeapAllocation.
if (!VirtualAlloc((PBYTE) pHeapAlloc + curHeapAllocation,
add, MEM_COMMIT, PAGE_READWRITE))
OOM();
curHeapAllocation += add;
if (pheap->fsStatus == INUSE) {
// Create a new heap element and attach it.
HEAP* pTmp = (HEAP*) ((PBYTE) pHeapAlloc +
(curHeapAllocation - HEAP_ALLOC_INCREMENT - sizeof(HEAP)));
pTmp->fsStatus = UNUSED;
pTmp->cb = add - (2 * sizeof(HEAP));
pTmp->pNext = NULL;
pTmp->pPrev = pheap;
pheap->pNext = pTmp;
pheap = pheap->pNext;
}
else {
pheap->cb += add;
}
}
continue;
}
else if (pheap->fsStatus == UNUSED) {
/*
* We put this check here with a jump in order to be certain
* to check the fsStatus (which will tell us if the heap
* gets corrupted).
*/
if (cbAligned == pheap->cb) { // perfect fit?
pheap->fsStatus = INUSE;
if (pheap > pHeapFirstAvail)
pHeapFirstAvail = pheap;
return (void *) ((PBYTE) pheap + sizeof(HEAP));
}
/*
* We don't want to leave little blocks of free memory, since
* typically we won't be able to fill them. So, we use
* cbReasonable to determine if we have a reasonable fit.
*/
if (cbReasonable > pheap->cb)
goto NextLink;
/*
* If the requested size is smaller then the block available,
* then we need to create a new heap pointer to the next free
* block. We already checked for an exact fit.
*/
if (cbAligned < pheap->cb) {
pNext = (HEAP*) (((PBYTE) pheap) + cbAligned);
pNext->fsStatus = UNUSED;
pNext->cb = pheap->cb - cbAligned;
pNext->pPrev = pheap;
pNext->pNext = pheap->pNext;
pheap->pNext = pNext;
/*
* We've created a new block, between two blocks. We must
* reset the next block's previous pointer to the new block.
*/
pNext = pNext->pNext;
if (pNext)
pNext->pPrev = pheap->pNext;
}
pheap->fsStatus = INUSE;
pheap->cb = cbAligned;
if (pheap > pHeapFirstAvail)
pHeapFirstAvail = pheap;
return (void *) ((PBYTE) pheap + sizeof(HEAP));
}
else {
CorruptedHeap(HEAP_CORRUPTED, pszCallersFile, line);
}
}
}
#ifdef _DEBUG
void* STDCALL tcalloc(int cb, int line, const char* pszCallersFile)
#else
void* STDCALL tcalloc(int cb)
#endif
{
#ifdef _DEBUG
void* pv = tmalloc(cb, line, pszCallersFile);
#else
void* pv = tmalloc(cb);
#endif
/*
* We need to clear the memory beyond the end of the allocation
* in case we realloc this to a larger block.
*/
cb += sizeof(HEAP);
int cbAligned = (cb & (ALIGNMENT - 1)) ? // aligned on a paragraph?
(cb += sizeof(HEAP)) / ALIGNMENT * ALIGNMENT + ALIGNMENT :
cb;
memset(pv, 0, cbAligned - sizeof(HEAP));
return pv;
}
/***************************************************************************
FUNCTION: tfree
PURPOSE: Free a block of memory
RETURNS:
COMMENTS:
In the process of freeing the block, check for a corrupted heap,
and combine free blocks.
MODIFICATION DATES:
16-Feb-1991 [ralphw]
***************************************************************************/
#ifdef _DEBUG
void STDCALL tfree(void *pv, int line, const char* pszCallersFile)
#else
void STDCALL tfree(void *pv)
#endif
{
HEAP* pNext;
if (!pv)
CorruptedHeap(HEAP_NULL_POINTER, pszCallersFile, line);
HEAP* pheap = (HEAP*) ((PBYTE) pv - sizeof(HEAP));
if ((PBYTE) pheap > (((PBYTE) pHeapAlloc) + curHeapAllocation) ||
pheap < pHeapAlloc)
CorruptedHeap(HEAP_INVALID_POINTER, pszCallersFile, line);
if (pheap->fsStatus != INUSE) {
if (pheap->fsStatus == UNUSED) {
CorruptedHeap(HEAP_ALREADY_FREED, pszCallersFile, line);
}
else {
CorruptedHeap(HEAP_CORRUPTED, pszCallersFile, line);
}
}
#ifdef _DEBUG
/*
* Deliberately and with malice of forethought, trash the freed memory
* so that if anyone tries to use it again, they'll get bad results.
*/
FillMemory(pv, pheap->cb - sizeof(HEAP), '6');
#endif
pheap->fsStatus = UNUSED;
HEAP* pPrev;
// Try to combine any previous free blocks
if ((pPrev = (HEAP*) pheap->pPrev) != NULL) {
if (pPrev->pNext != pheap) {
CorruptedHeap(HEAP_CORRUPTED, pszCallersFile, line);
}
// Combine all previous free blocks
if (pPrev->fsStatus == UNUSED) {
do {
pPrev->cb += pheap->cb;
pPrev->pNext = pheap->pNext;
pheap = pPrev;
if ((pPrev = (HEAP*) pheap->pPrev) == NULL)
break;
} while (pPrev->fsStatus == UNUSED);
if ((pNext = (HEAP*) pheap->pNext) != NULL)
pNext->pPrev = pheap;
if (pPrev != NULL && pPrev->pNext != pheap) {
CorruptedHeap(HEAP_CORRUPTED, pszCallersFile, line);
}
}
else if (pPrev->fsStatus != INUSE) {
/*
* If we get here, the freed block has data in it that extended
* past the original malloc'd size.
*/
CorruptedHeap(HEAP_CORRUPTED, pszCallersFile, line);
}
}
if (pheap < pHeapFirstAvail)
pHeapFirstAvail = pheap;
ASSERT(pHeapFirstAvail->fsStatus == UNUSED ||
pHeapFirstAvail->fsStatus == INUSE);
// Try to combine with any following free blocks
if ((pNext = (HEAP*) pheap->pNext) != NULL) {
// Combine all following free blocks
if (pNext->fsStatus == UNUSED) {
do {
pheap->cb += pNext->cb;
pheap->pNext = pNext->pNext;
// Is this the end of the heap?
if ((pNext = (HEAP*) pheap->pNext) == NULL) {
ASSERT(pHeapFirstAvail->fsStatus == UNUSED ||
pHeapFirstAvail->fsStatus == INUSE);
// Decommit excess memory.
if (pheap->cb > (80 * 1024)) {
int cbNewAllocation = curHeapAllocation;
while (pheap->cb > (80 * 1024)) {
pheap->cb -= (64 * 1024);
cbNewAllocation -= (64 * 1024);
}
VirtualFree((PBYTE) pHeapAlloc + cbNewAllocation,
curHeapAllocation - cbNewAllocation,
MEM_DECOMMIT);
curHeapAllocation = cbNewAllocation;
}
return;
}
} while (pNext->fsStatus == UNUSED);
pNext->pPrev = pheap;
return;
}
else if (pNext->fsStatus != INUSE) {
/*
* If we get here, the freed block has data in it that extended
* past the original malloc'd size.
*/
CorruptedHeap(HEAP_CORRUPTED, pszCallersFile, line);
}
}
}
/***************************************************************************
FUNCTION: tclearfree
PURPOSE: Variation of tfree, this one sets the caller's pointer to
NULL.
PARAMETERS:
*pv
RETURNS:
COMMENTS:
MODIFICATION DATES:
22-Apr-1994 [ralphw]
***************************************************************************/
#ifdef _DEBUG
void STDCALL tclearfree(void **pv, int line, const char* pszCallersFile)
#else
void STDCALL tclearfree(void **pv)
#endif
{
#ifdef _DEBUG
tfree(*pv, line, pszCallersFile);
#else
tfree(*pv);
#endif
*pv = NULL;
}
/***************************************************************************
FUNCTION: theapcheck
PURPOSE: Checks the heap for possible corruption
RETURNS:
COMMENTS:
MODIFICATION DATES:
23-Feb-1991 [ralphw]
***************************************************************************/
#ifdef _DEBUG
void STDCALL theapcheck(int line, const char* pszCallersFile)
#else
void STDCALL theapcheck(void)
#endif
{
HEAP* pheap = pHeapAlloc->pNext;
HEAP *pPrev;
if (!(pHeapFirstAvail->fsStatus == UNUSED ||
pHeapFirstAvail->fsStatus == INUSE))
CorruptedHeap(HEAP_CHECK_FAILURE, pszCallersFile, line);
do {
pPrev = pheap->pPrev;
if ((pheap->fsStatus != INUSE && pheap->fsStatus != UNUSED))
CorruptedHeap(HEAP_CHECK_FAILURE, pszCallersFile, line);
#ifdef _DEBUG
/*
* REVIEW: This stuff checks heap management code -- it should be removed
* once the heap management code is reliable.
*/
if (pPrev)
ASSERT(pPrev->pNext == pheap);
if (pheap->pNext)
ASSERT(((UINT) pheap) + pheap->cb == (UINT) pheap->pNext);
#endif
pheap = (HEAP*) pheap->pNext;
} while (pheap != NULL);
}
/***************************************************************************
FUNCTION: CorruptedHeap
PURPOSE: Report the type of heap error, and which heap it occurred in
RETURNS:
COMMENTS:
MODIFICATION DATES:
23-Feb-1991 [ralphw]
***************************************************************************/
static void STDCALL CorruptedHeap(int HeapError, const char* pszFile, int usLine)
{
char szBuf[256];
#ifdef _DEBUG
fCorupted = TRUE;
#endif
switch(HeapError) {
case HEAP_ALREADY_FREED:
strcpy(szBuf, "Pointer already freed");
break;
case HEAP_CHECK_FAILURE:
strcpy(szBuf, "Heap check failed (corrupted heap)");
break;
case HEAP_INVALID_POINTER:
strcpy(szBuf, "Attempt to free a non-heap pointer");
break;
case HEAP_CORRUPTED:
default:
strcpy(szBuf, "Heap is corrupted");
break;
}
AssertErrorReport(szBuf, usLine, pszFile);
}
/***************************************************************************
FUNCTION: lcSize
PURPOSE: Determine the actual size of the memory allocation. This
may be larger then was originally requested due to
alignment issues.
RETURNS:
COMMENTS:
MODIFICATION DATES:
29-May-1991 [ralphw]
***************************************************************************/
int STDCALL lcSize(void* pv)
{
if (!pv)
return 0;
HEAP* pheap = (HEAP*) ((PBYTE) pv - sizeof(HEAP));
if ((PBYTE) pheap > (((PBYTE) pHeapAlloc) + curHeapAllocation) ||
pheap < pHeapAlloc)
return 0;
if (pheap->fsStatus != INUSE)
return 0;
return pheap->cb - sizeof(HEAP);
}
PSTR STDCALL lcStrDup(const char* psz)
{
PSTR pszDup = (PSTR) lcMalloc(strlen(psz) + 1);
return strcpy(pszDup, psz);
}
/***************************************************************************
FUNCTION: tRealloc
PURPOSE:
PARAMETERS:
pv
cbNew
line
pszCallersFile
RETURNS:
COMMENTS:
MODIFICATION DATES:
20-Mar-1994 [ralphw]
***************************************************************************/
#ifdef _DEBUG
void* STDCALL trealloc(void* pv, int cbNew, int line, const char* pszCallersFile)
#else
void* STDCALL trealloc(void* pv, int cbNew)
#endif
{
if (!pv)
CorruptedHeap(HEAP_NULL_POINTER, pszCallersFile, line);
HEAP* pheap = (HEAP*) ((PBYTE) pv - sizeof(HEAP));
if ((PBYTE) pheap > (((PBYTE) pHeapAlloc) + curHeapAllocation) ||
pheap < pHeapAlloc)
CorruptedHeap(HEAP_INVALID_POINTER, pszCallersFile, line);
if (pheap->fsStatus != INUSE) {
if (pheap->fsStatus == UNUSED) {
CorruptedHeap(HEAP_ALREADY_FREED, pszCallersFile, line);
}
else {
CorruptedHeap(HEAP_CORRUPTED, pszCallersFile, line);
}
}
cbNew += sizeof(HEAP);
int cbAligned = (cbNew & (ALIGNMENT - 1)) ?
(cbNew += sizeof(HEAP)) / ALIGNMENT * ALIGNMENT + ALIGNMENT :
cbNew;
if (cbAligned < MINIMUM_LEFTOVER)
cbAligned = MINIMUM_LEFTOVER;
/*
* If we're not going to actually change the size, then just return.
* Note that we never reduce memory below cbAligned. This keeps us
* aligned and reduces heap fragmentation.
*/
if (cbAligned == pheap->cb)
return(pv);
// Are we reducing the size?
if (cbAligned < pheap->cb) {
// If the resultant free block would be less then MINIMUM_LEFTOVER
// then we do nothing. We don't want small holes lying around.
if (pheap->cb - cbAligned < MINIMUM_LEFTOVER)
return pv;
int cbDiff = pheap->cb - cbAligned;
pheap->cb = cbAligned;
if (pheap->pNext->fsStatus == UNUSED) {
// Next block is free, so just move it down
HEAP* pheapNext = (HEAP*) ((PBYTE) pheap + cbAligned);
memmove(pheapNext, pheap->pNext, sizeof(HEAP));
pheap->pNext = pheapNext;
pheapNext->cb += cbDiff;
pheapNext->pPrev = pheap;
if (pheapNext->pNext)
pheapNext->pNext->pPrev = pheapNext;
return pv;
}
else {
HEAP* pheapNext = (HEAP*) ((PBYTE) pheap + cbAligned);
pheapNext->fsStatus = UNUSED;
pheapNext->cb = cbDiff;
pheapNext->pPrev = pheap;
pheapNext->pNext = pheap->pNext;
pheap->pNext->pPrev = pheapNext;
pheap->pNext = pheapNext;
return pv;
}
}
// If we get here, we're increasing the size.
int cbDiff = cbAligned - pheap->cb;
if (pheap->pNext && pheap->pNext->fsStatus == UNUSED &&
(pheap->pNext->cb == cbDiff ||
pheap->pNext->cb > cbDiff + MINIMUM_LEFTOVER)) {
int clear = cbAligned - pheap->cb; // amount of memory to clear
pheap->cb = cbAligned;
if (pheap->pNext->cb == cbDiff) { // an exact match, blast the block
void* pvClear = pheap->pNext;
HEAP* pheapNext = pheap->pNext;
pheap->pNext = pheapNext->pNext;
if (pheap->pNext)
pheapNext->pNext->pPrev = pheap;
memset(pvClear, 0, clear);
return pv;
}
else { // move the next block and adjust
void* pvClear = pheap->pNext;
HEAP* pheapNext = (HEAP*) ((PBYTE) pheap + cbAligned);
memmove(pheapNext, pheap->pNext, sizeof(HEAP));
pheapNext->cb -= cbDiff;
if (pHeapFirstAvail == pheap->pNext)
pHeapFirstAvail = pheapNext;
pheap->pNext = pheapNext;
if (pheapNext->pNext)
pheapNext->pNext->pPrev = pheapNext;
memset(pvClear, 0, clear);
return pv;
}
}
// No room for expansion, so we must move the entire block.
// REVIEW: marginally faster to simply zero out the new memory rather
// then the entire block
PBYTE pbNew = (PBYTE) lcCalloc(cbAligned - sizeof(HEAP));
memcpy(pbNew, pv, pheap->cb - sizeof(HEAP));
lcFree(pv);
return pbNew;
}
#define HEAP_THRESHOLD (16 * 1024) // below this threshold, use local heap
CMem::CMem(int size)
{
if (size < HEAP_THRESHOLD) {
fLocal = TRUE;
pb = (PBYTE) lcMalloc(size);
}
else {
fLocal = FALSE;
pb = (PBYTE) LocalAlloc(LMEM_FIXED, size);
}
psz = (PSTR) pb;
if (!pb)
OOM();
};
CMem::~CMem(void)
{
if (fLocal)
lcFree(pb);
else
LocalFree((HLOCAL) pb);
}
void CMem::resize(int cb)
{
if (fLocal)
lcReAlloc(pb, cb);
else
LocalReAlloc((HLOCAL) pb, cb, LMEM_ZEROINIT);
}
void STDCALL lcReport(PSTR pszReport)
{
HEAP* pheap = pHeapAlloc->pNext;
HEAP *pPrev;
if (!(pHeapFirstAvail->fsStatus == UNUSED ||
pHeapFirstAvail->fsStatus == INUSE))
CorruptedHeap(HEAP_CHECK_FAILURE, __FILE__, __LINE__);
int cbAllocated = 0, cbDeAllocated = 0;
int cAllocated = 0, cDeAllocated = 0;
do {
pPrev = pheap->pPrev;
if (pheap->fsStatus == INUSE) {
cbAllocated += pheap->cb;
cAllocated++;
}
else if (pheap->fsStatus == UNUSED) {
cbDeAllocated += pheap->cb;
cDeAllocated++;
}
else {
CorruptedHeap(HEAP_CHECK_FAILURE, __FILE__, __LINE__);
}
pheap = (HEAP*) pheap->pNext;
} while (pheap != NULL);
wsprintf(pszReport,
"Committed: %s\r\n%s Allocated: %s\r\n%s DeAllocated: %s\r\n",
FormatNumber(curHeapAllocation),
FormatNumber(cAllocated), FormatNumber(cbAllocated),
FormatNumber(cDeAllocated), FormatNumber(cbDeAllocated));
}
/***************************************************************************
FUNCTION: FormatNumber
PURPOSE: Convert a number into a string, and insert commas every
3 digits
PARAMETERS:
num
RETURNS: Pointer to the string containing the number
COMMENTS:
Cycles through an array of strings, allowing up to MAX_STRING
requests before a duplicate would occur. This is important for
calls to sprintf() where all the pointers are retrieved before
the strings are actually used.
MODIFICATION DATES:
03-Jul-1994 [ralphw]
***************************************************************************/
#define MAX_NUM 15
#define MAX_STRING 10
#include <stdlib.h>
PCSTR STDCALL FormatNumber(int num)
{
static int pos = 0;
static char szNum[MAX_NUM * MAX_STRING];
PSTR pszNum = szNum + (pos * MAX_STRING);
if (++pos >= MAX_STRING)
pos = 0;
_itoa(num, pszNum, 10);
int cb = strlen(pszNum) - 3;
while (cb > 0) {
memmove(pszNum + cb + 1, pszNum + cb, strlen(pszNum + cb) + 1);
pszNum[cb] = ',';
cb -= 3;
}
return pszNum;
}