//+------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1993.
//
//  File:	bm_nstg.cxx
//
//  Contents:	Nested Storage test
//
//  Classes:	CNestedStorageTest
//
//  History:    09-June-94 t-vadims    Created
//
//--------------------------------------------------------------------------
#include <headers.cxx>
#pragma hdrstop

#include <bm_nstg.hxx>

#define  DEF_DATASIZE    4096
#define  INFINITY	 0xffffffff	// max value of 32-bit ulong
			        
TCHAR *CNestedStorageTest::Name ()
{
    return TEXT("NestedStorageTest");
}


SCODE CNestedStorageTest::Setup (CTestInput *pInput)
{
    SCODE sc;
    ULONG iIndex;
    TCHAR pszBuf[15];
    
    CTestBase::Setup(pInput);

    //	get iteration count
    m_ulIterations = pInput->GetIterations(Name());

    //	initialize timing arrays   
    INIT_RESULTS(m_ulStgCreateDocfile);
    INIT_RESULTS(m_ulFinalStorageCommit);
    INIT_RESULTS(m_ulFinalStorageRelease);

    for(iIndex = 0; iIndex <TEST_MAX_ITERATIONS; iIndex++)
    {
	m_ulCreateStorageTotal[iIndex] = 0;
	m_ulCreateStreamTotal[iIndex] = 0;
	m_ulStreamWriteTotal[iIndex] = 0;
	m_ulStreamReleaseTotal[iIndex] = 0;
	m_ulDestroyElementTotal[iIndex] = 0;
	m_ulStorageCommitTotal[iIndex] = 0;
	m_ulStorageReleaseTotal[iIndex] = 0;
	m_ulTotal[iIndex] = 0;

	m_ulCreateStreamMin[iIndex] = INFINITY;
	m_ulStreamWriteMin[iIndex] = INFINITY;
	m_ulStreamReleaseMin[iIndex] = INFINITY;
	m_ulDestroyElementMin[iIndex] = INFINITY;

	m_ulCreateStreamMax[iIndex] = 0;
	m_ulStreamWriteMax[iIndex] = 0;
	m_ulStreamReleaseMax[iIndex] = 0;
	m_ulDestroyElementMax[iIndex] = 0;

	for (ULONG iLevel =0; iLevel < MAX_NESTING; iLevel++)
	{
	    m_ulStorageCommitMin[iLevel][iIndex] = INFINITY;
	    m_ulStorageReleaseMin[iLevel][iIndex] = INFINITY;
	    m_ulCreateStorageMin[iLevel][iIndex] = INFINITY;

	    m_ulCreateStorageMax[iLevel][iIndex] = 0;
	    m_ulStorageCommitMax[iLevel][iIndex] = 0;
	    m_ulStorageReleaseMax[iLevel][iIndex] = 0;
    	}
    }



    sc = InitCOM();
    if (FAILED(sc))
    {
	Log (TEXT("Setup - CoInitialize failed."), sc);
        return	sc;
    }

    // get malloc interface for this task
    m_piMalloc = NULL;
    sc = CoGetMalloc(MEMCTX_TASK, &m_piMalloc);
    if (FAILED(sc))
    {
    	Log (TEXT("Setup - CoGetMalloc"), sc);
    	return sc;
    }

    m_cbSize = pInput->GetConfigInt(Name(), TEXT("DataSize"), DEF_DATASIZE);
 
    // initialize array to be written to the file.  
    m_pbData = (BYTE *)m_piMalloc->Alloc(m_cbSize);
    if(m_pbData == NULL)
    {
    	Log (TEXT("Setup - Cannot allocate memory"), E_OUTOFMEMORY);
	return E_OUTOFMEMORY;
    }

    for (iIndex=0; iIndex < m_cbSize; iIndex++)
        m_pbData[iIndex] = (BYTE)iIndex;


    // get file name to be used and values of other parameters
    pInput->GetConfigString(Name(), TEXT("FileName"), TEXT("stgtest.bm"),
    			    m_pszFile, MAX_PATH);
#ifdef UNICODE
    wcscpy(m_pwszFile, m_pszFile);
#else
    mbstowcs(m_pwszFile, m_pszFile, strlen(m_pszFile)+1);
#endif

    pInput->GetConfigString(Name(), TEXT("FileMode"), TEXT("DIRECT"), m_pszFileMode, 15);

    if(lstrcmpi(m_pszFileMode, TEXT("DIRECT")) == 0)
       	m_flCreateFlags = STGM_DIRECT;
    else 
       	m_flCreateFlags = STGM_TRANSACTED; 	

    // get the nesting factor
    m_cNesting = pInput->GetConfigInt(Name(), TEXT("Nesting"), 3);

    if(m_cNesting > MAX_NESTING)
    	m_cNesting = MAX_NESTING;
    
    if(m_cNesting == 0)
    	m_cNesting = 1;
    	 
    // get the branching factor
    m_cBranching = pInput->GetConfigInt(Name(), TEXT("Branching"), 3);

    if(m_cBranching > MAX_BRANCHING)
    	m_cBranching = MAX_BRANCHING;
    
    if(m_cBranching == 0)
    	m_cBranching = 1;

    // get the value of Delete element option
    pInput->GetConfigString(Name(), TEXT("Delete"), TEXT("OFF"), pszBuf, 15);
    
    if( lstrcmpi(pszBuf, TEXT("OFF")) == 0 ||
        lstrcmpi(pszBuf, TEXT("FALSE")) == 0)
    	m_bDelete = FALSE;
    else
    	m_bDelete = TRUE; 

    // now compute number of streams and storages, depeding on 
    // the nesting and branching factors.  Formulas are as follows:
    // Nesting = n;
    // Branching = b;
    //    	       n
    // Streams    =   b
    //
    //		       n
    //		      b	  - 1 
    // Storages   =  ---------
    //		      b   - 1
    //
    //		        n-1
    //		       b   - 1
    // ParentFactor = ---------
    //                 b   - 1
    //
    // Parent factor is used to determine the parent storage of the stream
    //
    m_cStreams = 1;
    ULONG n = m_cNesting;
    while(n-- > 0)                   // compute b^n
        m_cStreams *= m_cBranching;

    m_cStorages = (m_cStreams - 1) / (m_cBranching - 1);

    m_cParentFactor = (m_cStreams / m_cBranching - 1) / 
    		      (m_cBranching - 1);


    // allocate arrays for storages
    m_piStorages = (LPSTORAGE *)m_piMalloc->Alloc(m_cStorages * 
    						sizeof(LPSTORAGE));
    if (m_piStorages == NULL)
    {
    	Log(TEXT("Cannot allocate memory"), E_OUTOFMEMORY);
	return E_OUTOFMEMORY;
    }

    return S_OK;
}


