//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1992.
//
//  File:	vect.cxx
//
//  Contents:	Vector common code.
//
//  Classes:	
//
//  Functions:	
//
//  History:	27-Oct-92	PhilipLa	Created
//
//----------------------------------------------------------------------------

#include <msfhead.cxx>

#pragma hdrstop

#include <msffunc.hxx>
#include <vect.hxx>

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CPagedVector_GetNewVectBits)
#endif

//  BUGBUG: In order to work around an optimization bug in the
//          CairOLE build, we need to turn all optimizations off
//          for this function.
//
#if WIN32 == 300
#pragma optimize("", off)
#endif


inline CVectBits * CPagedVector::GetNewVectBits(ULONG ulSize)
{
    msfAssert(ulSize > 0);
    CVectBits *pfb = NULL;

    if (ulSize <= (_HEAP_MAXREQ / sizeof(CVectBits)))
    {
        pfb = (CVectBits *) _pmsParent->GetMalloc()->Alloc(ulSize *
                                                           sizeof(CVectBits));
        if (pfb)
        {
            memset(pfb, 0, (USHORT)(ulSize * sizeof(CVectBits)));
        }
    }
    return pfb;
}

//  BUGBUG: We need to turn default optimizations back on.  See
//          BUGBUG above for more information.
//
#if WIN32 == 300
#pragma optimize("", on)
#endif

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CPagedVector_GetNewPageArray)
#endif

inline CMSFPage DFBASED ** VECT_CLASS
    CPagedVector::GetNewPageArray(ULONG ulSize)
{
    msfAssert(ulSize > 0);
    if (ulSize > (_HEAP_MAXREQ / sizeof(CMSFPage *)))
    {
        return NULL;
    }

    return (CMSFPage DFBASED **)
        _pmsParent->GetMalloc()->Alloc(ulSize * sizeof(CMSFPage *));
}

//+-------------------------------------------------------------------------
//
//  Method:     CPagedVector::Init, public
//
//  Synopsis:   CPagedVector initialization function
//
//  Arguments:  [ulSize] -- size of vector
//              [uFatEntries] -- number of entries in each table
//
//  Algorithm:  Allocate an array of pointer of size ulSize
//              For each cell in the array, allocate a CFatSect
//
//  History:    27-Dec-91   PhilipLa    Created.
//
//  Notes:
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CPagedVector_Init)
#endif

SCODE VECT_CLASS CPagedVector::Init(CMStream *pmsParent,
                                    ULONG ulSize)
{
    msfDebugOut((DEB_ITRACE,"In CPagedVector::CPagedVector(%lu)\n",ulSize));
    SCODE sc = S_OK;
    _pmsParent = P_TO_BP(CMStream DFBASED *, pmsParent);

    CMSFPageTable *pmptTemp = _pmsParent->GetPageTable();
    _pmpt = P_TO_BP(CMSFPageTable DFBASED *, pmptTemp);

    msfAssert(_pmpt != NULL);

    USHORT i;

    //  We don't bother allocating more than necessary here
    _ulAllocSize = _ulSize = ulSize;

    if (_ulSize > 0)
    {
        CMSFPage DFBASED **ampTemp;
        msfMem(ampTemp = GetNewPageArray(ulSize));
        for (i = 0; i < _ulSize; i++)
        {
            ampTemp[i] = NULL;
        }
        _amp = P_TO_BP(CMSFPage DFBASED * DFBASED *, ampTemp);

        CVectBits *avbTemp;
        msfMem(avbTemp = GetNewVectBits(ulSize));
        _avb = P_TO_BP(CVectBits DFBASED *, avbTemp);
    }

    msfDebugOut((DEB_ITRACE,"Out CPagedVector::CPagedVector()\n"));
    return S_OK;

Err:
    //In the error case, discard whatever vectors we were able to allocate
    //   and return S_OK.
    _pmsParent->GetMalloc()->Free(BP_TO_P(CMSFPage DFBASED **, _amp));
    _amp = NULL;

    _pmsParent->GetMalloc()->Free(BP_TO_P(CVectBits *,_avb));
    _avb = NULL;

    return S_OK;

}

