xref: /openbsd-src/gnu/usr.bin/perl/win32/vmem.h (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
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  * Options:
10  *
11  * Defining _USE_MSVCRT_MEM_ALLOC will cause all memory allocations
12  * to be forwarded to MSVCRT.DLL. Defining _USE_LINKED_LIST as well will
13  * track all allocations in a doubly linked list, so that the host can
14  * free all memory allocated when it goes away.
15  * If _USE_MSVCRT_MEM_ALLOC is not defined then Knuth's boundary tag algorithm
16  * is used; defining _USE_BUDDY_BLOCKS will use Knuth's algorithm R
17  * (Buddy system reservation)
18  *
19  */
20 
21 #ifndef ___VMEM_H_INC___
22 #define ___VMEM_H_INC___
23 
24 #ifndef UNDER_CE
25 #define _USE_MSVCRT_MEM_ALLOC
26 #endif
27 #define _USE_LINKED_LIST
28 
29 // #define _USE_BUDDY_BLOCKS
30 
31 // #define _DEBUG_MEM
32 #ifdef _DEBUG_MEM
33 #define ASSERT(f) if(!(f)) DebugBreak();
34 
35 inline void MEMODS(char *str)
36 {
37     OutputDebugString(str);
38     OutputDebugString("\n");
39 }
40 
41 inline void MEMODSlx(char *str, long x)
42 {
43     char szBuffer[512];
44     sprintf(szBuffer, "%s %lx\n", str, x);
45     OutputDebugString(szBuffer);
46 }
47 
48 #define WALKHEAP() WalkHeap(0)
49 #define WALKHEAPTRACE() WalkHeap(1)
50 
51 #else
52 
53 #define ASSERT(f)
54 #define MEMODS(x)
55 #define MEMODSlx(x, y)
56 #define WALKHEAP()
57 #define WALKHEAPTRACE()
58 
59 #endif
60 
61 #ifdef _USE_MSVCRT_MEM_ALLOC
62 
63 #ifndef _USE_LINKED_LIST
64 // #define _USE_LINKED_LIST
65 #endif
66 
67 /*
68  * Pass all memory requests throught to msvcrt.dll
69  * optionaly track by using a doubly linked header
70  */
71 
72 typedef void (*LPFREE)(void *block);
73 typedef void* (*LPMALLOC)(size_t size);
74 typedef void* (*LPREALLOC)(void *block, size_t size);
75 #ifdef _USE_LINKED_LIST
76 class VMem;
77 typedef struct _MemoryBlockHeader* PMEMORY_BLOCK_HEADER;
78 typedef struct _MemoryBlockHeader {
79     PMEMORY_BLOCK_HEADER    pNext;
80     PMEMORY_BLOCK_HEADER    pPrev;
81     VMem *owner;
82 } MEMORY_BLOCK_HEADER, *PMEMORY_BLOCK_HEADER;
83 #endif
84 
85 class VMem
86 {
87 public:
88     VMem();
89     ~VMem();
90     virtual void* Malloc(size_t size);
91     virtual void* Realloc(void* pMem, size_t size);
92     virtual void Free(void* pMem);
93     virtual void GetLock(void);
94     virtual void FreeLock(void);
95     virtual int IsLocked(void);
96     virtual long Release(void);
97     virtual long AddRef(void);
98 
99     inline BOOL CreateOk(void)
100     {
101 	return TRUE;
102     };
103 
104 protected:
105 #ifdef _USE_LINKED_LIST
106     void LinkBlock(PMEMORY_BLOCK_HEADER ptr)
107     {
108 	PMEMORY_BLOCK_HEADER next = m_Dummy.pNext;
109 	m_Dummy.pNext = ptr;
110 	ptr->pPrev = &m_Dummy;
111 	ptr->pNext = next;
112         ptr->owner = this;
113 	next->pPrev = ptr;
114     }
115     void UnlinkBlock(PMEMORY_BLOCK_HEADER ptr)
116     {
117 	PMEMORY_BLOCK_HEADER next = ptr->pNext;
118 	PMEMORY_BLOCK_HEADER prev = ptr->pPrev;
119 	prev->pNext = next;
120 	next->pPrev = prev;
121     }
122 
123     MEMORY_BLOCK_HEADER	m_Dummy;
124 #endif
125 
126     long		m_lRefCount;	// number of current users
127     CRITICAL_SECTION	m_cs;		// access lock
128     HINSTANCE		m_hLib;
129     LPFREE		m_pfree;
130     LPMALLOC		m_pmalloc;
131     LPREALLOC		m_prealloc;
132 };
133 
134 VMem::VMem()
135 {
136     m_lRefCount = 1;
137     InitializeCriticalSection(&m_cs);
138 #ifdef _USE_LINKED_LIST
139     m_Dummy.pNext = m_Dummy.pPrev =  &m_Dummy;
140     m_Dummy.owner = this;
141 #endif
142     m_hLib = LoadLibrary("msvcrt.dll");
143     if (m_hLib) {
144 	m_pfree = (LPFREE)GetProcAddress(m_hLib, "free");
145 	m_pmalloc = (LPMALLOC)GetProcAddress(m_hLib, "malloc");
146 	m_prealloc = (LPREALLOC)GetProcAddress(m_hLib, "realloc");
147     }
148 }
149 
150 VMem::~VMem(void)
151 {
152 #ifdef _USE_LINKED_LIST
153     while (m_Dummy.pNext != &m_Dummy) {
154 	Free(m_Dummy.pNext+1);
155     }
156 #endif
157     if (m_hLib)
158 	FreeLibrary(m_hLib);
159     DeleteCriticalSection(&m_cs);
160 }
161 
162 void* VMem::Malloc(size_t size)
163 {
164 #ifdef _USE_LINKED_LIST
165     GetLock();
166     PMEMORY_BLOCK_HEADER ptr = (PMEMORY_BLOCK_HEADER)m_pmalloc(size+sizeof(MEMORY_BLOCK_HEADER));
167     if (!ptr) {
168 	FreeLock();
169 	return NULL;
170     }
171     LinkBlock(ptr);
172     FreeLock();
173     return (ptr+1);
174 #else
175     return m_pmalloc(size);
176 #endif
177 }
178 
179 void* VMem::Realloc(void* pMem, size_t size)
180 {
181 #ifdef _USE_LINKED_LIST
182     if (!pMem)
183 	return Malloc(size);
184 
185     if (!size) {
186 	Free(pMem);
187 	return NULL;
188     }
189 
190     GetLock();
191     PMEMORY_BLOCK_HEADER ptr = (PMEMORY_BLOCK_HEADER)(((char*)pMem)-sizeof(MEMORY_BLOCK_HEADER));
192     UnlinkBlock(ptr);
193     ptr = (PMEMORY_BLOCK_HEADER)m_prealloc(ptr, size+sizeof(MEMORY_BLOCK_HEADER));
194     if (!ptr) {
195 	FreeLock();
196 	return NULL;
197     }
198     LinkBlock(ptr);
199     FreeLock();
200 
201     return (ptr+1);
202 #else
203     return m_prealloc(pMem, size);
204 #endif
205 }
206 
207 void VMem::Free(void* pMem)
208 {
209 #ifdef _USE_LINKED_LIST
210     if (pMem) {
211 	PMEMORY_BLOCK_HEADER ptr = (PMEMORY_BLOCK_HEADER)(((char*)pMem)-sizeof(MEMORY_BLOCK_HEADER));
212         if (ptr->owner != this) {
213 	    if (ptr->owner) {
214 #if 1
215 		dTHX;
216 	    	int *nowhere = NULL;
217 	    	Perl_warn(aTHX_ "Free to wrong pool %p not %p",this,ptr->owner);
218             	*nowhere = 0; /* this segfault is deliberate,
219             	                 so you can see the stack trace */
220 #else
221                 ptr->owner->Free(pMem);
222 #endif
223 	    }
224 	    return;
225         }
226 	GetLock();
227 	UnlinkBlock(ptr);
228 	ptr->owner = NULL;
229 	m_pfree(ptr);
230 	FreeLock();
231     }
232 #else
233     m_pfree(pMem);
234 #endif
235 }
236 
237 void VMem::GetLock(void)
238 {
239     EnterCriticalSection(&m_cs);
240 }
241 
242 void VMem::FreeLock(void)
243 {
244     LeaveCriticalSection(&m_cs);
245 }
246 
247 int VMem::IsLocked(void)
248 {
249 #if 0
250     /* XXX TryEnterCriticalSection() is not available in some versions
251      * of Windows 95.  Since this code is not used anywhere yet, we
252      * skirt the issue for now. */
253     BOOL bAccessed = TryEnterCriticalSection(&m_cs);
254     if(bAccessed) {
255 	LeaveCriticalSection(&m_cs);
256     }
257     return !bAccessed;
258 #else
259     ASSERT(0);	/* alarm bells for when somebody calls this */
260     return 0;
261 #endif
262 }
263 
264 long VMem::Release(void)
265 {
266     long lCount = InterlockedDecrement(&m_lRefCount);
267     if(!lCount)
268 	delete this;
269     return lCount;
270 }
271 
272 long VMem::AddRef(void)
273 {
274     long lCount = InterlockedIncrement(&m_lRefCount);
275     return lCount;
276 }
277 
278 #else	/* _USE_MSVCRT_MEM_ALLOC */
279 
280 /*
281  * Knuth's boundary tag algorithm Vol #1, Page 440.
282  *
283  * Each block in the heap has tag words before and after it,
284  *  TAG
285  *  block
286  *  TAG
287  * The size is stored in these tags as a long word, and includes the 8 bytes
288  * of overhead that the boundary tags consume.  Blocks are allocated on long
289  * word boundaries, so the size is always multiples of long words.  When the
290  * block is allocated, bit 0, (the tag bit), of the size is set to 1.  When
291  * a block is freed, it is merged with adjacent free blocks, and the tag bit
292  * is set to 0.
293  *
294  * A linked list is used to manage the free list. The first two long words of
295  * the block contain double links.  These links are only valid when the block
296  * is freed, therefore space needs to be reserved for them.  Thus, the minimum
297  * block size (not counting the tags) is 8 bytes.
298  *
299  * Since memory allocation may occur on a single threaded, explict locks are not
300  * provided.
301  *
302  */
303 
304 const long lAllocStart = 0x00020000; /* start at 128K */
305 const long minBlockSize = sizeof(void*)*2;
306 const long sizeofTag = sizeof(long);
307 const long blockOverhead = sizeofTag*2;
308 const long minAllocSize = minBlockSize+blockOverhead;
309 #ifdef _USE_BUDDY_BLOCKS
310 const long lSmallBlockSize = 1024;
311 const size_t nListEntries = ((lSmallBlockSize-minAllocSize)/sizeof(long));
312 
313 inline size_t CalcEntry(size_t size)
314 {
315     ASSERT((size&(sizeof(long)-1)) == 0);
316     return ((size - minAllocSize) / sizeof(long));
317 }
318 #endif
319 
320 typedef BYTE* PBLOCK;	/* pointer to a memory block */
321 
322 /*
323  * Macros for accessing hidden fields in a memory block:
324  *
325  * SIZE	    size of this block (tag bit 0 is 1 if block is allocated)
326  * PSIZE    size of previous physical block
327  */
328 
329 #define SIZE(block)	(*(ULONG*)(((PBLOCK)(block))-sizeofTag))
330 #define PSIZE(block)	(*(ULONG*)(((PBLOCK)(block))-(blockOverhead)))
331 inline void SetTags(PBLOCK block, long size)
332 {
333     SIZE(block) = size;
334     PSIZE(block+(size&~1)) = size;
335 }
336 
337 /*
338  * Free list pointers
339  * PREV	pointer to previous block
340  * NEXT	pointer to next block
341  */
342 
343 #define PREV(block)	(*(PBLOCK*)(block))
344 #define NEXT(block)	(*(PBLOCK*)((block)+sizeof(PBLOCK)))
345 inline void SetLink(PBLOCK block, PBLOCK prev, PBLOCK next)
346 {
347     PREV(block) = prev;
348     NEXT(block) = next;
349 }
350 inline void Unlink(PBLOCK p)
351 {
352     PBLOCK next = NEXT(p);
353     PBLOCK prev = PREV(p);
354     NEXT(prev) = next;
355     PREV(next) = prev;
356 }
357 #ifndef _USE_BUDDY_BLOCKS
358 inline void AddToFreeList(PBLOCK block, PBLOCK pInList)
359 {
360     PBLOCK next = NEXT(pInList);
361     NEXT(pInList) = block;
362     SetLink(block, pInList, next);
363     PREV(next) = block;
364 }
365 #endif
366 
367 /* Macro for rounding up to the next sizeof(long) */
368 #define ROUND_UP(n)	(((ULONG)(n)+sizeof(long)-1)&~(sizeof(long)-1))
369 #define ROUND_UP64K(n)	(((ULONG)(n)+0x10000-1)&~(0x10000-1))
370 #define ROUND_DOWN(n)	((ULONG)(n)&~(sizeof(long)-1))
371 
372 /*
373  * HeapRec - a list of all non-contiguous heap areas
374  *
375  * Each record in this array contains information about a non-contiguous heap area.
376  */
377 
378 const int maxHeaps = 32; /* 64 was overkill */
379 const long lAllocMax   = 0x80000000; /* max size of allocation */
380 
381 #ifdef _USE_BUDDY_BLOCKS
382 typedef struct _FreeListEntry
383 {
384     BYTE    Dummy[minAllocSize];	// dummy free block
385 } FREE_LIST_ENTRY, *PFREE_LIST_ENTRY;
386 #endif
387 
388 #ifndef _USE_BUDDY_BLOCKS
389 #define USE_BIGBLOCK_ALLOC
390 #endif
391 /*
392  * performance tuning
393  * Use VirtualAlloc() for blocks bigger than nMaxHeapAllocSize since
394  * Windows 95/98/Me have heap managers that are designed for memory
395  * blocks smaller than four megabytes.
396  */
397 
398 #ifdef USE_BIGBLOCK_ALLOC
399 const int nMaxHeapAllocSize = (1024*512);  /* don't allocate anything larger than this from the heap */
400 #endif
401 
402 typedef struct _HeapRec
403 {
404     PBLOCK	base;	/* base of heap area */
405     ULONG	len;	/* size of heap area */
406 #ifdef USE_BIGBLOCK_ALLOC
407     BOOL	bBigBlock;  /* was allocate using VirtualAlloc */
408 #endif
409 } HeapRec;
410 
411 class VMem
412 {
413 public:
414     VMem();
415     ~VMem();
416     virtual void* Malloc(size_t size);
417     virtual void* Realloc(void* pMem, size_t size);
418     virtual void Free(void* pMem);
419     virtual void GetLock(void);
420     virtual void FreeLock(void);
421     virtual int IsLocked(void);
422     virtual long Release(void);
423     virtual long AddRef(void);
424 
425     inline BOOL CreateOk(void)
426     {
427 #ifdef _USE_BUDDY_BLOCKS
428 	return TRUE;
429 #else
430 	return m_hHeap != NULL;
431 #endif
432     };
433 
434     void ReInit(void);
435 
436 protected:
437     void Init(void);
438     int Getmem(size_t size);
439 
440     int HeapAdd(void* ptr, size_t size
441 #ifdef USE_BIGBLOCK_ALLOC
442 	, BOOL bBigBlock
443 #endif
444     );
445 
446     void* Expand(void* block, size_t size);
447 
448 #ifdef _USE_BUDDY_BLOCKS
449     inline PBLOCK GetFreeListLink(int index)
450     {
451 	if (index >= nListEntries)
452 	    index = nListEntries-1;
453 	return &m_FreeList[index].Dummy[sizeofTag];
454     }
455     inline PBLOCK GetOverSizeFreeList(void)
456     {
457 	return &m_FreeList[nListEntries-1].Dummy[sizeofTag];
458     }
459     inline PBLOCK GetEOLFreeList(void)
460     {
461 	return &m_FreeList[nListEntries].Dummy[sizeofTag];
462     }
463 
464     void AddToFreeList(PBLOCK block, size_t size)
465     {
466 	PBLOCK pFreeList = GetFreeListLink(CalcEntry(size));
467 	PBLOCK next = NEXT(pFreeList);
468 	NEXT(pFreeList) = block;
469 	SetLink(block, pFreeList, next);
470 	PREV(next) = block;
471     }
472 #endif
473     inline size_t CalcAllocSize(size_t size)
474     {
475 	/*
476 	 * Adjust the real size of the block to be a multiple of sizeof(long), and add
477 	 * the overhead for the boundary tags.  Disallow negative or zero sizes.
478 	 */
479 	return (size < minBlockSize) ? minAllocSize : (size_t)ROUND_UP(size) + blockOverhead;
480     }
481 
482 #ifdef _USE_BUDDY_BLOCKS
483     FREE_LIST_ENTRY	m_FreeList[nListEntries+1];	// free list with dummy end of list entry as well
484 #else
485     HANDLE		m_hHeap;		    // memory heap for this script
486     char		m_FreeDummy[minAllocSize];  // dummy free block
487     PBLOCK		m_pFreeList;		    // pointer to first block on free list
488 #endif
489     PBLOCK		m_pRover;		    // roving pointer into the free list
490     HeapRec		m_heaps[maxHeaps];	    // list of all non-contiguous heap areas
491     int			m_nHeaps;		    // no. of heaps in m_heaps
492     long		m_lAllocSize;		    // current alloc size
493     long		m_lRefCount;		    // number of current users
494     CRITICAL_SECTION	m_cs;			    // access lock
495 
496 #ifdef _DEBUG_MEM
497     void WalkHeap(int complete);
498     void MemoryUsageMessage(char *str, long x, long y, int c);
499     FILE*		m_pLog;
500 #endif
501 };
502 
503 VMem::VMem()
504 {
505     m_lRefCount = 1;
506 #ifndef _USE_BUDDY_BLOCKS
507     BOOL bRet = (NULL != (m_hHeap = HeapCreate(HEAP_NO_SERIALIZE,
508 				lAllocStart,	/* initial size of heap */
509 				0)));		/* no upper limit on size of heap */
510     ASSERT(bRet);
511 #endif
512 
513     InitializeCriticalSection(&m_cs);
514 #ifdef _DEBUG_MEM
515     m_pLog = 0;
516 #endif
517 
518     Init();
519 }
520 
521 VMem::~VMem(void)
522 {
523 #ifndef _USE_BUDDY_BLOCKS
524     ASSERT(HeapValidate(m_hHeap, HEAP_NO_SERIALIZE, NULL));
525 #endif
526     WALKHEAPTRACE();
527 
528     DeleteCriticalSection(&m_cs);
529 #ifdef _USE_BUDDY_BLOCKS
530     for(int index = 0; index < m_nHeaps; ++index) {
531 	VirtualFree(m_heaps[index].base, 0, MEM_RELEASE);
532     }
533 #else /* !_USE_BUDDY_BLOCKS */
534 #ifdef USE_BIGBLOCK_ALLOC
535     for(int index = 0; index < m_nHeaps; ++index) {
536 	if (m_heaps[index].bBigBlock) {
537 	    VirtualFree(m_heaps[index].base, 0, MEM_RELEASE);
538 	}
539     }
540 #endif
541     BOOL bRet = HeapDestroy(m_hHeap);
542     ASSERT(bRet);
543 #endif /* _USE_BUDDY_BLOCKS */
544 }
545 
546 void VMem::ReInit(void)
547 {
548     for(int index = 0; index < m_nHeaps; ++index) {
549 #ifdef _USE_BUDDY_BLOCKS
550 	VirtualFree(m_heaps[index].base, 0, MEM_RELEASE);
551 #else
552 #ifdef USE_BIGBLOCK_ALLOC
553 	if (m_heaps[index].bBigBlock) {
554 	    VirtualFree(m_heaps[index].base, 0, MEM_RELEASE);
555 	}
556 	else
557 #endif
558 	    HeapFree(m_hHeap, HEAP_NO_SERIALIZE, m_heaps[index].base);
559 #endif /* _USE_BUDDY_BLOCKS */
560     }
561 
562     Init();
563 }
564 
565 void VMem::Init(void)
566 {
567 #ifdef _USE_BUDDY_BLOCKS
568     PBLOCK pFreeList;
569     /*
570      * Initialize the free list by placing a dummy zero-length block on it.
571      * Set the end of list marker.
572      * Set the number of non-contiguous heaps to zero.
573      * Set the next allocation size.
574      */
575     for (int index = 0; index < nListEntries; ++index) {
576 	pFreeList = GetFreeListLink(index);
577 	SIZE(pFreeList) = PSIZE(pFreeList+minAllocSize) = 0;
578 	PREV(pFreeList) = NEXT(pFreeList) = pFreeList;
579     }
580     pFreeList = GetEOLFreeList();
581     SIZE(pFreeList) = PSIZE(pFreeList+minAllocSize) = 0;
582     PREV(pFreeList) = NEXT(pFreeList) = NULL;
583     m_pRover = GetOverSizeFreeList();
584 #else
585     /*
586      * Initialize the free list by placing a dummy zero-length block on it.
587      * Set the number of non-contiguous heaps to zero.
588      */
589     m_pFreeList = m_pRover = (PBLOCK)(&m_FreeDummy[sizeofTag]);
590     PSIZE(m_pFreeList+minAllocSize) = SIZE(m_pFreeList) = 0;
591     PREV(m_pFreeList) = NEXT(m_pFreeList) = m_pFreeList;
592 #endif
593 
594     m_nHeaps = 0;
595     m_lAllocSize = lAllocStart;
596 }
597 
598 void* VMem::Malloc(size_t size)
599 {
600     WALKHEAP();
601 
602     PBLOCK ptr;
603     size_t lsize, rem;
604     /*
605      * Disallow negative or zero sizes.
606      */
607     size_t realsize = CalcAllocSize(size);
608     if((int)realsize < minAllocSize || size == 0)
609 	return NULL;
610 
611 #ifdef _USE_BUDDY_BLOCKS
612     /*
613      * Check the free list of small blocks if this is free use it
614      * Otherwise check the rover if it has no blocks then
615      * Scan the free list entries use the first free block
616      * split the block if needed, stop at end of list marker
617      */
618     {
619 	int index = CalcEntry(realsize);
620 	if (index < nListEntries-1) {
621 	    ptr = GetFreeListLink(index);
622 	    lsize = SIZE(ptr);
623 	    if (lsize >= realsize) {
624 		rem = lsize - realsize;
625 		if(rem < minAllocSize) {
626 		    /* Unlink the block from the free list. */
627 		    Unlink(ptr);
628 		}
629 		else {
630 		    /*
631 		     * split the block
632 		     * The remainder is big enough to split off into a new block.
633 		     * Use the end of the block, resize the beginning of the block
634 		     * no need to change the free list.
635 		     */
636 		    SetTags(ptr, rem);
637 		    ptr += SIZE(ptr);
638 		    lsize = realsize;
639 		}
640 		SetTags(ptr, lsize | 1);
641 		return ptr;
642 	    }
643 	    ptr = m_pRover;
644 	    lsize = SIZE(ptr);
645 	    if (lsize >= realsize) {
646 		rem = lsize - realsize;
647 		if(rem < minAllocSize) {
648 		    /* Unlink the block from the free list. */
649 		    Unlink(ptr);
650 		}
651 		else {
652 		    /*
653 		     * split the block
654 		     * The remainder is big enough to split off into a new block.
655 		     * Use the end of the block, resize the beginning of the block
656 		     * no need to change the free list.
657 		     */
658 		    SetTags(ptr, rem);
659 		    ptr += SIZE(ptr);
660 		    lsize = realsize;
661 		}
662 		SetTags(ptr, lsize | 1);
663 		return ptr;
664 	    }
665 	    ptr = GetFreeListLink(index+1);
666 	    while (NEXT(ptr)) {
667 		lsize = SIZE(ptr);
668 		if (lsize >= realsize) {
669 		    size_t rem = lsize - realsize;
670 		    if(rem < minAllocSize) {
671 			/* Unlink the block from the free list. */
672 			Unlink(ptr);
673 		    }
674 		    else {
675 			/*
676 			 * split the block
677 			 * The remainder is big enough to split off into a new block.
678 			 * Use the end of the block, resize the beginning of the block
679 			 * no need to change the free list.
680 			 */
681 			SetTags(ptr, rem);
682 			ptr += SIZE(ptr);
683 			lsize = realsize;
684 		    }
685 		    SetTags(ptr, lsize | 1);
686 		    return ptr;
687 		}
688 		ptr += sizeof(FREE_LIST_ENTRY);
689 	    }
690 	}
691     }
692 #endif
693 
694     /*
695      * Start searching the free list at the rover.  If we arrive back at rover without
696      * finding anything, allocate some memory from the heap and try again.
697      */
698     ptr = m_pRover;	/* start searching at rover */
699     int loops = 2;	/* allow two times through the loop  */
700     for(;;) {
701 	lsize = SIZE(ptr);
702 	ASSERT((lsize&1)==0);
703 	/* is block big enough? */
704 	if(lsize >= realsize) {
705 	    /* if the remainder is too small, don't bother splitting the block. */
706 	    rem = lsize - realsize;
707 	    if(rem < minAllocSize) {
708 		if(m_pRover == ptr)
709 		    m_pRover = NEXT(ptr);
710 
711 		/* Unlink the block from the free list. */
712 		Unlink(ptr);
713 	    }
714 	    else {
715 		/*
716 		 * split the block
717 		 * The remainder is big enough to split off into a new block.
718 		 * Use the end of the block, resize the beginning of the block
719 		 * no need to change the free list.
720 		 */
721 		SetTags(ptr, rem);
722 		ptr += SIZE(ptr);
723 		lsize = realsize;
724 	    }
725 	    /* Set the boundary tags to mark it as allocated. */
726 	    SetTags(ptr, lsize | 1);
727 	    return ((void *)ptr);
728 	}
729 
730 	/*
731 	 * This block was unsuitable.  If we've gone through this list once already without
732 	 * finding anything, allocate some new memory from the heap and try again.
733 	 */
734 	ptr = NEXT(ptr);
735 	if(ptr == m_pRover) {
736 	    if(!(loops-- && Getmem(realsize))) {
737 		return NULL;
738 	    }
739 	    ptr = m_pRover;
740 	}
741     }
742 }
743 
744 void* VMem::Realloc(void* block, size_t size)
745 {
746     WALKHEAP();
747 
748     /* if size is zero, free the block. */
749     if(size == 0) {
750 	Free(block);
751 	return (NULL);
752     }
753 
754     /* if block pointer is NULL, do a Malloc(). */
755     if(block == NULL)
756 	return Malloc(size);
757 
758     /*
759      * Grow or shrink the block in place.
760      * if the block grows then the next block will be used if free
761      */
762     if(Expand(block, size) != NULL)
763 	return block;
764 
765     size_t realsize = CalcAllocSize(size);
766     if((int)realsize < minAllocSize)
767 	return NULL;
768 
769     /*
770      * see if the previous block is free, and is it big enough to cover the new size
771      * if merged with the current block.
772      */
773     PBLOCK ptr = (PBLOCK)block;
774     size_t cursize = SIZE(ptr) & ~1;
775     size_t psize = PSIZE(ptr);
776     if((psize&1) == 0 && (psize + cursize) >= realsize) {
777 	PBLOCK prev = ptr - psize;
778 	if(m_pRover == prev)
779 	    m_pRover = NEXT(prev);
780 
781 	/* Unlink the next block from the free list. */
782 	Unlink(prev);
783 
784 	/* Copy contents of old block to new location, make it the current block. */
785 	memmove(prev, ptr, cursize);
786 	cursize += psize;	/* combine sizes */
787 	ptr = prev;
788 
789 	size_t rem = cursize - realsize;
790 	if(rem >= minAllocSize) {
791 	    /*
792 	     * The remainder is big enough to be a new block.  Set boundary
793 	     * tags for the resized block and the new block.
794 	     */
795 	    prev = ptr + realsize;
796 	    /*
797 	     * add the new block to the free list.
798 	     * next block cannot be free
799 	     */
800 	    SetTags(prev, rem);
801 #ifdef _USE_BUDDY_BLOCKS
802 	    AddToFreeList(prev, rem);
803 #else
804 	    AddToFreeList(prev, m_pFreeList);
805 #endif
806 	    cursize = realsize;
807         }
808 	/* Set the boundary tags to mark it as allocated. */
809 	SetTags(ptr, cursize | 1);
810         return ((void *)ptr);
811     }
812 
813     /* Allocate a new block, copy the old to the new, and free the old. */
814     if((ptr = (PBLOCK)Malloc(size)) != NULL) {
815 	memmove(ptr, block, cursize-blockOverhead);
816 	Free(block);
817     }
818     return ((void *)ptr);
819 }
820 
821 void VMem::Free(void* p)
822 {
823     WALKHEAP();
824 
825     /* Ignore null pointer. */
826     if(p == NULL)
827 	return;
828 
829     PBLOCK ptr = (PBLOCK)p;
830 
831     /* Check for attempt to free a block that's already free. */
832     size_t size = SIZE(ptr);
833     if((size&1) == 0) {
834 	MEMODSlx("Attempt to free previously freed block", (long)p);
835 	return;
836     }
837     size &= ~1;	/* remove allocated tag */
838 
839     /* if previous block is free, add this block to it. */
840 #ifndef _USE_BUDDY_BLOCKS
841     int linked = FALSE;
842 #endif
843     size_t psize = PSIZE(ptr);
844     if((psize&1) == 0) {
845 	ptr -= psize;	/* point to previous block */
846 	size += psize;	/* merge the sizes of the two blocks */
847 #ifdef _USE_BUDDY_BLOCKS
848 	Unlink(ptr);
849 #else
850 	linked = TRUE;	/* it's already on the free list */
851 #endif
852     }
853 
854     /* if the next physical block is free, merge it with this block. */
855     PBLOCK next = ptr + size;	/* point to next physical block */
856     size_t nsize = SIZE(next);
857     if((nsize&1) == 0) {
858 	/* block is free move rover if needed */
859 	if(m_pRover == next)
860 	    m_pRover = NEXT(next);
861 
862 	/* unlink the next block from the free list. */
863 	Unlink(next);
864 
865 	/* merge the sizes of this block and the next block. */
866 	size += nsize;
867     }
868 
869     /* Set the boundary tags for the block; */
870     SetTags(ptr, size);
871 
872     /* Link the block to the head of the free list. */
873 #ifdef _USE_BUDDY_BLOCKS
874 	AddToFreeList(ptr, size);
875 #else
876     if(!linked) {
877 	AddToFreeList(ptr, m_pFreeList);
878     }
879 #endif
880 }
881 
882 void VMem::GetLock(void)
883 {
884     EnterCriticalSection(&m_cs);
885 }
886 
887 void VMem::FreeLock(void)
888 {
889     LeaveCriticalSection(&m_cs);
890 }
891 
892 int VMem::IsLocked(void)
893 {
894 #if 0
895     /* XXX TryEnterCriticalSection() is not available in some versions
896      * of Windows 95.  Since this code is not used anywhere yet, we
897      * skirt the issue for now. */
898     BOOL bAccessed = TryEnterCriticalSection(&m_cs);
899     if(bAccessed) {
900 	LeaveCriticalSection(&m_cs);
901     }
902     return !bAccessed;
903 #else
904     ASSERT(0);	/* alarm bells for when somebody calls this */
905     return 0;
906 #endif
907 }
908 
909 
910 long VMem::Release(void)
911 {
912     long lCount = InterlockedDecrement(&m_lRefCount);
913     if(!lCount)
914 	delete this;
915     return lCount;
916 }
917 
918 long VMem::AddRef(void)
919 {
920     long lCount = InterlockedIncrement(&m_lRefCount);
921     return lCount;
922 }
923 
924 
925 int VMem::Getmem(size_t requestSize)
926 {   /* returns -1 is successful 0 if not */
927 #ifdef USE_BIGBLOCK_ALLOC
928     BOOL bBigBlock;
929 #endif
930     void *ptr;
931 
932     /* Round up size to next multiple of 64K. */
933     size_t size = (size_t)ROUND_UP64K(requestSize);
934 
935     /*
936      * if the size requested is smaller than our current allocation size
937      * adjust up
938      */
939     if(size < (unsigned long)m_lAllocSize)
940 	size = m_lAllocSize;
941 
942     /* Update the size to allocate on the next request */
943     if(m_lAllocSize != lAllocMax)
944 	m_lAllocSize <<= 2;
945 
946 #ifndef _USE_BUDDY_BLOCKS
947     if(m_nHeaps != 0
948 #ifdef USE_BIGBLOCK_ALLOC
949 	&& !m_heaps[m_nHeaps-1].bBigBlock
950 #endif
951 		    ) {
952 	/* Expand the last allocated heap */
953 	ptr = HeapReAlloc(m_hHeap, HEAP_REALLOC_IN_PLACE_ONLY|HEAP_NO_SERIALIZE,
954 		m_heaps[m_nHeaps-1].base,
955 		m_heaps[m_nHeaps-1].len + size);
956 	if(ptr != 0) {
957 	    HeapAdd(((char*)ptr) + m_heaps[m_nHeaps-1].len, size
958 #ifdef USE_BIGBLOCK_ALLOC
959 		, FALSE
960 #endif
961 		);
962 	    return -1;
963 	}
964     }
965 #endif /* _USE_BUDDY_BLOCKS */
966 
967     /*
968      * if we didn't expand a block to cover the requested size
969      * allocate a new Heap
970      * the size of this block must include the additional dummy tags at either end
971      * the above ROUND_UP64K may not have added any memory to include this.
972      */
973     if(size == requestSize)
974 	size = (size_t)ROUND_UP64K(requestSize+(blockOverhead));
975 
976 Restart:
977 #ifdef _USE_BUDDY_BLOCKS
978     ptr = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
979 #else
980 #ifdef USE_BIGBLOCK_ALLOC
981     bBigBlock = FALSE;
982     if (size >= nMaxHeapAllocSize) {
983 	bBigBlock = TRUE;
984 	ptr = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
985     }
986     else
987 #endif
988     ptr = HeapAlloc(m_hHeap, HEAP_NO_SERIALIZE, size);
989 #endif /* _USE_BUDDY_BLOCKS */
990 
991     if (!ptr) {
992 	/* try to allocate a smaller chunk */
993 	size >>= 1;
994 	if(size > requestSize)
995 	    goto Restart;
996     }
997 
998     if(ptr == 0) {
999 	MEMODSlx("HeapAlloc failed on size!!!", size);
1000 	return 0;
1001     }
1002 
1003 #ifdef _USE_BUDDY_BLOCKS
1004     if (HeapAdd(ptr, size)) {
1005 	VirtualFree(ptr, 0, MEM_RELEASE);
1006 	return 0;
1007     }
1008 #else
1009 #ifdef USE_BIGBLOCK_ALLOC
1010     if (HeapAdd(ptr, size, bBigBlock)) {
1011 	if (bBigBlock) {
1012 	    VirtualFree(ptr, 0, MEM_RELEASE);
1013 	}
1014     }
1015 #else
1016     HeapAdd(ptr, size);
1017 #endif
1018 #endif /* _USE_BUDDY_BLOCKS */
1019     return -1;
1020 }
1021 
1022 int VMem::HeapAdd(void* p, size_t size
1023 #ifdef USE_BIGBLOCK_ALLOC
1024     , BOOL bBigBlock
1025 #endif
1026     )
1027 {   /* if the block can be succesfully added to the heap, returns 0; otherwise -1. */
1028     int index;
1029 
1030     /* Check size, then round size down to next long word boundary. */
1031     if(size < minAllocSize)
1032 	return -1;
1033 
1034     size = (size_t)ROUND_DOWN(size);
1035     PBLOCK ptr = (PBLOCK)p;
1036 
1037 #ifdef USE_BIGBLOCK_ALLOC
1038     if (!bBigBlock) {
1039 #endif
1040 	/*
1041 	 * Search for another heap area that's contiguous with the bottom of this new area.
1042 	 * (It should be extremely unusual to find one that's contiguous with the top).
1043 	 */
1044 	for(index = 0; index < m_nHeaps; ++index) {
1045 	    if(ptr == m_heaps[index].base + (int)m_heaps[index].len) {
1046 		/*
1047 		 * The new block is contiguous with a previously allocated heap area.  Add its
1048 		 * length to that of the previous heap.  Merge it with the dummy end-of-heap
1049 		 * area marker of the previous heap.
1050 		 */
1051 		m_heaps[index].len += size;
1052 		break;
1053 	    }
1054 	}
1055 #ifdef USE_BIGBLOCK_ALLOC
1056     }
1057     else {
1058 	index = m_nHeaps;
1059     }
1060 #endif
1061 
1062     if(index == m_nHeaps) {
1063 	/* The new block is not contiguous, or is BigBlock.  Add it to the heap list. */
1064 	if(m_nHeaps == maxHeaps) {
1065 	    return -1;	/* too many non-contiguous heaps */
1066 	}
1067 	m_heaps[m_nHeaps].base = ptr;
1068 	m_heaps[m_nHeaps].len = size;
1069 #ifdef USE_BIGBLOCK_ALLOC
1070 	m_heaps[m_nHeaps].bBigBlock = bBigBlock;
1071 #endif
1072 	m_nHeaps++;
1073 
1074 	/*
1075 	 * Reserve the first LONG in the block for the ending boundary tag of a dummy
1076 	 * block at the start of the heap area.
1077 	 */
1078 	size -= blockOverhead;
1079 	ptr += blockOverhead;
1080 	PSIZE(ptr) = 1;	/* mark the dummy previous block as allocated */
1081     }
1082 
1083     /*
1084      * Convert the heap to one large block.  Set up its boundary tags, and those of
1085      * marker block after it.  The marker block before the heap will already have
1086      * been set up if this heap is not contiguous with the end of another heap.
1087      */
1088     SetTags(ptr, size | 1);
1089     PBLOCK next = ptr + size;	/* point to dummy end block */
1090     SIZE(next) = 1;	/* mark the dummy end block as allocated */
1091 
1092     /*
1093      * Link the block to the start of the free list by calling free().
1094      * This will merge the block with any adjacent free blocks.
1095      */
1096     Free(ptr);
1097     return 0;
1098 }
1099 
1100 
1101 void* VMem::Expand(void* block, size_t size)
1102 {
1103     /*
1104      * Disallow negative or zero sizes.
1105      */
1106     size_t realsize = CalcAllocSize(size);
1107     if((int)realsize < minAllocSize || size == 0)
1108 	return NULL;
1109 
1110     PBLOCK ptr = (PBLOCK)block;
1111 
1112     /* if the current size is the same as requested, do nothing. */
1113     size_t cursize = SIZE(ptr) & ~1;
1114     if(cursize == realsize) {
1115 	return block;
1116     }
1117 
1118     /* if the block is being shrunk, convert the remainder of the block into a new free block. */
1119     if(realsize <= cursize) {
1120 	size_t nextsize = cursize - realsize;	/* size of new remainder block */
1121 	if(nextsize >= minAllocSize) {
1122 	    /*
1123 	     * Split the block
1124 	     * Set boundary tags for the resized block and the new block.
1125 	     */
1126 	    SetTags(ptr, realsize | 1);
1127 	    ptr += realsize;
1128 
1129 	    /*
1130 	     * add the new block to the free list.
1131 	     * call Free to merge this block with next block if free
1132 	     */
1133 	    SetTags(ptr, nextsize | 1);
1134 	    Free(ptr);
1135 	}
1136 
1137 	return block;
1138     }
1139 
1140     PBLOCK next = ptr + cursize;
1141     size_t nextsize = SIZE(next);
1142 
1143     /* Check the next block for consistency.*/
1144     if((nextsize&1) == 0 && (nextsize + cursize) >= realsize) {
1145 	/*
1146 	 * The next block is free and big enough.  Add the part that's needed
1147 	 * to our block, and split the remainder off into a new block.
1148 	 */
1149 	if(m_pRover == next)
1150 	    m_pRover = NEXT(next);
1151 
1152 	/* Unlink the next block from the free list. */
1153 	Unlink(next);
1154 	cursize += nextsize;	/* combine sizes */
1155 
1156 	size_t rem = cursize - realsize;	/* size of remainder */
1157 	if(rem >= minAllocSize) {
1158 	    /*
1159 	     * The remainder is big enough to be a new block.
1160 	     * Set boundary tags for the resized block and the new block.
1161 	     */
1162 	    next = ptr + realsize;
1163 	    /*
1164 	     * add the new block to the free list.
1165 	     * next block cannot be free
1166 	     */
1167 	    SetTags(next, rem);
1168 #ifdef _USE_BUDDY_BLOCKS
1169 	    AddToFreeList(next, rem);
1170 #else
1171 	    AddToFreeList(next, m_pFreeList);
1172 #endif
1173 	    cursize = realsize;
1174         }
1175 	/* Set the boundary tags to mark it as allocated. */
1176 	SetTags(ptr, cursize | 1);
1177 	return ((void *)ptr);
1178     }
1179     return NULL;
1180 }
1181 
1182 #ifdef _DEBUG_MEM
1183 #define LOG_FILENAME ".\\MemLog.txt"
1184 
1185 void VMem::MemoryUsageMessage(char *str, long x, long y, int c)
1186 {
1187     char szBuffer[512];
1188     if(str) {
1189 	if(!m_pLog)
1190 	    m_pLog = fopen(LOG_FILENAME, "w");
1191 	sprintf(szBuffer, str, x, y, c);
1192 	fputs(szBuffer, m_pLog);
1193     }
1194     else {
1195 	if(m_pLog) {
1196 	    fflush(m_pLog);
1197 	    fclose(m_pLog);
1198 	    m_pLog = 0;
1199 	}
1200     }
1201 }
1202 
1203 void VMem::WalkHeap(int complete)
1204 {
1205     if(complete) {
1206 	MemoryUsageMessage(NULL, 0, 0, 0);
1207 	size_t total = 0;
1208 	for(int i = 0; i < m_nHeaps; ++i) {
1209 	    total += m_heaps[i].len;
1210 	}
1211 	MemoryUsageMessage("VMem heaps used %d. Total memory %08x\n", m_nHeaps, total, 0);
1212 
1213 	/* Walk all the heaps - verify structures */
1214 	for(int index = 0; index < m_nHeaps; ++index) {
1215 	    PBLOCK ptr = m_heaps[index].base;
1216 	    size_t size = m_heaps[index].len;
1217 #ifndef _USE_BUDDY_BLOCKS
1218 #ifdef USE_BIGBLOCK_ALLOC
1219 	    if (!m_heaps[m_nHeaps].bBigBlock)
1220 #endif
1221 		ASSERT(HeapValidate(m_hHeap, HEAP_NO_SERIALIZE, ptr));
1222 #endif
1223 
1224 	    /* set over reserved header block */
1225 	    size -= blockOverhead;
1226 	    ptr += blockOverhead;
1227 	    PBLOCK pLast = ptr + size;
1228 	    ASSERT(PSIZE(ptr) == 1); /* dummy previous block is allocated */
1229 	    ASSERT(SIZE(pLast) == 1); /* dummy next block is allocated */
1230 	    while(ptr < pLast) {
1231 		ASSERT(ptr > m_heaps[index].base);
1232 		size_t cursize = SIZE(ptr) & ~1;
1233 		ASSERT((PSIZE(ptr+cursize) & ~1) == cursize);
1234 		MemoryUsageMessage("Memory Block %08x: Size %08x %c\n", (long)ptr, cursize, (SIZE(ptr)&1) ? 'x' : ' ');
1235 		if(!(SIZE(ptr)&1)) {
1236 		    /* this block is on the free list */
1237 		    PBLOCK tmp = NEXT(ptr);
1238 		    while(tmp != ptr) {
1239 			ASSERT((SIZE(tmp)&1)==0);
1240 			if(tmp == m_pFreeList)
1241 			    break;
1242 			ASSERT(NEXT(tmp));
1243 			tmp = NEXT(tmp);
1244 		    }
1245 		    if(tmp == ptr) {
1246 			MemoryUsageMessage("Memory Block %08x: Size %08x free but not in free list\n", (long)ptr, cursize, 0);
1247 		    }
1248 		}
1249 		ptr += cursize;
1250 	    }
1251 	}
1252 	MemoryUsageMessage(NULL, 0, 0, 0);
1253     }
1254 }
1255 #endif	/* _DEBUG_MEM */
1256 
1257 #endif	/* _USE_MSVCRT_MEM_ALLOC */
1258 
1259 #endif	/* ___VMEM_H_INC___ */
1260