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) { /* * 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; }
int __cdecl _heap_findaddr ( void * address, _PBLKDESC * ppdesc ) { REG1 _PBLKDESC pcurr; /* * See if heap there's anything in the heap */ if (_heap_desc.pfirstdesc == &_heap_desc.sentinel) return(_HEAPFIND_EMPTY); /* * See if entry is in the heap or not */ if (_ADDRESS(_heap_desc.pfirstdesc) > address) return(_HEAPFIND_BEFORE); if (_ADDRESS(&_heap_desc.sentinel) <= address) return(_HEAPFIND_AFTER); /* * Find the entry */ #ifdef DEBUG for (pcurr = _heap_desc.pfirstdesc; pcurr != &_heap_desc.sentinel; pcurr = pcurr->pnextdesc) { #else for (pcurr = _heap_desc.pfirstdesc; TRUE; pcurr = pcurr->pnextdesc) { #endif if ( _ADDRESS(pcurr->pnextdesc) > address ) { /* Address is contained in this entry */ *ppdesc = pcurr; /* Check for an exact fit */ if ( _ADDRESS(pcurr) == address) return(_HEAPFIND_EXACT); else return(_HEAPFIND_WITHIN); } } #ifdef DEBUG /* Should never reach here! */ _heap_abort(); #endif }
void __cdecl _free_base ( #endif /* _MT */ REG1 void *pblock ) { REG2 _PBLKDESC pdesc; /* * If the pointer is NULL, just return [ANSI]. */ if (pblock == NULL) return; /* * Point to block header and get the pointer back to the heap desc. */ pblock = (char *)pblock - _HDRSIZE; pdesc = *(_PBLKDESC*)pblock; /* * Validate the back pointer. */ if (_ADDRESS(pdesc) != pblock) _heap_abort(); /* * Pointer is ok. Mark block free. */ _SET_FREE(pdesc); /* * Check for special conditions under which the rover is reset. */ if ( (_heap_resetsize != 0xffffffff) && (_heap_desc.proverdesc->pblock > pdesc->pblock) && (_BLKSIZE(pdesc) >= _heap_resetsize) ) { _heap_desc.proverdesc = pdesc; } #if !defined (_M_MPPC) && !defined (_M_M68K) else if ( _heap_desc.proverdesc == pdesc->pnextdesc ) { _heap_desc.proverdesc = pdesc; } #endif /* !defined (_M_MPPC) && !defined (_M_M68K) */ }
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; default: /* * 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; } /* * 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); }
int __cdecl _heapmin(void) { REG1 int index; _PBLKDESC pdesc; REG2 _PBLKDESC pdesc2; void * regend; int region_min_count = 0; #if defined(_M_MPPC) || defined(_M_M68K) struct _heap_region_ *pHeapRegions; #endif /* * Lock the heap */ _mlock(_HEAP_LOCK); /* * Coalesce the heap (should return NULL) */ if ( _heap_search((unsigned)_HEAP_COALESCE) != NULL ) _heap_abort(); /* * Loop through the region descriptor table freeing as much * memory to the OS as possible. */ #if defined(_M_MPPC) || defined(_M_M68K) for ( index=0 ; index < _heap_region_table_cur ; index++ ) { pHeapRegions = (struct _heap_region_ *)(*hHeapRegions); if ( (pHeapRegions + index)->_regbase == NULL ) continue; /* region entry is empty */ /* * Get the entry that contains the last address of * the region (allocated so far, that is). */ regend = (char *) ( (pHeapRegions + index)->_regbase) + (pHeapRegions + index)->_currsize - 1; #else /* !defined(_M_MPPC) && !defined(_M_M68K) */ for ( index=0 ; index < _HEAP_REGIONMAX ; index++ ) { if ( _heap_regions[index]._regbase == NULL ) continue; /* region entry is empty */ /* * Get the entry that contains the last address of * the region (allocated so far, that is). */ regend = (char *) _heap_regions[index]._regbase + _heap_regions[index]._currsize - 1; #endif /* defined(_M_MPPC) || defined(_M_M68K) */ if ( _heap_findaddr(regend, &pdesc) != _HEAPFIND_WITHIN ) _heap_abort(); /* last address not within a block */ /* * See if the containing block is free */ if ( !(_IS_FREE(pdesc)) ) continue; /* block is not free */ /* * Region ends with a free block, go free as much mem * as possible. */ region_min_count += _heapmin_region(index, regend, pdesc); } /* region loop */ /* * By minimizing the heap, we've likely invalidated the rover and * may have produced contiguous dummy blocks so: * * (1) reset the rover * (2) coalesce contiguous dummy blocks */ if ( region_min_count ) { /* * Set proverdesc to pfirstdesc */ _heap_desc.proverdesc = _heap_desc.pfirstdesc; for ( pdesc = _heap_desc.pfirstdesc ; pdesc != &_heap_desc.sentinel ; pdesc = pdesc->pnextdesc ) { /* * Check and remove consecutive dummy blocks */ if ( _IS_DUMMY(pdesc) ) { for ( pdesc2 = pdesc->pnextdesc ; _IS_DUMMY(pdesc2) ; pdesc2 = pdesc->pnextdesc ) { /* * coalesce the dummy blocks */ pdesc->pnextdesc = pdesc2->pnextdesc; _PUTEMPTY(pdesc2); } /* dummy loop */ } /* if */ } /* heap loop */ } /* region_min_count */ /* * Good return */ /* goodrtn: unreferenced label to be removed */ /* * Release the heap lock */ _munlock(_HEAP_LOCK); return(0); } /*** *_heapmin_region() - Minimize a region * *Purpose: * Free as much of a region back to the OS as possible. * *Entry: * int index = index of the region in the region table * void * regend = last valid address in region * pdesc = pointer to the last block of memory in the region * (it has already been determined that this block is free) * *Exit: * int 1 = minimized region * 0 = no change to region * *Exceptions: * *******************************************************************************/ static int __cdecl _heapmin_region ( int index, void * regend, REG1 _PBLKDESC pdesc ) { unsigned size; REG2 _PBLKDESC pnew; #if defined(_M_MPPC) || defined(_M_M68K) struct _heap_region_ *pHeapRegions; #endif /* * Init some variables * * regend = 1st address AFTER region * size = amount of free memory at end of current region */ regend = (char *) regend + 1; /* "regend++" give compiler error... */ size = ((char *)regend - (char *)_ADDRESS(pdesc)); /* * See if there's enough free memory to release to the OS. * (NOTE: Need more than a page since we may need a back pointer.) */ if ( size <= _PAGESIZE_ ) return(0); /* 0 = no change to region */ /* * We're going to free some memory to the OS. See if the * free block crosses the end of the region and, if so, * split up the block appropriately. */ if ( (_MEMSIZE(pdesc) - size) != 0 ) { /* * The free block spans the end of the region. * Divide it up. */ /* * Get an empty descriptor */ if ( (pnew = __getempty()) == NULL ) return(0); pnew->pblock = regend; /* init block pointer */ * (_PBLKDESC*)regend = pnew; /* init back pointer */ _SET_FREE(pnew); /* set the block free */ pnew->pnextdesc = pdesc->pnextdesc; /* link it in */ pdesc->pnextdesc = pnew; } /* * At this point, we have a free block of memory that goes * up to (but not exceeding) the end of the region. * * pdesc = descriptor of the last free block in region * size = amount of free mem at end of region (i.e., _MEMSIZE(pdesc)) * regend = 1st address AFTER end of region */ /* * See if we should return the whole region of only part of it. */ #if defined(_M_MPPC) || defined(_M_M68K) pHeapRegions = (struct _heap_region_ *)(*hHeapRegions); if ( _ADDRESS(pdesc) == (pHeapRegions + index)->_regbase ) { #else if ( _ADDRESS(pdesc) == _heap_regions[index]._regbase ) { #endif /* * Whole region is free, return it to OS */ _heap_free_region(index); /* * Put a dummy block in the heap to hold space for * the memory we just freed up. */ _SET_DUMMY(pdesc); } else { /* * Whole region is NOT free, return part of it to OS */ #if !defined(_M_MPPC) && !defined(_M_M68K) _free_partial_region(pdesc, size, index); #endif } /* * Exit paths */ return(1); /* 1 = minimized region */ } /*** *_free_partial_region() - Free part of a region to the OS * *Purpose: * Free a portion of a region to the OS * *Entry: * pdesc = descriptor of last free block in region * size = amount of free mem at end of region (i.e., _MEMSIZE(pdesc)) * index = index of region * *Exit: * *Exceptions: * *******************************************************************************/ static void __cdecl _free_partial_region ( REG1 _PBLKDESC pdesc, unsigned size, int index ) { unsigned left; void * base; REG2 _PBLKDESC pnew; #if defined(_M_MPPC) || defined(_M_M68K) struct _heap_region_ *pHeapRegions; #endif /* * Init a few variables. */ left = (size & (_PAGESIZE_-1)); base = (char *)_ADDRESS(pdesc); /* * We return memory to the OS in page multiples. If the * free block is not page aligned, we'll insert a new free block * to fill in the difference. */ if ( left != 0 ) { /* * The block is not a multiple of pages so we need * to adjust variables accordingly. */ size -= left; base = (char *)base + left; } /* * Return the free pages to the OS. */ #if defined(_M_MPPC) || defined(_M_M68K) if (base) { DisposePtr(base); } /* * Adjust the region table entry */ pHeapRegions = (struct _heap_region_ *)(*hHeapRegions); (pHeapRegions + index)->_currsize -= size; #else /* !defined(_M_MPPC) && !defined(_M_M68K) */ if (!VirtualFree(base, size, MEM_DECOMMIT)) _heap_abort(); /* * Adjust the region table entry */ _heap_regions[index]._currsize -= size; #endif /* defined(_M_MPPC) || defined(_M_M68K) */ /* * Adjust the heap according to whether we released the whole * free block or not. (Don't worry about consecutive dummies, * we'll coalesce them later.) * * base = address of block we just gave back to OS * size = size of block we gave back to OS * left = size of block we did NOT give back to OS */ if ( left == 0 ) { /* * The free block was released to the OS in its * entirety. Make the free block a dummy place holder. */ _SET_DUMMY(pdesc); } else { /* * Did NOT release the whole free block to the OS. * There's a block of free memory we want to leave * in the heap. Insert a dummy entry after it. */ if ( (pnew = __getempty()) == NULL ) _heap_abort(); pnew->pblock = (char *)base; _SET_DUMMY(pnew); pnew->pnextdesc = pdesc->pnextdesc; pdesc->pnextdesc = pnew; } }
static int __cdecl _heap_checkset ( unsigned int _fill ) { REG1 _PBLKDESC pdesc; REG2 _PBLKDESC pnext; int roverfound=0; int retval = _HEAPOK; /* * lock the heap */ _mlock(_HEAP_LOCK); /* * Validate the sentinel */ if (_heap_desc.sentinel.pnextdesc != NULL) { _PRINTERR(_BADSENTINEL); retval = _HEAPBADNODE; goto done; } /* * Test for an empty heap */ if ( (_heap_desc.pfirstdesc == &_heap_desc.sentinel) && (_heap_desc.proverdesc == &_heap_desc.sentinel) ) { retval = _HEAPEMPTY; goto done; } /* * Get and validate the first descriptor */ if ((pdesc = _heap_desc.pfirstdesc) == NULL) { _PRINTERR(_EMPTYHEAP); retval = _HEAPBADBEGIN; goto done; } /* * Walk the heap descriptor list */ while (pdesc != &_heap_desc.sentinel) { /* * Make sure address for this entry is in range. */ if ( (_ADDRESS(pdesc) < _ADDRESS(_heap_desc.pfirstdesc)) || (_ADDRESS(pdesc) > _heap_desc.sentinel.pblock) ) { _PRINTERR(_BADRANGE); retval = _HEAPBADNODE; goto done; } pnext = pdesc->pnextdesc; /* * Make sure the blocks corresponding to pdesc and pnext are * in proper order. */ if ( _ADDRESS(pdesc) >= _ADDRESS(pnext) ) { _PRINTERR(_BADORDER); retval = _HEAPBADNODE; goto done; } /* * Check the backpointer. */ if (_IS_INUSE(pdesc) || _IS_FREE(pdesc)) { if (!_CHECK_PDESC(pdesc)) { retval = _HEAPBADPTR; goto done; } } /* * Check for proverdesc */ if (pdesc == _heap_desc.proverdesc) roverfound++; /* * If it is free, fill it in if appropriate */ if ( _IS_FREE(pdesc) && (_fill != _HEAP_NOFILL) ) memset( (void *)((unsigned)_ADDRESS(pdesc)+_HDRSIZE), _fill, _BLKSIZE(pdesc) ); /* * Onto the next block */ pdesc = pnext; } /* * Make sure we found 1 and only 1 rover */ if (_heap_desc.proverdesc == &_heap_desc.sentinel) roverfound++; if (roverfound != 1) { _PRINTERR(_BADROVER); retval = _HEAPBADBEGIN; goto done; } /* * Walk the empty list. We can't really compare values against * anything but we may loop forever or may cause a fault. */ pdesc = _heap_desc.emptylist; while (pdesc != NULL) { pnext = pdesc->pnextdesc; /* * Header should only appear once */ if (pnext == _heap_desc.emptylist) { _PRINTERR(_EMPTYLOOP) retval = _HEAPBADPTR; goto done; } pdesc = pnext; } /* * Common return */ done: /* * release the heap lock */ _munlock(_HEAP_LOCK); return(retval); }