//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1996 - 1999.
//
//  File:       tmem.cxx
//
//  Contents:   tmem - test mem allocators for big tables.
//
//--------------------------------------------------------------------------

#include "pch.cxx"
#pragma hdrstop

DECLARE_INFOLEVEL(tb)

#include "tblalloc.hxx"

BYTE MemPool[4096];

//BYTE* PoolPtrs[409];
//ULONG PoolOffs[409];

BOOL fInMemPool = TRUE;


//
//  ReportHeap - report heap blocks
//

void ReportHeap(
    void* pBlock,
    USHORT cbSize,
    USHORT fFree
) {
    BYTE* pbBlock = (BYTE*) pBlock;
    printf("%08x  %d%s\n", pbBlock, cbSize, fFree? " FREE": "");

    if (fInMemPool &&
	(pbBlock < &MemPool[0] ||
	 pbBlock + cbSize > &MemPool[ sizeof MemPool ])) {
	printf("\007***\tpBlock outside mem pool\t***\007");
    }
}

//
//  FreeBlocks -- free some of the allocated blocks
//

void FreeBlocks(
    BYTE* apbPtrs[],
    unsigned cPtrs,
    int fVerbose,
    CWindowDataAllocator& Alloc
) {
    unsigned i;
  
    // Free every third allocated block

    for (i = 2; i < cPtrs; i += 3) {
	if (apbPtrs[i]) {
	    Alloc.Free((void*)apbPtrs[i]);
	    apbPtrs[i] = 0;
	}
    }

    if (fVerbose) {
	printf("\nAfter freeing every third allocation\n");
	Alloc.WalkHeap(ReportHeap);
    }

    // Free every other allocated block, will cause some coalescing

    for (i = 0; i < cPtrs; i += 2) {
	if (apbPtrs[i]) {
	    Alloc.Free((void*)apbPtrs[i]);
	    apbPtrs[i] = 0;
	}
    }
    if (fVerbose) {
	printf("\nAfter freeing every other allocation\n");
	Alloc.WalkHeap(ReportHeap);
    }

    // Finally, Free every fourth allocated block, will cause more coalescing

    for (i = 3; i < cPtrs; i += 4) {
	if (apbPtrs[i]) {
	    Alloc.Free((void*)apbPtrs[i]);
	    apbPtrs[i] = 0;
	}
    }
    if (fVerbose) {
	printf("\nAfter freeing every other allocation\n");
	Alloc.WalkHeap(ReportHeap);
    }
}