SCODE CNestedStorageTest::Cleanup ()
{
    //	delete the file
    DeleteFile (m_pszFile);

    // free all memory
    if(m_piMalloc)
    {
    	if (m_pbData)
    	    m_piMalloc->Free(m_pbData);

	if (m_piStorages)
    	    m_piMalloc->Free(m_piStorages);

	m_piMalloc->Release();
	m_piMalloc = NULL;
    }

    UninitCOM();

    return S_OK;
}


// Some macros that are used only in Run() function.

#define  STG_PARENT(iIndex)    ((iIndex - 1) / m_cBranching )
#define  STREAM_PARENT(iIndex) (iIndex / m_cBranching + m_cParentFactor )

// STG_NAME and STREAM_NAME macros are very ugly, but they save
// doing a bunch of allocations and name generation in the beginning.

#define  STG_NAME(iIndex)      (swprintf(pwszBuf, L"Storage%d", iIndex), pwszBuf)
#define  STREAM_NAME(iIndex)   (swprintf(pwszBuf, L"Stream%d", iIndex), pwszBuf)


SCODE CNestedStorageTest::Run ()
{
    CStopWatch   sw;
    HRESULT	 hr;
    ULONG	 cb;
    ULONG        iCurStream;
    LONG	 iCurStg;    // has to be long, not ulong, for looping down to zero.
    LPSTREAM	 piStream;
    ULONG	 iIter;
    OLECHAR	 pwszBuf[20];
    ULONG	 ulTime;
    ULONG	 iLevel;
    ULONG	 iParent;
 

    for (iIter =0; iIter < m_ulIterations; iIter++)
    {

        sw.Reset();
    	hr = StgCreateDocfile(m_pwszFile,  m_flCreateFlags  | STGM_WRITE
           	| STGM_CREATE | STGM_SHARE_EXCLUSIVE, 0, &m_piStorages[0]);
	m_ulStgCreateDocfile[iIter] = sw.Read();
  	Log(TEXT("StgCreateDocfile"), hr);
	if (FAILED(hr))
	    return hr;

	// Create a complete tree of storages 
    	for (iCurStg = 1; iCurStg < (LONG)m_cStorages; iCurStg++)
    	{
	    // determine level of curent storage.
	    iLevel = 0;
	    iParent = STG_PARENT(iCurStg);
	    while(iParent > 0)
	    {
	    	iParent = STG_PARENT(iParent);
		iLevel++;
	    }

	    sw.Reset();
 	    hr = m_piStorages[STG_PARENT(iCurStg)]->
 		      CreateStorage(STG_NAME(iCurStg), 
 			      m_flCreateFlags | STGM_WRITE | 
 			      STGM_CREATE | STGM_SHARE_EXCLUSIVE,
 			      0, 0, &m_piStorages[iCurStg]);
	    ulTime = sw.Read();
	    m_ulCreateStorageTotal[iIter] += ulTime;

	    if(ulTime < m_ulCreateStorageMin[iLevel][iIter])
	    	m_ulCreateStorageMin[iLevel][iIter] = ulTime;

	    if(ulTime > m_ulCreateStorageMax[iLevel][iIter])
	    	m_ulCreateStorageMax[iLevel][iIter] = ulTime;

	    if (FAILED(hr))
	    {
	        Log(TEXT("CreateStorage"), hr);
	    	return hr;
	    }
    	}
        Log(TEXT("CreateStorage"), S_OK);
  	// For each storage in final level, open several streams,
	// write some data to them, and release them
	for (iCurStream = 0; iCurStream < m_cStreams; iCurStream++)
    	{
	    sw.Reset();
 	    hr = m_piStorages[STREAM_PARENT(iCurStream)]->
 	               CreateStream(STREAM_NAME(iCurStream), 
 	         	     STGM_DIRECT | STGM_WRITE | 
 	         	     STGM_CREATE | STGM_SHARE_EXCLUSIVE,
 			     0, 0, &piStream);
	    ulTime = sw.Read();
	    m_ulCreateStreamTotal[iIter] += ulTime;

	    if(ulTime < m_ulCreateStreamMin[iIter])
	    	m_ulCreateStreamMin[iIter] = ulTime;

	    if(ulTime > m_ulCreateStreamMax[iIter])
	    	m_ulCreateStreamMax[iIter] = ulTime;

	    if (FAILED(hr))
	    {
	    	Log(TEXT("CreateStream"), hr);
	    	return hr;
	    }

	    sw.Reset();
	    piStream->Write((LPVOID)m_pbData, m_cbSize, &cb);
	    ulTime = sw.Read();
	    m_ulStreamWriteTotal[iIter] += ulTime;

	    if(ulTime < m_ulStreamWriteMin[iIter])
	    	m_ulStreamWriteMin[iIter] = ulTime;

	    if(ulTime > m_ulStreamWriteMax[iIter])
	    	m_ulStreamWriteMax[iIter] = ulTime;

	    sw.Reset();
	    piStream->Release();
	    ulTime = sw.Read();
	    m_ulStreamReleaseTotal[iIter] += ulTime;

	    if(ulTime < m_ulStreamReleaseMin[iIter])
	    	m_ulStreamReleaseMin[iIter] = ulTime;

	    if(ulTime > m_ulStreamReleaseMax[iIter])
	    	m_ulStreamReleaseMax[iIter] = ulTime;
  	}
	Log(TEXT("CreateStream"), S_OK);
	Log(TEXT("StreamWrite"), S_OK);	    
	Log(TEXT("StreamRelease"), S_OK);	    

        if (m_bDelete)
	{
	    // delete 1 stream from every branch.
	    for (iCurStream = 1; iCurStream < m_cStreams; iCurStream += m_cBranching)
    	    {
	    	sw.Reset();
 	    	hr = m_piStorages[STREAM_PARENT(iCurStream)]->
 	               		DestroyElement(STREAM_NAME(iCurStream));
	    	ulTime = sw.Read();
	    	m_ulDestroyElementTotal[iIter] += ulTime;

	    	if (ulTime < m_ulDestroyElementMin[iIter])
	    	    m_ulDestroyElementMin[iIter] = ulTime;

	    	if (ulTime > m_ulDestroyElementMax[iIter])
	    	    m_ulDestroyElementMax[iIter] = ulTime;

	    	if (FAILED(hr))
		{
	    	    Log(TEXT("DestroyElement"), hr);
	    	    return hr;
		}
             }
	     Log( TEXT("DestroyElement"), S_OK);

	     m_ulDestroyElementAverage[iIter] = m_ulDestroyElementTotal[iIter] / 
	     					 (m_cStreams / m_cBranching);
	}
        // for each storage, do commit if in transacted mode
	// and release the storage.
    	for (iCurStg = m_cStorages-1 ; iCurStg >= 0 ; iCurStg--)
    	{
	    // determine level of curent storage.
	    iLevel = 0;
	    iParent = STG_PARENT(iCurStg);
	    while(iParent > 0)
	    {
	    	iParent = STG_PARENT(iParent);
		iLevel++;
	    }

    	    if (m_flCreateFlags == STGM_TRANSACTED)
	    {
	     	sw.Reset();
    	    	m_piStorages[iCurStg]->Commit(STGC_DEFAULT);
	    	ulTime = sw.Read();
	    	m_ulStorageCommitTotal[iIter] += ulTime;

		if (iCurStg != 0)
		{
	    	    if (ulTime < m_ulStorageCommitMin[iLevel][iIter])
	    	    	m_ulStorageCommitMin[iLevel][iIter] = ulTime;

	    	    if (ulTime > m_ulStorageCommitMax[iLevel][iIter])
	    	    	m_ulStorageCommitMax[iLevel][iIter] = ulTime;
		}
		else
		{
		    m_ulFinalStorageCommit[iIter] = ulTime; 
		}
	    }

	    sw.Reset();
    	    m_piStorages[iCurStg]->Release();
	    ulTime = sw.Read();
	    m_ulStorageReleaseTotal[iIter] += ulTime;

	    if (iCurStg != 0)
	    {
	    	if (ulTime < m_ulStorageReleaseMin[iLevel][iIter])
	            m_ulStorageReleaseMin[iLevel][iIter] = ulTime;

	    	if (ulTime > m_ulStorageReleaseMax[iLevel][iIter])
	            m_ulStorageReleaseMax[iLevel][iIter] = ulTime;
	    }
	    else
	    {
		m_ulFinalStorageRelease[iIter] = ulTime; 
	    }
	}

	Log(TEXT("StorageCommit"), S_OK);
	Log(TEXT("StorageRelease"), S_OK);


	m_ulCreateStorageAverage[iIter] = m_ulCreateStorageTotal[iIter] / m_cStorages;
	m_ulCreateStreamAverage[iIter]  = m_ulCreateStreamTotal[iIter]  / m_cStreams;
	m_ulStreamWriteAverage[iIter]   = m_ulStreamWriteTotal[iIter]   / m_cStreams;
	m_ulStreamReleaseAverage[iIter] = m_ulStreamReleaseTotal[iIter] / m_cStreams;
	m_ulStorageCommitAverage[iIter] = m_ulStorageCommitTotal[iIter] / m_cStorages;
	m_ulStorageReleaseAverage[iIter] = m_ulStorageReleaseTotal[iIter] / m_cStorages;

	m_ulTotal[iIter] = m_ulStgCreateDocfile[iIter] +
			   m_ulCreateStorageTotal[iIter] + 
			   m_ulCreateStreamTotal[iIter] +
			   m_ulStreamWriteTotal[iIter] +
			   m_ulStreamReleaseTotal[iIter] +
			   m_ulDestroyElementTotal[iIter] +
			   m_ulStorageCommitTotal[iIter] +
			   m_ulStorageReleaseTotal[iIter];
			      	
    }

    return S_OK;
}


