/***
*heapadd.c - Add a block of memory to the heap
*
* Copyright (c) 1989-1991, 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_.
*
*******************************************************************************/
#include <cruntime.h>
#include <heap.h>
#include <malloc.h>
#include <os2dll.h>
#include <stdlib.h>
static void _CALLTYPE4 _before(_PBLKDESC, size_t, _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 _CALLTYPE1 _heapadd (
void * block,
size_t size
)
{
int retval;
/*
* Validate user's input
*/
if ( (size == 0) ||
((unsigned)block & (sizeof(int)-1)) ||
(size & (sizeof(int)-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 _CALLTYPE1 _heap_addblock (
void * block,
size_t size
)
{
_PBLKDESC pdesc;
REG1 _PBLKDESC pnewdesc;
size_t lastsize;
int find;
/*
* 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 error1;
pnewdesc = pdesc;
}
else {
_GETEMPTY(pnewdesc);
}
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);
_heap_desc.pfirstdesc = _heap_desc.proverdesc =
pnewdesc;
break;
case(_HEAPFIND_BEFORE):
/*
* New block is before the heap
*/
_before(pnewdesc, size, _heap_desc.pfirstdesc);
_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);
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);
break;
#ifdef DEBUG
case(_HEAPFIND_WITHIN):
#else
default:
#endif
/*
* New block is within heap
*/
if (!(_IS_DUMMY(pdesc)))
goto error0;
_before(pnewdesc, size, pdesc->pnextdesc);
_before(pdesc, _MEMSIZE(pdesc), pnewdesc);
break;
#ifdef DEBUG
/*
* Return value unknown -- abort!
*/
default:
_heap_abort();
#endif
}
/*
* Update rover, if appropriate
*/
#ifdef _OLDROVER_
if (block < _ADDRESS(_heap_desc.proverdesc))
_heap_desc.proverdesc = pnewdesc;
#else /* ndef _OLDROVER_ */
if ( (block < _ADDRESS(_heap_desc.proverdesc)) &&
(_BLKSIZE(pnewdesc) >= _heap_resetsize) )
_heap_desc.proverdesc = pnewdesc;
#endif /* _OLDROVER_ */
/*
* Good return
*/
/* good: unreferenced label to be removed */
return(0);
/*
* Error return
*/
error0:
_PUTEMPTY(pnewdesc);
error1:
return(-1);
}
/***
*static void _before(pdesc1, size, pdesc2) - 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
*
*Exit:
* (void)
*
*Exceptions:
*
*******************************************************************************/
static void _CALLTYPE4 _before (
REG1 _PBLKDESC pdesc1,
size_t size,
REG2 _PBLKDESC pdesc2
)
{
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.
*/
_GETEMPTY(pdummydesc);
pdummydesc->pblock = (char *) dummyaddr;
_SET_DUMMY(pdummydesc);
pdesc1->pnextdesc = pdummydesc;
pdesc1 = pdummydesc;
}
/*
* Put the new block in the heap.
*/
link:
pdesc1->pnextdesc = pdesc2;
}