//-------------------------------------------------------------------------
//
//  For conditions of distribution and use, see copyright notice
//  in Flashpix.h
//
//  Copyright (c) 1999 Digital Imaging Group, Inc.
//
//  Contents:   CExposedStream code
//
//  Notes:      See the header file expst.hxx for details
//
//--------------------------------------------------------------------------

#include "exphead.cxx"

#include "expst.hxx"
#include "logfile.hxx"

#ifndef min
#define min(a, b) ((a)<(b) ? (a) : (b))
#endif
#ifndef max
#define max(a, b) ((a)>(b) ? (a) : (b))
#endif

// Maximum stream size supported by exposed streams
// This is MAX_ULONG with one subtracted so that
// the seek pointer has a spot to sit even at the
// end of the stream
#define CBMAXSTREAM 0xfffffffeUL
// Maximum seek pointer value
#define CBMAXSEEK (CBMAXSTREAM+1)

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::CExposedStream, public
//
//  Synopsis:   Empty object constructor
//
//---------------------------------------------------------------


CExposedStream::CExposedStream()
{
    olDebugOut((DEB_ITRACE, "In  CExposedStream::CExposedStream()\n"));
    _cReferences = 0;
    _ulAccessLockBase = 0;
    _ulPos = 0;
    _pst = NULL;
    _pdfParent = NULL;
    _fDirty = FALSE;
#ifdef NEWPROPS
    _pb = NULL;
    _cbUsed = 0;
    _cbOriginalStreamSize = 0;
    _fChangePending = FALSE;
#endif

    olDebugOut((DEB_ITRACE, "Out CExposedStream::CExposedStream\n"));
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::Init, public
//
//  Synopsis:   Base constructor
//
//  Arguments:  [pst] - Direct stream
//              [pdfParent] - the storage parent
//              [df]  - Permission flags
//              [pdfn] - name of stream
//              [ulPos] - offset
//
//  Returns:    Appropriate status code
//
//  Note:       We add "this" as a child to the parent to
//              1) Check for multiple instantiation of a child
//              2) Uses the RevertFromAbove() function to check
//                 for reverted state.
//
//---------------------------------------------------------------

SCODE CExposedStream::Init(CDirectStream *pst,
                           CExposedDocFile* pdfParent,
                           const DFLAGS df,
                           const CDfName *pdfn,
                           const ULONG ulPos)
{
    olDebugOut((DEB_ITRACE, "In  CExposedStream::Init("
                "%p, %lu)\n", pst, ulPos));
    _ulPos = ulPos;
    _pst = pst;
    _pdfParent = pdfParent;
    _df = df;
    _dfn.Set(pdfn->GetLength(), pdfn->GetBuffer());
    olAssert(pdfParent);
    _pdfParent->AddChild(this);
    _cReferences = 1;
    _sig = CEXPOSEDSTREAM_SIG;
    olDebugOut((DEB_ITRACE, "Out CExposedStream::Init\n"));
    return S_OK;
}


//+--------------------------------------------------------------
//
//  Member:     CExposedStream::Read, public
//
//  Synopsis:   Read from a stream
//
//  Arguments:  [pb] - Buffer
//              [cb] - Count of bytes to read
//              [pcbRead] - Return number of bytes read
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pcbRead]
//
//---------------------------------------------------------------


STDMETHODIMP CExposedStream::Read(VOID HUGEP *pb, ULONG cb, ULONG *pcbRead)
{
    SCODE sc;
    ULONG cbRead = 0;

    olLog(("%p::In  CExposedStream::Read(%p, %lu, %p)\n",
           this, pb, cb, pcbRead));
    olDebugOut((DEB_ITRACE, "In CExposedStream::Read(%p,%lu,%p)\n",
                 pb, cb, pcbRead));
    if (pcbRead)
        olChkTo(EH_BadPtr, ValidateOutBuffer(pcbRead, sizeof(ULONG)));
    olChk(ValidateHugeOutBuffer(pb, cb));
    olChk(Validate());
    olChk(CheckReverted());

    if (!P_READ(_df))
        sc = STG_E_ACCESSDENIED;
    else
        sc = _pst->ReadAt(_ulPos, pb, cb,
                          (ULONG STACKBASED *)&cbRead);
    olAssert( CBMAXSEEK - _ulPos >= cbRead);
    _ulPos+=cbRead;

    olDebugOut((DEB_ITRACE, "Out CExposedStream::Read => %lu\n", cbRead));

EH_Err:
    if (pcbRead)
    {
        *pcbRead = cbRead;
        olLog(("%p::Out CExposedStream::Read() *pcbRead==%lu, ret=%lx\n",
               this, SAFE_DREF(pcbRead), sc));
    }
    else
    {
        olLog(("%p::Out CExposedStream::Read().  ret == %lx\n", this, sc));
    }

EH_BadPtr:
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::Write, public
//
//  Synopsis:   Write to a stream
//
//  Arguments:  [pb] - Buffer
//              [cb] - Count of bytes to write
//              [pcbWritten] - Return of bytes written
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pcbWritten]
//
//---------------------------------------------------------------


