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

  1. /***
  2. *dbgheap.c - Debug CRT Heap Functions
  3. *
  4. *       Copyright (c) 1988-1997, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. *       Defines debug versions of heap functions.
  8. *
  9. *******************************************************************************/
  10.  
  11. #ifdef _DEBUG
  12.  
  13. #ifdef _WIN32
  14. #include <windows.h>
  15. #include <winheap.h>
  16. #else  /* _WIN32 */
  17. #include <memory.h>
  18. #include <string.h>
  19. #define FALSE 0
  20. #define TRUE  1
  21. #define BYTE char
  22. #endif  /* _WIN32 */
  23.  
  24. #include <ctype.h>
  25. #include <dbgint.h>
  26. #ifndef WINHEAP
  27. #include <heap.h>
  28. #endif  /* WINHEAP */
  29. #include <internal.h>
  30. #include <limits.h>
  31. #include <malloc.h>
  32. #include <mtdll.h>
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35.  
  36.  
  37. /*---------------------------------------------------------------------------
  38.  *
  39.  * Heap management
  40.  *
  41.  --------------------------------------------------------------------------*/
  42.  
  43. #ifdef _MAC
  44. extern Handle hHeapRegions;
  45. #endif  /* _MAC */
  46.  
  47. #define IGNORE_REQ  0L              /* Request number for ignore block */
  48. #define IGNORE_LINE 0xFEDCBABC      /* Line number for ignore block */
  49.  
  50.  
  51. /*
  52.  * Bitfield flag that controls CRT heap behavior --
  53.  * default is to record all allocations (_CRTDBG_ALLOC_MEM_DF)
  54.  */
  55. int _crtDbgFlag = _CRTDBG_ALLOC_MEM_DF;
  56.  
  57. static long _lRequestCurr = 1;      /* Current request number */
  58.  
  59. _CRTIMP long _crtBreakAlloc = -1L;          /* Break on allocation by request number */
  60.  
  61. static unsigned long _lTotalAlloc;  /* Grand total - sum of all allocations */
  62. static unsigned long _lCurAlloc;    /* Total amount currently allocated */
  63. static unsigned long _lMaxAlloc;    /* Largest ever allocated at once */
  64.  
  65. /*
  66.  * The following values are non-zero, constant, odd, large, and atypical
  67.  *      Non-zero values help find bugs assuming zero filled data.
  68.  *      Constant values are good so that memory filling is deterministic
  69.  *          (to help make bugs reproducable).  Of course it is bad if
  70.  *          the constant filling of weird values masks a bug.
  71.  *      Mathematically odd numbers are good for finding bugs assuming a cleared
  72.  *          lower bit, as well as useful for trapping on the Mac.
  73.  *      Large numbers (byte values at least) are less typical, and are good
  74.  *          at finding bad addresses.
  75.  *      Atypical values (i.e. not too often) are good since they typically
  76.  *          cause early detection in code.
  77.  *      For the case of no-man's land and free blocks, if you store to any
  78.  *          of these locations, the memory integrity checker will detect it.
  79.  */
  80.  
  81. static unsigned char _bNoMansLandFill = 0xFD;   /* fill no-man's land with this */
  82. static unsigned char _bDeadLandFill   = 0xDD;   /* fill free objects with this */
  83. static unsigned char _bCleanLandFill  = 0xCD;   /* fill new objects with this */
  84.  
  85. static _CrtMemBlockHeader * _pFirstBlock;
  86. static _CrtMemBlockHeader * _pLastBlock;
  87.  
  88. _CRT_DUMP_CLIENT _pfnDumpClient;
  89.  
  90.  
  91. #if _FREE_BLOCK != 0 || _NORMAL_BLOCK != 1 || _CRT_BLOCK != 2 || _IGNORE_BLOCK != 3 || _CLIENT_BLOCK != 4
  92. #error Block numbers have changed !
  93. #endif  /* _FREE_BLOCK != 0 || _NORMAL_BLOCK != 1 || _CRT_BLOCK != 2 || _IGNORE_BLOCK != 3 || _CLIENT_BLOCK != 4 */
  94.  
  95. static char * szBlockUseName[_MAX_BLOCKS] = {
  96.         "Free",
  97.         "Normal",
  98.         "CRT",
  99.         "Ignore",
  100.         "Client",
  101.         };
  102.  
  103. int __cdecl CheckBytes(unsigned char *, unsigned char, size_t);
  104.  
  105.  
  106. /***
  107. *void *malloc() - Get a block of memory from the debug heap
  108. *
  109. *Purpose:
  110. *       Allocate of block of memory of at least size bytes from the heap and
  111. *       return a pointer to it.
  112. *
  113. *       Allocates 'normal' memory block.
  114. *
  115. *Entry:
  116. *       size_t          nSize       - size of block requested
  117. *
  118. *Exit:
  119. *       Success:  Pointer to memory block
  120. *       Failure:  NULL (or some error value)
  121. *
  122. *Exceptions:
  123. *
  124. *******************************************************************************/
  125.  
  126. _CRTIMP void * __cdecl malloc (
  127.         size_t nSize
  128.         )
  129. {
  130.         return _nh_malloc_dbg(nSize, _newmode, _NORMAL_BLOCK, NULL, 0);
  131. }
  132.  
  133. /***
  134. *void * _malloc_dbg() - Get a block of memory from the debug heap
  135. *
  136. *Purpose:
  137. *       Allocate of block of memory of at least size bytes from the heap and
  138. *       return a pointer to it.
  139. *
  140. *       Allocates any type of supported memory block.
  141. *
  142. *Entry:
  143. *       size_t          nSize       - size of block requested
  144. *       int             nBlockUse   - block type
  145. *       char *          szFileName  - file name
  146. *       int             nLine       - line number
  147. *
  148. *Exit:
  149. *       Success:  Pointer to memory block
  150. *       Failure:  NULL (or some error value)
  151. *
  152. *Exceptions:
  153. *
  154. *******************************************************************************/
  155.  
  156. _CRTIMP void * __cdecl _malloc_dbg (
  157.         size_t nSize,
  158.         int nBlockUse,
  159.         const char * szFileName,
  160.         int nLine
  161.         )
  162. {
  163.         return _nh_malloc_dbg(nSize, _newmode, nBlockUse, szFileName, nLine);
  164. }
  165.  
  166. /***
  167. *void * _nh_malloc() - Get a block of memory from the debug heap
  168. *
  169. *Purpose:
  170. *       Allocate of block of memory of at least size bytes from the debug
  171. *       heap and return a pointer to it. Assumes heap already locked.
  172. *
  173. *       If no blocks available, call new handler.
  174. *
  175. *       Allocates 'normal' memory block.
  176. *
  177. *Entry:
  178. *       size_t          nSize       - size of block requested
  179. *       int             nhFlag      - TRUE if new handler function
  180. *
  181. *Exit:
  182. *       Success:  Pointer to (user portion of) memory block
  183. *       Failure:  NULL
  184. *
  185. *Exceptions:
  186. *
  187. *******************************************************************************/
  188.  
  189. void * __cdecl _nh_malloc (
  190.         size_t nSize,
  191.         int nhFlag
  192.         )
  193. {
  194.         return _nh_malloc_dbg(nSize, nhFlag, _NORMAL_BLOCK, NULL, 0);
  195. }
  196.  
  197.  
  198. /***
  199. *void * _nh_malloc_dbg() - Get a block of memory from the debug heap
  200. *
  201. *Purpose:
  202. *       Allocate of block of memory of at least size bytes from the debug
  203. *       heap and return a pointer to it. Assumes heap already locked.
  204. *
  205. *       If no blocks available, call new handler.
  206. *
  207. *       Allocates any type of supported memory block.
  208. *
  209. *Entry:
  210. *       size_t          nSize       - size of block requested
  211. *       int             nhFlag      - TRUE if new handler function
  212. *       int             nBlockUse   - block type
  213. *       char *          szFileName  - file name
  214. *       int             nLine       - line number
  215. *
  216. *Exit:
  217. *       Success:  Pointer to (user portion of) memory block
  218. *       Failure:  NULL
  219. *
  220. *Exceptions:
  221. *
  222. *******************************************************************************/
  223.  
  224. void * __cdecl _nh_malloc_dbg (
  225.         size_t nSize,
  226.         int nhFlag,
  227.         int nBlockUse,
  228.         const char * szFileName,
  229.         int nLine
  230.         )
  231. {
  232.         void * pvBlk;
  233.  
  234.         for (;;)
  235.         {
  236.             /* lock the heap
  237.              */
  238.             _mlock(_HEAP_LOCK);
  239.  
  240.             /* do the allocation
  241.              */
  242.             pvBlk = _heap_alloc_dbg(nSize, nBlockUse, szFileName, nLine);
  243.  
  244.             /* unlock the heap
  245.              */
  246.             _munlock(_HEAP_LOCK);
  247.  
  248.             if (pvBlk || nhFlag == 0)
  249.                 return pvBlk;
  250.  
  251.             /* call installed new handler */
  252.             if (!_callnewh(nSize))
  253.                 return NULL;
  254.  
  255.             /* new handler was successful -- try to allocate again */
  256.         }
  257. }
  258.  
  259. /***
  260. *void * _heap_alloc() - does actual allocation
  261. *
  262. *Purpose:
  263. *       Does heap allocation.
  264. *
  265. *       Allocates 'normal' memory block.
  266. *
  267. *Entry:
  268. *       size_t          nSize       - size of block requested
  269. *
  270. *Exit:
  271. *       Success:  Pointer to (user portion of) memory block
  272. *       Failure:  NULL
  273. *
  274. *Exceptions:
  275. *
  276. *******************************************************************************/
  277.  
  278. void * __cdecl _heap_alloc(
  279.         size_t nSize
  280.         )
  281. {
  282.         return _heap_alloc_dbg(nSize, _NORMAL_BLOCK, NULL, 0);
  283. }
  284.  
  285. /***
  286. *void * _heap_alloc_dbg() - does actual allocation
  287. *
  288. *Purpose:
  289. *       Does heap allocation.
  290. *
  291. *       Allocates any type of supported memory block.
  292. *
  293. *Entry:
  294. *       size_t          nSize       - size of block requested
  295. *       int             nBlockUse   - block type
  296. *       char *          szFileName  - file name
  297. *       int             nLine       - line number
  298. *
  299. *Exit:
  300. *       Success:  Pointer to (user portion of) memory block
  301. *       Failure:  NULL
  302. *
  303. *Exceptions:
  304. *
  305. *******************************************************************************/
  306.  
  307. void * __cdecl _heap_alloc_dbg(
  308.         size_t nSize,
  309.         int nBlockUse,
  310.         const char * szFileName,
  311.         int nLine
  312.         )
  313. {
  314.         long lRequest;
  315.         size_t blockSize;
  316.         int fIgnore = FALSE;
  317.         _CrtMemBlockHeader * pHead;
  318.  
  319.         /* verify heap before allocation */
  320.         if (_crtDbgFlag & _CRTDBG_CHECK_ALWAYS_DF)
  321.             _ASSERTE(_CrtCheckMemory());
  322.  
  323.         lRequest = _lRequestCurr;
  324.  
  325.         /* break into debugger at specific memory allocation */
  326.         if (lRequest == _crtBreakAlloc)
  327.             _CrtDbgBreak();
  328.  
  329.         /* forced failure */
  330.         if (!(*_pfnAllocHook)(_HOOK_ALLOC, NULL, nSize, nBlockUse, lRequest, szFileName, nLine))
  331.         {
  332.             if (szFileName)
  333.                 _RPT2(_CRT_WARN, "Client hook allocation failure at file %hs line %d.\n",
  334.                     szFileName, nLine);
  335.             else
  336.                 _RPT0(_CRT_WARN, "Client hook allocation failure.\n");
  337.  
  338.             return NULL;
  339.         }
  340.  
  341.         /* cannot ignore CRT allocations */
  342.         if (_BLOCK_TYPE(nBlockUse) != _CRT_BLOCK &&
  343.             !(_crtDbgFlag & _CRTDBG_ALLOC_MEM_DF))
  344.             fIgnore = TRUE;
  345.  
  346.         /* Diagnostic memory allocation from this point on */
  347.  
  348.         if (nSize > (size_t)_HEAP_MAXREQ ||
  349.             nSize + nNoMansLandSize + sizeof(_CrtMemBlockHeader) > (size_t)_HEAP_MAXREQ)
  350.         {
  351.             _RPT1(_CRT_ERROR, "Invalid allocation size: %u bytes.\n", nSize);
  352.             return NULL;
  353.         }
  354.  
  355.         if (!_BLOCK_TYPE_IS_VALID(nBlockUse))
  356.         {
  357.             _RPT0(_CRT_ERROR, "Error: memory allocation: bad memory block type.\n");
  358.         }
  359.  
  360.         blockSize = sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize;
  361.  
  362. #ifndef WINHEAP
  363.         /* round requested size */
  364.         blockSize = _ROUND2(blockSize, _GRANULARITY);
  365. #endif  /* WINHEAP */
  366.  
  367.         pHead = (_CrtMemBlockHeader *)_heap_alloc_base(blockSize);
  368.  
  369.         if (pHead == NULL)
  370.             return NULL;
  371.  
  372.         /* commit allocation */
  373.         ++_lRequestCurr;
  374.  
  375.         if (fIgnore)
  376.         {
  377.             pHead->pBlockHeaderNext = NULL;
  378.             pHead->pBlockHeaderPrev = NULL;
  379.             pHead->szFileName = NULL;
  380.             pHead->nLine = IGNORE_LINE;
  381.             pHead->nDataSize = nSize;
  382.             pHead->nBlockUse = _IGNORE_BLOCK;
  383.             pHead->lRequest = IGNORE_REQ;
  384.         }
  385.         else {
  386.             /* keep track of total amount of memory allocated */
  387.             _lTotalAlloc += nSize;
  388.             _lCurAlloc += nSize;
  389.  
  390.             if (_lCurAlloc > _lMaxAlloc)
  391.                 _lMaxAlloc = _lCurAlloc;
  392.  
  393.             if (_pFirstBlock)
  394.                 _pFirstBlock->pBlockHeaderPrev = pHead;
  395.             else
  396.                 _pLastBlock = pHead;
  397.  
  398.             pHead->pBlockHeaderNext = _pFirstBlock;
  399.             pHead->pBlockHeaderPrev = NULL;
  400.             pHead->szFileName = (char *)szFileName;
  401.             pHead->nLine = nLine;
  402.             pHead->nDataSize = nSize;
  403.             pHead->nBlockUse = nBlockUse;
  404.             pHead->lRequest = lRequest;
  405.  
  406.             /* link blocks together */
  407.             _pFirstBlock = pHead;
  408.         }
  409.  
  410.         /* fill in gap before and after real block */
  411.         memset((void *)pHead->gap, _bNoMansLandFill, nNoMansLandSize);
  412.         memset((void *)(pbData(pHead) + nSize), _bNoMansLandFill, nNoMansLandSize);
  413.  
  414.         /* fill data with silly value (but non-zero) */
  415.         memset((void *)pbData(pHead), _bCleanLandFill, nSize);
  416.  
  417.         return (void *)pbData(pHead);
  418. }
  419.  
  420.  
  421. /***
  422. *void * calloc() - Get a block of memory from the debug heap, init to 0
  423. *
  424. *Purpose:
  425. *       Allocate of block of memory of at least size bytes from the debug
  426. *       heap and return a pointer to it.
  427. *
  428. *       Allocates 'normal' memory block.
  429. *
  430. *Entry:
  431. *       size_t nNum     - number of elements in the array
  432. *       size_t nSize - size of each element
  433. *
  434. *Exit:
  435. *       Success:  Pointer to (user portion of) memory block
  436. *       Failure:  NULL
  437. *
  438. *Exceptions:
  439. *
  440. *******************************************************************************/
  441. _CRTIMP void * __cdecl calloc(
  442.         size_t nNum,
  443.         size_t nSize
  444.         )
  445. {
  446.         return _calloc_dbg(nNum, nSize, _NORMAL_BLOCK, NULL, 0);
  447. }
  448.  
  449.  
  450. /***
  451. *void * _calloc_dbg() - Get a block of memory from the debug heap, init to 0
  452. *                    - with info
  453. *
  454. *Purpose:
  455. *       Allocate of block of memory of at least size bytes from the debug
  456. *       heap and return a pointer to it.
  457. *
  458. *       Allocates any type of supported memory block.
  459. *
  460. *Entry:
  461. *       size_t          nNum        - number of elements in the array
  462. *       size_t          nSize       - size of each element
  463. *       int             nBlockUse   - block type
  464. *       char *          szFileName  - file name
  465. *       int             nLine       - line number
  466. *
  467. *Exit:
  468. *       Success:  Pointer to (user portion of) memory block
  469. *       Failure:  NULL
  470. *
  471. *Exceptions:
  472. *
  473. *******************************************************************************/
  474.  
  475. _CRTIMP void * __cdecl _calloc_dbg(
  476.         size_t nNum,
  477.         size_t nSize,
  478.         int nBlockUse,
  479.         const char * szFileName,
  480.         int nLine
  481.         )
  482. {
  483.         void * pvBlk;
  484.         unsigned char *pStart;
  485.         unsigned char *pLast;
  486.  
  487.         nSize *= nNum;
  488.  
  489.         /*
  490.          * try to malloc the requested space
  491.          */
  492.  
  493.         pvBlk = _malloc_dbg(nSize, nBlockUse, szFileName, nLine);
  494.  
  495.         /*
  496.          * If malloc() succeeded, initialize the allocated space to zeros.
  497.          * Note that unlike _calloc_base, exactly nNum bytes are set to zero.
  498.          */
  499.  
  500.         if ( pvBlk != NULL )
  501.         {
  502.             pStart = (unsigned char *)pvBlk;
  503.             pLast = pStart + nSize;
  504.             while ( pStart < pLast )
  505.                  *(pStart++) = 0;
  506.         }
  507.  
  508.         return(pvBlk);
  509. }
  510.  
  511.  
  512. /***
  513. *static void * realloc_help() - does all the work for _realloc and _expand
  514. *
  515. *Purpose:
  516. *       Helper function for _realloc and _expand.
  517. *
  518. *Entry:
  519. *       void *          pUserData   - pointer previously allocated block
  520. *       size_t          nNewSize    - requested size for the re-allocated block
  521. *       int             nBlockUse   - block type
  522. *       char *          szFileName  - file name
  523. *       int             nLine       - line number
  524. *       int             fRealloc    - TRUE when _realloc, FALSE when _expand
  525. *
  526. *Exit:
  527. *       Success:  Pointer to (user portion of) memory block
  528. *       Failure:  NULL
  529. *
  530. *Exceptions:
  531. *
  532. *******************************************************************************/
  533.  
  534. static void * __cdecl realloc_help(
  535.         void * pUserData,
  536.         size_t nNewSize,
  537.         int nBlockUse,
  538.         const char * szFileName,
  539.         int nLine,
  540.         int fRealloc
  541.         )
  542. {
  543.         long lRequest;
  544.         int fIgnore = FALSE;
  545.         unsigned char *pUserBlock;
  546.         _CrtMemBlockHeader * pOldBlock;
  547.         _CrtMemBlockHeader * pNewBlock;
  548.  
  549.         /*
  550.          * ANSI: realloc(NULL, newsize) is equivalent to malloc(newsize)
  551.          */
  552.         if (pUserData == NULL)
  553.         {
  554.             return _malloc_dbg(nNewSize, nBlockUse, szFileName, nLine);
  555.         }
  556.  
  557.         /*
  558.          * ANSI: realloc(pUserData, 0) is equivalent to free(pUserData)
  559.          * (except that NULL is returned)
  560.          */
  561.         if (fRealloc && nNewSize == 0)
  562.         {
  563.             _free_dbg(pUserData, nBlockUse);
  564.             return NULL;
  565.         }
  566.  
  567.         /* verify heap before re-allocation */
  568.         if (_crtDbgFlag & _CRTDBG_CHECK_ALWAYS_DF)
  569.             _ASSERTE(_CrtCheckMemory());
  570.  
  571.         lRequest = _lRequestCurr;
  572.  
  573.         if (lRequest == _crtBreakAlloc)
  574.             _CrtDbgBreak(); /* break into debugger at specific memory leak */
  575.  
  576.         /* forced failure */
  577.         if (!(*_pfnAllocHook)(_HOOK_REALLOC, pUserData, nNewSize, nBlockUse, lRequest, szFileName, nLine))
  578.         {
  579.             if (szFileName)
  580.                 _RPT2(_CRT_WARN, "Client hook re-allocation failure at file %hs line %d.\n",
  581.                     szFileName, nLine);
  582.             else
  583.                 _RPT0(_CRT_WARN, "Client hook re-allocation failure.\n");
  584.  
  585.             return NULL;
  586.         }
  587.  
  588.         /* Diagnostic memory allocation from this point on */
  589.  
  590.         if (nNewSize > (size_t)UINT_MAX - nNoMansLandSize - sizeof(_CrtMemBlockHeader))
  591.         {
  592.             _RPT1(_CRT_ERROR, "Allocation too large or negative: %u bytes.\n",
  593.                 nNewSize);
  594.             return NULL;
  595.         }
  596.  
  597.         if (nBlockUse != _NORMAL_BLOCK
  598.             && _BLOCK_TYPE(nBlockUse) != _CLIENT_BLOCK
  599.             && _BLOCK_TYPE(nBlockUse) != _CRT_BLOCK)
  600.         {
  601.             _RPT0(_CRT_ERROR, "Error: memory allocation: bad memory block type.\n");
  602.         }
  603.  
  604.         /*
  605.          * If this ASSERT fails, a bad pointer has been passed in. It may be
  606.          * totally bogus, or it may have been allocated from another heap.
  607.          * The pointer MUST come from the 'local' heap.
  608.          */
  609.         _ASSERTE(_CrtIsValidHeapPointer(pUserData));
  610.  
  611.         /* get a pointer to memory block header */
  612.         pOldBlock = pHdr(pUserData);
  613.  
  614.         if (pOldBlock->nBlockUse == _IGNORE_BLOCK)
  615.             fIgnore = TRUE;
  616.  
  617.         if (fIgnore)
  618.         {
  619.             _ASSERTE(pOldBlock->nLine == IGNORE_LINE && pOldBlock->lRequest == IGNORE_REQ);
  620.         }
  621.         else {
  622.             /* Error if freeing incorrect memory type */
  623.             /* CRT blocks can be treated as NORMAL blocks */
  624.             if (_BLOCK_TYPE(pOldBlock->nBlockUse) == _CRT_BLOCK && _BLOCK_TYPE(nBlockUse) == _NORMAL_BLOCK)
  625.                 nBlockUse = _CRT_BLOCK;
  626.             _ASSERTE(_BLOCK_TYPE(pOldBlock->nBlockUse)==_BLOCK_TYPE(nBlockUse));
  627.         }
  628.  
  629.         /*
  630.          * note that all header values will remain valid
  631.          * and min(nNewSize,nOldSize) bytes of data will also remain valid
  632.          */
  633.         if (fRealloc)
  634.         {
  635.             if (NULL == (pNewBlock = _realloc_base(pOldBlock,
  636.                 sizeof(_CrtMemBlockHeader) + nNewSize + nNoMansLandSize)))
  637.                 return NULL;
  638.         }
  639.         else {
  640.             if (NULL == (pNewBlock = _expand_base(pOldBlock,
  641.                 sizeof(_CrtMemBlockHeader) + nNewSize + nNoMansLandSize)))
  642.                 return NULL;
  643.         }
  644.  
  645.         /* commit allocation */
  646.         ++_lRequestCurr;
  647.  
  648.         if (!fIgnore)
  649.         {
  650.             /* keep track of total amount of memory allocated */
  651.             _lTotalAlloc -= pNewBlock->nDataSize;
  652.             _lTotalAlloc += nNewSize;
  653.  
  654.             _lCurAlloc -= pNewBlock->nDataSize;
  655.             _lCurAlloc += nNewSize;
  656.  
  657.             if (_lCurAlloc > _lMaxAlloc)
  658.                 _lMaxAlloc = _lCurAlloc;
  659.         }
  660.  
  661.         pUserBlock = pbData(pNewBlock);
  662.  
  663.         /* if the block grew, put in special value */
  664.         if (nNewSize > pNewBlock->nDataSize)
  665.             memset(pUserBlock + pNewBlock->nDataSize, _bCleanLandFill,
  666.                 nNewSize - pNewBlock->nDataSize);
  667.  
  668.         /* fill in gap after real block */
  669.         memset(pUserBlock + nNewSize, _bNoMansLandFill, nNoMansLandSize);
  670.  
  671.         if (!fIgnore)
  672.         {
  673.             pNewBlock->szFileName = (char *)szFileName;
  674.             pNewBlock->nLine = nLine;
  675.             pNewBlock->lRequest = lRequest;
  676.         }
  677.  
  678.         pNewBlock->nDataSize = nNewSize;
  679.  
  680.         _ASSERTE(fRealloc || (!fRealloc && pNewBlock == pOldBlock));
  681.  
  682.         /* if block did not move or ignored, we are done */
  683.         if (pNewBlock == pOldBlock || fIgnore)
  684.             return (void *)pUserBlock;
  685.  
  686.         /* must remove old memory from dbg heap list */
  687.         /* note that new block header pointers still valid */
  688.         if (pNewBlock->pBlockHeaderNext)
  689.         {
  690.             pNewBlock->pBlockHeaderNext->pBlockHeaderPrev
  691.                 = pNewBlock->pBlockHeaderPrev;
  692.         }
  693.         else
  694.         {
  695.             _ASSERTE(_pLastBlock == pOldBlock);
  696.             _pLastBlock = pNewBlock->pBlockHeaderPrev;
  697.         }
  698.  
  699.         if (pNewBlock->pBlockHeaderPrev)
  700.         {
  701.             pNewBlock->pBlockHeaderPrev->pBlockHeaderNext
  702.                 = pNewBlock->pBlockHeaderNext;
  703.         }
  704.         else
  705.         {
  706.             _ASSERTE(_pFirstBlock == pOldBlock);
  707.             _pFirstBlock = pNewBlock->pBlockHeaderNext;
  708.         }
  709.  
  710.         /* put new memory into list */
  711.         if (_pFirstBlock)
  712.             _pFirstBlock->pBlockHeaderPrev = pNewBlock;
  713.         else
  714.             _pLastBlock = pNewBlock;
  715.  
  716.         pNewBlock->pBlockHeaderNext = _pFirstBlock;
  717.         pNewBlock->pBlockHeaderPrev = NULL;
  718.  
  719.         /* link blocks together */
  720.         _pFirstBlock = pNewBlock;
  721.  
  722.         return (void *)pUserBlock;
  723. }
  724.  
  725.  
  726. /***
  727. *void * realloc() - reallocate a block of memory in the heap
  728. *
  729. *Purpose:
  730. *       Re-allocates a block in the heap to nNewSize bytes. nNewSize may be
  731. *       either greater or less than the original size of the block. The
  732. *       re-allocation may result in moving the block as well as changing
  733. *       the size. If the block is moved, the contents of the original block
  734. *       are copied over.
  735. *
  736. *       Re-allocates 'normal' memory block.
  737. *
  738. *Entry:
  739. *       void *          pUserData   - pointer to previously allocated block
  740. *       size_t          nNewSize    - requested size for the re-allocated block
  741. *
  742. *Exit:
  743. *       Success:  Pointer to (user portion of) memory block
  744. *       Failure:  NULL
  745. *
  746. *Exceptions:
  747. *
  748. *******************************************************************************/
  749.  
  750. _CRTIMP void * __cdecl realloc(
  751.         void * pUserData,
  752.         size_t nNewSize
  753.         )
  754. {
  755.         return _realloc_dbg(pUserData, nNewSize, _NORMAL_BLOCK, NULL, 0);
  756. }
  757.  
  758.  
  759. /***
  760. *void * _realloc_dbg() - reallocate a block of memory in the heap
  761. *                     - with info
  762. *
  763. *Purpose:
  764. *       Re-allocates a block in the heap to nNewSize bytes. nNewSize may be
  765. *       either greater or less than the original size of the block. The
  766. *       re-allocation may result in moving the block as well as changing
  767. *       the size. If the block is moved, the contents of the original block
  768. *       are copied over.
  769. *
  770. *       Re-allocates any type of supported memory block.
  771. *
  772. *Entry:
  773. *       void *          pUserData   - pointer previously allocated block
  774. *       size_t          nNewSize    - requested size for the re-allocated block
  775. *       int             nBlockUse   - block type
  776. *       char *          szFileName  - file name
  777. *       int             nLine       - line number
  778. *
  779. *Exit:
  780. *       Success:  Pointer to (user portion of) memory block
  781. *       Failure:  NULL
  782. *
  783. *Exceptions:
  784. *
  785. *******************************************************************************/
  786.  
  787. _CRTIMP void * __cdecl _realloc_dbg(
  788.         void * pUserData,
  789.         size_t nNewSize,
  790.         int nBlockUse,
  791.         const char * szFileName,
  792.         int nLine
  793.         )
  794. {
  795.         void * pvBlk;
  796.  
  797.         _mlock(_HEAP_LOCK);  /* block other threads */
  798.  
  799.         /* allocate the block
  800.          */
  801.         pvBlk = realloc_help(pUserData,
  802.                              nNewSize,
  803.                              nBlockUse,
  804.                              szFileName,
  805.                              nLine,
  806.                              TRUE);
  807.  
  808.         _munlock(_HEAP_LOCK);  /* release other threads */
  809.  
  810.         return pvBlk;
  811. }
  812.  
  813. /***
  814. *void * _expand() - expand/contract a block of memory in the heap
  815. *
  816. *Purpose:
  817. *       Resizes a block in the heap to newsize bytes. newsize may be either
  818. *       greater (expansion) or less (contraction) than the original size of
  819. *       the block. The block is NOT moved. In the case of expansion, if the
  820. *       block cannot be expanded to newsize bytes, it is expanded as much as
  821. *       possible.
  822. *
  823. *       Re-allocates 'normal' memory block.
  824. *
  825. *Entry:
  826. *       void * pUserData    - pointer to block in the heap previously allocated
  827. *              by a call to malloc(), realloc() or _expand().
  828. *
  829. *       size_t nNewSize    - requested size for the resized block
  830. *
  831. *Exit:
  832. *       Success:  Pointer to the resized memory block (i.e., pUserData)
  833. *       Failure:  NULL
  834. *
  835. *Uses:
  836. *
  837. *Exceptions:
  838. *       If pUserData does not point to a valid allocation block in the heap,
  839. *       _expand() will behave unpredictably and probably corrupt the heap.
  840. *
  841. *******************************************************************************/
  842.  
  843. _CRTIMP void * __cdecl _expand(
  844.         void * pUserData,
  845.         size_t nNewSize
  846.         )
  847. {
  848.         return _expand_dbg(pUserData, nNewSize, _NORMAL_BLOCK, NULL, 0);
  849. }
  850.  
  851.  
  852. /***
  853. *void * _expand() - expand/contract a block of memory in the heap
  854. *
  855. *Purpose:
  856. *       Resizes a block in the heap to newsize bytes. newsize may be either
  857. *       greater (expansion) or less (contraction) than the original size of
  858. *       the block. The block is NOT moved. In the case of expansion, if the
  859. *       block cannot be expanded to newsize bytes, it is expanded as much as
  860. *       possible.
  861. *
  862. *       Re-allocates any type of supported memory block.
  863. *
  864. *Entry:
  865. *       void * pUserData   - pointer to block in the heap previously allocated
  866. *              by a call to malloc(), realloc() or _expand().
  867. *
  868. *       size_t nNewSize    - requested size for the resized block
  869. *
  870. *Exit:
  871. *       Success:  Pointer to the resized memory block (i.e., pUserData)
  872. *       Failure:  NULL
  873. *
  874. *Uses:
  875. *
  876. *Exceptions:
  877. *       If pUserData does not point to a valid allocation block in the heap,
  878. *       _expand() will behave unpredictably and probably corrupt the heap.
  879. *
  880. *******************************************************************************/
  881.  
  882. _CRTIMP void * __cdecl _expand_dbg(
  883.         void * pUserData,
  884.         size_t nNewSize,
  885.         int nBlockUse,
  886.         const char * szFileName,
  887.         int nLine
  888.         )
  889. {
  890.         void * pvBlk;
  891.  
  892.         _mlock(_HEAP_LOCK);  /* block other threads */
  893.  
  894.         /* allocate the block
  895.          */
  896.         pvBlk = realloc_help(pUserData,
  897.                              nNewSize,
  898.                              nBlockUse,
  899.                              szFileName,
  900.                              nLine,
  901.                              FALSE);
  902.  
  903.         _munlock(_HEAP_LOCK);  /* release other threads */
  904.  
  905.         return pvBlk;
  906. }
  907.  
  908. /***
  909. *void free() - free a block in the debug heap
  910. *
  911. *Purpose:
  912. *       Frees a 'normal' memory block.
  913. *
  914. *Entry:
  915. *       void * pUserData -  pointer to a (user portion) of memory block in the
  916. *                       debug heap
  917. *
  918. *Return:
  919. *       <void>
  920. *
  921. *******************************************************************************/
  922. _CRTIMP void __cdecl free(
  923.         void * pUserData
  924.         )
  925. {
  926.         _free_dbg(pUserData, _NORMAL_BLOCK);
  927. }
  928.  
  929. #ifdef _MT
  930.  
  931. void __cdecl _free_lk(
  932.         void * pUserData
  933.         )
  934. {
  935.         _free_dbg_lk(pUserData, _NORMAL_BLOCK);
  936. }
  937.  
  938. #endif  /* _MT */
  939.  
  940.  
  941. /***
  942. *void _free_dbg() - free a block in the debug heap
  943. *
  944. *Purpose:
  945. *       Frees any type of supported block.
  946. *
  947. *Entry:
  948. *       void * pUserData    - pointer to a (user portion) of memory block in the
  949. *                             debug heap
  950. *       int nBlockUse       - block type
  951. *
  952. *Return:
  953. *       <void>
  954. *
  955. *******************************************************************************/
  956.  
  957. #ifdef _MT
  958.  
  959. _CRTIMP void __cdecl _free_dbg(
  960.         void * pUserData,
  961.         int nBlockUse
  962.         )
  963. {
  964.         /* lock the heap
  965.          */
  966.         _mlock(_HEAP_LOCK);
  967.  
  968.         /* allocate the block
  969.          */
  970.         _free_dbg_lk(pUserData, nBlockUse);
  971.  
  972.         /* unlock the heap
  973.          */
  974.         _munlock(_HEAP_LOCK);
  975. }
  976.  
  977. void __cdecl _free_dbg_lk(
  978.  
  979. #else  /* _MT */
  980.  
  981. _CRTIMP void __cdecl _free_dbg(
  982.  
  983. #endif  /* _MT */
  984.  
  985.         void * pUserData,
  986.         int nBlockUse
  987.         )
  988. {
  989.         _CrtMemBlockHeader * pHead;
  990.  
  991.         /* verify heap before freeing */
  992.         if (_crtDbgFlag & _CRTDBG_CHECK_ALWAYS_DF)
  993.             _ASSERTE(_CrtCheckMemory());
  994.  
  995.         if (pUserData == NULL)
  996.             return;
  997.  
  998.         /* forced failure */
  999.         if (!(*_pfnAllocHook)(_HOOK_FREE, pUserData, 0, nBlockUse, 0L, NULL, 0))
  1000.         {
  1001.             _RPT0(_CRT_WARN, "Client hook free failure.\n");
  1002.  
  1003.             return;
  1004.         }
  1005.  
  1006.         /*
  1007.          * If this ASSERT fails, a bad pointer has been passed in. It may be
  1008.          * totally bogus, or it may have been allocated from another heap.
  1009.          * The pointer MUST come from the 'local' heap.
  1010.          */
  1011.         _ASSERTE(_CrtIsValidHeapPointer(pUserData));
  1012.  
  1013.         /* get a pointer to memory block header */
  1014.         pHead = pHdr(pUserData);
  1015.  
  1016.         /* verify block type */
  1017.         _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
  1018.  
  1019.         /* if we didn't already check entire heap, at least check this object */
  1020.         if (!(_crtDbgFlag & _CRTDBG_CHECK_ALWAYS_DF))
  1021.         {
  1022.             /* check no-mans-land gaps */
  1023.             if (!CheckBytes(pHead->gap, _bNoMansLandFill, nNoMansLandSize))
  1024.                 _RPT3(_CRT_ERROR, "DAMAGE: before %hs block (#%d) at 0x%08X.\n",
  1025.                     szBlockUseName[_BLOCK_TYPE(pHead->nBlockUse)],
  1026.                     pHead->lRequest,
  1027.                     (BYTE *) pbData(pHead));
  1028.  
  1029.             if (!CheckBytes(pbData(pHead) + pHead->nDataSize, _bNoMansLandFill, nNoMansLandSize))
  1030.                 _RPT3(_CRT_ERROR, "DAMAGE: after %hs block (#%d) at 0x%08X.\n",
  1031.                     szBlockUseName[_BLOCK_TYPE(pHead->nBlockUse)],
  1032.                     pHead->lRequest,
  1033.                     (BYTE *) pbData(pHead));
  1034.         }
  1035.  
  1036.         if (pHead->nBlockUse == _IGNORE_BLOCK)
  1037.         {
  1038.             _ASSERTE(pHead->nLine == IGNORE_LINE && pHead->lRequest == IGNORE_REQ);
  1039.             /* fill the entire block including header with dead-land-fill */
  1040.             memset(pHead, _bDeadLandFill,
  1041.                 sizeof(_CrtMemBlockHeader) + pHead->nDataSize + nNoMansLandSize);
  1042.             _free_base(pHead);
  1043.             return;
  1044.         }
  1045.  
  1046.         /* CRT blocks can be freed as NORMAL blocks */
  1047.         if (pHead->nBlockUse == _CRT_BLOCK && nBlockUse == _NORMAL_BLOCK)
  1048.             nBlockUse = _CRT_BLOCK;
  1049.  
  1050.         /* Error if freeing incorrect memory type */
  1051.         _ASSERTE(pHead->nBlockUse == nBlockUse);
  1052.  
  1053.         /* keep track of total amount of memory allocated */
  1054.         _lCurAlloc -= pHead->nDataSize;
  1055.  
  1056.         /* optionally reclaim memory */
  1057.         if (!(_crtDbgFlag & _CRTDBG_DELAY_FREE_MEM_DF))
  1058.         {
  1059.             /* remove from the linked list */
  1060.             if (pHead->pBlockHeaderNext)
  1061.             {
  1062.                 pHead->pBlockHeaderNext->pBlockHeaderPrev = pHead->pBlockHeaderPrev;
  1063.             }
  1064.             else
  1065.             {
  1066.                 _ASSERTE(_pLastBlock == pHead);
  1067.                 _pLastBlock = pHead->pBlockHeaderPrev;
  1068.             }
  1069.  
  1070.             if (pHead->pBlockHeaderPrev)
  1071.             {
  1072.                 pHead->pBlockHeaderPrev->pBlockHeaderNext = pHead->pBlockHeaderNext;
  1073.             }
  1074.             else
  1075.             {
  1076.                 _ASSERTE(_pFirstBlock == pHead);
  1077.                 _pFirstBlock = pHead->pBlockHeaderNext;
  1078.             }
  1079.  
  1080.             /* fill the entire block including header with dead-land-fill */
  1081.             memset(pHead, _bDeadLandFill,
  1082.                 sizeof(_CrtMemBlockHeader) + pHead->nDataSize + nNoMansLandSize);
  1083.             _free_base(pHead);
  1084.         }
  1085.         else
  1086.         {
  1087.             pHead->nBlockUse = _FREE_BLOCK;
  1088.  
  1089.             /* keep memory around as dead space */
  1090.             memset(pbData(pHead), _bDeadLandFill, pHead->nDataSize);
  1091.         }
  1092. }
  1093.  
  1094. /***
  1095. *size_t _msize() - calculate the size of specified block in the heap
  1096. *
  1097. *Purpose:
  1098. *       Calculates the size of memory block (in the heap) pointed to by
  1099. *       pUserData.
  1100. *
  1101. *       For 'normal' memory block.
  1102. *
  1103. *Entry:
  1104. *       void * pUserData - pointer to a memory block in the heap
  1105. *
  1106. *Return:
  1107. *       size of the block
  1108. *
  1109. *******************************************************************************/
  1110.  
  1111. _CRTIMP size_t __cdecl _msize (
  1112.         void * pUserData
  1113.         )
  1114. {
  1115.         return _msize_dbg(pUserData, _NORMAL_BLOCK);
  1116. }
  1117.  
  1118.  
  1119. /***
  1120. *size_t _msize_dbg() - calculate the size of specified block in the heap
  1121. *
  1122. *Purpose:
  1123. *       Calculates the size of memory block (in the heap) pointed to by
  1124. *       pUserData.
  1125. *
  1126. *Entry:
  1127. *       void * pUserData    - pointer to a (user portion) of memory block in the
  1128. *                             debug heap
  1129. *       int nBlockUse       - block type
  1130. *
  1131. *       For any type of supported block.
  1132. *
  1133. *Return:
  1134. *       size of the block
  1135. *
  1136. *******************************************************************************/
  1137.  
  1138. _CRTIMP size_t __cdecl _msize_dbg (
  1139.         void * pUserData,
  1140.         int nBlockUse
  1141.         )
  1142. {
  1143.         size_t nSize;
  1144.         _CrtMemBlockHeader * pHead;
  1145.  
  1146.         /* verify heap before getting size */
  1147.         if (_crtDbgFlag & _CRTDBG_CHECK_ALWAYS_DF)
  1148.             _ASSERTE(_CrtCheckMemory());
  1149.  
  1150.         _mlock(_HEAP_LOCK);  /* block other threads */
  1151.  
  1152.         /*
  1153.          * If this ASSERT fails, a bad pointer has been passed in. It may be
  1154.          * totally bogus, or it may have been allocated from another heap.
  1155.          * The pointer MUST come from the 'local' heap.
  1156.          */
  1157.         _ASSERTE(_CrtIsValidHeapPointer(pUserData));
  1158.  
  1159.         /* get a pointer to memory block header */
  1160.         pHead = pHdr(pUserData);
  1161.  
  1162.          /* verify block type */
  1163.         _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
  1164.  
  1165.         /* CRT blocks can be treated as NORMAL blocks */
  1166.         if (pHead->nBlockUse == _CRT_BLOCK && nBlockUse == _NORMAL_BLOCK)
  1167.             nBlockUse = _CRT_BLOCK;
  1168.  
  1169.         if (pHead->nBlockUse != _IGNORE_BLOCK)
  1170.             _ASSERTE(pHead->nBlockUse == nBlockUse);
  1171.  
  1172.         nSize = pHead->nDataSize;
  1173.  
  1174.         _munlock(_HEAP_LOCK);  /* release other threads */
  1175.  
  1176.         return nSize;
  1177. }
  1178.  
  1179. /***
  1180. *long _CrtSetBreakAlloc() - set allocation on which to break
  1181. *
  1182. *Purpose:
  1183. *       set allocation on which to break
  1184. *
  1185. *Entry:
  1186. *       long lBreakAlloc
  1187. *
  1188. *Exit:
  1189. *       return previous break number
  1190. *
  1191. *Exceptions:
  1192. *
  1193. *******************************************************************************/
  1194. _CRTIMP long __cdecl _CrtSetBreakAlloc(
  1195.         long lNewBreakAlloc
  1196.         )
  1197. {
  1198.         long lOldBreakAlloc = _crtBreakAlloc;
  1199.         _crtBreakAlloc = lNewBreakAlloc;
  1200.         return lOldBreakAlloc;
  1201. }
  1202.  
  1203. /***
  1204. *void _CrtSetDbgBlockType() - change memory block type
  1205. *
  1206. *Purpose:
  1207. *       change memory block type
  1208. *
  1209. *Entry:
  1210. *       void * pUserData    - pointer to a (user portion) of memory block in the
  1211. *                             debug heap
  1212. *       int nBlockUse       - block type
  1213. *
  1214. *Exit:
  1215. *
  1216. *Exceptions:
  1217. *
  1218. *******************************************************************************/
  1219. _CRTIMP void __cdecl _CrtSetDbgBlockType(
  1220.         void * pUserData,
  1221.         int nBlockUse
  1222.         )
  1223. {
  1224.         _CrtMemBlockHeader * pHead;
  1225.  
  1226.         _mlock(_HEAP_LOCK);  /* block other threads */
  1227.  
  1228.         /* If from local heap, then change block type. */
  1229.         if (_CrtIsValidHeapPointer(pUserData))
  1230.         {
  1231.             /* get a pointer to memory block header */
  1232.             pHead = pHdr(pUserData);
  1233.  
  1234.             /* verify block type */
  1235.             _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
  1236.  
  1237.             pHead->nBlockUse = nBlockUse;
  1238.         }
  1239.  
  1240.         _munlock(_HEAP_LOCK);  /* release other threads */
  1241.  
  1242.         return;
  1243. }
  1244.  
  1245. /*---------------------------------------------------------------------------
  1246.  *
  1247.  * Client-defined allocation hook
  1248.  *
  1249.  --------------------------------------------------------------------------*/
  1250.  
  1251. /***
  1252. *_CRT_ALLOC_HOOK _CrtSetAllocHook() - set client allocation hook
  1253. *
  1254. *Purpose:
  1255. *       set client allocation hook
  1256. *
  1257. *Entry:
  1258. *       _CRT_ALLOC_HOOK pfnNewHook - new allocation hook
  1259. *
  1260. *Exit:
  1261. *       return previous hook
  1262. *
  1263. *Exceptions:
  1264. *
  1265. *******************************************************************************/
  1266. _CRTIMP _CRT_ALLOC_HOOK __cdecl _CrtSetAllocHook(
  1267.         _CRT_ALLOC_HOOK pfnNewHook
  1268.         )
  1269. {
  1270.         _CRT_ALLOC_HOOK pfnOldHook = _pfnAllocHook;
  1271.         _pfnAllocHook = pfnNewHook;
  1272.         return pfnOldHook;
  1273. }
  1274.  
  1275.  
  1276. /*---------------------------------------------------------------------------
  1277.  *
  1278.  * Memory management
  1279.  *
  1280.  --------------------------------------------------------------------------*/
  1281.  
  1282. /***
  1283. *static int CheckBytes() - verify byte range set to proper value
  1284. *
  1285. *Purpose:
  1286. *       verify byte range set to proper value
  1287. *
  1288. *Entry:
  1289. *       unsigned char *pb       - pointer to start of byte range
  1290. *       unsigned char bCheck    - value byte range should be set to
  1291. *       size_t nSize            - size of byte range to be checked
  1292. *
  1293. *Return:
  1294. *       TRUE - if all bytes in range equal bcheck
  1295. *       FALSE otherwise
  1296. *
  1297. *******************************************************************************/
  1298. static int __cdecl CheckBytes(
  1299.         unsigned char * pb,
  1300.         unsigned char bCheck,
  1301.         size_t nSize
  1302.         )
  1303. {
  1304.         int bOkay = TRUE;
  1305.         while (nSize--)
  1306.         {
  1307.             if (*pb++ != bCheck)
  1308.             {
  1309.                 _RPT3(_CRT_WARN, "memory check error at 0x%08X = 0x%02X, should be 0x%02X.\n",
  1310.                     (BYTE *)(pb-1),*(pb-1), bCheck);
  1311.                 bOkay = FALSE;
  1312.             }
  1313.         }
  1314.         return bOkay;
  1315. }
  1316.  
  1317.  
  1318. /***
  1319. *int _CrtCheckMemory() - check heap integrity
  1320. *
  1321. *Purpose:
  1322. *       Confirm integrity of debug heap. Call _heapchk to validate underlying
  1323. *       heap.
  1324. *
  1325. *Entry:
  1326. *       void
  1327. *
  1328. *Return:
  1329. *       TRUE - if debug and underlying heap appear valid
  1330. *       FALSE otherwise
  1331. *
  1332. *******************************************************************************/
  1333. _CRTIMP int __cdecl _CrtCheckMemory(
  1334.         void
  1335.         )
  1336. {
  1337.         int allOkay = TRUE;
  1338.         int nHeapCheck;
  1339.         _CrtMemBlockHeader * pHead;
  1340.  
  1341.         if (!(_crtDbgFlag & _CRTDBG_ALLOC_MEM_DF))
  1342.             return TRUE;        /* can't do any checking */
  1343.  
  1344.         _mlock(_HEAP_LOCK);  /* block other threads */
  1345.  
  1346.         /* check underlying heap */
  1347.  
  1348.         nHeapCheck = _heapchk();
  1349.         if (nHeapCheck != _HEAPEMPTY && nHeapCheck != _HEAPOK)
  1350.         {
  1351.             switch (nHeapCheck)
  1352.             {
  1353.             case _HEAPBADBEGIN:
  1354.                 _RPT0(_CRT_WARN, "_heapchk fails with _HEAPBADBEGIN.\n");
  1355.                 break;
  1356.             case _HEAPBADNODE:
  1357.                 _RPT0(_CRT_WARN, "_heapchk fails with _HEAPBADNODE.\n");
  1358.                 break;
  1359.             case _HEAPEND:
  1360.                 _RPT0(_CRT_WARN, "_heapchk fails with _HEAPBADEND.\n");
  1361.                 break;
  1362.             case _HEAPBADPTR:
  1363.                 _RPT0(_CRT_WARN, "_heapchk fails with _HEAPBADPTR.\n");
  1364.                 break;
  1365.             default:
  1366.                 _RPT0(_CRT_WARN, "_heapchk fails with unknown return value!\n");
  1367.                 break;
  1368.             }
  1369.             _munlock(_HEAP_LOCK);  /* release other threads */
  1370.             return FALSE;
  1371.         }
  1372.  
  1373.         /* check all allocated blocks */
  1374.  
  1375.         for (pHead = _pFirstBlock; pHead != NULL; pHead = pHead->pBlockHeaderNext)
  1376.         {
  1377.             int okay = TRUE;       /* this block okay ? */
  1378.             unsigned char * blockUse;
  1379.  
  1380.             if (_BLOCK_TYPE_IS_VALID(pHead->nBlockUse))
  1381.                 blockUse = szBlockUseName[_BLOCK_TYPE(pHead->nBlockUse)];
  1382.             else
  1383.                 blockUse = "DAMAGED";
  1384.  
  1385.  
  1386.             /* check no-mans-land gaps */
  1387.             if (!CheckBytes(pHead->gap, _bNoMansLandFill, nNoMansLandSize))
  1388.             {
  1389.                 _RPT3(_CRT_WARN, "DAMAGE: before %hs block (#%d) at 0x%08X.\n",
  1390.                     blockUse, pHead->lRequest, (BYTE *) pbData(pHead));
  1391.                 okay = FALSE;
  1392.             }
  1393.  
  1394.             if (!CheckBytes(pbData(pHead) + pHead->nDataSize, _bNoMansLandFill,
  1395.               nNoMansLandSize))
  1396.             {
  1397.                 _RPT3(_CRT_WARN, "DAMAGE: after %hs block (#%d) at 0x%08X.\n",
  1398.                     blockUse, pHead->lRequest, (BYTE *) pbData(pHead));
  1399.                 okay = FALSE;
  1400.             }
  1401.  
  1402.             /* free blocks should remain undisturbed */
  1403.             if (pHead->nBlockUse == _FREE_BLOCK &&
  1404.               !CheckBytes(pbData(pHead), _bDeadLandFill, pHead->nDataSize))
  1405.             {
  1406.                 _RPT1(_CRT_WARN, "DAMAGE: on top of Free block at 0x%08X.\n",
  1407.                     (BYTE *) pbData(pHead));
  1408.                 okay = FALSE;
  1409.             }
  1410.  
  1411.             if (!okay)
  1412.             {
  1413.                 /* report some more statistics about the broken object */
  1414.  
  1415.                 if (pHead->szFileName != NULL)
  1416.                     _RPT3(_CRT_WARN, "%hs allocated at file %hs(%d).\n",
  1417.                         blockUse, pHead->szFileName, pHead->nLine);
  1418.  
  1419.                 _RPT3(_CRT_WARN, "%hs located at 0x%08X is %u bytes long.\n",
  1420.                     blockUse, (BYTE *)pbData(pHead), pHead->nDataSize);
  1421.  
  1422.                 allOkay = FALSE;
  1423.             }
  1424.         }
  1425.         _munlock(_HEAP_LOCK);  /* release other threads */
  1426.  
  1427.         return allOkay;
  1428. }
  1429.  
  1430.  
  1431. /***
  1432. *int _CrtSetDbgFlag() - get/set the _crtDbgFlag
  1433. *
  1434. *Purpose:
  1435. *       get or set the _crtDbgFlag
  1436. *
  1437. *Entry:
  1438. *       int bNewBits - new Flag or _CRTDBG_REPORT_FLAG
  1439. *
  1440. *Return:
  1441. *       previous flag state
  1442. *
  1443. *******************************************************************************/
  1444. _CRTIMP int __cdecl _CrtSetDbgFlag(
  1445.         int fNewBits
  1446.         )
  1447. {
  1448.         int fOldBits = _crtDbgFlag;
  1449.  
  1450.         if (fNewBits != _CRTDBG_REPORT_FLAG)
  1451.             _crtDbgFlag = fNewBits;
  1452.  
  1453.         return fOldBits;
  1454. }
  1455.  
  1456.  
  1457. /***
  1458. *int _CrtDoForAllClientObjects() - call a client-supplied function for all
  1459. *                                  client objects in the heap
  1460. *
  1461. *Purpose:
  1462. *       call a client-supplied function for all client objects in the heap
  1463. *
  1464. *Entry:
  1465. *       void (*pfn)(void *, void *) - pointer to client function to call
  1466. *       void * pContext - pointer to user supplied context to pass to function
  1467. *
  1468. *Return:
  1469. *    void
  1470. *
  1471. *******************************************************************************/
  1472. _CRTIMP void __cdecl _CrtDoForAllClientObjects(
  1473.         void (*pfn)(void *, void *),
  1474.         void * pContext
  1475.         )
  1476. {
  1477.         _CrtMemBlockHeader * pHead;
  1478.  
  1479.         if (!(_crtDbgFlag & _CRTDBG_ALLOC_MEM_DF))
  1480.             return;         /* sorry not enabled */
  1481.  
  1482.         _mlock(_HEAP_LOCK);  /* block other threads */
  1483.  
  1484.         for (pHead = _pFirstBlock; pHead != NULL; pHead = pHead->pBlockHeaderNext)
  1485.         {
  1486.             if (_BLOCK_TYPE(pHead->nBlockUse) == _CLIENT_BLOCK)
  1487.                 (*pfn)((void *) pbData(pHead), pContext);
  1488.         }
  1489.  
  1490.         _munlock(_HEAP_LOCK);  /* release other threads */
  1491. }
  1492.  
  1493.  
  1494. /***
  1495. *int _CrtIsValidPointer() - verify memory range is valid for reading/writing
  1496. *
  1497. *Purpose:
  1498. *       verify memory range range is valid for reading/writing
  1499. *
  1500. *Entry:
  1501. *       const void * pv     - start of memory range to test
  1502. *       unsigned int nBytes - size of memory range
  1503. *       int bReadWrite      - TRUE if read/write, FALSE if read-only
  1504. *
  1505. *Return:
  1506. *       TRUE - if valid address
  1507. *       FALSE otherwise
  1508. *
  1509. *******************************************************************************/
  1510. _CRTIMP int __cdecl _CrtIsValidPointer(
  1511.         const void * pv,
  1512.         unsigned int nBytes,
  1513.         int bReadWrite
  1514.         )
  1515. {
  1516.         return (
  1517.             pv != NULL
  1518. #ifdef _WIN32
  1519.             && !IsBadReadPtr(pv, nBytes) &&
  1520.             (!bReadWrite || !IsBadWritePtr((LPVOID)pv, nBytes))
  1521. #endif  /* _WIN32 */
  1522.             );
  1523. }
  1524.  
  1525. /***
  1526. *int _CrtIsValidHeapPointer() - verify pointer is from 'local' heap
  1527. *
  1528. *Purpose:
  1529. *       Verify pointer is not only a valid pointer but also that it is from
  1530. *       the 'local' heap. Pointers from another copy of the C runtime (even in the
  1531. *       same process) will be caught.
  1532. *
  1533. *Entry:
  1534. *       const void * pUserData     - pointer of interest
  1535. *
  1536. *Return:
  1537. *       TRUE - if valid and from local heap
  1538. *       FALSE otherwise
  1539. *
  1540. *******************************************************************************/
  1541. _CRTIMP int __cdecl _CrtIsValidHeapPointer(
  1542.         const void * pUserData
  1543.         )
  1544. {
  1545. #ifdef WINHEAP
  1546.         PHEADER     pHeader;
  1547. #else  /* WINHEAP */
  1548.         int i;
  1549.         void * base;
  1550. #endif  /* WINHEAP */
  1551.  
  1552.         if (!pUserData)
  1553.             return FALSE;
  1554.  
  1555.         if (!_CrtIsValidPointer(pHdr(pUserData), sizeof(_CrtMemBlockHeader), TRUE))
  1556.             return FALSE;
  1557.  
  1558. #ifdef WINHEAP
  1559.  
  1560.         if (pHeader = __sbh_find_block(pHdr(pUserData)))
  1561.         {
  1562.             return __sbh_verify_block(pHeader, pHdr(pUserData));
  1563.         }
  1564.         else if ( (_osver & 0x8000) != 0 )
  1565.             return TRUE;
  1566.         else
  1567.             return HeapValidate( _crtheap, 0, pHdr(pUserData) );
  1568.  
  1569. #else  /* WINHEAP */
  1570.  
  1571.         /*
  1572.          * Go through the heap regions and see if the pointer lies within one
  1573.          * of the regions of the local heap.
  1574.          *
  1575.          * Pointers from non-local heaps cannot be handled. For example, a
  1576.          * non-local pointer may come from a DLL that has the CRT linked-in.
  1577.          *
  1578.          */
  1579.  
  1580. #ifdef _WIN32
  1581.         for (i = 0; (base = _heap_regions[i]._regbase) != NULL &&
  1582.                 i < _HEAP_REGIONMAX; i++)
  1583.         {
  1584.             if (pUserData >= base && pUserData <
  1585.                     (void *)(((char *)base)+_heap_regions[i]._currsize))
  1586.                 return TRUE;
  1587.         }
  1588. #else  /* _WIN32 */
  1589.         {
  1590.             struct _heap_region_ *pHeapRegions
  1591.                 = (struct _heap_region_ *)(*hHeapRegions);
  1592.  
  1593.             for (i = 0; (base = (pHeapRegions+i)->_regbase) != NULL &&
  1594.                         i < _HEAP_REGIONMAX; i++)
  1595.             {
  1596.                 if (pUserData >= base && pUserData <
  1597.                         (void *)(((char *)base)+(pHeapRegions+i)->_currsize))
  1598.                     return TRUE;
  1599.             }
  1600.         }
  1601. #endif  /* _WIN32 */
  1602.  
  1603.         return FALSE;
  1604.  
  1605. #endif  /* WINHEAP */
  1606. }
  1607.  
  1608.  
  1609. /***
  1610. *int _CrtIsMemoryBlock() - verify memory block is debug heap block
  1611. *
  1612. *Purpose:
  1613. *       verify memory block is debug heap block
  1614. *
  1615. *Entry:
  1616. *       const void *    pUserData       - start of memory block
  1617. *       unsigned int    nBytes          - size of memory block
  1618. *       long * plRequestNumber          - if !NULL, set to request number
  1619. *       char **         pszFileName     - if !NULL, set to file name
  1620. *       int *           pnLine          - if !NULL, set to line number
  1621. *
  1622. *Return:
  1623. *       TRUE - if debug memory heap address
  1624. *       FALSE otherwise
  1625. *
  1626. *******************************************************************************/
  1627. _CRTIMP int __cdecl _CrtIsMemoryBlock(
  1628.         const void * pUserData,
  1629.         unsigned int nBytes,
  1630.         long * plRequestNumber,
  1631.         char ** pszFileName,
  1632.         int * pnLine
  1633.         )
  1634. {
  1635.         _CrtMemBlockHeader * pHead;
  1636.  
  1637.         if (!_CrtIsValidHeapPointer(pUserData))
  1638.             return FALSE;
  1639.  
  1640.         _mlock(_HEAP_LOCK);  /* block other threads */
  1641.  
  1642.         pHead = pHdr(pUserData);
  1643.  
  1644.         if (_BLOCK_TYPE_IS_VALID(pHead->nBlockUse) &&
  1645.             _CrtIsValidPointer(pUserData, nBytes, TRUE) &&
  1646.             pHead->nDataSize == nBytes &&
  1647.             pHead->lRequest <= _lRequestCurr
  1648.            )
  1649.         {
  1650.             if (plRequestNumber != NULL)
  1651.                 *plRequestNumber = pHead->lRequest;
  1652.             if (pszFileName != NULL)
  1653.                 *pszFileName = pHead->szFileName;
  1654.             if (pnLine != NULL)
  1655.                 *pnLine = pHead->nLine;
  1656.  
  1657.             _munlock(_HEAP_LOCK);  /* release other threads */
  1658.             return TRUE;
  1659.         }
  1660.  
  1661.         _munlock(_HEAP_LOCK);  /* release other threads */
  1662.  
  1663.         return FALSE;
  1664. }
  1665.  
  1666.  
  1667. /*---------------------------------------------------------------------------
  1668.  *
  1669.  * Memory state
  1670.  *
  1671.  --------------------------------------------------------------------------*/
  1672.  
  1673.  
  1674. /***
  1675. *_CRT_DUMP_CLIENT _CrtSetDumpClient() - set client dump routine
  1676. *
  1677. *Purpose:
  1678. *       set client dump routine
  1679. *
  1680. *Entry:
  1681. *       _CRT_DUMP_CLIENT pfnNewDumpClient - new dump routine
  1682. *
  1683. *Exit:
  1684. *       return previous dump routine
  1685. *
  1686. *Exceptions:
  1687. *
  1688. *******************************************************************************/
  1689. _CRTIMP _CRT_DUMP_CLIENT __cdecl _CrtSetDumpClient(
  1690.         _CRT_DUMP_CLIENT pfnNewDump
  1691.         )
  1692. {
  1693.         _CRT_DUMP_CLIENT pfnOldDump = _pfnDumpClient;
  1694.         _pfnDumpClient = pfnNewDump;
  1695.         return pfnOldDump;
  1696. }
  1697.  
  1698.  
  1699. /***
  1700. *_CrtMemState * _CrtMemStateCheckpoint() - checkpoint current memory state
  1701. *
  1702. *Purpose:
  1703. *       checkpoint current memory state
  1704. *
  1705. *Entry:
  1706. *       _CrtMemState * state - state structure to fill in, will be
  1707. *       allocated if NULL
  1708. *
  1709. *Return:
  1710. *       current memory state
  1711. *
  1712. *******************************************************************************/
  1713. _CRTIMP void __cdecl _CrtMemCheckpoint(
  1714.         _CrtMemState * state
  1715.         )
  1716. {
  1717.         int use;
  1718.         _CrtMemBlockHeader * pHead;
  1719.  
  1720.         if (state == NULL)
  1721.         {
  1722.             _RPT0(_CRT_WARN, "_CrtMemCheckPoint: NULL state pointer.\n");
  1723.             return;
  1724.         }
  1725.  
  1726.         _mlock(_HEAP_LOCK);  /* block other threads */
  1727.  
  1728.         state->pBlockHeader = _pFirstBlock;
  1729.         for (use = 0; use < _MAX_BLOCKS; use++)
  1730.             state->lCounts[use] = state->lSizes[use] = 0;
  1731.  
  1732.         for (pHead = _pFirstBlock; pHead != NULL; pHead = pHead->pBlockHeaderNext)
  1733.         {
  1734.             if (_BLOCK_TYPE(pHead->nBlockUse) >= 0 && _BLOCK_TYPE(pHead->nBlockUse) < _MAX_BLOCKS)
  1735.             {
  1736.                 state->lCounts[_BLOCK_TYPE(pHead->nBlockUse)]++;
  1737.                 state->lSizes[_BLOCK_TYPE(pHead->nBlockUse)] += pHead->nDataSize;
  1738.             }
  1739.             else
  1740.             {
  1741.                 _RPT1(_CRT_WARN, "Bad memory block found at 0x%08X.\n", (BYTE *)pHead);
  1742.             }
  1743.         }
  1744.  
  1745.         state->lHighWaterCount = _lMaxAlloc;
  1746.         state->lTotalCount = _lTotalAlloc;
  1747.  
  1748.         _munlock(_HEAP_LOCK);  /* release other threads */
  1749. }
  1750.  
  1751.  
  1752. /***
  1753. *int _CrtMemDifference() - compare two memory states
  1754. *
  1755. *Purpose:
  1756. *       compare two memory states
  1757. *
  1758. *Entry:
  1759. *       _CrtMemState * state - return memory state difference
  1760. *       _CrtMemState * oldState - earlier memory state
  1761. *       _CrtMemState * newState - later memory state
  1762. *
  1763. *Return:
  1764. *       TRUE if difference
  1765. *       FALSE otherwise
  1766. *
  1767. *******************************************************************************/
  1768. _CRTIMP int __cdecl _CrtMemDifference(
  1769.         _CrtMemState * state,
  1770.         const _CrtMemState * oldState,
  1771.         const _CrtMemState * newState
  1772.         )
  1773. {
  1774.         int use;
  1775.         int bSignificantDifference = FALSE;
  1776.  
  1777.         if (state == NULL || oldState == NULL || newState == NULL)
  1778.         {
  1779.             _RPT0(_CRT_WARN, "_CrtMemDifference: NULL state pointer.\n");
  1780.             return bSignificantDifference;
  1781.         }
  1782.  
  1783.         for (use = 0; use < _MAX_BLOCKS; use++)
  1784.         {
  1785.             state->lSizes[use] = newState->lSizes[use] - oldState->lSizes[use];
  1786.             state->lCounts[use] = newState->lCounts[use] - oldState->lCounts[use];
  1787.  
  1788.             if (    (state->lSizes[use] != 0 || state->lCounts[use] != 0) &&
  1789.                      use != _FREE_BLOCK &&
  1790.                     (use != _CRT_BLOCK ||
  1791.                     (use == _CRT_BLOCK && (_crtDbgFlag & _CRTDBG_CHECK_CRT_DF)))
  1792.                     )
  1793.                 bSignificantDifference = TRUE;
  1794.         }
  1795.         state->lHighWaterCount = newState->lHighWaterCount - oldState->lHighWaterCount;
  1796.         state->lTotalCount = newState->lTotalCount - oldState->lTotalCount;
  1797.         state->pBlockHeader = NULL;
  1798.  
  1799.         return bSignificantDifference;
  1800. }
  1801.  
  1802. #define MAXPRINT 16
  1803.  
  1804. static void __cdecl _printMemBlockData(
  1805.         _CrtMemBlockHeader * pHead
  1806.         )
  1807. {
  1808.         int i;
  1809.         unsigned char ch;
  1810.         unsigned char printbuff[MAXPRINT+1];
  1811.         unsigned char valbuff[MAXPRINT*3+1];
  1812.  
  1813.         for (i = 0; i < min((int)pHead->nDataSize, MAXPRINT); i++)
  1814.         {
  1815.             ch = pbData(pHead)[i];
  1816.             printbuff[i] = isprint(ch) ? ch : ' ';
  1817.             sprintf(&valbuff[i*3], "%.2X ", ch);
  1818.         }
  1819.         printbuff[i] = '\0';
  1820.  
  1821.         _RPT2(_CRT_WARN, " Data: <%s> %s\n", printbuff, valbuff);
  1822. }
  1823.  
  1824.  
  1825. /***
  1826. *void _CrtMemDumpAllObjectsSince() - dump all objects since memory state
  1827. *
  1828. *Purpose:
  1829. *       dump all objects since memory state
  1830. *
  1831. *Entry:
  1832. *       _CrtMemState * state - dump since this state
  1833. *
  1834. *Return:
  1835. *       void
  1836. *
  1837. *******************************************************************************/
  1838. _CRTIMP void __cdecl _CrtMemDumpAllObjectsSince(
  1839.         const _CrtMemState * state
  1840.         )
  1841. {
  1842.         _CrtMemBlockHeader * pHead;
  1843.         _CrtMemBlockHeader * pStopBlock = NULL;
  1844.  
  1845.         _mlock(_HEAP_LOCK);  /* block other threads */
  1846.  
  1847.         _RPT0(_CRT_WARN, "Dumping objects ->\n");
  1848.  
  1849.         if (state)
  1850.             pStopBlock = state->pBlockHeader;
  1851.  
  1852.         for (pHead = _pFirstBlock; pHead != NULL && pHead != pStopBlock;
  1853.             pHead = pHead->pBlockHeaderNext)
  1854.         {
  1855.             if (_BLOCK_TYPE(pHead->nBlockUse) == _IGNORE_BLOCK ||
  1856.                 _BLOCK_TYPE(pHead->nBlockUse) == _FREE_BLOCK ||
  1857.                 (_BLOCK_TYPE(pHead->nBlockUse) == _CRT_BLOCK &&
  1858.                !(_crtDbgFlag & _CRTDBG_CHECK_CRT_DF))
  1859.                )
  1860.             {
  1861.                 /* ignore it for dumping */
  1862.             }
  1863.             else {
  1864.                 if (pHead->szFileName != NULL)
  1865.                 {
  1866.                     if (!_CrtIsValidPointer(pHead->szFileName, 1, FALSE))
  1867.                         _RPT1(_CRT_WARN, "#File Error#(%d) : ", pHead->nLine);
  1868.                     else
  1869.                         _RPT2(_CRT_WARN, "%hs(%d) : ", pHead->szFileName, pHead->nLine);
  1870.                 }
  1871.  
  1872.                 _RPT1(_CRT_WARN, "{%ld} ", pHead->lRequest);
  1873.  
  1874.                 if (_BLOCK_TYPE(pHead->nBlockUse) == _CLIENT_BLOCK)
  1875.                 {
  1876.                     _RPT3(_CRT_WARN, "client block at 0x%08X, subtype %x, %u bytes long.\n",
  1877.                         (BYTE *)pbData(pHead), _BLOCK_SUBTYPE(pHead->nBlockUse), pHead->nDataSize);
  1878.  
  1879.                     if (_pfnDumpClient)
  1880.                         (*_pfnDumpClient)( (void *) pbData(pHead), pHead->nDataSize);
  1881.                     else
  1882.                         _printMemBlockData(pHead);
  1883.                 }
  1884.                 else if (pHead->nBlockUse == _NORMAL_BLOCK)
  1885.                 {
  1886.                     _RPT2(_CRT_WARN, "normal block at 0x%08X, %u bytes long.\n",
  1887.                         (BYTE *)pbData(pHead), pHead->nDataSize);
  1888.  
  1889.                     _printMemBlockData(pHead);
  1890.                 }
  1891.                 else if (_BLOCK_TYPE(pHead->nBlockUse) == _CRT_BLOCK)
  1892.                 {
  1893.                     _RPT3(_CRT_WARN, "crt block at 0x%08X, subtype %x, %u bytes long.\n",
  1894.                         (BYTE *)pbData(pHead), _BLOCK_SUBTYPE(pHead->nBlockUse), pHead->nDataSize);
  1895.  
  1896.                     _printMemBlockData(pHead);
  1897.                 }
  1898.             }
  1899.         }
  1900.         _munlock(_HEAP_LOCK);  /* release other threads */
  1901.  
  1902.         _RPT0(_CRT_WARN, "Object dump complete.\n");
  1903. }
  1904.  
  1905.  
  1906. /***
  1907. *void _CrtMemDumpMemoryLeaks() - dump all objects still in heap
  1908. *
  1909. *Purpose:
  1910. *       dump all objects still in heap. used to detect memory leaks over the
  1911. *       life of a program
  1912. *
  1913. *Entry:
  1914. *       void
  1915. *
  1916. *Return:
  1917. *       TRUE if memory leaks
  1918. *       FALSE otherwise
  1919. *
  1920. *******************************************************************************/
  1921. _CRTIMP int __cdecl _CrtDumpMemoryLeaks(
  1922.         void
  1923.         )
  1924. {
  1925.         /* only dump leaks when there are in fact leaks */
  1926.         _CrtMemState msNow;
  1927.  
  1928.         _CrtMemCheckpoint(&msNow);
  1929.  
  1930.         if (msNow.lCounts[_CLIENT_BLOCK] != 0 ||
  1931.             msNow.lCounts[_NORMAL_BLOCK] != 0 ||
  1932.             (_crtDbgFlag & _CRTDBG_CHECK_CRT_DF &&
  1933.             msNow.lCounts[_CRT_BLOCK] != 0)
  1934.            )
  1935.         {
  1936.             /* difference detected: dump objects since start. */
  1937.             _RPT0(_CRT_WARN, "Detected memory leaks!\n");
  1938.  
  1939.             _CrtMemDumpAllObjectsSince(NULL);
  1940.             return TRUE;
  1941.         }
  1942.  
  1943.         return FALSE;   /* no leaked objects */
  1944. }
  1945.  
  1946.  
  1947. /***
  1948. *_CrtMemState * _CrtMemDumpStatistics() - dump memory state
  1949. *
  1950. *Purpose:
  1951. *       dump memory state
  1952. *
  1953. *Entry:
  1954. *       _CrtMemState * state - dump this state
  1955. *
  1956. *Return:
  1957. *       void
  1958. *
  1959. *******************************************************************************/
  1960. _CRTIMP void __cdecl _CrtMemDumpStatistics(
  1961.         const _CrtMemState * state
  1962.         )
  1963. {
  1964.         int use;
  1965.  
  1966.         if (state == NULL)
  1967.             return;
  1968.  
  1969.         for (use = 0; use < _MAX_BLOCKS; use++)
  1970.         {
  1971.             _RPT3(_CRT_WARN, "%ld bytes in %ld %hs Blocks.\n",
  1972.                 state->lSizes[use], state->lCounts[use], szBlockUseName[use]);
  1973.         }
  1974.  
  1975.         _RPT1(_CRT_WARN, "Largest number used: %ld bytes.\n", state->lHighWaterCount);
  1976.         _RPT1(_CRT_WARN, "Total allocations: %ld bytes.\n", state->lTotalCount);
  1977. }
  1978.  
  1979.  
  1980. #endif  /* _DEBUG */
  1981.