home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / crt / src / realloc.c < prev    next >
C/C++ Source or Header  |  1998-06-17  |  18KB  |  525 lines

  1. /***
  2. *realloc.c - Reallocate a block of memory in the heap
  3. *
  4. *       Copyright (c) 1989-1997, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. *       Defines the realloc() and _expand() functions.
  8. *
  9. *******************************************************************************/
  10.  
  11. #ifdef WINHEAP
  12.  
  13. #include <cruntime.h>
  14. #include <malloc.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <winheap.h>
  18. #include <windows.h>
  19. #include <internal.h>
  20. #include <mtdll.h>
  21. #include <dbgint.h>
  22.  
  23.  
  24. /***
  25. *void *realloc(pblock, newsize) - reallocate a block of memory in the heap
  26. *
  27. *Purpose:
  28. *       Reallocates a block in the heap to newsize bytes. newsize may be
  29. *       either greater or less than the original size of the block. The
  30. *       reallocation may result in moving the block as well as changing
  31. *       the size. If the block is moved, the contents of the original block
  32. *       are copied over.
  33. *
  34. *       Special ANSI Requirements:
  35. *
  36. *       (1) realloc(NULL, newsize) is equivalent to malloc(newsize)
  37. *
  38. *       (2) realloc(pblock, 0) is equivalent to free(pblock) (except that
  39. *           NULL is returned)
  40. *
  41. *       (3) if the realloc() fails, the object pointed to by pblock is left
  42. *           unchanged
  43. *
  44. *Entry:
  45. *       void *pblock    - pointer to block in the heap previously allocated
  46. *                         by a call to malloc(), realloc() or _expand().
  47. *
  48. *       size_t newsize  - requested size for the re-allocated block
  49. *
  50. *Exit:
  51. *       Success:  Pointer to the re-allocated memory block
  52. *       Failure:  NULL
  53. *
  54. *Uses:
  55. *
  56. *Exceptions:
  57. *       If pblock does not point to a valid allocation block in the heap,
  58. *       realloc() will behave unpredictably and probably corrupt the heap.
  59. *
  60. *******************************************************************************/
  61.  
  62. void * __cdecl _realloc_base (void * pBlock, size_t newsize)
  63. {
  64.     PHEADER     pHeader;
  65.     void *      pvReturn;
  66.     size_t      oldsize;
  67.  
  68.     //  if ptr is NULL, call malloc
  69.     if (pBlock == NULL)
  70.         return(_malloc_base(newsize));
  71.  
  72.     //  if ptr is nonNULL and size is zero, call free and return NULL
  73.     if (newsize == 0)
  74.     {
  75.         _free_base(pBlock);
  76.         return(NULL);
  77.     }
  78.  
  79.  
  80.     for (;;) {
  81.  
  82.         pvReturn = NULL;
  83.         if (newsize <= _HEAP_MAXREQ)
  84.         {
  85.             _mlock(_HEAP_LOCK);
  86.  
  87.             //  test if current block is in the small-block heap
  88.             if ((pHeader = __sbh_find_block(pBlock)) != NULL)
  89.             {
  90.                 //  if the new size is not over __sbh_threshold, attempt
  91.                 //  to reallocate within the small-block heap
  92.                 if (newsize <= __sbh_threshold)
  93.                 {
  94.                     if (__sbh_resize_block(pHeader, pBlock, newsize))
  95.                         pvReturn = pBlock;
  96.                     else if ((pvReturn = __sbh_alloc_block(newsize)) != NULL)
  97.                     {
  98.                         oldsize = ((PENTRY)((char *)pBlock -
  99.                                             sizeof(int)))->sizeFront - 1;
  100.                         memcpy(pvReturn, pBlock, __min(oldsize, newsize));
  101.                         __sbh_free_block(pHeader, pBlock);
  102.                     }
  103.                 }
  104.  
  105.                 //  If the reallocation has not been (successfully)
  106.                 //  performed in the small-block heap, try to allocate
  107.                 //  a new block with HeapAlloc.
  108.                 if (pvReturn == NULL)
  109.                 {
  110.                     if (newsize == 0)
  111.                         newsize = 1;
  112.                     newsize = (newsize + BYTES_PER_PARA - 1) &
  113.                                        ~(BYTES_PER_PARA - 1);
  114.                     if ((pvReturn = HeapAlloc(_crtheap, 0, newsize)) != NULL)
  115.                     {
  116.                         oldsize = ((PENTRY)((char *)pBlock -
  117.                                                 sizeof(int)))->sizeFront - 1;
  118.                         memcpy(pvReturn, pBlock, __min(oldsize, newsize));
  119.                         __sbh_free_block(pHeader, pBlock);
  120.                     }
  121.                 }
  122.                 _munlock(_HEAP_LOCK);
  123.             }
  124.             else
  125.             {
  126.                 _munlock(_HEAP_LOCK);
  127.                 if (newsize == 0)
  128.                     newsize = 1;
  129.                 newsize = (newsize + BYTES_PER_PARA - 1) &
  130.                                    ~(BYTES_PER_PARA - 1);
  131.                 pvReturn = HeapReAlloc(_crtheap, 0, pBlock, newsize);
  132.             }
  133.         }
  134.  
  135.         if ( pvReturn || _newmode == 0)
  136.             return pvReturn;
  137.  
  138.         //  call installed new handler
  139.         if (!_callnewh(newsize))
  140.             return NULL;
  141.  
  142.         //  new handler was successful -- try to allocate again
  143.     }
  144. }
  145.  
  146. #else  /* WINHEAP */
  147.  
  148.  
  149. #include <cruntime.h>
  150. #include <heap.h>
  151. #include <malloc.h>
  152. #include <mtdll.h>
  153. #include <stddef.h>
  154. #include <string.h>
  155. #include <dbgint.h>
  156.  
  157. #if defined (_M_MPPC) || defined (_M_M68K)
  158. #include <macos\memory.h>       // Mac OS interface header
  159. #endif  /* defined (_M_MPPC) || defined (_M_M68K) */
  160.  
  161. /* useful macro to compute the size of an allocation block given both a
  162.  * pointer to the descriptor and a pointer to the user area of the block
  163.  * (more efficient variant of _BLKSIZE macro, given the extra information)
  164.  */
  165. #define BLKSZ(pdesc_m,pblock_m)   ((unsigned)_ADDRESS((pdesc_m)->pnextdesc) - \
  166.                     (unsigned)(pblock_m))
  167.  
  168. /* expand an allocation block, in place, up to or beyond a specified size
  169.  * by coalescing it with subsequent free blocks (if possible)
  170.  */
  171. static int __cdecl _heap_expand_block(_PBLKDESC, size_t *, size_t);
  172.  
  173. #if defined (_M_MPPC) || defined (_M_M68K)
  174. extern Handle hHeapRegions;
  175. extern int _heap_region_table_cur;
  176. #endif  /* defined (_M_MPPC) || defined (_M_M68K) */
  177.  
  178. /***
  179. *void *realloc(void *pblock, size_t newsize) - reallocate a block of memory in
  180. *       the heap
  181. *
  182. *Purpose:
  183. *       Re-allocates a block in the heap to newsize bytes. newsize may be
  184. *       either greater or less than the original size of the block. The
  185. *       re-allocation may result in moving the block as well as changing
  186. *       the size. If the block is moved, the contents of the original block
  187. *       are copied over.
  188. *
  189. *       Special ANSI Requirements:
  190. *
  191. *       (1) realloc(NULL, newsize) is equivalent to malloc(newsize)
  192. *
  193. *       (2) realloc(pblock, 0) is equivalent to free(pblock) (except that
  194. *           NULL is returned)
  195. *
  196. *       (3) if the realloc() fails, the object pointed to by pblock is left
  197. *           unchanged
  198. *
  199. *       Special Notes For Multi-thread: The heap is locked immediately prior
  200. *       to assigning pdesc. This is after special cases (1) and (2), listed
  201. *       above, are taken care of. The lock is released immediately prior to
  202. *       the final return statement.
  203. *
  204. *Entry:
  205. *       void *pblock - pointer to block in the heap previously allocated
  206. *                 by a call to malloc(), realloc() or _expand().
  207. *
  208. *       size_t newsize  - requested size for the re-allocated block
  209. *
  210. *Exit:
  211. *       Success:  Pointer to the re-allocated memory block
  212. *       Failure:  NULL
  213. *
  214. *Uses:
  215. *
  216. *Exceptions:
  217. *       If pblock does not point to a valid allocation block in the heap,
  218. *       realloc() will behave unpredictably and probably corrupt the heap.
  219. *
  220. *******************************************************************************/
  221.  
  222. void * __cdecl _realloc_base (
  223.         REG1 void *pblock,
  224.         size_t newsize
  225.         )
  226. {
  227.         REG2 _PBLKDESC pdesc;
  228.         _PBLKDESC pdesc2;
  229.         void *retp;
  230.         size_t oldsize;
  231.         size_t currsize;
  232.  
  233.         /* special cases, handling mandated by ANSI
  234.          */
  235.         if ( pblock == NULL )
  236.             /* just do a malloc of newsize bytes and return a pointer to
  237.              * the new block
  238.               */
  239.             return( _malloc_base(newsize) );
  240.  
  241.         if ( newsize == 0 ) {
  242.             /* free the block and return NULL
  243.              */
  244.             _free_base(pblock);
  245.             return( NULL );
  246.         }
  247.  
  248.         /* make newsize a valid allocation block size (i.e., round up to the
  249.          * nearest whole number of dwords)
  250.          */
  251.         newsize = _ROUND2(newsize, _GRANULARITY);
  252.  
  253.  
  254.         /* if multi-thread support enabled, lock the heap here
  255.          */
  256.         _mlock(_HEAP_LOCK);
  257.  
  258.         /* set pdesc to point to the descriptor for *pblock
  259.          */
  260.         pdesc = _BACKPTR(pblock);
  261.  
  262.         if ( _ADDRESS(pdesc) != ((char *)pblock - _HDRSIZE) )
  263.             _heap_abort();
  264.  
  265.         /* see if pblock is big enough already, or can be expanded (in place)
  266.          * to be big enough.
  267.          */
  268.         if ( ((oldsize = currsize = BLKSZ(pdesc, pblock)) > newsize) ||
  269.              (_heap_expand_block(pdesc, &currsize, newsize) == 0) ) {
  270.  
  271.             /* if necessary, mark pdesc as inuse
  272.              */
  273.             if ( _IS_FREE(pdesc) ) {
  274.                 _SET_INUSE(pdesc);
  275.             }
  276.  
  277.             /* trim pdesc down to be exactly newsize bytes, if necessary
  278.              */
  279.             if ( (currsize > newsize) &&
  280.                  ((pdesc2 = _heap_split_block(pdesc, newsize)) != NULL) )
  281.             {
  282.                 _SET_FREE(pdesc2);
  283.             }
  284.  
  285.             retp = pblock;
  286.             goto realloc_done;
  287.         }
  288.  
  289.         /* try malloc-ing a new block of the requested size. if successful,
  290.          * copy over the data from the original block and free it.
  291.          */
  292.         if ( (retp = _malloc_base(newsize)) != NULL ) {
  293.             memcpy(retp, pblock, oldsize);
  294.             _free_base_lk(pblock);
  295.         }
  296.         /* else if unsuccessful, return retp (== NULL) */
  297.  
  298. realloc_done:
  299.         /* if multi-thread support is enabled, unlock the heap here
  300.          */
  301.         _munlock(_HEAP_LOCK);
  302.  
  303.         return(retp);
  304. }
  305.  
  306.  
  307. /***
  308. *void *_expand(void *pblock, size_t newsize) - expand/contract a block of memory
  309. *       in the heap
  310. *
  311. *Purpose:
  312. *       Resizes a block in the heap to newsize bytes. newsize may be either
  313. *       greater (expansion) or less (contraction) than the original size of
  314. *       the block. The block is NOT moved. In the case of expansion, if the
  315. *       block cannot be expanded to newsize bytes, it is expanded as much as
  316. *       possible.
  317. *
  318. *       Special Notes For Multi-thread: The heap is locked just before pdesc
  319. *       is assigned and unlocked immediately prior to the return statement.
  320. *
  321. *Entry:
  322. *       void *pblock - pointer to block in the heap previously allocated
  323. *                 by a call to malloc(), realloc() or _expand().
  324. *
  325. *       size_t newsize  - requested size for the resized block
  326. *
  327. *Exit:
  328. *       Success:  Pointer to the resized memory block (i.e., pblock)
  329. *       Failure:  NULL
  330. *
  331. *Uses:
  332. *
  333. *Exceptions:
  334. *       If pblock does not point to a valid allocation block in the heap,
  335. *       _expand() will behave unpredictably and probably corrupt the heap.
  336. *
  337. *******************************************************************************/
  338.  
  339. void * __cdecl _expand_base (
  340.         REG1 void *pblock,
  341.         size_t newsize
  342.         )
  343. {
  344.         REG2 _PBLKDESC pdesc;
  345.         _PBLKDESC pdesc2;
  346.         void *retp;
  347.         size_t oldsize;
  348.         size_t currsize;
  349.         int index;
  350. #if defined (_M_MPPC) || defined (_M_M68K)
  351.         struct _heap_region_ *pHeapRegions;
  352. #endif  /* defined (_M_MPPC) || defined (_M_M68K) */
  353.  
  354.         /* make newsize a valid allocation block size (i.e., round up to the
  355.          * nearest whole number of dwords)
  356.          */
  357.         newsize = _ROUND2(newsize, _GRANULARITY);
  358.  
  359.  
  360.         retp = pblock;
  361.  
  362.         /* validate size */
  363.         if ( newsize > _HEAP_MAXREQ )
  364.             newsize = _HEAP_MAXREQ;
  365.  
  366.         /* if multi-thread support enabled, lock the heap here
  367.          */
  368.         _mlock(_HEAP_LOCK);
  369.  
  370.         /* set pdesc to point to the descriptor for *pblock
  371.          */
  372.         pdesc = _BACKPTR(pblock);
  373.  
  374.         /* see if pblock is big enough already, or can be expanded (in place)
  375.          * to be big enough.
  376.          */
  377.         if ( ((oldsize = currsize = BLKSZ(pdesc, pblock)) >= newsize) ||
  378.              (_heap_expand_block(pdesc, &currsize, newsize) == 0) ) {
  379.             /* pblock is (now) big enough. trim it down, if necessary
  380.              */
  381.             if ( (currsize > newsize) &&
  382.                  ((pdesc2 = _heap_split_block(pdesc, newsize)) != NULL) )
  383.             {
  384.                 _SET_FREE(pdesc2);
  385.                 currsize = newsize;
  386.             }
  387.             goto expand_done;
  388.         }
  389.  
  390.         /* if the heap block is at the end of a region, attempt to grow the
  391.          * region
  392.          */
  393.         if ( (pdesc->pnextdesc == &_heap_desc.sentinel) ||
  394.              _IS_DUMMY(pdesc->pnextdesc) ) {
  395.  
  396.             /* look up the region index
  397.              */
  398. #if defined (_M_MPPC) || defined (_M_M68K)
  399.             pHeapRegions = (struct _heap_region_ *)(*hHeapRegions);
  400.             for ( index = 0 ; index < _heap_region_table_cur ; index++ )
  401.                 if (((pHeapRegions + index)->_regbase < pblock) &&
  402.                 (((char *)((pHeapRegions + index)->_regbase) +
  403.                 (pHeapRegions + index)->_currsize) >= (char *)pblock) )
  404.                     break;
  405. #else  /* defined (_M_MPPC) || defined (_M_M68K) */
  406.             for ( index = 0 ; index < _HEAP_REGIONMAX ; index++ )
  407.                 if ( (_heap_regions[index]._regbase < pblock) &&
  408.                      (((char *)(_heap_regions[index]._regbase) +
  409.                        _heap_regions[index]._currsize) >=
  410.                      (char *)pblock) )
  411.                     break;
  412. #endif  /* defined (_M_MPPC) || defined (_M_M68K) */
  413.             /* make sure a valid region index was obtained (pblock could
  414.              * lie in a portion of heap memory donated by a user call to
  415.              * _heapadd(), which therefore would not appear in the region
  416.              * table)
  417.              */
  418. #if defined (_M_MPPC) || defined (_M_M68K)
  419.             if ( index == _heap_region_table_cur ) {
  420. #else  /* defined (_M_MPPC) || defined (_M_M68K) */
  421.             if ( index == _HEAP_REGIONMAX ) {
  422. #endif  /* defined (_M_MPPC) || defined (_M_M68K) */
  423.                 retp = NULL;
  424.                 goto expand_done;
  425.             }
  426.  
  427.             /* try growing the region. the difference between newsize and
  428.              * the current size of the block, rounded up to the nearest
  429.              * whole number of pages, is the amount the region needs to
  430.              * be grown. if successful, try expanding the block again
  431.              */
  432.             if ( (_heap_grow_region(index, _ROUND2(newsize - currsize,
  433.                   _PAGESIZE_)) == 0) &&
  434.                  (_heap_expand_block(pdesc, &currsize, newsize) == 0) )
  435.             {
  436.                 /* pblock is (now) big enough. trim it down to be
  437.                  * exactly size bytes, if necessary
  438.                  */
  439.                 if ( (currsize > newsize) && ((pdesc2 =
  440.                        _heap_split_block(pdesc, newsize)) != NULL) )
  441.                 {
  442.                     _SET_FREE(pdesc2);
  443.                     currsize = newsize;
  444.                 }
  445.             }
  446.             else
  447.                 retp = NULL;
  448.         }
  449.         else
  450.             retp = NULL;
  451.  
  452. expand_done:
  453.         /* if multi-thread support is enabled, unlock the heap here
  454.          */
  455.         _munlock(_HEAP_LOCK);
  456.  
  457.         return(retp);
  458. }
  459.  
  460.  
  461. /***
  462. *int _heap_expand_block(pdesc, pcurrsize, newsize) - expand an allocation block
  463. *       in place (without trying to 'grow' the heap)
  464. *
  465. *Purpose:
  466. *
  467. *Entry:
  468. *       _PBLKDESC pdesc   - pointer to the allocation block descriptor
  469. *       size_t *pcurrsize - pointer to size of the allocation block (i.e.,
  470. *                   *pcurrsize == _BLKSIZE(pdesc), on entry)
  471. *       size_t newsize    - requested minimum size for the expanded allocation
  472. *                   block (i.e., newsize >= _BLKSIZE(pdesc), on exit)
  473. *
  474. *Exit:
  475. *       Success:  0
  476. *       Failure: -1
  477. *       In either case, *pcurrsize is updated with the new size of the block
  478. *
  479. *Exceptions:
  480. *       It is assumed that pdesc points to a valid allocation block descriptor.
  481. *       It is also assumed that _BLKSIZE(pdesc) == *pcurrsize on entry. If
  482. *       either of these assumptions is violated, _heap_expand_block will almost
  483. *       certainly trash the heap.
  484. *
  485. *******************************************************************************/
  486.  
  487. static int __cdecl _heap_expand_block (
  488.         REG1 _PBLKDESC pdesc,
  489.         REG3 size_t *pcurrsize,
  490.         size_t newsize
  491.         )
  492. {
  493.         REG2 _PBLKDESC pdesc2;
  494.  
  495.         _ASSERTE(("_heap_expand_block: bad pdesc arg", _CHECK_PDESC(pdesc)));
  496.         _ASSERTE(("_heap_expand_block: bad pcurrsize arg", *pcurrsize == _BLKSIZE(pdesc)));
  497.  
  498.         for ( pdesc2 = pdesc->pnextdesc ; _IS_FREE(pdesc2) ;
  499.               pdesc2 = pdesc->pnextdesc ) {
  500.  
  501.             /* coalesce with pdesc. check for special case of pdesc2
  502.              * being proverdesc.
  503.              */
  504.             pdesc->pnextdesc = pdesc2->pnextdesc;
  505.  
  506.             if ( pdesc2 == _heap_desc.proverdesc )
  507.                 _heap_desc.proverdesc = pdesc;
  508.  
  509.             /* update *pcurrsize, place *pdesc2 on the empty descriptor
  510.              * list and see if the coalesced block is now big enough
  511.              */
  512.             *pcurrsize += _MEMSIZE(pdesc2);
  513.  
  514.             _PUTEMPTY(pdesc2)
  515.         }
  516.  
  517.         if ( *pcurrsize >= newsize )
  518.             return(0);
  519.         else
  520.             return(-1);
  521. }
  522.  
  523.  
  524. #endif  /* WINHEAP */
  525.