//+-------------------------------------------------------------------------
//
//  Method:     CPagedVector::~CPagedVector, public
//
//  Synopsis:   CPagedVector constructor
//
//  Algorithm:  Delete the pointer array.
//
//  History:    27-Oct-92   PhilipLa    Created.
//
//  Notes:
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CPagedVector_1CPagedVector)
#endif

VECT_CLASS CPagedVector::~CPagedVector()
{
    if (_pmsParent != NULL)
    {
        _pmsParent->GetMalloc()->Free(BP_TO_P(CMSFPage DFBASED **, _amp));
        _pmsParent->GetMalloc()->Free(BP_TO_P(CVectBits *, _avb));
    }
    else
        msfAssert(_amp == NULL && _avb == NULL &&
                  aMsg("Can't free arrays without allocator"));
}


//+---------------------------------------------------------------------------
//
//  Member:	CPagedVector::Empty, public
//
//  Synopsis:	Discard the storage associated with this vector.
//
//  Arguments:	None.
//
//  Returns:	void.
//
//  History:	04-Dec-92	PhilipLa	Created
//
//----------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CPagedVector_Empty)
#endif

void CPagedVector::Empty(void)
{
    if (_pmpt != NULL)
    {
        _pmpt->FreePages(this);
    }

    msfAssert(((_pmsParent != NULL) || ((_amp == NULL) && (_avb == NULL))) &&
            aMsg("Can't get to IMalloc for vector memory."));
    
    if (_pmsParent != NULL)
    {
        _pmsParent->GetMalloc()->Free(BP_TO_P(CMSFPage DFBASED **, _amp));
        _pmsParent->GetMalloc()->Free(BP_TO_P(CVectBits *, _avb));
    }
    
    _amp = NULL;
    _avb = NULL;
    _pmpt = NULL;
    _ulAllocSize = _ulSize = 0;
    _pmsParent = NULL;
}

//+---------------------------------------------------------------------------
//
//  Member:	CPagedVector::Flush, public
//
//  Synopsis:	Flush the dirty pages for this vector
//
//  Arguments:	None.
//
//  Returns:	Appropriate status code
//
//  History:	02-Nov-92	PhilipLa	Created
//
//----------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CPagedVector_Flush)
#endif

SCODE CPagedVector::Flush(void)
{
    SCODE sc;
    SCODE scRet = S_OK;

    if (_ulSize > 0)
    {
        if (_amp != NULL)
        {
            for (USHORT i = 0; i < _ulSize; i++)
            {
                if ((_amp[i] != NULL) && (_amp[i]->IsDirty()))
                {
                    sc = _pmpt->FlushPage(BP_TO_P(CMSFPage *, _amp[i]));
                    if ((FAILED(sc)) && (SUCCEEDED(scRet)))
                    {
                        scRet = sc;
                    }
                }
            }
        }
        else
        {
            scRet = _pmpt->Flush();
        }
    }

    return scRet;
}


//+-------------------------------------------------------------------------
//
//  Method:     CPagedVector::GetTable, public
//
//  Synopsis:   Return a pointer to a page for the given index
//              into the vector.
//
//  Arguments:  [iTable] -- index into vector
//              [ppmp] -- Pointer to return location
//
//  Returns:    S_OK if call completed OK.
//
//  History:    27-Oct-92   PhilipLa    Created.
//
//  Notes:
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CPagedVector_GetTable)
#endif

