home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / perl560.zip / win32 / vmem.h < prev    next >
C/C++ Source or Header  |  2000-03-20  |  19KB  |  712 lines

  1. /* vmem.h
  2.  *
  3.  * (c) 1999 Microsoft Corporation. All rights reserved. 
  4.  * Portions (c) 1999 ActiveState Tool Corp, http://www.ActiveState.com/
  5.  *
  6.  *    You may distribute under the terms of either the GNU General Public
  7.  *    License or the Artistic License, as specified in the README file.
  8.  *
  9.  *
  10.  * Knuth's boundary tag algorithm Vol #1, Page 440.
  11.  *
  12.  * Each block in the heap has tag words before and after it,
  13.  *  TAG
  14.  *  block
  15.  *  TAG
  16.  * The size is stored in these tags as a long word, and includes the 8 bytes
  17.  * of overhead that the boundary tags consume.  Blocks are allocated on long
  18.  * word boundaries, so the size is always multiples of long words.  When the
  19.  * block is allocated, bit 0, (the tag bit), of the size is set to 1.  When 
  20.  * a block is freed, it is merged with adjacent free blocks, and the tag bit
  21.  * is set to 0.
  22.  *
  23.  * A linked list is used to manage the free list. The first two long words of
  24.  * the block contain double links.  These links are only valid when the block
  25.  * is freed, therefore space needs to be reserved for them.  Thus, the minimum
  26.  * block size (not counting the tags) is 8 bytes.
  27.  *
  28.  * Since memory allocation may occur on a single threaded, explict locks are
  29.  * provided.
  30.  * 
  31.  */
  32.  
  33. #ifndef ___VMEM_H_INC___
  34. #define ___VMEM_H_INC___
  35.  
  36. const long lAllocStart = 0x00010000; /* start at 64K */
  37. const long minBlockSize = sizeof(void*)*2;
  38. const long sizeofTag = sizeof(long);
  39. const long blockOverhead = sizeofTag*2;
  40. const long minAllocSize = minBlockSize+blockOverhead;
  41.  
  42. typedef BYTE* PBLOCK;    /* pointer to a memory block */
  43.  
  44. /*
  45.  * Macros for accessing hidden fields in a memory block:
  46.  *
  47.  * SIZE        size of this block (tag bit 0 is 1 if block is allocated)
  48.  * PSIZE    size of previous physical block
  49.  */
  50.  
  51. #define SIZE(block)    (*(ULONG*)(((PBLOCK)(block))-sizeofTag))
  52. #define PSIZE(block)    (*(ULONG*)(((PBLOCK)(block))-(sizeofTag*2)))
  53. inline void SetTags(PBLOCK block, long size)
  54. {
  55.     SIZE(block) = size;
  56.     PSIZE(block+(size&~1)) = size;
  57. }
  58.  
  59. /*
  60.  * Free list pointers
  61.  * PREV    pointer to previous block
  62.  * NEXT    pointer to next block
  63.  */
  64.  
  65. #define PREV(block)    (*(PBLOCK*)(block))
  66. #define NEXT(block)    (*(PBLOCK*)((block)+sizeof(PBLOCK)))
  67. inline void SetLink(PBLOCK block, PBLOCK prev, PBLOCK next)
  68. {
  69.     PREV(block) = prev;
  70.     NEXT(block) = next;
  71. }
  72. inline void Unlink(PBLOCK p)
  73. {
  74.     PBLOCK next = NEXT(p);
  75.     PBLOCK prev = PREV(p);
  76.     NEXT(prev) = next;
  77.     PREV(next) = prev;
  78. }
  79. inline void AddToFreeList(PBLOCK block, PBLOCK pInList)
  80. {
  81.     PBLOCK next = NEXT(pInList);
  82.     NEXT(pInList) = block;
  83.     SetLink(block, pInList, next);
  84.     PREV(next) = block;
  85. }
  86.  
  87.  
  88. /* Macro for rounding up to the next sizeof(long) */
  89. #define ROUND_UP(n)    (((ULONG)(n)+sizeof(long)-1)&~(sizeof(long)-1))
  90. #define ROUND_UP64K(n)    (((ULONG)(n)+0x10000-1)&~(0x10000-1))
  91. #define ROUND_DOWN(n)    ((ULONG)(n)&~(sizeof(long)-1))
  92.  
  93. /*
  94.  * HeapRec - a list of all non-contiguous heap areas
  95.  *
  96.  * Each record in this array contains information about a non-contiguous heap area.
  97.  */
  98.  
  99. const int maxHeaps = 64;
  100. const long lAllocMax   = 0x80000000; /* max size of allocation */
  101.  
  102. typedef struct _HeapRec
  103. {
  104.     PBLOCK    base;    /* base of heap area */
  105.     ULONG    len;    /* size of heap area */
  106. } HeapRec;
  107.  
  108.  
  109. class VMem
  110. {
  111. public:
  112.     VMem();
  113.     ~VMem();
  114.     virtual void* Malloc(size_t size);
  115.     virtual void* Realloc(void* pMem, size_t size);
  116.     virtual void Free(void* pMem);
  117.     virtual void GetLock(void);
  118.     virtual void FreeLock(void);
  119.     virtual int IsLocked(void);
  120.     virtual long Release(void);
  121.     virtual long AddRef(void);
  122.  
  123.     inline BOOL CreateOk(void)
  124.     {
  125.     return m_hHeap != NULL;
  126.     };
  127.  
  128.     void ReInit(void);
  129.  
  130. protected:
  131.     void Init(void);
  132.     int Getmem(size_t size);
  133.     int HeapAdd(void* ptr, size_t size);
  134.     void* Expand(void* block, size_t size);
  135.     void WalkHeap(void);
  136.  
  137.     HANDLE        m_hHeap;            // memory heap for this script
  138.     char        m_FreeDummy[minAllocSize];  // dummy free block
  139.     PBLOCK        m_pFreeList;            // pointer to first block on free list
  140.     PBLOCK        m_pRover;            // roving pointer into the free list
  141.     HeapRec        m_heaps[maxHeaps];        // list of all non-contiguous heap areas 
  142.     int            m_nHeaps;            // no. of heaps in m_heaps 
  143.     long        m_lAllocSize;            // current alloc size
  144.     long        m_lRefCount;            // number of current users
  145.     CRITICAL_SECTION    m_cs;                // access lock
  146. };
  147.  
  148. // #define _DEBUG_MEM
  149. #ifdef _DEBUG_MEM
  150. #define ASSERT(f) if(!(f)) DebugBreak();
  151.  
  152. inline void MEMODS(char *str)
  153. {
  154.     OutputDebugString(str);
  155.     OutputDebugString("\n");
  156. }
  157.  
  158. inline void MEMODSlx(char *str, long x)
  159. {
  160.     char szBuffer[512];    
  161.     sprintf(szBuffer, "%s %lx\n", str, x);
  162.     OutputDebugString(szBuffer);
  163. }
  164.  
  165. #define WALKHEAP() WalkHeap()
  166. #define WALKHEAPTRACE() m_pRover = NULL; WalkHeap()
  167.  
  168. #else
  169.  
  170. #define ASSERT(f)
  171. #define MEMODS(x)
  172. #define MEMODSlx(x, y)
  173. #define WALKHEAP()
  174. #define WALKHEAPTRACE()
  175.  
  176. #endif
  177.  
  178.  
  179. VMem::VMem()
  180. {
  181.     m_lRefCount = 1;
  182.     BOOL bRet = (NULL != (m_hHeap = HeapCreate(HEAP_NO_SERIALIZE,
  183.                 lAllocStart,    /* initial size of heap */
  184.                 0)));        /* no upper limit on size of heap */
  185.     ASSERT(bRet);
  186.  
  187.     InitializeCriticalSection(&m_cs);
  188.  
  189.     Init();
  190. }
  191.  
  192. VMem::~VMem(void)
  193. {
  194.     ASSERT(HeapValidate(m_hHeap, HEAP_NO_SERIALIZE, NULL));
  195.     WALKHEAPTRACE();
  196.     DeleteCriticalSection(&m_cs);
  197.     BOOL bRet = HeapDestroy(m_hHeap);
  198.     ASSERT(bRet);
  199. }
  200.  
  201. void VMem::ReInit(void)
  202. {
  203.     for(int index = 0; index < m_nHeaps; ++index)
  204.     HeapFree(m_hHeap, HEAP_NO_SERIALIZE, m_heaps[index].base);
  205.  
  206.     Init();
  207. }
  208.  
  209. void VMem::Init(void)
  210. {   /*
  211.      * Initialize the free list by placing a dummy zero-length block on it.
  212.      * Set the number of non-contiguous heaps to zero.
  213.      */
  214.     m_pFreeList = m_pRover = (PBLOCK)(&m_FreeDummy[minBlockSize]);
  215.     PSIZE(m_pFreeList) = SIZE(m_pFreeList) = 0;
  216.     PREV(m_pFreeList) = NEXT(m_pFreeList) = m_pFreeList;
  217.  
  218.     m_nHeaps = 0;
  219.     m_lAllocSize = lAllocStart;
  220. }
  221.  
  222. void* VMem::Malloc(size_t size)
  223. {
  224.     WALKHEAP();
  225.  
  226.     /*
  227.      * Adjust the real size of the block to be a multiple of sizeof(long), and add
  228.      * the overhead for the boundary tags.  Disallow negative or zero sizes.
  229.      */
  230.     size_t realsize = (size < blockOverhead) ? minAllocSize : (size_t)ROUND_UP(size) + minBlockSize;
  231.     if((int)realsize < minAllocSize || size == 0)
  232.     return NULL;
  233.  
  234.     /*
  235.      * Start searching the free list at the rover.  If we arrive back at rover without
  236.      * finding anything, allocate some memory from the heap and try again.
  237.      */
  238.     PBLOCK ptr = m_pRover;    /* start searching at rover */
  239.     int loops = 2;        /* allow two times through the loop  */
  240.     for(;;) {
  241.     size_t lsize = SIZE(ptr);
  242.     ASSERT((lsize&1)==0);
  243.     /* is block big enough? */
  244.     if(lsize >= realsize) {    
  245.         /* if the remainder is too small, don't bother splitting the block. */
  246.         size_t rem = lsize - realsize;
  247.         if(rem < minAllocSize) {
  248.         if(m_pRover == ptr)
  249.             m_pRover = NEXT(ptr);
  250.  
  251.         /* Unlink the block from the free list. */
  252.         Unlink(ptr);
  253.         }
  254.         else {
  255.         /*
  256.          * split the block
  257.          * The remainder is big enough to split off into a new block.
  258.          * Use the end of the block, resize the beginning of the block
  259.          * no need to change the free list.
  260.          */
  261.         SetTags(ptr, rem);
  262.         ptr += SIZE(ptr);
  263.         lsize = realsize;
  264.         }
  265.         /* Set the boundary tags to mark it as allocated. */
  266.         SetTags(ptr, lsize | 1);
  267.         return ((void *)ptr);
  268.     }
  269.  
  270.     /*
  271.      * This block was unsuitable.  If we've gone through this list once already without
  272.      * finding anything, allocate some new memory from the heap and try again.
  273.      */
  274.     ptr = NEXT(ptr);
  275.     if(ptr == m_pRover) {
  276.         if(!(loops-- && Getmem(realsize))) {
  277.         return NULL;
  278.         }
  279.         ptr = m_pRover;
  280.     }
  281.     }
  282. }
  283.  
  284. void* VMem::Realloc(void* block, size_t size)
  285. {
  286.     WALKHEAP();
  287.  
  288.     /* if size is zero, free the block. */
  289.     if(size == 0) {
  290.     Free(block);
  291.     return (NULL);
  292.     }
  293.  
  294.     /* if block pointer is NULL, do a Malloc(). */
  295.     if(block == NULL)
  296.     return Malloc(size);
  297.  
  298.     /*
  299.      * Grow or shrink the block in place.
  300.      * if the block grows then the next block will be used if free
  301.      */
  302.     if(Expand(block, size) != NULL)
  303.     return block;
  304.  
  305.     /*
  306.      * adjust the real size of the block to be a multiple of sizeof(long), and add the
  307.      * overhead for the boundary tags.  Disallow negative or zero sizes.
  308.      */
  309.     size_t realsize = (size < blockOverhead) ? minAllocSize : (size_t)ROUND_UP(size) + minBlockSize;
  310.     if((int)realsize < minAllocSize)
  311.     return NULL;
  312.  
  313.     /*
  314.      * see if the previous block is free, and is it big enough to cover the new size
  315.      * if merged with the current block.
  316.      */
  317.     PBLOCK ptr = (PBLOCK)block;
  318.     size_t cursize = SIZE(ptr) & ~1;
  319.     size_t psize = PSIZE(ptr);
  320.     if((psize&1) == 0 && (psize + cursize) >= realsize) {
  321.     PBLOCK prev = ptr - psize;
  322.     if(m_pRover == prev)
  323.         m_pRover = NEXT(prev);
  324.  
  325.     /* Unlink the next block from the free list. */
  326.     Unlink(prev);
  327.  
  328.     /* Copy contents of old block to new location, make it the current block. */
  329.     memmove(prev, ptr, cursize);
  330.     cursize += psize;    /* combine sizes */
  331.     ptr = prev;
  332.  
  333.     size_t rem = cursize - realsize;
  334.     if(rem >= minAllocSize) {
  335.         /*
  336.          * The remainder is big enough to be a new block.  Set boundary
  337.          * tags for the resized block and the new block.
  338.          */
  339.         prev = ptr + realsize;
  340.         /*
  341.          * add the new block to the free list.
  342.          * next block cannot be free
  343.          */
  344.         SetTags(prev, rem);
  345.         AddToFreeList(prev, m_pFreeList);
  346.         cursize = realsize;
  347.         }
  348.     /* Set the boundary tags to mark it as allocated. */
  349.     SetTags(ptr, cursize | 1);
  350.         return ((void *)ptr);
  351.     }
  352.  
  353.     /* Allocate a new block, copy the old to the new, and free the old. */
  354.     if((ptr = (PBLOCK)Malloc(size)) != NULL) {
  355.     memmove(ptr, block, cursize-minBlockSize);
  356.     Free(block);
  357.     }
  358.     return ((void *)ptr);
  359. }
  360.  
  361. void VMem::Free(void* p)
  362. {
  363.     WALKHEAP();
  364.  
  365.     /* Ignore null pointer. */
  366.     if(p == NULL)
  367.     return;
  368.  
  369.     PBLOCK ptr = (PBLOCK)p;
  370.  
  371.     /* Check for attempt to free a block that's already free. */
  372.     size_t size = SIZE(ptr);
  373.     if((size&1) == 0) {
  374.     MEMODSlx("Attempt to free previously freed block", (long)p);
  375.     return;
  376.     }
  377.     size &= ~1;    /* remove allocated tag */
  378.  
  379.     /* if previous block is free, add this block to it. */
  380.     int linked = FALSE;
  381.     size_t psize = PSIZE(ptr);
  382.     if((psize&1) == 0) {
  383.     ptr -= psize;    /* point to previous block */
  384.     size += psize;    /* merge the sizes of the two blocks */
  385.     linked = TRUE;    /* it's already on the free list */
  386.     }
  387.  
  388.     /* if the next physical block is free, merge it with this block. */
  389.     PBLOCK next = ptr + size;    /* point to next physical block */
  390.     size_t nsize = SIZE(next);
  391.     if((nsize&1) == 0) {
  392.     /* block is free move rover if needed */
  393.     if(m_pRover == next)
  394.         m_pRover = NEXT(next);
  395.  
  396.     /* unlink the next block from the free list. */
  397.     Unlink(next);
  398.  
  399.     /* merge the sizes of this block and the next block. */
  400.     size += nsize;
  401.     }
  402.  
  403.     /* Set the boundary tags for the block; */
  404.     SetTags(ptr, size);
  405.  
  406.     /* Link the block to the head of the free list. */
  407.     if(!linked) {
  408.     AddToFreeList(ptr, m_pFreeList);
  409.     }
  410. }
  411.  
  412. void VMem::GetLock(void)
  413. {
  414.     EnterCriticalSection(&m_cs);
  415. }
  416.  
  417. void VMem::FreeLock(void)
  418. {
  419.     LeaveCriticalSection(&m_cs);
  420. }
  421.  
  422. int VMem::IsLocked(void)
  423. {
  424. #if 0
  425.     /* XXX TryEnterCriticalSection() is not available in some versions
  426.      * of Windows 95.  Since this code is not used anywhere yet, we 
  427.      * skirt the issue for now. */
  428.     BOOL bAccessed = TryEnterCriticalSection(&m_cs);
  429.     if(bAccessed) {
  430.     LeaveCriticalSection(&m_cs);
  431.     }
  432.     return !bAccessed;
  433. #else
  434.     ASSERT(0);    /* alarm bells for when somebody calls this */
  435.     return 0;
  436. #endif
  437. }
  438.  
  439.  
  440. long VMem::Release(void)
  441. {
  442.     long lCount = InterlockedDecrement(&m_lRefCount);
  443.     if(!lCount)
  444.     delete this;
  445.     return lCount;
  446. }
  447.  
  448. long VMem::AddRef(void)
  449. {
  450.     long lCount = InterlockedIncrement(&m_lRefCount);
  451.     return lCount;
  452. }
  453.  
  454.  
  455. int VMem::Getmem(size_t requestSize)
  456. {   /* returns -1 is successful 0 if not */
  457.     void *ptr;
  458.  
  459.     /* Round up size to next multiple of 64K. */
  460.     size_t size = (size_t)ROUND_UP64K(requestSize);
  461.     
  462.     /*
  463.      * if the size requested is smaller than our current allocation size
  464.      * adjust up
  465.      */
  466.     if(size < (unsigned long)m_lAllocSize)
  467.     size = m_lAllocSize;
  468.  
  469.     /* Update the size to allocate on the next request */
  470.     if(m_lAllocSize != lAllocMax)
  471.     m_lAllocSize <<= 1;
  472.  
  473.     if(m_nHeaps != 0) {
  474.     /* Expand the last allocated heap */
  475.     ptr = HeapReAlloc(m_hHeap, HEAP_REALLOC_IN_PLACE_ONLY|HEAP_ZERO_MEMORY|HEAP_NO_SERIALIZE,
  476.         m_heaps[m_nHeaps-1].base,
  477.         m_heaps[m_nHeaps-1].len + size);
  478.     if(ptr != 0) {
  479.         HeapAdd(((char*)ptr) + m_heaps[m_nHeaps-1].len, size);
  480.         return -1;
  481.     }
  482.     }
  483.  
  484.     /*
  485.      * if we didn't expand a block to cover the requested size
  486.      * allocate a new Heap
  487.      * the size of this block must include the additional dummy tags at either end
  488.      * the above ROUND_UP64K may not have added any memory to include this.
  489.      */
  490.     if(size == requestSize)
  491.     size = (size_t)ROUND_UP64K(requestSize+(sizeofTag*2));
  492.  
  493.     ptr = HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY|HEAP_NO_SERIALIZE, size);
  494.     if(ptr == 0) {
  495.     MEMODSlx("HeapAlloc failed on size!!!", size);
  496.     return 0;
  497.     }
  498.  
  499.     HeapAdd(ptr, size);
  500.     return -1;
  501. }
  502.  
  503. int VMem::HeapAdd(void *p, size_t size)
  504. {   /* if the block can be succesfully added to the heap, returns 0; otherwise -1. */
  505.     int index;
  506.  
  507.     /* Check size, then round size down to next long word boundary. */
  508.     if(size < minAllocSize)
  509.     return -1;
  510.  
  511.     size = (size_t)ROUND_DOWN(size);
  512.     PBLOCK ptr = (PBLOCK)p;
  513.  
  514.     /*
  515.      * Search for another heap area that's contiguous with the bottom of this new area.
  516.      * (It should be extremely unusual to find one that's contiguous with the top).
  517.      */
  518.     for(index = 0; index < m_nHeaps; ++index) {
  519.     if(ptr == m_heaps[index].base + (int)m_heaps[index].len) {
  520.         /*
  521.          * The new block is contiguous with a previously allocated heap area.  Add its
  522.          * length to that of the previous heap.  Merge it with the the dummy end-of-heap
  523.          * area marker of the previous heap.
  524.          */
  525.         m_heaps[index].len += size;
  526.         break;
  527.     }
  528.     }
  529.  
  530.     if(index == m_nHeaps) {
  531.     /* The new block is not contiguous.  Add it to the heap list. */
  532.     if(m_nHeaps == maxHeaps) {
  533.         return -1;    /* too many non-contiguous heaps */
  534.     }
  535.     m_heaps[m_nHeaps].base = ptr;
  536.     m_heaps[m_nHeaps].len = size;
  537.     m_nHeaps++;
  538.  
  539.     /*
  540.      * Reserve the first LONG in the block for the ending boundary tag of a dummy
  541.      * block at the start of the heap area.
  542.      */
  543.     size -= minBlockSize;
  544.     ptr += minBlockSize;
  545.     PSIZE(ptr) = 1;    /* mark the dummy previous block as allocated */
  546.     }
  547.  
  548.     /*
  549.      * Convert the heap to one large block.  Set up its boundary tags, and those of
  550.      * marker block after it.  The marker block before the heap will already have
  551.      * been set up if this heap is not contiguous with the end of another heap.
  552.      */
  553.     SetTags(ptr, size | 1);
  554.     PBLOCK next = ptr + size;    /* point to dummy end block */
  555.     SIZE(next) = 1;    /* mark the dummy end block as allocated */
  556.  
  557.     /*
  558.      * Link the block to the start of the free list by calling free().
  559.      * This will merge the block with any adjacent free blocks.
  560.      */
  561.     Free(ptr);
  562.     return 0;
  563. }
  564.  
  565.  
  566. void* VMem::Expand(void* block, size_t size)
  567. {
  568.     /*
  569.      * Adjust the size of the block to be a multiple of sizeof(long), and add the
  570.      * overhead for the boundary tags.  Disallow negative or zero sizes.
  571.      */
  572.     size_t realsize = (size < blockOverhead) ? minAllocSize : (size_t)ROUND_UP(size) + minBlockSize;
  573.     if((int)realsize < minAllocSize || size == 0)
  574.     return NULL;
  575.  
  576.     PBLOCK ptr = (PBLOCK)block; 
  577.  
  578.     /* if the current size is the same as requested, do nothing. */
  579.     size_t cursize = SIZE(ptr) & ~1;
  580.     if(cursize == realsize) {
  581.     return block;
  582.     }
  583.  
  584.     /* if the block is being shrunk, convert the remainder of the block into a new free block. */
  585.     if(realsize <= cursize) {
  586.     size_t nextsize = cursize - realsize;    /* size of new remainder block */
  587.     if(nextsize >= minAllocSize) {
  588.         /*
  589.          * Split the block
  590.          * Set boundary tags for the resized block and the new block.
  591.          */
  592.         SetTags(ptr, realsize | 1);
  593.         ptr += realsize;
  594.  
  595.         /*
  596.          * add the new block to the free list.
  597.          * call Free to merge this block with next block if free
  598.          */
  599.         SetTags(ptr, nextsize | 1);
  600.         Free(ptr);
  601.     }
  602.  
  603.     return block;
  604.     }
  605.  
  606.     PBLOCK next = ptr + cursize;
  607.     size_t nextsize = SIZE(next);
  608.  
  609.     /* Check the next block for consistency.*/
  610.     if((nextsize&1) == 0 && (nextsize + cursize) >= realsize) {
  611.     /*
  612.      * The next block is free and big enough.  Add the part that's needed
  613.      * to our block, and split the remainder off into a new block.
  614.      */
  615.     if(m_pRover == next)
  616.         m_pRover = NEXT(next);
  617.  
  618.     /* Unlink the next block from the free list. */
  619.     Unlink(next);
  620.     cursize += nextsize;    /* combine sizes */
  621.  
  622.     size_t rem = cursize - realsize;    /* size of remainder */
  623.     if(rem >= minAllocSize) {
  624.         /*
  625.          * The remainder is big enough to be a new block.
  626.          * Set boundary tags for the resized block and the new block.
  627.          */
  628.         next = ptr + realsize;
  629.         /*
  630.          * add the new block to the free list.
  631.          * next block cannot be free
  632.          */
  633.         SetTags(next, rem);
  634.         AddToFreeList(next, m_pFreeList);
  635.         cursize = realsize;
  636.         }
  637.     /* Set the boundary tags to mark it as allocated. */
  638.     SetTags(ptr, cursize | 1);
  639.     return ((void *)ptr);
  640.     }
  641.     return NULL;
  642. }
  643.  
  644. #ifdef _DEBUG_MEM
  645. #define LOG_FILENAME "P:\\Apps\\Perl\\Result.txt"
  646.  
  647. void MemoryUsageMessage(char *str, long x, long y, int c)
  648. {
  649.     static FILE* fp = NULL;
  650.     char szBuffer[512];
  651.     if(str) {
  652.     if(!fp)
  653.         fp = fopen(LOG_FILENAME, "w");
  654.     sprintf(szBuffer, str, x, y, c);
  655.     fputs(szBuffer, fp);
  656.     }
  657.     else {
  658.     fflush(fp);
  659.     fclose(fp);
  660.     }
  661. }
  662.  
  663. void VMem::WalkHeap(void)
  664. {
  665.     if(!m_pRover) {
  666.     MemoryUsageMessage("VMem heaps used %d\n", m_nHeaps, 0, 0);
  667.     }
  668.  
  669.     /* Walk all the heaps - verify structures */
  670.     for(int index = 0; index < m_nHeaps; ++index) {
  671.     PBLOCK ptr = m_heaps[index].base;
  672.     size_t size = m_heaps[index].len;
  673.     ASSERT(HeapValidate(m_hHeap, HEAP_NO_SERIALIZE, p));
  674.  
  675.     /* set over reserved header block */
  676.     size -= minBlockSize;
  677.     ptr += minBlockSize;
  678.     PBLOCK pLast = ptr + size;
  679.     ASSERT(PSIZE(ptr) == 1); /* dummy previous block is allocated */
  680.     ASSERT(SIZE(pLast) == 1); /* dummy next block is allocated */
  681.     while(ptr < pLast) {
  682.         ASSERT(ptr > m_heaps[index].base);
  683.         size_t cursize = SIZE(ptr) & ~1;
  684.         ASSERT((PSIZE(ptr+cursize) & ~1) == cursize);
  685.         if(!m_pRover) {
  686.         MemoryUsageMessage("Memory Block %08x: Size %08x %c\n", (long)ptr, cursize, (SIZE(p)&1) ? 'x' : ' ');
  687.         }
  688.         if(!(SIZE(ptr)&1)) {
  689.         /* this block is on the free list */
  690.         PBLOCK tmp = NEXT(ptr);
  691.         while(tmp != ptr) {
  692.             ASSERT((SIZE(tmp)&1)==0);
  693.             if(tmp == m_pFreeList)
  694.             break;
  695.             ASSERT(NEXT(tmp));
  696.             tmp = NEXT(tmp);
  697.         }
  698.         if(tmp == ptr) {
  699.             MemoryUsageMessage("Memory Block %08x: Size %08x free but not in free list\n", (long)ptr, cursize, 0);
  700.         }
  701.         }
  702.         ptr += cursize;
  703.     }
  704.     }
  705.     if(!m_pRover) {
  706.     MemoryUsageMessage(NULL, 0, 0, 0);
  707.     }
  708. }
  709. #endif
  710.  
  711. #endif    /* ___VMEM_H_INC___ */
  712.