/*** *realloc.c - Reallocate a block of memory in the heap * * Copyright (c) 1989-2001, Microsoft Corporation. All rights reserved. * *Purpose: * Defines the realloc() and _expand() functions. * *Revision History: * 10-25-89 GJF Module created. * 11-06-89 GJF Massively revised to handle 'tiling' and to properly * update proverdesc. * 11-10-89 GJF Added MTHREAD support. * 11-17-89 GJF Fixed pblck validation (i.e., conditional call to * _heap_abort()) * 12-18-89 GJF Changed header file name to heap.h, also added explicit * _cdecl or _pascal to function defintions * 12-20-89 GJF Removed references to plastdesc * 01-04-90 GJF Fixed a couple of subtle and nasty bugs in _expand(). * 03-11-90 GJF Replaced _cdecl with _CALLTYPE1, added #include * and removed #include . * 03-29-90 GJF Made _heap_expand_block() _CALLTYPE4. * 07-25-90 SBM Replaced by , replaced * by * 09-28-90 GJF New-style function declarators. * 12-28-90 SRW Added cast of void * to char * for Mips C Compiler * 03-05-91 GJF Changed strategy for rover - old version available * by #define-ing _OLDROVER_. * 04-08-91 GJF Temporary hack for Win32/DOS folks - special version * of realloc that uses just malloc, _msize, memcpy and * free. Change conditioned on _WIN32DOS_. * 05-28-91 GJF Removed M_I386 conditionals and put in _WIN32_ * conditionals to build non-tiling heap for Win32. * 04-06-93 SKS Replace _CRTAPI* with __cdecl * 08-06-93 SKS Fix bug in realloc() - if block cannot be expanded in * place, and call to malloc() fails, the code in this * routine was inadvertently freeing the sucessor block. * Reported by Phar Lap TNT team after MSVCNT was final. * 09-27-93 GJF Added checks of block size argument(s) against * _HEAP_MAXREQ. Removed old _CRUISER_ and _WIN32DOS_ * support. Added some indenting to complicated exprs. * 12-10-93 GJF Replace 4 (bytes) with _GRANULARITY. * 03-02-94 GJF If _heap_split_block() returns failure, which it now * can, return the untrimmed allocation block. * 11-03-94 CFW Debug heap support. * 12-01-94 CFW Use malloc with new handler. * 02-06-95 CFW assert -> _ASSERTE. * 02-07-95 GJF Merged in Mac version. Temporarily #ifdef-ed out the * dbgint.h stuff. Removed obsolete _OLDROVER_ code. * 02-09-95 GJF Restored *_base names. * 05-01-95 GJF Spliced on winheap version. * 05-08-95 CFW Changed new handler scheme. * 05-22-95 GJF Test against _HEAP_MAXREQ before calling API. * 05-24-95 CFW Official ANSI C++ new handler added. * 03-05-96 GJF Added support for small-block heap. * 04-10-96 GJF Return type of __sbh_find_block, __sbh_resize_block * and __sbh_free_block changed to __map_t *. * 05-30-96 GJF Minor changes for latest version of small-block heap. * 05-22-97 RDK New small-block heap scheme implemented. * 09-26-97 BWT Fix POSIX * 11-05-97 GJF Small POSIX fix from Roger Lanser. * 12-17-97 GJF Exception-safe locking. * 05-22-98 JWM Support for KFrei's RTC work. * 07-28-98 JWM RTC update. * 09-30-98 GJF Bypass all small-block heap code when __sbh_initialized * is 0. * 11-16-98 GJF Merged in VC++ 5.0 version of small-block heap. * 12-18-98 GJF Changes for 64-bit size_t. * 03-30-99 GJF __sbh_alloc_block may have moved the header list * 05-01-99 PML Disable small-block heap for Win64. * 05-17-99 PML Remove all Macintosh support. * 05-26-99 KBF Updated RTC hook func params * 06-22-99 GJF Removed old small-block heap from static libs. * 08-04-00 PML Don't round allocation sizes when using system * heap (VS7#131005). * *******************************************************************************/ #ifdef WINHEAP #include #include #include #include #include #include #include #include #include #include /*** *void *realloc(pblock, newsize) - reallocate a block of memory in the heap * *Purpose: * Reallocates a block in the heap to newsize bytes. newsize may be * either greater or less than the original size of the block. The * reallocation may result in moving the block as well as changing * the size. If the block is moved, the contents of the original block * are copied over. * * Special ANSI Requirements: * * (1) realloc(NULL, newsize) is equivalent to malloc(newsize) * * (2) realloc(pblock, 0) is equivalent to free(pblock) (except that * NULL is returned) * * (3) if the realloc() fails, the object pointed to by pblock is left * unchanged * *Entry: * void *pblock - pointer to block in the heap previously allocated * by a call to malloc(), realloc() or _expand(). * * size_t newsize - requested size for the re-allocated block * *Exit: * Success: Pointer to the re-allocated memory block * Failure: NULL * *Uses: * *Exceptions: * If pblock does not point to a valid allocation block in the heap, * realloc() will behave unpredictably and probably corrupt the heap. * *******************************************************************************/ void * __cdecl _realloc_base (void * pBlock, size_t newsize) { #ifdef _POSIX_ return HeapReAlloc(_crtheap, 0, pBlock, newsize); #else void * pvReturn; size_t origSize = newsize; // if ptr is NULL, call malloc if (pBlock == NULL) return(_malloc_base(newsize)); // if ptr is nonNULL and size is zero, call free and return NULL if (newsize == 0) { _free_base(pBlock); return(NULL); } #ifdef HEAPHOOK // call heap hook if installed if (_heaphook) { if ((*_heaphook)(_HEAP_REALLOC, newsize, pBlock, (void *)&pvReturn)) return pvReturn; } #endif /* HEAPHOOK */ #ifndef _WIN64 if ( __active_heap == __V6_HEAP ) { PHEADER pHeader; size_t oldsize; for (;;) { pvReturn = NULL; if (newsize <= _HEAP_MAXREQ) { #ifdef _MT _mlock( _HEAP_LOCK ); __try { #endif // test if current block is in the small-block heap if ((pHeader = __sbh_find_block(pBlock)) != NULL) { // if the new size is not over __sbh_threshold, attempt // to reallocate within the small-block heap if (newsize <= __sbh_threshold) { if (__sbh_resize_block(pHeader, pBlock, (int)newsize)) pvReturn = pBlock; else if ((pvReturn = __sbh_alloc_block((int)newsize)) != NULL) { oldsize = ((PENTRY)((char *)pBlock - sizeof(int)))->sizeFront - 1; memcpy(pvReturn, pBlock, __min(oldsize, newsize)); // headers may have moved, get pHeader again pHeader = __sbh_find_block(pBlock); __sbh_free_block(pHeader, pBlock); } } // If the reallocation has not been (successfully) // performed in the small-block heap, try to allocate // a new block with HeapAlloc. if (pvReturn == NULL) { if (newsize == 0) newsize = 1; newsize = (newsize + BYTES_PER_PARA - 1) & ~(BYTES_PER_PARA - 1); if ((pvReturn = HeapAlloc(_crtheap, 0, newsize)) != NULL) { oldsize = ((PENTRY)((char *)pBlock - sizeof(int)))->sizeFront - 1; memcpy(pvReturn, pBlock, __min(oldsize, newsize)); __sbh_free_block(pHeader, pBlock); } } } #ifdef _MT } __finally { _munlock( _HEAP_LOCK ); } #endif // the current block is NOT in the small block heap iff pHeader // is NULL if ( pHeader == NULL ) { if (newsize == 0) newsize = 1; newsize = (newsize + BYTES_PER_PARA - 1) & ~(BYTES_PER_PARA - 1); pvReturn = HeapReAlloc(_crtheap, 0, pBlock, newsize); } } if ( pvReturn || _newmode == 0) { if (pvReturn) { RTCCALLBACK(_RTC_Free_hook, (pBlock, 0)); RTCCALLBACK(_RTC_Allocate_hook, (pvReturn, newsize, 0)); } return pvReturn; } // call installed new handler if (!_callnewh(newsize)) return NULL; // new handler was successful -- try to allocate again } } #ifdef CRTDLL else if ( __active_heap == __V5_HEAP ) { __old_sbh_region_t *preg; __old_sbh_page_t * ppage; __old_page_map_t * pmap; size_t oldsize; // round up to the nearest paragrap if ( newsize <= _HEAP_MAXREQ ) if ( newsize > 0 ) newsize = (newsize + _OLD_PARASIZE - 1) & ~(_OLD_PARASIZE - 1); else newsize = _OLD_PARASIZE; for (;;) { pvReturn = NULL; if ( newsize <= _HEAP_MAXREQ ) { #ifdef _MT _mlock( _HEAP_LOCK ); __try { #endif if ( (pmap = __old_sbh_find_block(pBlock, &preg, &ppage)) != NULL ) { // If the new size falls below __sbh_threshold, try to // carry out the reallocation within the small-block // heap. if ( newsize < __old_sbh_threshold ) { if ( __old_sbh_resize_block(preg, ppage, pmap, newsize >> _OLD_PARASHIFT) ) { pvReturn = pBlock; } else if ( (pvReturn = __old_sbh_alloc_block(newsize >> _OLD_PARASHIFT)) != NULL ) { oldsize = ((size_t)(*pmap)) << _OLD_PARASHIFT ; memcpy(pvReturn, pBlock, __min(oldsize, newsize)); __old_sbh_free_block(preg, ppage, pmap); } } // If the reallocation has not been (successfully) // performed in the small-block heap, try to allocate a // new block with HeapAlloc. if ( (pvReturn == NULL) && ((pvReturn = HeapAlloc(_crtheap, 0, newsize)) != NULL) ) { oldsize = ((size_t)(*pmap)) << _OLD_PARASHIFT; memcpy(pvReturn, pBlock, __min(oldsize, newsize)); __old_sbh_free_block(preg, ppage, pmap); } } else { pvReturn = HeapReAlloc(_crtheap, 0, pBlock, newsize); } #ifdef _MT } __finally { _munlock(_HEAP_LOCK); } #endif } if ( pvReturn || _newmode == 0) { if (pvReturn) { RTCCALLBACK(_RTC_Free_hook, (pBlock, 0)); RTCCALLBACK(_RTC_Allocate_hook, (pvReturn, newsize, 0)); } return pvReturn; } // call installed new handler if (!_callnewh(newsize)) return NULL; // new handler was successful -- try to allocate again } } #endif /* CRTDLL */ else // __active_heap == __SYSTEM_HEAP ) #endif /* ndef _WIN64 */ { for (;;) { pvReturn = NULL; if (newsize <= _HEAP_MAXREQ) { if (newsize == 0) newsize = 1; pvReturn = HeapReAlloc(_crtheap, 0, pBlock, newsize); } if ( pvReturn || _newmode == 0) { if (pvReturn) { RTCCALLBACK(_RTC_Free_hook, (pBlock, 0)); RTCCALLBACK(_RTC_Allocate_hook, (pvReturn, newsize, 0)); } return pvReturn; } // call installed new handler if (!_callnewh(newsize)) return NULL; // new handler was successful -- try to allocate again } } #endif /* ndef _POSIX_ */ } #else /* ndef WINHEAP */ #include #include #include #include #include #include #include /* useful macro to compute the size of an allocation block given both a * pointer to the descriptor and a pointer to the user area of the block * (more efficient variant of _BLKSIZE macro, given the extra information) */ #define BLKSZ(pdesc_m,pblock_m) ((unsigned)_ADDRESS((pdesc_m)->pnextdesc) - \ (unsigned)(pblock_m)) /* expand an allocation block, in place, up to or beyond a specified size * by coalescing it with subsequent free blocks (if possible) */ static int __cdecl _heap_expand_block(_PBLKDESC, size_t *, size_t); /*** *void *realloc(void *pblock, size_t newsize) - reallocate a block of memory in * the heap * *Purpose: * Re-allocates a block in the heap to newsize bytes. newsize may be * either greater or less than the original size of the block. The * re-allocation may result in moving the block as well as changing * the size. If the block is moved, the contents of the original block * are copied over. * * Special ANSI Requirements: * * (1) realloc(NULL, newsize) is equivalent to malloc(newsize) * * (2) realloc(pblock, 0) is equivalent to free(pblock) (except that * NULL is returned) * * (3) if the realloc() fails, the object pointed to by pblock is left * unchanged * * Special Notes For Multi-thread: The heap is locked immediately prior * to assigning pdesc. This is after special cases (1) and (2), listed * above, are taken care of. The lock is released immediately prior to * the final return statement. * *Entry: * void *pblock - pointer to block in the heap previously allocated * by a call to malloc(), realloc() or _expand(). * * size_t newsize - requested size for the re-allocated block * *Exit: * Success: Pointer to the re-allocated memory block * Failure: NULL * *Uses: * *Exceptions: * If pblock does not point to a valid allocation block in the heap, * realloc() will behave unpredictably and probably corrupt the heap. * *******************************************************************************/ void * __cdecl _realloc_base ( REG1 void *pblock, size_t newsize ) { REG2 _PBLKDESC pdesc; _PBLKDESC pdesc2; void *retp; size_t oldsize; size_t currsize; /* special cases, handling mandated by ANSI */ if ( pblock == NULL ) /* just do a malloc of newsize bytes and return a pointer to * the new block */ return( _malloc_base(newsize) ); if ( newsize == 0 ) { /* free the block and return NULL */ _free_base(pblock); return( NULL ); } /* make newsize a valid allocation block size (i.e., round up to the * nearest whole number of dwords) */ newsize = _ROUND2(newsize, _GRANULARITY); #ifdef HEAPHOOK /* call heap hook if installed */ if (_heaphook) { if ((*_heaphook)(_HEAP_REALLOC, newsize, pblock, (void *)&retp)) return retp; } #endif /* HEAPHOOK */ /* if multi-thread support enabled, lock the heap here */ _mlock(_HEAP_LOCK); /* set pdesc to point to the descriptor for *pblock */ pdesc = _BACKPTR(pblock); if ( _ADDRESS(pdesc) != ((char *)pblock - _HDRSIZE) ) _heap_abort(); /* see if pblock is big enough already, or can be expanded (in place) * to be big enough. */ if ( ((oldsize = currsize = BLKSZ(pdesc, pblock)) > newsize) || (_heap_expand_block(pdesc, &currsize, newsize) == 0) ) { /* if necessary, mark pdesc as inuse */ if ( _IS_FREE(pdesc) ) { _SET_INUSE(pdesc); } /* trim pdesc down to be exactly newsize bytes, if necessary */ if ( (currsize > newsize) && ((pdesc2 = _heap_split_block(pdesc, newsize)) != NULL) ) { _SET_FREE(pdesc2); } retp = pblock; goto realloc_done; } /* try malloc-ing a new block of the requested size. if successful, * copy over the data from the original block and free it. */ if ( (retp = _malloc_base(newsize)) != NULL ) { memcpy(retp, pblock, oldsize); _free_base_lk(pblock); } /* else if unsuccessful, return retp (== NULL) */ realloc_done: /* if multi-thread support is enabled, unlock the heap here */ _munlock(_HEAP_LOCK); return(retp); } /*** *void *_expand(void *pblock, size_t newsize) - expand/contract a block of memory * in the heap * *Purpose: * Resizes a block in the heap to newsize bytes. newsize may be either * greater (expansion) or less (contraction) than the original size of * the block. The block is NOT moved. In the case of expansion, if the * block cannot be expanded to newsize bytes, it is expanded as much as * possible. * * Special Notes For Multi-thread: The heap is locked just before pdesc * is assigned and unlocked immediately prior to the return statement. * *Entry: * void *pblock - pointer to block in the heap previously allocated * by a call to malloc(), realloc() or _expand(). * * size_t newsize - requested size for the resized block * *Exit: * Success: Pointer to the resized memory block (i.e., pblock) * Failure: NULL * *Uses: * *Exceptions: * If pblock does not point to a valid allocation block in the heap, * _expand() will behave unpredictably and probably corrupt the heap. * *******************************************************************************/ void * __cdecl _expand_base ( REG1 void *pblock, size_t newsize ) { REG2 _PBLKDESC pdesc; _PBLKDESC pdesc2; void *retp; size_t oldsize; size_t currsize; int index; /* make newsize a valid allocation block size (i.e., round up to the * nearest whole number of dwords) */ newsize = _ROUND2(newsize, _GRANULARITY); #ifdef HEAPHOOK /* call heap hook if installed */ if (_heaphook) { if ((*_heaphook)(_HEAP_EXPAND, newsize, pblock, (void *)&retp)) return retp; } #endif /* HEAPHOOK */ retp = pblock; /* validate size */ if ( newsize > _HEAP_MAXREQ ) newsize = _HEAP_MAXREQ; /* if multi-thread support enabled, lock the heap here */ _mlock(_HEAP_LOCK); /* set pdesc to point to the descriptor for *pblock */ pdesc = _BACKPTR(pblock); /* see if pblock is big enough already, or can be expanded (in place) * to be big enough. */ if ( ((oldsize = currsize = BLKSZ(pdesc, pblock)) >= newsize) || (_heap_expand_block(pdesc, &currsize, newsize) == 0) ) { /* pblock is (now) big enough. trim it down, if necessary */ if ( (currsize > newsize) && ((pdesc2 = _heap_split_block(pdesc, newsize)) != NULL) ) { _SET_FREE(pdesc2); currsize = newsize; } goto expand_done; } /* if the heap block is at the end of a region, attempt to grow the * region */ if ( (pdesc->pnextdesc == &_heap_desc.sentinel) || _IS_DUMMY(pdesc->pnextdesc) ) { /* look up the region index */ for ( index = 0 ; index < _HEAP_REGIONMAX ; index++ ) if ( (_heap_regions[index]._regbase < pblock) && (((char *)(_heap_regions[index]._regbase) + _heap_regions[index]._currsize) >= (char *)pblock) ) break; /* make sure a valid region index was obtained (pblock could * lie in a portion of heap memory donated by a user call to * _heapadd(), which therefore would not appear in the region * table) */ if ( index == _HEAP_REGIONMAX ) { retp = NULL; goto expand_done; } /* try growing the region. the difference between newsize and * the current size of the block, rounded up to the nearest * whole number of pages, is the amount the region needs to * be grown. if successful, try expanding the block again */ if ( (_heap_grow_region(index, _ROUND2(newsize - currsize, _PAGESIZE_)) == 0) && (_heap_expand_block(pdesc, &currsize, newsize) == 0) ) { /* pblock is (now) big enough. trim it down to be * exactly size bytes, if necessary */ if ( (currsize > newsize) && ((pdesc2 = _heap_split_block(pdesc, newsize)) != NULL) ) { _SET_FREE(pdesc2); currsize = newsize; } } else retp = NULL; } else retp = NULL; expand_done: /* if multi-thread support is enabled, unlock the heap here */ _munlock(_HEAP_LOCK); return(retp); } /*** *int _heap_expand_block(pdesc, pcurrsize, newsize) - expand an allocation block * in place (without trying to 'grow' the heap) * *Purpose: * *Entry: * _PBLKDESC pdesc - pointer to the allocation block descriptor * size_t *pcurrsize - pointer to size of the allocation block (i.e., * *pcurrsize == _BLKSIZE(pdesc), on entry) * size_t newsize - requested minimum size for the expanded allocation * block (i.e., newsize >= _BLKSIZE(pdesc), on exit) * *Exit: * Success: 0 * Failure: -1 * In either case, *pcurrsize is updated with the new size of the block * *Exceptions: * It is assumed that pdesc points to a valid allocation block descriptor. * It is also assumed that _BLKSIZE(pdesc) == *pcurrsize on entry. If * either of these assumptions is violated, _heap_expand_block will almost * certainly trash the heap. * *******************************************************************************/ static int __cdecl _heap_expand_block ( REG1 _PBLKDESC pdesc, REG3 size_t *pcurrsize, size_t newsize ) { REG2 _PBLKDESC pdesc2; _ASSERTE(("_heap_expand_block: bad pdesc arg", _CHECK_PDESC(pdesc))); _ASSERTE(("_heap_expand_block: bad pcurrsize arg", *pcurrsize == _BLKSIZE(pdesc))); for ( pdesc2 = pdesc->pnextdesc ; _IS_FREE(pdesc2) ; pdesc2 = pdesc->pnextdesc ) { /* coalesce with pdesc. check for special case of pdesc2 * being proverdesc. */ pdesc->pnextdesc = pdesc2->pnextdesc; if ( pdesc2 == _heap_desc.proverdesc ) _heap_desc.proverdesc = pdesc; /* update *pcurrsize, place *pdesc2 on the empty descriptor * list and see if the coalesced block is now big enough */ *pcurrsize += _MEMSIZE(pdesc2); _PUTEMPTY(pdesc2) } if ( *pcurrsize >= newsize ) return(0); else return(-1); } #endif /* WINHEAP */