|
|
/***
*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 */
|