/*** *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 * and removed #include . * 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 #include #include #include int __cdecl _heapadd ( void * block, size_t size ) { errno = ENOSYS; return(-1); } #else /* ndef WINHEAP */ #include #include #include #include #include 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 */