__cdecl main(int argc, char** argv)
{
    BYTE* pMem = MemPool;
    int fVerbose = 0;
    unsigned i;

    for (i=1; i<(unsigned)argc; i++) {
	if (argv[i][0] == '-' &&
	    tolower(argv[i][1]) == 'v')
	    fVerbose++;
	else {
	    printf("Usage: %s [-v]\n", argv[0]);
	}
    }

    // Test the simple allocator with forward allocation.
    memset(pMem, 0xD5, sizeof MemPool);
    CVarBufferAllocator	Alloc1(pMem, sizeof MemPool);
    BYTE* pbuf;
    BOOL fBreak = FALSE;

    for (i=0; i<0xFFFFFFFF && !fBreak; i++)
    {
        TRY
        {
            pbuf = 0;
            pbuf = (BYTE *)Alloc1.Allocate(10);
        }
        CATCH (CException, e)
        {
            if (e.GetErrorCode() == E_OUTOFMEMORY)
                fBreak = TRUE;
            else
                RETHROW();
        }
        END_CATCH

	if (pbuf)
            memset(pbuf, '0' + i%10, 10);
    }
    Win4Assert(i == sizeof MemPool/10);

    for (i=0; i<sizeof MemPool - (sizeof MemPool % 10); i++) {
	Win4Assert(MemPool[i] == ((i/10) % 10 + '0'));
    }

#if 0
    // Test the simple allocator with top-down allocation.
    memset(pMem, 0xD5, sizeof MemPool);
    CVarBufferAllocator	Alloc2(pMem, sizeof MemPool, TRUE);

    for (i=0; pbuf = (BYTE *)Alloc2.Allocate(10); i++) {
	memset(pbuf, '0' + i%10, 10);
    }
    Win4Assert(i == sizeof MemPool/10);

    for (i=0; i<sizeof MemPool; i++) {
	if (i < (sizeof MemPool % 10)) {
	    Win4Assert(MemPool[i] == 0xD5);
	} else {
	    Win4Assert(MemPool[i] ==
			((((sizeof MemPool - 1) - i)/10) % 10 + '0'));
	}
    }
#endif //0

    // Test the heap allocator
    memset(pMem, 0, sizeof MemPool);

    BYTE **paPoolPtrs = (BYTE**)&MemPool;
    unsigned cPoolPtrs = sizeof MemPool / sizeof (BYTE *);

    CWindowDataAllocator	Alloc3;

    fInMemPool = FALSE;
    for (i=0; pbuf = (BYTE *)Alloc3.Allocate(10); i++) {
	if (i == cPoolPtrs)
	    break;
	paPoolPtrs[i] = pbuf;
	memset(pbuf, '0' + i%10, 10);
    }
    Win4Assert(i == cPoolPtrs);

    for (i=0; i<sizeof MemPool; i++) {
	Win4Assert(paPoolPtrs[i/10][i%10] == ((i/10) % 10 + '0'));
    }

    if (fVerbose) {
	printf("Grow forward allocation\n");
	Alloc3.WalkHeap(ReportHeap);
    }

    FreeBlocks(paPoolPtrs, cPoolPtrs, fVerbose, Alloc3);


    // Test the fixed/variable allocator
    memset(pMem, 0, sizeof MemPool);

    Win4Assert(sizeof (BYTE*) == sizeof (ULONG));

    paPoolPtrs = (BYTE **)&MemPool;
    cPoolPtrs = (sizeof MemPool / sizeof (BYTE *)) / 2;

    ULONG *paPoolOffs = (ULONG *)&paPoolPtrs[cPoolPtrs];

    memset(paPoolPtrs, 0, sizeof (BYTE *) * cPoolPtrs);
    memset(paPoolOffs, 0, sizeof (BYTE *) * cPoolPtrs);
    CFixedVarAllocator	Alloc4(TRUE, TRUE, 10);

//    for (i=0; i<sizeof MemPool; i++) {
//	Alloc4.SetLimit(i);
//	Win4Assert(Alloc4.Limit() == i);
//    }
//
//    Alloc4.SetLimit(1000);

    for (i=0; (pbuf = (BYTE *)Alloc4.Allocate(10)) && i < cPoolPtrs; i++) {
	memset(pbuf, '0' + i%10, 10);
	paPoolOffs[i] = Alloc4.PointerToOffset(pbuf);

	pbuf = (BYTE*)Alloc4.AllocFixed();
	memset(pbuf, '9' - i%10, 10);
    }
    Win4Assert(i == cPoolPtrs);

    CWindowDataAllocator* pVAlloc = Alloc4.VarAllocator();
    if (fVerbose && pVAlloc) {
	printf("\nFixed/Var allocation\n");
	pVAlloc->WalkHeap(ReportHeap);
    }

    if (pVAlloc) {
	for (i=0; i<cPoolPtrs; i++) {
	    if (paPoolOffs[i]) {
		paPoolPtrs[i] = (BYTE*)Alloc4.OffsetToPointer(paPoolOffs[i]);
	    }
	}
	FreeBlocks(paPoolPtrs, cPoolPtrs, fVerbose, *pVAlloc);
    }


    // Test the fixed/variable allocator
    memset(pMem, 0, sizeof MemPool);

    Win4Assert(sizeof (BYTE*) == sizeof (ULONG));

    paPoolPtrs = (BYTE **)&MemPool;
    cPoolPtrs = (sizeof MemPool / sizeof (BYTE *));

    memset(paPoolPtrs, 0, sizeof (BYTE *) * cPoolPtrs);
    CFixedVarAllocator	Alloc5(TRUE, TRUE, 0x28);

    for (i=0; (pbuf = (BYTE *)Alloc5.AllocFixed()) && i < cPoolPtrs; i++) {
	memset(pbuf, '0' + i%10, 0x28);
    }
    Win4Assert(i == cPoolPtrs);

    for (i=0; i<cPoolPtrs; i++) {
	if (paPoolOffs[i]) {
	    paPoolPtrs[i] = (BYTE*)Alloc5.OffsetToPointer(paPoolOffs[i]);
	}
    }

    return 0;
}