SCODE CNestedStorageTest::Report (CTestOutput &output)
{	       
    TCHAR pszBuf[80];

    wsprintf(pszBuf, TEXT("Nested Storage Test in %s Mode writing %d bytes"),
    	      m_pszFileMode, m_cbSize);

    output.WriteSectionHeader (Name(), pszBuf, *m_pInput);
    output.WriteString (TEXT("\n"));

    for ( ULONG iLevel = 0; iLevel < m_cNesting - 1; iLevel++)
    {
    	wsprintf(pszBuf, TEXT("\nLevel %d\n"), iLevel + 1);
    	output.WriteString (pszBuf);   
    	output.WriteResults (TEXT("CreateStorage    Min"), m_ulIterations, 
    			     m_ulCreateStorageMin[iLevel]);
    	output.WriteResults (TEXT("CreateStorage    Max"), m_ulIterations, 
    			     m_ulCreateStorageMax[iLevel]);

    	if (m_flCreateFlags == STGM_TRANSACTED)
	{
            output.WriteResults (TEXT("StorageCommit    Min"), m_ulIterations, 
            			 m_ulStorageCommitMin[iLevel] );
            output.WriteResults (TEXT("StorageCommit    Max"), m_ulIterations, 
            			 m_ulStorageCommitMax[iLevel] );
	}

    	output.WriteResults (TEXT("StorageRelease   Min"), m_ulIterations, 
    			     m_ulStorageReleaseMin[iLevel]);
    	output.WriteResults (TEXT("StorageRelease   Max"), m_ulIterations, 
    			     m_ulStorageReleaseMax[iLevel]);
    }

    output.WriteString (TEXT("\nOverall\n"));

    output.WriteResults (TEXT("StgCreateDocfile    "), m_ulIterations, 
    			 m_ulStgCreateDocfile);

    if (m_flCreateFlags == STGM_TRANSACTED)
    	output.WriteResults (TEXT("Final Storage Commit"), m_ulIterations, 
    			     m_ulFinalStorageCommit);

    output.WriteResults (TEXT("Final Storage Release"), m_ulIterations, 
    			 m_ulFinalStorageRelease);
    output.WriteString (TEXT("\n"));

    output.WriteResults (TEXT("CreateStorage Average"), m_ulIterations, 
    			 m_ulCreateStorageAverage );
    output.WriteResults (TEXT("CreateStorage  Total"), m_ulIterations, 
    			 m_ulCreateStorageTotal );
    output.WriteString (TEXT("\n"));

    output.WriteResults (TEXT("CreateStream     Min"), m_ulIterations, 
    			 m_ulCreateStreamMin);
    output.WriteResults (TEXT("CreateStream     Max"), m_ulIterations, 
    			 m_ulCreateStreamMax);
    output.WriteResults (TEXT("CreateStream Average"), m_ulIterations, 
    			 m_ulCreateStreamAverage);
    output.WriteResults (TEXT("CreateStream   Total"), m_ulIterations, 
    			 m_ulCreateStreamTotal );
    output.WriteString (TEXT("\n"));

    output.WriteResults (TEXT("StreamWrite      Min"), m_ulIterations, 
    			 m_ulStreamWriteMin );
    output.WriteResults (TEXT("StreamWrite      Max"), m_ulIterations,
    			 m_ulStreamWriteMax );
    output.WriteResults (TEXT("StreamWrite  Average"), m_ulIterations,
    			 m_ulStreamWriteAverage );
    output.WriteResults (TEXT("StreamWrite    Total"), m_ulIterations,
    			 m_ulStreamWriteTotal );
    output.WriteString (TEXT("\n"));

    output.WriteResults (TEXT("StreamRelease    Min"), m_ulIterations,
    			 m_ulStreamReleaseMin );
    output.WriteResults (TEXT("StreamRelease    Max"), m_ulIterations,
    			 m_ulStreamReleaseMax );
    output.WriteResults (TEXT("StreamRelease Average"), m_ulIterations, 
    			 m_ulStreamReleaseAverage );
    output.WriteResults (TEXT("StreamRelease  Total"), m_ulIterations,
    			 m_ulStreamReleaseTotal );
    output.WriteString (TEXT("\n"));

    if(m_bDelete)		       
    {
    	output.WriteResults (TEXT("DestroyElement   Min"), m_ulIterations,
    			     m_ulDestroyElementMin );
    	output.WriteResults (TEXT("DestroyElement   Max"), m_ulIterations, 
    			     m_ulDestroyElementMax );
    	output.WriteResults (TEXT("DestroyElement Average"), m_ulIterations, 
    			     m_ulDestroyElementAverage );
    	output.WriteResults (TEXT("DestroyElement Total"), m_ulIterations, 
    			     m_ulDestroyElementTotal );
    	output.WriteString (TEXT("\n"));
    }

    if (m_flCreateFlags == STGM_TRANSACTED)
    {
        output.WriteResults (TEXT("StorageCommit Average"), m_ulIterations, 
        		     m_ulStorageCommitAverage );
        output.WriteResults (TEXT("StorageCommit   Total"), m_ulIterations, 
        		     m_ulStorageCommitTotal );
    	output.WriteString (TEXT("\n"));
    }

    output.WriteResults (TEXT("StorageRelease Average"), m_ulIterations, 
    			 m_ulStorageReleaseAverage );
    output.WriteResults (TEXT("StorageRelease  Total"), m_ulIterations, 
    			 m_ulStorageReleaseTotal );
    output.WriteString (TEXT("\n"));

    output.WriteResults (TEXT("Overall Total       "), m_ulIterations, m_ulTotal );

    return S_OK;
}