SCODE VECT_CLASS CPagedVector::GetTableWithSect(
        const FSINDEX iTable,
        DWORD dwFlags,
        SECT sectKnown,
        void **ppmp)
{
    SCODE sc = S_OK;
    CMSFPage *pmp;

    msfAssert((_pmsParent->GetILB() != NULL) &&
            aMsg("Null ILB found on GetTable - need SetAccess call?"));

    if ((_amp == NULL) || (_amp[iTable] == NULL))
    {
        if (dwFlags & FB_NEW)
        {
            //We know that the page isn't in the page table,
            //  so we can just get a free page an allocate it
            //  ourselves.

            msfChk(_pmpt->GetFreePage(&pmp));

            pmp->SetVector(this);
            pmp->SetSid(_sid);
            pmp->SetOffset(iTable);
            pmp->SetSect(ENDOFCHAIN);

            sc = STG_S_NEWPAGE;
            dwFlags = (dwFlags & ~FB_NEW) | FB_DIRTY;
        }
        else
        {
            msfChk(_pmpt->GetPage(this,
                                  _sid, iTable, sectKnown, &pmp));
            msfAssert((pmp->GetVector() == this) &&
                    aMsg("GetPage returned wrong page."));
        }


        if (_amp != NULL)
        {
            _amp[iTable] = P_TO_BP(CMSFPage DFBASED *, pmp);
        }

    }
    else
    {
        pmp = BP_TO_P(CMSFPage *, _amp[iTable]);
        msfAssert((pmp->GetVector() == this) &&
                aMsg("Cached page has wrong vector pointer"));
    }

    pmp->AddRef();

    if (((dwFlags & FB_DIRTY) && !(pmp->IsDirty())) &&
        (sc != STG_S_NEWPAGE))
    {
        //If we are not a newly created page, and we are being
        //   dirtied for the first time, make sure that our
        //   _sect field is correct.
        //
        //Newly created pages have to have their sect set manually
        //  _before_ being released.  This is very important.

#ifndef REF
        msfAssert(!_pmsParent->IsShadow() &&
                aMsg("Dirtying page in shadow multistream."));

        msfChkTo(Err_Rel, _pmsParent->GetFat()->QueryRemapped(pmp->GetSect()));

        if (sc == S_FALSE)
        {
#endif //!REF
            pmp->SetSect(ENDOFCHAIN);

            SECT sect;
            msfChkTo(Err_Rel, _pmsParent->GetESect(
                    pmp->GetSid(),
                    pmp->GetOffset(),
                    &sect));

            pmp->SetSect(sect);
#ifndef REF
        }
#endif //!REF
    }
#ifndef REF
#if DBG == 1
    else if ((pmp->IsDirty()) && (!pmp->IsInUse()) && (sc != STG_S_NEWPAGE))
    {
        msfAssert((_pmsParent->GetFat()->QueryRemapped(pmp->GetSect()) ==
                S_OK) &&
                aMsg("Found unremapped dirty page."));
    }
#endif
#endif //!REF

    pmp->SetFlags(pmp->GetFlags() | dwFlags | FB_TOUCHED);
    msfAssert((pmp->GetVector() == this) &&
            aMsg("GetTable returned wrong page."));
    *ppmp = pmp->GetData();

Err:
    return sc;

Err_Rel:
    pmp->Release();
    return sc;
}


//+---------------------------------------------------------------------------
//
//  Member:	CPagedVector::SetDirty, public
//
//  Synopsis:	Set the dirty bit on the specified page
//
//  Arguments:	[iTable] -- Table to set bit on
//
//  History:	28-Oct-92	PhilipLa	Created
//
//  Notes:  This function is always called on a page with an
//              open reference.  Therefore, the page is
//              guaranteed to be in the page table, and that
//              FindPage call should never return an error.
//
//----------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CPagedVector_SetDirty)
#endif

