658 lines
15 KiB
C
658 lines
15 KiB
C
/*++
|
|
|
|
Copyright (c) 1988-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cmem.c
|
|
|
|
Abstract:
|
|
|
|
Memory allocation support
|
|
|
|
--*/
|
|
|
|
#include "cmd.h"
|
|
|
|
extern DWORD DosErr ;
|
|
|
|
/* Data Stack - a stack of pointers to memory that has been allocated *M005*/
|
|
|
|
typedef struct _DSTACK {
|
|
ULONG cb ; /* malloc's length value (M011) */
|
|
struct _DSTACK *pdstkPrev; /* Pointer to the previous list element */
|
|
CHAR data ; /* The data block */
|
|
} DSTACK, *PDSTACK;
|
|
|
|
#define PTRSIZE FIELD_OFFSET(DSTACK, data) /* Size of element header*/
|
|
|
|
PDSTACK DHead = NULL ; /* Head of the data list */
|
|
ULONG DCount = 0 ; /* Number of elements in the list */
|
|
|
|
#define MAX_NUM_BIG_BUF 2
|
|
PVOID BigBufHandle[MAX_NUM_BIG_BUF] = {0, 0}; /* Handle/segment of buffer used by type & copy */
|
|
|
|
|
|
#if DBG
|
|
|
|
|
|
|
|
|
|
/*** MemChk1 - Sanity check on one element of the data stack
|
|
*
|
|
* Purpose:
|
|
* Verifies the integrity and length of a single data element
|
|
*
|
|
* int MemChk1(PDSTACK s)
|
|
*
|
|
* Args:
|
|
* s - Pointer to the data stack element to check on
|
|
*
|
|
* Returns:
|
|
* 0 - If element is intact and okay
|
|
* 1 - If element size or integrity is off
|
|
*
|
|
*/
|
|
|
|
MemChk1(
|
|
IN PDSTACK pdstk
|
|
)
|
|
{
|
|
return 0;
|
|
#if 0
|
|
if (pdstk->cb != HeapSize(GetProcessHeap(), 0, pdstk)) {
|
|
printf( "My Size is %x, heap says %x\n", pdstk->cb, HeapSize(GetProcessHeap(), 0, pdstk));
|
|
cmd_printf (TEXT("len = %d"), pdstk->cb) ;
|
|
return(1) ;
|
|
} else {
|
|
return(0) ;
|
|
} ;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** MemChkBk - Sanity check on data stack elements from here back
|
|
*
|
|
* Purpose:
|
|
* Verifies the integrity of the CMD data stack from a single
|
|
* point back to the beginning.
|
|
*
|
|
* int MemChkBk(PDSTACK s)
|
|
*
|
|
* Args:
|
|
* s - Pointer to the data stack element to start with
|
|
*
|
|
* Returns:
|
|
* 0 - If elements are intact and okay
|
|
* 1 - If elements' size or integrity are off
|
|
*
|
|
*/
|
|
|
|
MemChkBk(
|
|
IN PDSTACK pdstk
|
|
)
|
|
{
|
|
#if 0
|
|
ULONG cnt ; // Element counter
|
|
PDSTACK pdstkCur; // Element pointer
|
|
|
|
cnt = DCount ;
|
|
|
|
for (pdstkCur = DHead, cnt = DCount ; pdstkCur ; pdstkCur = (PDSTACK)pdstkCur->pdstkPrev, cnt--) {
|
|
if (pdstkCur == pdstk) {
|
|
break ;
|
|
} ;
|
|
} ;
|
|
|
|
while (pdstkCur) {
|
|
if (MemChk1(pdstkCur)) {
|
|
cmd_printf(TEXT("Memory Element %d @ %04x contaminated!"), cnt, pdstkCur) ;
|
|
abort() ;
|
|
} ;
|
|
pdstkCur = (PDSTACK)pdstkCur->pdstkPrev ;
|
|
--cnt ;
|
|
} ;
|
|
|
|
#endif
|
|
return(0) ;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** MemChkAll - Sanity check on one element of the data stack
|
|
*
|
|
* Purpose:
|
|
* Checks the entire data stack for integrity.
|
|
*
|
|
* int MemChkAll()
|
|
*
|
|
* Args:
|
|
*
|
|
* Returns:
|
|
* 0 - If elements are intact and okay
|
|
* 1 - If elements' size or integrity are off
|
|
*
|
|
*/
|
|
|
|
MemChkAll()
|
|
{
|
|
return(MemChkBk(DHead)) ;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*** FreeBigBuf - free the buffer used by the type and copy commands
|
|
*
|
|
* Purpose:
|
|
* If BigBufHandle contains a handle, unlock it and free it.
|
|
*
|
|
* FreeBigBuf()
|
|
*
|
|
* *** NOTE: This routine manipulates Command's Buffer handle, and ***
|
|
* *** should be called with signal processing postponed. ***
|
|
*/
|
|
|
|
void FreeBigBuf(
|
|
int BigBufID
|
|
)
|
|
{
|
|
|
|
if (BigBufID >= MAX_NUM_BIG_BUF)
|
|
return;
|
|
|
|
if (BigBufHandle[BigBufID]) {
|
|
DEBUG((MMGRP, LMLVL, " FREEBIGBUF: Freeing bigbufhandle = 0x%04x", BigBufHandle[BigBufID])) ;
|
|
|
|
VirtualFree(BigBufHandle[BigBufID],0,MEM_RELEASE) ;
|
|
BigBufHandle[BigBufID] = 0 ;
|
|
} ;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** FreeStack - free the memory on the data stack
|
|
*
|
|
* Purpose:
|
|
* Free the memory pointed to by all but the first n elements of the
|
|
* data stack and free BigBufHandle if it is nonzero.
|
|
*
|
|
* FreeStack(int n)
|
|
*
|
|
* Args:
|
|
* n - the number of elements to leave on the stack
|
|
*
|
|
* W A R N I N G
|
|
* !!! THIS ROUTINE CAUSES AN ABORT IF DATA STACK CONTAMINATED !!!
|
|
*/
|
|
|
|
void FreeStack(
|
|
IN ULONG n
|
|
)
|
|
{
|
|
PDSTACK pdstkPtr ;
|
|
int i;
|
|
|
|
DEBUG((MMGRP, LMLVL, " FREESTACK: n = %d DCount = %d", n, DCount)) ;
|
|
|
|
while (DCount > n && (pdstkPtr = DHead)) {
|
|
/* Free the top item in the data stack and pop the stack */
|
|
|
|
DHead = (PDSTACK)DHead->pdstkPrev ;
|
|
-- DCount ;
|
|
DEBUG((MMGRP, LMLVL, " FREESTACK: Freeing %x", pdstkPtr)) ;
|
|
memset( pdstkPtr, 0, sizeof( *pdstkPtr ));
|
|
HeapFree(GetProcessHeap(), 0, pdstkPtr) ;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
MemChkAll() ; /* CAUSES abort() IF CONTAMINATED */
|
|
|
|
#endif
|
|
for (i=0; i<MAX_NUM_BIG_BUF; i++) {
|
|
FreeBigBuf(i) ;
|
|
}
|
|
|
|
DEBUG((MMGRP, LMLVL, " FREESTACK: n = %d, DCount = %d", n, DCount)) ;
|
|
}
|
|
|
|
/*** FreeStr - free a memory block
|
|
*
|
|
* Purpose:
|
|
* Free a single memory block from the stack.
|
|
*
|
|
* Args:
|
|
* pbFree - pointer to block being freed.
|
|
*
|
|
* W A R N I N G
|
|
* !!! THIS ROUTINE CAUSES AN ABORT IF DATA STACK CONTAMINATED !!!
|
|
*/
|
|
|
|
void
|
|
FreeStr(
|
|
IN PTCHAR pbFree
|
|
)
|
|
{
|
|
|
|
PDSTACK pdstkCur;
|
|
PDSTACK pdstkPtr, pdstkLast ;
|
|
ULONG cdstk;
|
|
|
|
DEBUG(( MMGRP, LMLVL, " FreeStr: pbFree = %x DCount = %d", pbFree, DCount )) ;
|
|
|
|
if ((pbFree == NULL) || (DHead == NULL)) {
|
|
return;
|
|
}
|
|
|
|
pdstkPtr = (PDSTACK)((CHAR*)pbFree - PTRSIZE);
|
|
|
|
//
|
|
// Walk through current stack trying to find object
|
|
//
|
|
|
|
for (pdstkCur = DHead, cdstk = DCount; cdstk; cdstk--) {
|
|
|
|
//
|
|
// If we've found the object, remove it from the list
|
|
//
|
|
|
|
if (pdstkCur == pdstkPtr) {
|
|
|
|
//
|
|
// remove from chain
|
|
//
|
|
DEBUG(( MMGRP, LMLVL, " FreeStr: Prev %x, Cur %x, DCount %d",
|
|
pdstkLast, pdstkCur, DCount )) ;
|
|
|
|
if (pdstkCur == DHead) {
|
|
|
|
DHead = (PDSTACK)pdstkCur->pdstkPrev;
|
|
|
|
} else {
|
|
|
|
pdstkLast->pdstkPrev = pdstkCur->pdstkPrev;
|
|
|
|
}
|
|
|
|
HeapFree( GetProcessHeap( ), 0, pdstkCur );
|
|
DCount--;
|
|
#if DBG
|
|
MemChkAll( ) ;
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
pdstkLast = pdstkCur;
|
|
pdstkCur = (PDSTACK)pdstkCur->pdstkPrev;
|
|
|
|
}
|
|
|
|
//
|
|
// The object wasn't in the stack at all!
|
|
//
|
|
|
|
#if DBG
|
|
DEBUG((MMGRP, LMLVL, " FreeStr: object not in stack")) ;
|
|
// cmd_printf( TEXT( "Object @ %04x not in memory stack!" ), pbFree ) ;
|
|
MemChkAll( ) ;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*** GetBigBuf - allocate a large buffer
|
|
*
|
|
* Purpose:
|
|
* To allocate a buffer for data transferrals.
|
|
* The buffer will be as large as possible, up to MAXBUFSIZE bytes,
|
|
* but no smaller than MINBUFSIZE bytes.
|
|
*
|
|
* TCHAR *GetBigBuf(unsigned *blen)
|
|
*
|
|
* Args:
|
|
* blen = the variable pointed to by blen will be assigned the size of
|
|
* the buffer
|
|
*
|
|
* Returns:
|
|
* A TCHAR pointer containing segment:0.
|
|
* Returns 0L if unable to allocate a reasonable length buffer
|
|
*
|
|
*/
|
|
|
|
PVOID
|
|
|
|
GetBigBuf(
|
|
IN ULONG CbMaxToAllocate,
|
|
IN ULONG CbMinToAllocate,
|
|
OUT unsigned int *CbAllocated,
|
|
IN int BigBufID
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
To allocate a buffer for data transferrals.
|
|
|
|
Arguments:
|
|
|
|
CbMinToAllocate - Fail if can't allocate this number
|
|
CbMaxToAllocate - Initial try and allocation will use this number
|
|
CbAllocated - Number of bytes allocated
|
|
BigBufID - BigBuf index
|
|
|
|
Return Value:
|
|
|
|
Return: NULL - if failed to allocate anything
|
|
pointer to allocated buffer if success
|
|
--*/
|
|
|
|
{
|
|
ULONG cbToDecrease;
|
|
PVOID handle ;
|
|
|
|
DEBUG((MMGRP, MALVL, "GETBIGBUF: MinToAlloc %d, MaxToAlloc %d", CbMinToAllocate, CbMaxToAllocate)) ;
|
|
|
|
cbToDecrease = CbMaxToAllocate;
|
|
//bytesdecrease = CbMaxToAllocate ;
|
|
|
|
while (!(handle = VirtualAlloc(NULL, CbMaxToAllocate,MEM_COMMIT,PAGE_READWRITE))) {
|
|
|
|
//
|
|
// Decrease the desired buffer size by CbToDecrease
|
|
// If the decrease is too large, make it smaller
|
|
//
|
|
if ( cbToDecrease >= CbMaxToAllocate ) {
|
|
cbToDecrease = ((CbMaxToAllocate >> 2) & 0xFE00) + 0x200;
|
|
}
|
|
|
|
if ( cbToDecrease < CbMinToAllocate ) {
|
|
cbToDecrease = CbMinToAllocate ;
|
|
}
|
|
|
|
CbMaxToAllocate -= cbToDecrease ;
|
|
|
|
if ( CbMaxToAllocate < CbMinToAllocate ) {
|
|
|
|
//
|
|
// Unable to allocate a reasonable buffer
|
|
//
|
|
*CbAllocated = 0 ;
|
|
PutStdErr(ERROR_NOT_ENOUGH_MEMORY, NOARGS);
|
|
return ( NULL ) ;
|
|
}
|
|
}
|
|
|
|
*CbAllocated = CbMaxToAllocate ;
|
|
|
|
FreeBigBuf(BigBufID) ;
|
|
BigBufHandle[BigBufID] = handle ;
|
|
|
|
DEBUG((MMGRP, MALVL, " GETBIGBUF: Bytes Allocated = %d Handle = 0x%04x", *CbAllocated, BigBufHandle[BigBufID])) ;
|
|
|
|
return(handle) ;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** mknode - allocata a parse tree node
|
|
*
|
|
* Purpose:
|
|
* To allocate space for a new parse tree node. Grow the data segment
|
|
* if necessary.
|
|
*
|
|
* struct node *mknode()
|
|
*
|
|
* Returns:
|
|
* A pointer to the node that was just allocated.
|
|
*
|
|
* Notes:
|
|
* This routine must always use calloc(). Many other parts of Command
|
|
* depend on the fact that the fields in these nodes are initialized to 0.
|
|
*
|
|
* THIS ROUTINE RETURNS `NULL' IF THE C RUN-TIME CANNOT ALLOCATE MEMORY
|
|
*/
|
|
|
|
struct node *mknode()
|
|
{
|
|
struct node *Node = (struct node *) mkstr( sizeof( struct node ));
|
|
DEBUG((MMGRP, MALVL, " MKNODE: Entered")) ;
|
|
return Node;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** mkstr - allocate space for a string
|
|
*
|
|
* Purpose:
|
|
* To allocate space for a new string. Grow the data segment if necessary.
|
|
*
|
|
* TCHAR *mkstr(size)
|
|
*
|
|
* Args:
|
|
* size - size of the string to be allocated
|
|
*
|
|
* Returns:
|
|
* A pointer to the string that was just allocated.
|
|
*
|
|
* Notes:
|
|
* This routine must always use calloc(). Many other parts of Command
|
|
* depend on the fact that memory that is allocated is initialized to 0.
|
|
*
|
|
* - M005 * The piece of memory allocated is large enough to include
|
|
* a pointer at the beginning. This pointer is part of the list of
|
|
* allocated memory. The routine calling mkstr() receives the address
|
|
* of the first byte after that pointer. resize() knows about this,
|
|
* and so must any other routines which directly modify memory
|
|
* allocation.
|
|
* - M011 * This function is the same as mentioned above except that the
|
|
* pointer is now preceeded by a header consisting of two signature
|
|
* bytes and the length of the memory allocated. This was added for
|
|
* sanity checks.
|
|
*
|
|
* THIS ROUTINE RETURNS `NULL' IF THE C RUN-TIME CANNOT ALLOCATE MEMORY
|
|
*
|
|
* W A R N I N G
|
|
* !!! THIS ROUTINE CAUSES AN ABORT IF DATA STACK CONTAMINATED !!!
|
|
*/
|
|
|
|
void*
|
|
mkstr(
|
|
IN int cbNew
|
|
)
|
|
{
|
|
PDSTACK pdstkCur ; // Ptr to the memory being allocated
|
|
|
|
DEBUG((MMGRP, MALVL, " MKSTR: Entered.")) ;
|
|
|
|
#if DBG
|
|
|
|
MemChkAll() ; /* CAUSES abort() IF CONTAMINATED */
|
|
|
|
#endif
|
|
|
|
if ((pdstkCur = (PDSTACK)(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbNew + PTRSIZE + 4))) == NULL) {
|
|
PutStdErr(ERROR_NOT_ENOUGH_MEMORY, NOARGS);
|
|
return(0) ;
|
|
} ;
|
|
|
|
DEBUG((MMGRP, MALVL, " MKSTR: Adding to stack")) ;
|
|
|
|
pdstkCur->cb = cbNew + PTRSIZE + 4 ;
|
|
pdstkCur->pdstkPrev = (PDSTACK)DHead ;
|
|
DHead = pdstkCur ;
|
|
DCount++ ;
|
|
|
|
DEBUG((MMGRP, MALVL, " MKSTR: ptr = %04x cbNew = %04x DCount = %d",
|
|
pdstkCur, cbNew, DCount)) ;
|
|
|
|
#if DBG
|
|
|
|
MemChkBk(pdstkCur) ; /* CAUSES abort() IF CONTAMINATED */
|
|
|
|
#endif
|
|
|
|
return(&(pdstkCur->data)) ; /*M005*/
|
|
}
|
|
|
|
|
|
/*** dupstr - Duplicate a string
|
|
*
|
|
* Purpose:
|
|
* Create a copy of a string in a new heap block
|
|
*
|
|
* TCHAR *dupstr( TCHAR *String )
|
|
*
|
|
* Args:
|
|
* String to be duplicated
|
|
*
|
|
* Returns:
|
|
* A pointer to the string that was just allocated and copied. The caller retains
|
|
* ownership of the input string and the returned string.
|
|
*
|
|
* Notes:
|
|
*
|
|
* W A R N I N G
|
|
* !!! THIS ROUTINE CAUSES AN ABORT IF DATA STACK CONTAMINATED !!!
|
|
*/
|
|
|
|
TCHAR *
|
|
dupstr( TCHAR *String )
|
|
{
|
|
TCHAR *New = mkstr( (mystrlen( String ) + 1) * sizeof( TCHAR ));
|
|
|
|
mystrcpy( New, String );
|
|
return New;
|
|
}
|
|
|
|
|
|
|
|
/*** gmkstr - allocate a piece of memory, with no return on failure
|
|
*
|
|
* Purpose:
|
|
* Same as "mkstr" except that if memory cannot be allocated, this
|
|
* routine will jump out to code which will clean things up and
|
|
* return to the top level of command.
|
|
*
|
|
*/
|
|
|
|
void*
|
|
gmkstr(
|
|
IN int cbNew
|
|
)
|
|
|
|
{
|
|
PTCHAR pbNew ;
|
|
|
|
if (!(pbNew = (PTCHAR)mkstr(cbNew)))
|
|
Abort() ;
|
|
|
|
return(pbNew) ;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** resize - resize a piece of memory
|
|
*
|
|
* Purpose:
|
|
* Change the size of a previously allocated piece of memory. Grow the
|
|
* data segment if necessary. If a new different pointer is returned by
|
|
* realloc(0), search the dstk for the pointer to the old piece and
|
|
* update that pointer to point to the new piece.
|
|
*
|
|
* TCHAR *resize(TCHAR *ptr, unsigned size)
|
|
*
|
|
* Args:
|
|
* ptr - pointer to the memory to be resized
|
|
* size - the new size for the block of memory
|
|
*
|
|
* Returns:
|
|
* A pointer to the new piece of memory.
|
|
*
|
|
* - M005 * Modified for the new scheme for keeping a list of allocated
|
|
* blocks
|
|
* - M011 * Modified to use and check new header.
|
|
*
|
|
* THIS ROUTINE RETURNS `NULL' IF THE C RUN-TIME CANNOT ALLOCATE MEMORY
|
|
*
|
|
* W A R N I N G
|
|
* !!! THIS ROUTINE CAUSES AN ABORT IF DATA STACK CONTAMINATED !!!
|
|
*/
|
|
|
|
void*
|
|
resize (
|
|
IN void* pv,
|
|
IN unsigned int cbNew
|
|
)
|
|
|
|
{
|
|
PDSTACK pdstkCur ;
|
|
PDSTACK pdstkNew, pdstkOld;
|
|
CHAR* pbOld = pv;
|
|
|
|
DEBUG((MMGRP, MALVL, " RESIZE: Entered %x.", pv)) ;
|
|
|
|
pbOld -= PTRSIZE ;
|
|
pdstkOld = (PDSTACK)pbOld ;
|
|
|
|
#if DBG
|
|
|
|
if (MemChk1(pdstkOld)) {
|
|
|
|
cmd_printf(TEXT("Memory Element @ %04x contaminated!"), pdstkOld) ;
|
|
abort() ;
|
|
|
|
} ;
|
|
|
|
#endif
|
|
|
|
if (!(pdstkNew = (PDSTACK)HeapReAlloc(GetProcessHeap(), 0, pbOld, cbNew + PTRSIZE + 4))) {
|
|
PutStdErr(ERROR_NOT_ENOUGH_MEMORY, NOARGS);
|
|
return(0) ;
|
|
} ;
|
|
|
|
|
|
|
|
pdstkNew->cb = cbNew + PTRSIZE + 4 ; // Update to new length
|
|
if (HeapSize(GetProcessHeap(), 0, pdstkNew) != pdstkNew->cb) {
|
|
DEBUG((MMGRP, LMLVL, " resize: My Size is %x, heap says %x", pdstkNew->cb, HeapSize(GetProcessHeap(), 0, pdstkNew)));
|
|
}
|
|
|
|
//
|
|
// revise Data Stack information, updating chain of pdstk's with
|
|
// new pointer
|
|
//
|
|
if (pdstkNew != pdstkOld) {
|
|
if (DHead == pdstkOld) { // Is head of List
|
|
DHead = pdstkNew ;
|
|
} else { // Is in middle of list
|
|
for (pdstkCur = DHead ; pdstkCur ; pdstkCur = (PDSTACK)(pdstkCur->pdstkPrev)) {
|
|
if ((PDSTACK)(pdstkCur->pdstkPrev) == pdstkOld) {
|
|
|
|
pdstkCur->pdstkPrev = (PDSTACK)pdstkNew ;
|
|
break ;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
|
|
MemChkBk(pdstkOld) ; // CAUSES abort() IF CONTAMINATED
|
|
|
|
#endif
|
|
|
|
DEBUG((MMGRP, MALVL, " RESIZE: pbOld = %04x cbNew = %04x",&(pdstkNew->data),cbNew)) ;
|
|
|
|
return(&(pdstkNew->data)) ;
|
|
}
|