STDMETHODIMP CExposedStream::Write(
        VOID const HUGEP *pb,
        ULONG cb,
        ULONG *pcbWritten)
{
    SCODE sc;
    ULONG cbWritten = 0;

    olLog(("%p::In  CExposedStream::Write(%p, %lu, %p)\n",
           this, pb, cb, pcbWritten));
    olDebugOut((DEB_ITRACE,
                "In CExposedStream::Write(%p, %lu, %p)\n",
                pb, cb, pcbWritten));

    if (pcbWritten)
    {
        olChkTo(EH_BadPtr,
                ValidateOutBuffer(pcbWritten, sizeof(ULONG)));
    }
    olChk(ValidateHugeBuffer(pb, cb));
    olChk(Validate());
    olChk(CheckReverted());
    if (!P_WRITE(_df))
        sc = STG_E_ACCESSDENIED;
    else
    {
        sc = _pst->WriteAt(_ulPos, pb, cb,
                           (ULONG STACKBASED *)&cbWritten);
        if (SUCCEEDED(sc))
            SetDirty();
    }
    olAssert( CBMAXSEEK - _ulPos >= cbWritten);
    _ulPos += cbWritten;

    olDebugOut((DEB_ITRACE, "Out CExposedStream::Write => %lu\n",
                cbWritten));
EH_Err:
    if (pcbWritten)
    {
        *pcbWritten = cbWritten;
        olLog(("%p::Out CExposedStream::Write().  *pcbWritten == %lu, ret = %lx\n",
               this, *pcbWritten, sc));
    }
    else
    {
        olLog(("%p::Out CExposedStream::Write().  ret == %lx\n",this, sc));
    }

EH_BadPtr:
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::Seek, public
//
//  Synopsis:   Seek to a point in a stream
//
//  Arguments:  [dlibMove] - Offset to move by
//              [dwOrigin] - SEEK_SET, SEEK_CUR, SEEK_END
//              [plibNewPosition] - Return of new offset
//
//  Returns:    Appropriate status code
//
//  Modifies:   [plibNewPosition]
//
//---------------------------------------------------------------


STDMETHODIMP CExposedStream::Seek(LARGE_INTEGER dlibMove,
                                  DWORD dwOrigin,
                                  ULARGE_INTEGER *plibNewPosition)
{
    SCODE sc;
    LONG lMove;
    ULARGE_INTEGER ulPos;
    ULONG cbSize;

    olLog(("%p::In  CExposedStream::Seek(%ld, %lu, %p)\n",
           this, LIGetLow(dlibMove), dwOrigin, plibNewPosition));
    olDebugOut((DEB_ITRACE, "In  CExposedStream::Seek(%ld, %lu, %p)\n",
                LIGetLow(dlibMove), dwOrigin, plibNewPosition));

    if (plibNewPosition)
    {
        olChk(ValidateOutBuffer(plibNewPosition, sizeof(ULARGE_INTEGER)));
        ULISet32(*plibNewPosition, 0);
    }
    if (dwOrigin != STREAM_SEEK_SET && dwOrigin != STREAM_SEEK_CUR &&
        dwOrigin != STREAM_SEEK_END)
        olErr(EH_Err, STG_E_INVALIDFUNCTION);

    // Truncate dlibMove to 32 bits
    if (dwOrigin == STREAM_SEEK_SET)
    {
        // Make sure we don't seek too far
        if (LIGetHigh(dlibMove) != 0)
            LISet32(dlibMove, /*(LONG)*/0xffffffff);
    }
    else
    {
        // High dword must be zero for positive values or -1 for
        // negative values
        // Additionally, for negative values, the low dword can't
        // exceed -0x80000000 because the 32nd bit is the sign
        // bit
        if (LIGetHigh(dlibMove) > 0 ||
            (LIGetHigh(dlibMove) == 0 &&
             LIGetLow(dlibMove) >= 0x80000000))
            LISet32(dlibMove, 0x7fffffff);
        else if (LIGetHigh(dlibMove) < -1 ||
                 (LIGetHigh(dlibMove) == -1 &&
                  LIGetLow(dlibMove) <= 0x7fffffff))
            LISet32(dlibMove, /*(LONG)*/0x80000000);
    }

    lMove = (LONG)LIGetLow(dlibMove);
    olChk(Validate());
    olChk(CheckReverted());
    ULISet32(ulPos, _ulPos);
    switch(dwOrigin)
    {
    case STREAM_SEEK_SET:
        ULISetLow(ulPos, (ULONG)lMove);
        break;
    case STREAM_SEEK_END:
        olChk(GetSize(&cbSize));
        if (lMove < 0)
        {
            if ((ULONG)(-lMove) > cbSize)
                olErr(EH_Err, STG_E_INVALIDFUNCTION);
        }
        else if ((ULONG)lMove > CBMAXSEEK-cbSize)
            lMove = (LONG)(CBMAXSEEK-cbSize);
        ULISetLow(ulPos, cbSize+lMove);
        break;
    case STREAM_SEEK_CUR:
        if (lMove < 0)
        {
            if ((ULONG)(-lMove) > _ulPos)
                olErr(EH_Err, STG_E_INVALIDFUNCTION);
        }
        else if ((ULONG)lMove > CBMAXSEEK - _ulPos)
            lMove = (LONG)(CBMAXSEEK- _ulPos);
        ULISetLow(ulPos, _ulPos+lMove);
        break;
    }
    _ulPos = ULIGetLow(ulPos);
    if (plibNewPosition)
        *plibNewPosition = ulPos;

    olDebugOut((DEB_ITRACE, "Out CExposedStream::Seek => %lu\n",
                ULIGetLow(ulPos)));
EH_Err:
    olLog(("%p::Out CExposedStream::Seek().  ulPos == %lu,  ret == %lx\n",
           this, ULIGetLow(ulPos), sc));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::SetSize, public
//
//  Synopsis:   Sets the size of a stream
//
//  Arguments:  [ulNewSize] - New size
//
//  Returns:    Appropriate status code
//
//---------------------------------------------------------------
SCODE CExposedStream::SetSize(ULONG cb)
{
    olDebugOut((DEB_ITRACE,
                "In CExposedStream::SetSize(ULONG %lu)\n", cb));
    SCODE sc;

    olChk(Validate());
    olChk(CheckReverted());
    if (!P_WRITE(_df))
        sc = STG_E_ACCESSDENIED;
    else
    {
        olChk(_pst->SetSize(cb));
        SetDirty();
    }

EH_Err:
    olDebugOut((DEB_ITRACE, "Out  CExposedStream::SetSize()\n"));
    return sc;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::SetSize, public
//
//  Synopsis:   Sets the size of a stream
//
//  Arguments:  [ulNewSize] - New size
//
//  Returns:    Appropriate status code
//
//---------------------------------------------------------------

STDMETHODIMP CExposedStream::SetSize(ULARGE_INTEGER ulNewSize)
{
    SCODE sc;

    olLog(("%p::In  CExposedStream::SetSize(%lu)\n",
           this, ULIGetLow(ulNewSize)));
    olDebugOut((DEB_ITRACE, "In  CExposedStream::SetSize(%lu)\n",
                ULIGetLow(ulNewSize)));

    if (ULIGetHigh(ulNewSize) != 0)
        olErr(EH_Err, STG_E_INVALIDFUNCTION);
    olChk(SetSize(ULIGetLow(ulNewSize)));

    olDebugOut((DEB_ITRACE, "Out CExposedStream::SetSize\n"));
EH_Err:
    olLog(("%p::Out CExposedStream::SetSize().  ret == %lx\n", this, sc));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::CopyTo, public
//
//  Synopsis:   Copies information from one stream to another
//
//  Arguments:  [pstm] - Destination
//              [cb] - Number of bytes to copy
//              [pcbRead] - Return number of bytes read
//              [pcbWritten] - Return number of bytes written
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pcbRead]
//              [pcbWritten]
//
//  Notes:      We do our best to handle overlap correctly.  This allows
//              CopyTo to be used to insert and remove space within a
//              stream.
//
//              In the error case, we make no gurantees as to the
//              validity of pcbRead, pcbWritten, or either stream's
//              seek position.
//
//---------------------------------------------------------------


STDMETHODIMP CExposedStream::CopyTo(IStream *pstm,
                                    ULARGE_INTEGER cb,
                                    ULARGE_INTEGER *pcbRead,
                                    ULARGE_INTEGER *pcbWritten)
{
    SCODE sc;
    ULONG ulCopySize;
    ULONG ulSrcSize;
    ULONG ulSrcOrig;
    ULARGE_INTEGER uliDestOrig;
    LARGE_INTEGER liDestPos;
    BYTE *pb = NULL;
    BOOL fOverlap;
    ULONG ulBytesCopied = 0;

    olLog(("%p::In  CExposedStream::CopyTo(%p, %lu, %p, %p)\n",
           this, pstm, ULIGetLow(cb), pcbRead, pcbWritten));
    olDebugOut((DEB_TRACE, "In  CExposedStream::CopyTo("
                "%p, %lu, %p, %p)\n", pstm, ULIGetLow(cb),
                pcbRead, pcbWritten));

    if (pcbRead) // okay to set to NULL => not interested
    {
        olChk(ValidateOutBuffer(pcbRead, sizeof(ULARGE_INTEGER)));
        ULISet32(*pcbRead, 0);
    }
    if (pcbWritten) // okay to set to NULL => not interested
    {
        olChk(ValidateOutBuffer(pcbWritten, sizeof(ULARGE_INTEGER)));
        ULISet32(*pcbWritten, 0);
    }

    olChk(ValidateInterface(pstm, IID_IStream));
    olChk(Validate());
    olChk(CheckReverted());

    //  Bound the size of the copy
    //  1.  The maximum we can copy is 0xffffffff

    if (ULIGetHigh(cb) == 0)
        ulCopySize = ULIGetLow(cb);
    else
        ulCopySize = 0xffffffff;

    //  2.  We can only copy what's available in the source stream

    olChk(GetSize(&ulSrcSize));

    ulSrcOrig = _ulPos;
    if (ulSrcSize < ulSrcOrig)
    {
        //  Nothing in source to copy
        ulCopySize = 0;
    }
    else if ((ulSrcSize - ulSrcOrig) < ulCopySize)
    {
        //  Shrink ulCopySize to fit bytes in source
        ulCopySize = ulSrcSize - ulSrcOrig;
    }

    //  3.  We can only copy what will fit in the destination

    LISet32(liDestPos, 0);
    olHChk(pstm->Seek(liDestPos, STREAM_SEEK_CUR, &uliDestOrig));
    olAssert(ULIGetHigh(uliDestOrig) == 0);

    if (ulCopySize > CBMAXSEEK - ULIGetLow(uliDestOrig))
        ulCopySize = CBMAXSEEK - ULIGetLow(uliDestOrig);

    //  We are allowed to fail here with out-of-memory
    olMem(pb = new BYTE[STREAMBUFFERSIZE]);

    // Since we have no reliable way to determine if the source and
    // destination represent the same stream, we assume they
    // do and always handle overlap.

    fOverlap = (ULIGetLow(uliDestOrig) > ulSrcOrig &&
                ULIGetLow(uliDestOrig) < ulSrcOrig + ulCopySize);

    ULONG ulSrcCopyOffset;
    ULONG ulDstCopyOffset;
    if (fOverlap)
    {
        //  We're going to copy back to front, so determine the
        //  stream end positions
        ulSrcCopyOffset = ulSrcOrig + ulCopySize;

        //  uliDestOrig is the destination starting offset
        ulDstCopyOffset = ULIGetLow(uliDestOrig) + ulCopySize;
    }

    while (ulCopySize > 0)
    {
        //  We can only copy up to STREAMBUFFERSIZE bytes at a time
        ULONG cbPart = min(ulCopySize, STREAMBUFFERSIZE);

        if (fOverlap)
        {
            //  We're copying back to front so we need to seek to
            //  set up the streams correctly

            ulSrcCopyOffset -= cbPart;
            ulDstCopyOffset -= cbPart;

            //  Set source stream position
            _ulPos = ulSrcCopyOffset;

            //  Set destination stream position
            LISet32(liDestPos, ulDstCopyOffset);
            olHChk(pstm->Seek(liDestPos, STREAM_SEEK_SET, NULL));
        }

        {
            ULONG ulRead;
            olHChk(Read(pb, cbPart, &ulRead));
            if (cbPart != ulRead)
            {
                //  There was no error, but we were unable to read cbPart
                //  bytes.  Something's wrong (the underlying ILockBytes?)
                //  but we can't control it;  just return an error.
                olErr(EH_Err, STG_E_READFAULT);
            }
        }


        {
            ULONG ulWritten;
            olHChk(pstm->Write(pb, cbPart, &ulWritten));
            if (cbPart != ulWritten)
            {
                //  There was no error, but we were unable to write
                //  ulWritten bytes.  We can't trust the pstm
                //  implementation, so all we can do here is return
                //  an error.
                olErr(EH_Err, STG_E_WRITEFAULT);
            }
        }

        olAssert(ulCopySize >= cbPart);
        ulCopySize -= cbPart;
        ulBytesCopied += cbPart;
    }

    if (fOverlap)
    {
        //  Set the seek pointers to the correct location
        _ulPos = ulSrcOrig + ulBytesCopied;

        LISet32(liDestPos, ULIGetLow(uliDestOrig) + ulBytesCopied);
        olHChk(pstm->Seek(liDestPos, STREAM_SEEK_SET, NULL));
    }

    if (pcbRead)
        ULISet32(*pcbRead, ulBytesCopied);
    if (pcbWritten)
        ULISet32(*pcbWritten, ulBytesCopied);

    olDebugOut((DEB_ITRACE, "Out CExposedStream::CopyTo => %lu, %lu\n",
                pcbRead ? ULIGetLow(*pcbRead) : 0,
                pcbWritten ? ULIGetLow(*pcbWritten) : 0));
    // Fall through
EH_Err:
    delete [] pb;
    olLog(("%p::Out CExposedStream::CopyTo().  "
           "cbRead == %lu, cbWritten == %lu, ret == %lx\n",
           this, pcbRead ? ULIGetLow(*pcbRead) : 0,
           pcbWritten ? ULIGetLow(*pcbWritten) : 0, sc));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::Release, public
//
//  Synopsis:   Releases a stream
//
//  Returns:    Appropriate status code
//
//---------------------------------------------------------------


STDMETHODIMP_(ULONG) CExposedStream::Release(void)
{
    LONG lRet;

    olLog(("%p::In  CExposedStream::Release()\n", this));
    olDebugOut((DEB_ITRACE, "In  CExposedStream::Release()\n"));

    if (FAILED(Validate()))
        return 0;
    olAssert(_cReferences > 0);
    lRet = AtomicDec(&_cReferences);
    if (lRet == 0)
    {
        Commit(0); //  flush data
        delete this;
    }
    else if (lRet < 0)
        lRet = 0;

    olDebugOut((DEB_ITRACE, "Out CExposedStream::Release\n"));
    olLog(("%p::Out CExposedStream::Release().  ret == %lu\n", this, lRet));
    FreeLogFile();
    return lRet;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::Stat, public
//
//  Synopsis:   Fills in a buffer of information about this object
//
//  Arguments:  [pstatstg] - Buffer
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pstatstg]
//
//---------------------------------------------------------------


STDMETHODIMP_(SCODE) CExposedStream::Stat(STATSTGW *pstatstg, DWORD grfStatFlag)
{
    SCODE sc;

    olLog(("%p::In  CExposedStream::Stat(%p)\n", this, pstatstg));
    olDebugOut((DEB_ITRACE, "In  CExposedStream::Stat(%p)\n",
                pstatstg));

    olChkTo(EH_RetSc, ValidateOutBuffer(pstatstg, sizeof(STATSTGW)));
    olChk(VerifyStatFlag(grfStatFlag));
    olChk(Validate());
    olChk(CheckReverted());
    pstatstg->grfMode = DFlagsToMode(_df);

    pstatstg->clsid = CLSID_NULL;  // irrelevant for streams
    pstatstg->grfStateBits = 0;    // irrelevant for streams
    pstatstg->type = STGTY_STREAM;
    pstatstg->grfLocksSupported = 0;
    pstatstg->reserved = 0;

    // we null these values 'cos they are not interesting for
    // direct streams ...
    pstatstg->ctime.dwLowDateTime = pstatstg->ctime.dwHighDateTime = 0;
    pstatstg->mtime.dwLowDateTime = pstatstg->mtime.dwHighDateTime = 0;
    pstatstg->atime.dwLowDateTime = pstatstg->atime.dwHighDateTime = 0;
    pstatstg->pwcsName = NULL;
    if ((grfStatFlag & STATFLAG_NONAME) == 0)
    {                       // fill in name
        olChk(DfAllocWCS((WCHAR *)_dfn.GetBuffer(),
                         &pstatstg->pwcsName));
        wcscpy(pstatstg->pwcsName, (WCHAR *)_dfn.GetBuffer());
    }
    ULONG cbSize;
    GetSize(&cbSize);
    ULISet32(pstatstg->cbSize, cbSize);

    olDebugOut((DEB_ITRACE, "Out CExposedStream::Stat\n"));
EH_Err:
    if (FAILED(sc))
        memset(pstatstg, 0, sizeof(STATSTGW));
EH_RetSc:
    olLog(("%p::Out CExposedStream::Stat().  ret == %lx\n",
           this, sc));
    return sc;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::Clone, public
//
//  Synopsis:   Clones a stream
//
//  Returns:    Appropriate status code
//
//---------------------------------------------------------------


STDMETHODIMP CExposedStream::Clone(IStream **ppstm)
{
    CExposedStream *pst;
    SCODE sc;

    olLog(("%p::In  CExposedStream::Clone(%p)\n", this, ppstm));
    olDebugOut((DEB_ITRACE, "In  CExposedStream::Clone(%p)\n", ppstm));

    olChk(ValidateOutPtrBuffer(ppstm));
    *ppstm = NULL;
    olChk(Validate());
    olChk(CheckReverted());
    olMemTo(EH_pst, pst = new CExposedStream);
    olChkTo(EH_pst, pst->Init(_pst, _pdfParent, _df, &_dfn, _ulPos));
    _pst->AddRef();
    *ppstm = pst;

    olDebugOut((DEB_ITRACE, "Out CExposedStream::Clone => %p\n", *ppstm));
    return ResultFromScode(sc);

EH_pst:
    delete pst;
EH_Err:
    olLog(("%p::Out CExposedStream::Clone(). *ppstm == %p, ret == %lx\n",
           this, SAFE_DREFppstm, sc));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::AddRef, public
//
//  Synopsis:   Increments the ref count
//
//  Returns:    Appropriate status code
//
//---------------------------------------------------------------

STDMETHODIMP_(ULONG) CExposedStream::AddRef(void)
{
    ULONG ulRet;

    olLog(("%p::In  CExposedStream::AddRef()\n", this));
    olDebugOut((DEB_ITRACE, "In  CExposedStream::AddRef()\n"));

    if (FAILED(Validate()))
        return 0;
    AtomicInc(&_cReferences);
    ulRet = _cReferences;

    olDebugOut((DEB_ITRACE, "Out CExposedStream::AddRef\n"));
    olLog(("%p::Out CExposedStream::AddRef().  ret == %lu\n", this, ulRet));
    return ulRet;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::LockRegion, public
//
//  Synopsis:   Nonfunctional
//
//  Returns:    Appropriate status code
//
//---------------------------------------------------------------

STDMETHODIMP CExposedStream::LockRegion(ULARGE_INTEGER UNUSED_PARM(libOffset),
                                        ULARGE_INTEGER UNUSED_PARM(cb),
                                        DWORD UNUSED_PARM(dwLockType))
{
    olDebugOut((DEB_ITRACE, "In  CExposedStream::LockRegion("
                "%lu, %lu\n", ULIGetLow(cb), dwLockType));
    olDebugOut((DEB_ITRACE, "Out CExposedStream::LockRegion\n"));
    olLog(("%p::INVALID CALL TO CExposedStream::LockRegion()\n"));
    UNREFERENCED_PARM(libOffset);
    UNREFERENCED_PARM(cb);
    UNREFERENCED_PARM(dwLockType);
    olAssert(FALSE && aMsg("function not implemented!"));
    return ResultFromScode(STG_E_INVALIDFUNCTION);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::UnlockRegion, public
//
//  Synopsis:   Nonfunctional
//
//  Returns:    Appropriate status code
//
//---------------------------------------------------------------

STDMETHODIMP CExposedStream::UnlockRegion(ULARGE_INTEGER UNUSED_PARM(libOffset),
                                          ULARGE_INTEGER UNUSED_PARM(cb),
                                          DWORD UNUSED_PARM(dwLockType))
{
    olDebugOut((DEB_ITRACE, "In  CExposedStream::UnlockRegion(%lu, %lu)\n",
                ULIGetLow(cb), dwLockType));
    olDebugOut((DEB_ITRACE, "Out CExposedStream::UnlockRegion\n"));
    olLog(("%p::INVALID CALL TO CExposedStream::UnlockRegion()\n"));
    UNREFERENCED_PARM(libOffset);
    UNREFERENCED_PARM(cb);
    UNREFERENCED_PARM(dwLockType);
    olAssert(FALSE && aMsg("function not implemented!"));
    return ResultFromScode(STG_E_INVALIDFUNCTION);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::Commit, public
//
//  Returns:    Appropriate status code
//
//---------------------------------------------------------------


STDMETHODIMP CExposedStream::Commit(DWORD grfCommitFlags)
{
    SCODE sc;
    olDebugOut((DEB_ITRACE, "In  CExposedStream::Commit(%lu)\n",
                grfCommitFlags));
    olLog(("%p::In  CExposedStream::Commit(%lx)\n", this, grfCommitFlags));

    olChk(Validate());
    olChk(CheckReverted());

    if (_fDirty)
    {   //  We're a stream so we must have a parent
        //  We dirty all parents up to the next
        //  transacted storage
        _pdfParent->SetDirty();
        sc = _pdfParent->GetBaseMS()
            ->Flush(FLUSH_CACHE(grfCommitFlags));
    }

    olDebugOut((DEB_ITRACE, "Out CExposedStream::Commit\n"));
EH_Err:
    olLog(("%p::Out CExposedStream::Commit().  ret == %lx", this, sc));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::Revert, public
//
//  Synopsis:   No-op in current implementation
//
//  Returns:    Appropriate status code
//
//---------------------------------------------------------------


STDMETHODIMP CExposedStream::Revert(void)
{
    olDebugOut((DEB_ITRACE, "In  CExposedStream::Revert()\n"));
    olDebugOut((DEB_ITRACE, "Out CExposedStream::Revert\n"));

    olLog(("%p::In  CExposedStream::Revert()\n", this));
    olLog(("%p::Out CExposedStream::Revert().  ret == %lx", this, S_OK));

    return ResultFromScode(STG_E_UNIMPLEMENTEDFUNCTION);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedStream::QueryInterface, public
//
//  Synopsis:   Returns an object for the requested interface
//
//  Arguments:  [iid] - Interface ID
//              [ppvObj] - Object return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppvObj]
//
//---------------------------------------------------------------


STDMETHODIMP CExposedStream::QueryInterface(REFIID iid, void **ppvObj)
{
    SCODE sc;

    olLog(("%p::In  CExposedStream::QueryInterface(?, %p)\n",
           this, ppvObj));
    olDebugOut((DEB_ITRACE, "In  CExposedStream::QueryInterface(?, %p)\n",
                ppvObj));
    olChk(ValidateOutPtrBuffer(ppvObj));
    *ppvObj = NULL;
    olChk(ValidateIid(iid));
    olChk(Validate());
    olChk(CheckReverted());
    if (IsEqualIID(iid, IID_IStream) || IsEqualIID(iid, IID_IUnknown))
    {
        olChk(AddRef());
        *ppvObj = this;
    }
    else
        olErr(EH_Err, E_NOINTERFACE);
    sc = S_OK;

    olDebugOut((DEB_ITRACE, "Out CExposedStream::QueryInterface => %p\n",
                ppvObj));
EH_Err:
    olLog(("%p::Out CExposedStream::QueryInterface().  *ppvObj == %p, ret == %lx\n",
           this, *ppvObj, sc));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member: CExposedStream::RevertFromAbove, public (virtual)
//
//  Synopsis: Parent has asked for reversion
//
//---------------------------------------------------------------

void CExposedStream::RevertFromAbove(void)
{
    msfDebugOut((DEB_ITRACE,
                 "In CExposedStream::RevertFromAbove:%p()\n", this));
    _df |= DF_REVERTED;
    _pst->Release();
#if DBG == 1
    _pst = NULL;
#endif
    msfDebugOut((DEB_ITRACE, "Out CExposedStream::RevertFromAbove\n"));
}

#ifdef NEWPROPS
//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::Open
//
//  Synopsis:   Opens mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:      Gets the size of the underlying stream and reads it
//              into memory so that it can be "mapped."
//
//--------------------------------------------------------------------

VOID CExposedStream::Open(IN VOID *powner, OUT LONG *phr)
{
    LONG& sc = *phr;
    sc = S_OK;

    // If given a pointer to the owner of this mapped stream,
    // save it. This could be NULL. (i.e. when called from ReOpen)
    if( NULL != powner  )
        _powner = (BYTE*) powner;

    if (_pb == NULL)
    {
        VOID *pv;
        _cbUsed = 0;
        olChk(CheckReverted());
        _pst->GetSize(&_cbOriginalStreamSize);

        if (_cbOriginalStreamSize > CBMAXPROPSETSTREAM)
            olErr(EH_Err, STG_E_INVALIDHEADER);

        _cbUsed = _cbOriginalStreamSize;
        olMemTo(EH_Err, pv = new BYTE[_cbOriginalStreamSize]);
        _pb = (BYTE*) pv;
        olChkTo(EH_Read,
                    _pst->ReadAt(0, pv, _cbOriginalStreamSize, &_cbUsed));
        olAssert(_cbOriginalStreamSize == _cbUsed &&
                 "CExposedStream did not read in all the info!");

        // Notify our owner that we have new data
        if (*phr == S_OK && _powner != NULL && 0 != _cbUsed)
        {
            *phr = RtlOnMappedStreamEvent((VOID*)_powner, pv, _cbUsed );
        }

    }
    olDebugOut((DEB_PROP_MAP, "CExposedStream(%X):Open returns normally\n", this));
    return;

// Error handling
EH_Read:
    delete[] _pb;
    _pb = NULL;
    _cbUsed = 0;

EH_Err:
    olDebugOut((DEB_PROP_MAP,
                "CExposedStream(%X):Open exception returns %08X\n",
                this, *phr));
    return;
}

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::Close
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:      Does nothing because the object may be mapped in
//              another process.
//
//--------------------------------------------------------------------

VOID CExposedStream::Close(OUT LONG *phr)
{
    // Write the changes.  We don't need to Commit them,
    // they will be implicitely committed when the
    // Stream is Released.

    *phr = Write();

    if( FAILED(*phr) )
    {
        olDebugOut( (DEB_PROP_MAP,
                     "CPubStream(%08X)::Close exception returns %08X\n",
                     this, *phr));
    }

    return;
}

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::ReOpen
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:      Combined open and map.
//
//--------------------------------------------------------------------

VOID CExposedStream::ReOpen(IN OUT VOID **ppv, OUT LONG *phr)
{
    *ppv = NULL;
    Open( (void*)NULL,          // unspecified owner
          phr);
    if ( SUCCEEDED(*phr) )
        *ppv = _pb;
}

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::Quiesce
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:      Meaningless for docfile mapped stream.
//
//--------------------------------------------------------------------

VOID CExposedStream::Quiesce(VOID)
{
    olAssert(_pb != NULL);
    DfpdbgCheckUnusedMemory();
}

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::Map
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:      Return the address of the "mapping" buffer.
//
//--------------------------------------------------------------------

VOID CExposedStream::Map(BOOLEAN fCreate, VOID **ppv)
{
    UNREFERENCED_PARM(fCreate);
    olAssert(_pb != NULL);
    DfpdbgCheckUnusedMemory();
    *ppv = _pb;
}

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::Unmap
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:      Unmapping is merely zeroing the pointer.  We don't
//              flush because that's done explicitly by the
//              CPropertyStorage class.
//
//
//--------------------------------------------------------------------

VOID CExposedStream::Unmap(BOOLEAN fFlush, VOID **pv)
{
    UNREFERENCED_PARM(fFlush);
    DfpdbgCheckUnusedMemory();
    *pv = NULL;
}

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::Flush
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//              Flush the memory property set to disk and commit it.
//
//--------------------------------------------------------------------

VOID CExposedStream::Flush(OUT LONG *phr)
{
    *phr = S_OK;
    // write out any data we have cached to the stream
    if (S_OK == (*phr = Write()))
    {
        // commit the stream
        (*phr) = Commit(STGC_DEFAULT);
    }

    return;
}

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::GetSize
//
//  Synopsis:   Returns size of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:
//--------------------------------------------------------------------

ULONG CExposedStream::GetSize(OUT LONG *phr)
{
    *phr = S_OK;
    if (_pb == NULL)
        Open((void*)NULL,       // unspecified owner
             phr);

    if( SUCCEEDED(*phr) )
    {
        olAssert(_pb != NULL);
        DfpdbgCheckUnusedMemory();
    }

    return _cbUsed;
}

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::SetSize
//
//  Synopsis:   Sets size of "map." Called by
//              NtCreatePropertySet et al.
//
//  Arguments:  [cb] -- requested size.
//    [fPersistent] -- FALSE if expanding in-memory read-only image
//              [ppv] -- new mapped address.
//
//  Signals:    Not enough disk space.
//
//
//--------------------------------------------------------------------

VOID  CExposedStream::SetSize(ULONG cb, IN BOOLEAN fPersistent,
                              VOID **ppv, OUT LONG *phr)
{
    *phr = S_OK;
    LONG& sc=*phr;
    BYTE *pbNew;

    olAssert(cb != 0);
    DfpdbgCheckUnusedMemory();
    olChk(CheckReverted());

    //
    // if we are growing the data, we should grow the stream
    //
    if (fPersistent && cb > _cbUsed)
    {
        olChk(_pst->SetSize(cb));
    }

    olMem(pbNew = new BYTE[cb]);

    memcpy(pbNew, _pb, (cb < _cbUsed) ? cb : _cbUsed); // smaller of the 2
    delete[] _pb;

    _pb = pbNew;
    _cbUsed = cb;
    *ppv = _pb;

    DfpdbgFillUnusedMemory();

EH_Err:
    olDebugOut((DEB_PROP_MAP, "CPubStream(%08X):SetSize %s returns hr=%08X\n",
        this, *phr != S_OK ? "exception" : "", *phr));
    return;
}


//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::QueryTimeStamps
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:
//
//--------------------------------------------------------------------

VOID CExposedStream::QueryTimeStamps(STATPROPSETSTG *pspss,
                                     BOOLEAN fNonSimple) const
{
    UNREFERENCED_PARM(fNonSimple);
    UNREFERENCED_PARM(pspss);
}

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::QueryModifyTime
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:
//
//--------------------------------------------------------------------

BOOLEAN CExposedStream::QueryModifyTime(OUT LONGLONG *pll) const
{
    UNREFERENCED_PARM(pll);
    return (FALSE);
}

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::QuerySecurity
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:
//
//--------------------------------------------------------------------

BOOLEAN CExposedStream::QuerySecurity(OUT ULONG *pul) const
{
    UNREFERENCED_PARM(pul);
    return FALSE;
}

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::IsWriteable
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:
//
//--------------------------------------------------------------------

BOOLEAN CExposedStream::IsWriteable() const
{
    return TRUE;
}

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::SetChangePending
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:
//
//--------------------------------------------------------------------

#if DBGPROP
BOOLEAN CExposedStream::SetChangePending(BOOLEAN f)
{
    BOOL fOld = _fChangePending;
    _fChangePending = f;
    return(_fChangePending);
}
#endif

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::IsNtMappedStream
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:
//
//--------------------------------------------------------------------

#if DBGPROP
BOOLEAN CExposedStream::IsNtMappedStream(VOID) const
{
    return FALSE;
}
#endif

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::GetHandle
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:
//
//--------------------------------------------------------------------

HANDLE CExposedStream::GetHandle(VOID) const
{
    return INVALID_HANDLE_VALUE;
}

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::SetModified
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:
//
//--------------------------------------------------------------------

VOID CExposedStream::SetModified(VOID)
{
    _fDirty = TRUE;
}

//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::IsModified
//
//  Synopsis:   Operates on mapped view of exposed stream. Called by
//              NtCreatePropertySet et al.
//
//  Notes:
//
//--------------------------------------------------------------------

BOOLEAN CExposedStream::IsModified(VOID) const
{
    return _fDirty;
}

#if DBG
//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::DfpdbgFillUnusedMemory
//
//--------------------------------------------------------------------

VOID CExposedStream::DfpdbgFillUnusedMemory(VOID)
{
    if (_pb == NULL)
        return;

    BYTE * pbEndPlusOne = _pb + BytesCommitted();

    for (BYTE *pbUnused = _pb + _cbUsed;
         pbUnused < pbEndPlusOne;
         pbUnused++)
    {
        *pbUnused = (BYTE)(DWORD)pbUnused;
    }
}


//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::DfpdbgCheckUnusedMemory
//
//--------------------------------------------------------------------

VOID CExposedStream::DfpdbgCheckUnusedMemory(VOID)
{

    if (_pb == NULL)
        return;

    if (_cbUsed == 0)
        return;

    BYTE * pbEndPlusOne = _pb + BytesCommitted();

    for (BYTE *pbUnused =  _pb + _cbUsed;
         pbUnused < pbEndPlusOne;
         pbUnused ++)
    {
        olAssert(*pbUnused == (BYTE)(DWORD)pbUnused);
    }
}

#endif    // DBG


//+-------------------------------------------------------------------
//
//  Member:     CExposedStream::Write, private
//
//  Synopsis:   Writes a mapped view of an exposed Stream to the
//              underlying Stream.  Used by RtlCreatePropertySet et al.
//
//  Notes:      The Stream is not commited.  To commit the Stream, in
//              addition to writing it, the Flush method should be used.
//              The Commit is omitted so that it can be skipped in
//              the Property Set Close path, thus eliminating a
//              performance penalty.
//
//--------------------------------------------------------------------

HRESULT CExposedStream::Write(VOID)
{
    HRESULT hr;
    ULONG cbWritten;

    if (!_fDirty ||!_pb)
    {
        olDebugOut((DEB_PROP_MAP,
                 "CExposedStream(%08X):Flush returns with not-dirty\n", this));

        // flushing a stream which isn't a property stream
        // this could be optimized by propagating a 'no property streams'
        // flag up the storage hierachy such that FlushBufferedData is
        // not even called for non-property streams.
        return S_OK;
    }

    hr=CheckReverted();
    if (S_OK!=hr) goto Exit;
    olAssert( _pst != NULL );
    olAssert( _pb != NULL );
    olAssert( _powner != NULL );

    // notify our owner that we are about to perform a write
    hr = RtlOnMappedStreamEvent( (void*)_powner, (void*) _pb, _cbUsed );
    if ( S_OK != hr ) goto Exit;

    hr = _pst->WriteAt(0, _pb, _cbUsed, &cbWritten);

    if( S_OK != hr ) goto Exit;
    // notify our owner that we are done with the write
    hr = RtlOnMappedStreamEvent( (VOID*)_powner, (VOID *) _pb, _cbUsed );
    if( S_OK != hr ) goto Exit;

    if (_cbUsed < _cbOriginalStreamSize)
    {
        // if the stream is shrinking, this is a good time to do it.
        hr = _pst->SetSize(_cbUsed);
        if (S_OK!=hr) goto Exit;
    }

Exit:
    if (hr == S_OK || hr == STG_E_REVERTED)
    {
        _fDirty = FALSE;
    }

    olDebugOut((DEB_PROP_MAP, "CPubStream(%08X):Flush %s returns hr=%08X\n",
        this, hr != S_OK ? "exception" : "", hr));

    return hr;
}

//+--------------------------------------------------------------
//
//  Member:         CExposedStream::FlushBufferedData, public
//
//  Synopsis:   Flush out the property buffers.
//
//---------------------------------------------------------------

SCODE CExposedStream::FlushBufferedData()
{
    SCODE sc = S_OK;

    olDebugOut((DEB_ITRACE, "In  CExposedStream::FlushBufferedData:%p()\n",
                this));

    Flush(&sc);

    olDebugOut((DEB_PROP_MAP,
                "CExposedStream(%08X):FlushBufferedData returns %08X\n",
                this, sc));

    return sc;
}

#endif // ifdef NEWPROPS