SCODE CPagedVector::SetDirty(ULONG iTable)
{
    SCODE sc = S_OK;
    CMSFPage *pmp;

#ifndef REF
    msfAssert((!_pmsParent->IsShadow()) &&
            aMsg("Dirtying page in shadow."));
#endif //!REF

    if (_amp == NULL)
    {

        msfChk(_pmpt->FindPage(this, _sid, iTable, &pmp));
        msfAssert(sc == STG_S_FOUND);
        msfAssert(pmp->IsInUse() &&
                aMsg("Called SetDirty on page not in use."));
    }
    else
    {
        msfAssert(_amp != NULL);
        msfAssert(_amp[iTable] != NULL);
        pmp = BP_TO_P(CMSFPage *, _amp[iTable]);
    }

    if (!pmp->IsDirty())
    {
        //We are not a newly created page, and we are being
        //   dirtied for the first time, make sure that our
        //   _sect field is correct.
        //

#ifndef REF
        msfAssert(!_pmsParent->IsShadow() &&
                aMsg("Dirtying page in shadow multistream."));
#endif //!REF
        pmp->AddRef();

#ifndef REF
        msfChkTo(Err_Rel, _pmsParent->GetFat()->QueryRemapped(pmp->GetSect()));

        if (sc == S_FALSE)
        {
#endif //!REF
            pmp->SetSect(ENDOFCHAIN);

            SECT sect;
            msfChkTo(Err_Rel, _pmsParent->GetESect(
                    pmp->GetSid(),
                    pmp->GetOffset(),
                    &sect));

            pmp->SetSect(sect);
#ifndef REF
        }
#endif //!REF

        pmp->Release();
    }
#ifndef REF
#if DBG == 1
    else
    {
        pmp->AddRef();
        sc = _pmsParent->GetFat()->QueryRemapped(pmp->GetSect());
        msfAssert((SUCCEEDED(sc)) &&
                aMsg("QueryRemapped returned error"));
        msfAssert((sc == S_OK) &&
                aMsg("QueryRemapped returned non-TRUE value."));
        pmp->Release();
    }
#endif
#endif //!REF

    pmp->SetDirty();

 Err:
    return sc;

 Err_Rel:
    pmp->Release();
    return sc;
}


//+-------------------------------------------------------------------------
//
//  Method:     CPagedVector::Resize, public
//
//  Synopsis:   Resize a CPagedVector
//
//  Arguments:  [ulSize] -- Size of new vector
//
//  Algorithm:  Create new pointer array of size ulSize.
//              For each entry in old array, copy the pointer over.
//
//  History:    27-Oct-92   PhilipLa    Created.
//              08-Feb-93   AlexT       Add LARGETHRESHOLD support
//
//  Notes:
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CPagedVector_Resize)
#endif

#define LARGETHRESHOLD  1024
#define VECTORBLOCK     1024    //  Must be power of 2

