478 lines
10 KiB
C
478 lines
10 KiB
C
|
/***
|
||
|
*heapadd.c - Add a block of memory to the heap
|
||
|
*
|
||
|
* Copyright (c) 1989-2001, Microsoft Corporation. All rights reserved.
|
||
|
*
|
||
|
*Purpose:
|
||
|
* Add a block of memory to the heap.
|
||
|
*
|
||
|
*Revision History:
|
||
|
* 07-07-89 JCR Module created.
|
||
|
* 07-20-89 JCR Re-use dummy descriptor on exact fit (dummy collection)
|
||
|
* 11-09-89 JCR Corrected plastdesc updating code
|
||
|
* 11-13-89 GJF Added MTHREAD support, also fixed copyright
|
||
|
* 11-15-89 JCR Minor improvement (got rid of a local variable)
|
||
|
* 11-16-89 JCR Bug fix - squirrly case in _HEAPFIND_EXACT
|
||
|
* 12-04-89 GJF A little tuning and cleanup. Also, changed header file
|
||
|
* name to heap.h.
|
||
|
* 12-18-89 GJF Removed DEBUG286 stuff. Also, added explicit _cdecl to
|
||
|
* function definitions.
|
||
|
* 12-19-89 GJF Removed references and uses of plastdesc (revising
|
||
|
* code as necessary)
|
||
|
* 03-09-90 GJF Replaced _cdecl with _CALLTYPE1, added #include
|
||
|
* <cruntime.h> and removed #include <register.h>.
|
||
|
* 03-29-90 GJF Made _before() _CALLTYPE4.
|
||
|
* 07-24-90 SBM Compiles cleanly with -W3 (tentatively removed
|
||
|
* unreferenced label)
|
||
|
* 09-27-90 GJF New-style function declarators.
|
||
|
* 03-05-91 GJF Changed strategy for rover - old version available
|
||
|
* by #define-ing _OLDROVER_.
|
||
|
* 04-06-93 SKS Replace _CRTAPI* with __cdecl
|
||
|
* 12-10-93 GJF Test alignment of user's pointer and block size
|
||
|
* against _GRANULARITY.
|
||
|
* 01-03-94 SKS Fix bug where sentinel gets out of sync with dummy
|
||
|
* blocks and large allocations. _heapmin was likely
|
||
|
* to cause the situation that showed the bug.
|
||
|
* 03-03-94 GJF Revised to provide for graceful failure in the event
|
||
|
* there aren't enough empty descriptors.
|
||
|
* 02-08-95 GJF Removed obsolete _OLDROVER_ support.
|
||
|
* 04-29-95 GJF Spliced on winheap version.
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
|
||
|
#ifdef WINHEAP
|
||
|
|
||
|
|
||
|
#include <cruntime.h>
|
||
|
#include <errno.h>
|
||
|
#include <malloc.h>
|
||
|
#include <winheap.h>
|
||
|
|
||
|
int __cdecl _heapadd (
|
||
|
void * block,
|
||
|
size_t size
|
||
|
)
|
||
|
{
|
||
|
errno = ENOSYS;
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
|
||
|
#else /* ndef WINHEAP */
|
||
|
|
||
|
|
||
|
#include <cruntime.h>
|
||
|
#include <heap.h>
|
||
|
#include <malloc.h>
|
||
|
#include <mtdll.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
static void __cdecl _before(_PBLKDESC, size_t, _PBLKDESC, _PBLKDESC **);
|
||
|
|
||
|
/***
|
||
|
*int _heapadd(block, size) - Add a block of memory to the heap
|
||
|
*
|
||
|
*Purpose:
|
||
|
* Add a block of user memory the heap.
|
||
|
*
|
||
|
* NOTE: The reason for the level of indirection between _heapadd
|
||
|
* and _heap_addblock is (1) to validate the input, and (2) for
|
||
|
* mthread locking/unlocking purposes.
|
||
|
*
|
||
|
* NOTE: _heapadd() DOES NOT enter the block of memory into the region
|
||
|
* table! This is the cleanest way to avoid nasty bugs such as attempting
|
||
|
* to grow, shrink or free static memory (e.g., a block that started out
|
||
|
* being a static array). If the memory block does in fact belong in the
|
||
|
* region table, it is the caller's responsibility to do it (internal
|
||
|
* routines only, user programs should NEVER do this).
|
||
|
*
|
||
|
*Entry:
|
||
|
* void * block = block of memory
|
||
|
* size_t size = size of memory block
|
||
|
*
|
||
|
*Exit:
|
||
|
* 0 = success
|
||
|
* -1 = failure
|
||
|
*
|
||
|
*Exceptions:
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
int __cdecl _heapadd (
|
||
|
void * block,
|
||
|
size_t size
|
||
|
)
|
||
|
{
|
||
|
int retval;
|
||
|
|
||
|
/*
|
||
|
* Validate user's input. Note that _GRANULARITY must be a power
|
||
|
* of 2 for the tests below to be valid!
|
||
|
*/
|
||
|
|
||
|
if ( (size == 0) ||
|
||
|
((unsigned)block & (_GRANULARITY - 1)) ||
|
||
|
(size & (_GRANULARITY - 1))
|
||
|
)
|
||
|
return(-1);
|
||
|
|
||
|
/*
|
||
|
* Add the block to the heap.
|
||
|
*/
|
||
|
|
||
|
_mlock(_HEAP_LOCK);
|
||
|
retval = _heap_addblock(block, size);
|
||
|
_munlock(_HEAP_LOCK);
|
||
|
|
||
|
return(retval);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/***
|
||
|
*int _heap_addblock(block, size) - Add a block of memory to the heap
|
||
|
*
|
||
|
*Purpose:
|
||
|
* Add a block of memory to the heap.
|
||
|
*
|
||
|
* Notes:
|
||
|
* (1) Must handle case where new memory is already in heap
|
||
|
* (i.e., could be the address of a previous 'dummy' entry).
|
||
|
*
|
||
|
*Entry:
|
||
|
* void * block = address of memory block
|
||
|
* size_t size = size of memory block
|
||
|
*
|
||
|
*Exit:
|
||
|
* 0 = success
|
||
|
* -1 = failure
|
||
|
*
|
||
|
*Exceptions:
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
int __cdecl _heap_addblock (
|
||
|
void * block,
|
||
|
size_t size
|
||
|
)
|
||
|
{
|
||
|
_PBLKDESC pdesc;
|
||
|
REG1 _PBLKDESC pnewdesc;
|
||
|
_PBLKDESC pdescs[4] = { NULL, NULL, NULL, NULL };
|
||
|
_PBLKDESC *ppdesc = pdescs;
|
||
|
size_t lastsize;
|
||
|
int find;
|
||
|
|
||
|
/*
|
||
|
* Make sure we enough empty descriptors to do the job! Do it here
|
||
|
* and now because recovering from an out-of-descriptors condition
|
||
|
* is too dicey later on.
|
||
|
*/
|
||
|
if ( ((pdescs[0] = __getempty()) == NULL) ||
|
||
|
((pdescs[1] = __getempty()) == NULL) ||
|
||
|
((pdescs[2] = __getempty()) == NULL) )
|
||
|
{
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Find where the address fits into the heap.
|
||
|
*/
|
||
|
|
||
|
find = _heap_findaddr(block, &pdesc);
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Fill in the new heap descriptor.
|
||
|
* (1) If the new address is an exact fit, use the dummy
|
||
|
* descriptor that already exists for it.
|
||
|
* (2) If the address is NOT in the heap, allocate a new one.
|
||
|
*/
|
||
|
|
||
|
if ( find == _HEAPFIND_EXACT ) {
|
||
|
|
||
|
if ( !(_IS_DUMMY(pdesc)) )
|
||
|
goto error;
|
||
|
|
||
|
pnewdesc = pdesc;
|
||
|
}
|
||
|
else {
|
||
|
pnewdesc = *(ppdesc++);
|
||
|
}
|
||
|
|
||
|
pnewdesc->pblock = block; /* pointer to block */
|
||
|
_SET_FREE(pnewdesc); /* set me free (why don't ya, babe) */
|
||
|
*(_PBLKDESC*)block = pnewdesc; /* init back pointer */
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Put the block in the heap
|
||
|
* find = result of _heap_findaddr() call
|
||
|
* pnewdesc = points to desc to be inserted
|
||
|
* pdesc = filled in by _heap_findaddr() call as appropriate
|
||
|
*/
|
||
|
|
||
|
switch (find) {
|
||
|
|
||
|
|
||
|
case(_HEAPFIND_EMPTY):
|
||
|
|
||
|
/*
|
||
|
* No memory in heap yet
|
||
|
*/
|
||
|
|
||
|
_heap_desc.sentinel.pblock = (char *) block + size;
|
||
|
_before(pnewdesc, size, &_heap_desc.sentinel,
|
||
|
&ppdesc);
|
||
|
|
||
|
_heap_desc.pfirstdesc = _heap_desc.proverdesc =
|
||
|
pnewdesc;
|
||
|
|
||
|
break;
|
||
|
|
||
|
|
||
|
case(_HEAPFIND_BEFORE):
|
||
|
|
||
|
/*
|
||
|
* New block is before the heap
|
||
|
*/
|
||
|
|
||
|
_before(pnewdesc, size, _heap_desc.pfirstdesc,
|
||
|
&ppdesc);
|
||
|
_heap_desc.pfirstdesc = pnewdesc;
|
||
|
break;
|
||
|
|
||
|
|
||
|
case(_HEAPFIND_AFTER):
|
||
|
|
||
|
/*
|
||
|
* New block is after the heap
|
||
|
*
|
||
|
* Find the current last block in the heap
|
||
|
*/
|
||
|
|
||
|
if ( _heap_findaddr((void *)((char *)
|
||
|
(_heap_desc.sentinel.pblock) - 1), &pdesc) !=
|
||
|
_HEAPFIND_WITHIN )
|
||
|
_heap_abort();
|
||
|
|
||
|
lastsize = _MEMSIZE(pdesc);
|
||
|
|
||
|
/*
|
||
|
* Start insertion by placing new block immediately
|
||
|
* in front of the sentinel
|
||
|
*/
|
||
|
|
||
|
_heap_desc.sentinel.pblock = (char *) block + size;
|
||
|
pnewdesc->pnextdesc = &_heap_desc.sentinel;
|
||
|
|
||
|
/*
|
||
|
* Finish insertion by placing new block after the
|
||
|
* old last block (with a possible intervening dummy
|
||
|
* block being created)
|
||
|
*/
|
||
|
|
||
|
_before(pdesc, lastsize, pnewdesc,
|
||
|
&ppdesc);
|
||
|
break;
|
||
|
|
||
|
|
||
|
case(_HEAPFIND_EXACT):
|
||
|
|
||
|
/*
|
||
|
* Block is already in the heap (and we've checked
|
||
|
* that it was a "dummy" before this call).
|
||
|
*
|
||
|
* [NOTES: (1) pnewdesc and pdesc are the same,
|
||
|
* (2) pnewdesc is already linked to the previous
|
||
|
* heap entry, (3) pdesc->pnextdesc is still valid!
|
||
|
* (4) Also, if pdesc->pnextdesc is the sentinel,
|
||
|
* then simply update the sentinel size (calling
|
||
|
* before will cause an error if the previous last
|
||
|
* block was bigger than the current one!).
|
||
|
* (see code at top of this routine).]
|
||
|
*/
|
||
|
|
||
|
if (pdesc->pnextdesc == &_heap_desc.sentinel)
|
||
|
|
||
|
_heap_desc.sentinel.pblock =
|
||
|
(char *) _ADDRESS(pdesc) + size;
|
||
|
|
||
|
else
|
||
|
_before(pnewdesc, size, pdesc->pnextdesc,
|
||
|
&ppdesc);
|
||
|
|
||
|
break;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
case(_HEAPFIND_WITHIN):
|
||
|
#else
|
||
|
default:
|
||
|
#endif
|
||
|
/*
|
||
|
* New block is within heap
|
||
|
*/
|
||
|
|
||
|
if (!(_IS_DUMMY(pdesc)))
|
||
|
goto error;
|
||
|
|
||
|
/*
|
||
|
* If the last block in the heap is a dummy region
|
||
|
* and a new region is allocated which lies within
|
||
|
* that region, we need to update sentinel.pblock.
|
||
|
*/
|
||
|
if (pdesc->pnextdesc == &_heap_desc.sentinel)
|
||
|
{
|
||
|
void * newend = (char *) _ADDRESS(pnewdesc) + size;
|
||
|
|
||
|
if (_heap_desc.sentinel.pblock < newend)
|
||
|
_heap_desc.sentinel.pblock = newend;
|
||
|
}
|
||
|
|
||
|
_before(pnewdesc, size, pdesc->pnextdesc,
|
||
|
&ppdesc);
|
||
|
_before(pdesc, _MEMSIZE(pdesc), pnewdesc,
|
||
|
&ppdesc);
|
||
|
break;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
/*
|
||
|
* Return value unknown -- abort!
|
||
|
*/
|
||
|
|
||
|
default:
|
||
|
_heap_abort();
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Update rover, if appropriate
|
||
|
*/
|
||
|
|
||
|
if ( (block < _ADDRESS(_heap_desc.proverdesc)) &&
|
||
|
(_BLKSIZE(pnewdesc) >= _heap_resetsize) )
|
||
|
_heap_desc.proverdesc = pnewdesc;
|
||
|
|
||
|
/*
|
||
|
* Good return
|
||
|
*/
|
||
|
|
||
|
/* good: unreferenced label to be removed */
|
||
|
return(0);
|
||
|
|
||
|
/*
|
||
|
* Error return
|
||
|
*/
|
||
|
|
||
|
error:
|
||
|
while ( *ppdesc != NULL ) {
|
||
|
_PUTEMPTY(*ppdesc);
|
||
|
ppdesc++;
|
||
|
}
|
||
|
|
||
|
return(-1);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/***
|
||
|
*static void _before(pdesc1, size, pdesc2, pppdesc) - Insert a block before
|
||
|
* a supplied descriptor
|
||
|
*
|
||
|
*Purpose:
|
||
|
* This routine inserts a new descriptor before another descriptor.
|
||
|
*
|
||
|
* Notes:
|
||
|
* (1) A dummy descriptor will be inserted into the heap as
|
||
|
* necessary.
|
||
|
* (2) This routine only updates FORWARD links. Call this
|
||
|
* routine twice to update links in both directions.
|
||
|
*
|
||
|
*Entry:
|
||
|
* _PBLKDESC pdesc1 = new descriptor to insert in the heap
|
||
|
* size_t size = size of pdesc1 block
|
||
|
* _PBLKDESC pdesc2 = descriptor before which block should go
|
||
|
* _PBLKDESC **pppdesc = pointer to a pointer to the list of pointers
|
||
|
* of empty descriptors
|
||
|
*
|
||
|
*Exit:
|
||
|
* (void)
|
||
|
*
|
||
|
*Exceptions:
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
static void __cdecl _before (
|
||
|
REG1 _PBLKDESC pdesc1,
|
||
|
size_t size,
|
||
|
REG2 _PBLKDESC pdesc2,
|
||
|
_PBLKDESC **pppdesc
|
||
|
)
|
||
|
{
|
||
|
size_t diff;
|
||
|
_PBLKDESC pdummydesc;
|
||
|
void * dummyaddr;
|
||
|
|
||
|
/*
|
||
|
* Check for dummy descriptors:
|
||
|
* (1) If first block is dummy, no adjustement needed.
|
||
|
* (2) If second block is dummy, simply adjust size.
|
||
|
*/
|
||
|
|
||
|
if (_IS_DUMMY(pdesc1))
|
||
|
goto link;
|
||
|
|
||
|
if (_IS_DUMMY(pdesc2)) {
|
||
|
pdesc2->pblock = (char *)_ADDRESS(pdesc1) + size;
|
||
|
_SET_DUMMY(pdesc2);
|
||
|
goto link;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* See how much space is between this block and the next one.
|
||
|
*/
|
||
|
|
||
|
diff = ( (char *) _ADDRESS(pdesc2) -
|
||
|
(char *) (dummyaddr = (char *) _ADDRESS(pdesc1) + size) );
|
||
|
|
||
|
if (diff != 0) {
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
/*
|
||
|
* Internal bogosity check
|
||
|
*/
|
||
|
|
||
|
if ((int)diff < 0)
|
||
|
_heap_abort();
|
||
|
#endif
|
||
|
/*
|
||
|
* There is some space between the two blocks. Insert
|
||
|
* a fake "in use" block. Remember, there is no 'back
|
||
|
* pointer' in dummy blocks.
|
||
|
*/
|
||
|
|
||
|
pdummydesc = *((*pppdesc)++);
|
||
|
|
||
|
pdummydesc->pblock = (char *) dummyaddr;
|
||
|
_SET_DUMMY(pdummydesc);
|
||
|
|
||
|
pdesc1->pnextdesc = pdummydesc;
|
||
|
pdesc1 = pdummydesc;
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Put the new block in the heap.
|
||
|
*/
|
||
|
|
||
|
link:
|
||
|
pdesc1->pnextdesc = pdesc2;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
#endif /* WINHEAP */
|