SCODE VECT_CLASS CPagedVector::Resize(FSINDEX ulSize)
{
    msfDebugOut((DEB_ITRACE,"In CPagedVector::CPagedVector(%lu)\n",ulSize));

    msfAssert(ulSize >= _ulSize);
    msfAssert(_ulSize <= _ulAllocSize);
    msfAssert(((VECTORBLOCK & (VECTORBLOCK - 1)) == 0) &&
              aMsg("VECTORBLOCK must be power of 2"));

    msfAssert(!((_amp == NULL) && (_avb != NULL)) &&
            aMsg("Resize precondition failed."));

    if (ulSize > _ulAllocSize)
    {
        //  We don't have room in the existing vector;  grow it
        ULONG ulNewAllocSize = ulSize;

        if (ulNewAllocSize > LARGETHRESHOLD)
        {
            //  We're dealing with a large vector;  grow it a VECTORBLOCK
            //  at a time
            ulNewAllocSize = (ulNewAllocSize + VECTORBLOCK - 1) &
                             ~(VECTORBLOCK - 1);
        }

        CMSFPage DFBASED **amp = GetNewPageArray(ulNewAllocSize);
        CVectBits *avb = GetNewVectBits(ulNewAllocSize);

        //  Can't fail after this point

        _ulAllocSize = ulNewAllocSize;

        //  Copy over the old entries


        if ((amp != NULL) && (avb != NULL))
        {
            if ((_amp != NULL) && (_avb != NULL))
            {
                //  Both allocations succeeded
                for (ULONG iamp = 0; iamp < _ulSize; iamp++)
                {
                    amp[iamp] = _amp[iamp];
                    avb[iamp] = _avb[iamp];
                }
            }
            else if (_amp != NULL)
            {
                for (ULONG iamp = 0; iamp < _ulSize; iamp++)
                {
                    amp[iamp] = _amp[iamp];
                }
            }
            else
            {
                for (ULONG iamp = 0; iamp < _ulSize; iamp++)
                {
                    amp[iamp] = NULL;
                }
            }
        }
        else
        {
            //  At least one of the allocations failed
            _pmsParent->GetMalloc()->Free(avb);
            avb = NULL;

            _pmsParent->GetMalloc()->Free(amp);
            amp = NULL;
        }

        //  Delete the old vector and put in the new one (if any).
        //  In the error case, throw away the vectors we are currently
        //  holding (since they are of insufficient size) and return S_OK.

        _pmsParent->GetMalloc()->Free(BP_TO_P(CMSFPage DFBASED **, _amp));
        _amp = P_TO_BP(CMSFPage DFBASED * DFBASED*, amp);

        _pmsParent->GetMalloc()->Free(BP_TO_P(CVectBits *, _avb));
        _avb = P_TO_BP(CVectBits DFBASED *, avb);
    }

    if (_amp != NULL)
    {
        //  Initialize the new elements in the vector

        for (ULONG iamp = _ulSize; iamp < ulSize; iamp++)
            _amp[iamp] = NULL;
    }

    _ulSize = ulSize;

    msfDebugOut((DEB_ITRACE,"Out CPagedVector resize constructor\n"));
    return S_OK;
}

#ifndef REF
//+-------------------------------------------------------------------------
//
//  Method:     CPagedVector::InitCopy, public
//
//  Synopsis:   CPagedVector Init function for copying
//
//  Arguments:  [vectOld] -- Reference to vector to be copied.
//
//  Algorithm:  *Finish This*
//
//  History:    27-Oct-92   PhilipLa    Created.
//
//  Notes:
//
//--------------------------------------------------------------------------

#ifdef CODESEGMENTS
#pragma code_seg(SEG_CPagedVector_InitCopy)
#endif

void VECT_CLASS CPagedVector::InitCopy(CPagedVector *pvectOld)
{
    msfDebugOut((DEB_ITRACE,"In CPagedVector copy constructor\n"));
    SCODE sc;
    USHORT i;

    _pmsParent = pvectOld->_pmsParent;

    CMSFPageTable *pmpt;
    pmpt = _pmsParent->GetPageTable();
    _pmpt = P_TO_BP(CMSFPageTable DFBASED *, pmpt);

    _ulAllocSize = _ulSize = pvectOld->_ulSize;

    if (_ulSize > 0)
    {
        CMSFPage DFBASED ** amp;
        msfMem(amp = GetNewPageArray(_ulSize));
        for (i = 0; i < _ulSize; i++)
        {
            amp[i] = NULL;
            if (pvectOld->_amp != NULL)
            {
                _pmpt->CopyPage(this,
                                BP_TO_P(CMSFPage *, pvectOld->_amp[i]),
                                &(amp[i]));
            }
        }
        _amp = P_TO_BP(CMSFPage DFBASED * DFBASED *, amp);

        CVectBits *avb;
        msfMem(avb = GetNewVectBits(_ulSize));
        if (pvectOld->_avb != NULL)
        {
            for (i = 0; i < _ulSize; i++)
            {
                avb[i] = ((CPagedVector *)pvectOld)->_avb[i];
            }
        }
        _avb = P_TO_BP(CVectBits DFBASED *, avb);
    }

    msfDebugOut((DEB_ITRACE,"Out CPagedVector copy constructor\n"));

    //In the error case, keep whatever vectors we managed to allocate
    //  and return.
Err:
    return;
}
#endif //!REF


