//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1998.
//
//  File:       mmstrm.hxx
//
//  Contents:   Memory Mapped Stream
//
//  Classes:    CMmStream, CMmStreamBuf
//
//  History:    10-Mar-93 BartoszM  Created
//
//----------------------------------------------------------------------------

#pragma once

#include <pmmstrm.hxx>
#include <driveinf.hxx>

//+---------------------------------------------------------------------------
//
//  Class:      CMmStream
//
//  Purpose:    Memory Mapped Stream
//
//  History:    10-Mar-93       BartoszM               Created
//              18-Mar-98       KitmanH                Added a protected member
//                                                     _fIsReadOnly
//              26-Oct-98       KLam                   Added _cMegToLeaveOnDisk
//                                                     Added CDriveInfo smart pointer
//
//----------------------------------------------------------------------------

class CMmStream: public PMmStream
{
    friend class CMmStreamBuf;
    friend class CDupStream;

public:

    CMmStream( ULONG cbDiskSpaceToLeave = CI_MIN_DISK_SPACE_TO_LEAVE_DEFAULT,
               BOOL fIsReadOnly = FALSE );
     
    BOOL Ok() { return _hFile != INVALID_HANDLE_VALUE; }

    virtual ~CMmStream();

    void OpenExclusive ( WCHAR* wcsPath, BOOL fReadOnly);

    void Open ( const WCHAR* wcsPath,
                ULONG modeAccess,
                ULONG modeShare,
                ULONG modeCreate,
                ULONG modeAttribute = FILE_ATTRIBUTE_NORMAL,
                BOOL fSparse = FALSE );

    void Close();

    void   SetSize ( PStorage& storage,
                     ULONG newSizeLow,
                     ULONG newSizeHigh=0 );

    BOOL   isEmpty() { return _sizeHigh == 0 && _sizeLow == 0; }

    ULONG  SizeLow() { return _sizeLow; }

    ULONG  SizeHigh() { return _sizeHigh; }

    LONGLONG Size()
    {
        return llfromuls( _sizeLow, _sizeHigh );
    }

    BOOL   isWritable() { return _fWrite; }

    void   MapAll ( CMmStreamBuf& sbuf );

    void   Map ( CMmStreamBuf& sbuf,
                         ULONG cb = 0,
                         ULONG offLow = 0,
                         ULONG offHigh = 0,
                         BOOL  fMapForWrite = FALSE );

    void   Unmap ( CMmStreamBuf& sbuf );

    void   Flush ( CMmStreamBuf& sbuf, ULONG cb, BOOL fThrowOnFailure = TRUE );

    void   FlushMetaData( BOOL fThrowOnFailure = TRUE );

    BOOL   FStatusFileNotFound()
    {
        return _status == STATUS_OBJECT_NAME_NOT_FOUND
               || _status == STATUS_OBJECT_PATH_NOT_FOUND;
    }

    NTSTATUS GetStatus() const { return _status; }

    ULONG ShrinkFromFront( ULONG firstPage, ULONG numPages );

#if CIDBG == 1
    WCHAR const * GetPath() { return _xwcPath.Get(); }
#endif

    void InitFIsReadOnly( BOOL fIsReadOnly ) { _fIsReadOnly = fIsReadOnly; }
      
    void Read( void * pvBuffer, ULONGLONG oStart, DWORD cbToRead, DWORD & cbRead );

    void Write( void * pvBuffer, ULONGLONG oStart, DWORD cbToWrite );

protected:

    HANDLE              _hFile;
    HANDLE              _hMap;
    ULONG               _sizeLow;
    ULONG               _sizeHigh;

    BOOL                _fWrite;
    BOOL                _fSparse;
    NTSTATUS            _status;
    unsigned            _cMap;
    BOOL                _fIsReadOnly;
    ULONG               _cMegToLeaveOnDisk;
    XPtr<CDriveInfo>    _xDriveInfo;

#if CIDBG == 1
    XGrowable<WCHAR>    _xwcPath;
#endif // CIDBG == 1
};

//+---------------------------------------------------------------------------
//
//  Class:      CLockingMmStream
//
//  Purpose:    Memory Mapped Stream with reader/writer locking
//
//  History:    13-Nov-97       dlee         Created
//              27-Oct-98       KLam         Added cbDiskSpaceToLeave
//
//----------------------------------------------------------------------------

class CLockingMmStream: public PMmStream
{
public:

    CLockingMmStream( ULONG cbDiskSpaceToLeave ) 
        : _cRef( 0 ), 
          _mmStream( cbDiskSpaceToLeave ) 
    {}

    ~CLockingMmStream()
    {
        Win4Assert( 0 == _cRef );

        CWriteAccess lock( _rwLock );
    }

    void Open ( const WCHAR* wcsPath,
                ULONG modeAccess,
                ULONG modeShare,
                ULONG modeCreate,
                ULONG modeAttribute = FILE_ATTRIBUTE_NORMAL,
                BOOL fSparse = FALSE )
    {
        CWriteAccess lock( _rwLock );
        _mmStream.Open( wcsPath, modeAccess, modeShare, modeCreate,
                        modeAttribute, fSparse );
    }

    BOOL Ok() { return _mmStream.Ok(); }

    void   Close()
    {
        CWriteAccess lock( _rwLock );
        Win4Assert( 0 == _cRef );
        _mmStream.Close();
    }

    void   SetSize ( PStorage & storage,
                     ULONG      newSizeLow,
                     ULONG      newSizeHigh=0 )
    {
        CWriteAccess lock( _rwLock );
        _mmStream.SetSize( storage, newSizeLow, newSizeHigh );
    }

    BOOL   isEmpty()
    {
        CReadAccess lock( _rwLock );
        return _mmStream.isEmpty();
    }

    ULONG  SizeLow()
    {
        CReadAccess lock( _rwLock );
        return _mmStream.SizeLow();
    }

    ULONG  SizeHigh()
    {
        CReadAccess lock( _rwLock );
        return _mmStream.SizeHigh();
    }

    LONGLONG Size()
    {
        return _mmStream.Size();
    }

    BOOL   isWritable()
    {
        CReadAccess lock( _rwLock );
        return _mmStream.isWritable();
    }

    void   MapAll ( CMmStreamBuf& sbuf )
    {
        CWriteAccess lock( _rwLock );
        _mmStream.MapAll( sbuf );
    }

    void   Map ( CMmStreamBuf& sbuf,
                 ULONG cb = 0,
                 ULONG offLow = 0,
                 ULONG offHigh = 0,
                 BOOL  fMapForWrite = FALSE )
    {
        CWriteAccess lock( _rwLock );
        _mmStream.Map( sbuf, cb, offLow, offHigh, fMapForWrite );
    }

    void   Unmap ( CMmStreamBuf& sbuf )
    {
        CReadAccess lock( _rwLock );
        _mmStream.Unmap( sbuf );
    }

    void   Flush ( CMmStreamBuf& sbuf, ULONG cb, BOOL fThrowOnFailure = TRUE )
    {
        CReadAccess lock( _rwLock );
        _mmStream.Flush( sbuf, cb, fThrowOnFailure );
    }

    void   FlushMetaData( BOOL fThrowOnFailure = TRUE )
    {
        CReadAccess lock( _rwLock );
        _mmStream.FlushMetaData( fThrowOnFailure );
    }

    ULONG  ShrinkFromFront( ULONG firstPage, ULONG numPages )
    {
        CWriteAccess lock( _rwLock );
        return _mmStream.ShrinkFromFront( firstPage, numPages );
    }

#if CIDBG == 1
    WCHAR const * GetPath() { return _mmStream.GetPath(); }
#endif

    void RefCount( CDupStream * p ) { _cRef++; }
    void UnRefCount( CDupStream * p ) { _cRef--; }

    void Read( void * pvBuffer, ULONGLONG oStart, DWORD cbToRead, DWORD & cbRead )
    {
        CWriteAccess lock( _rwLock );

        _mmStream.Read( pvBuffer, oStart, cbToRead, cbRead );
    }

    void Write( void * pvBuffer, ULONGLONG oStart, DWORD cbToWrite )
    {
        CWriteAccess lock( _rwLock );

        _mmStream.Write( pvBuffer, oStart, cbToWrite );
    }

private:

    unsigned         _cRef;
    CReadWriteAccess _rwLock;
    CMmStream        _mmStream;
};

//+---------------------------------------------------------------------------
//
//  Class:      CDupMmStream
//
//  Purpose:    Duplicates an existing stream, so multiple storages can
//              share 1 stream.
//
//  History:    13-Nov-97       dlee         Created
//
//----------------------------------------------------------------------------

class CDupStream: public PMmStream
{
public:

    CDupStream( CLockingMmStream & mmStream ) : _mmStream(mmStream)
    {
        _mmStream.RefCount( this );
    }

    ~CDupStream()
    {
        _mmStream.UnRefCount( this );
    }

    BOOL Ok() { return _mmStream.Ok(); }

    void   Close()
    {
        Win4Assert( !"Must Not Be Called" );
    }

    void   SetSize ( PStorage& storage,
                     ULONG newSizeLow,
                     ULONG newSizeHigh=0 )
    {
        _mmStream.SetSize( storage, newSizeLow, newSizeHigh );
    }

    BOOL   isEmpty()
    {
        return _mmStream.isEmpty();
    }

    ULONG  SizeLow()
    {
        return _mmStream.SizeLow();
    }

    ULONG  SizeHigh()
    {
        return _mmStream.SizeHigh();
    }

    LONGLONG Size()
    {
        return  llfromuls( _mmStream.SizeLow(), _mmStream.SizeHigh() );
    }

    BOOL   isWritable()
    {
        return _mmStream.isWritable();
    }

    void   MapAll ( CMmStreamBuf& sbuf )
    {
        _mmStream.MapAll( sbuf );
    }

    void   Map ( CMmStreamBuf& sbuf,
                 ULONG cb = 0,
                 ULONG offLow = 0,
                 ULONG offHigh = 0,
                 BOOL  fMapForWrite = FALSE )
    {
        _mmStream.Map( sbuf, cb, offLow, offHigh, fMapForWrite );
    }

    void   Unmap ( CMmStreamBuf& sbuf )
    {
        _mmStream.Unmap( sbuf );
    }

    void   Flush ( CMmStreamBuf& sbuf, ULONG cb, BOOL fThrowOnFailure = TRUE )
    {
        _mmStream.Flush( sbuf, cb, fThrowOnFailure );
    }

    void   FlushMetaData ( BOOL fThrowOnFailure = TRUE )
    {
        _mmStream.FlushMetaData( fThrowOnFailure );
    }

    ULONG  ShrinkFromFront( ULONG firstPage, ULONG numPages )
    {
        Win4Assert( "!Must not be called" );
        return(0);
    }

    PMmStream * DupStream()
    {
        Win4Assert( !"Must not be called" );
        return(0);
    }

    void Read( void * pvBuffer, ULONGLONG oStart, DWORD cbToRead, DWORD & cbRead )
    {
        _mmStream.Read( pvBuffer, oStart, cbToRead, cbRead );
    }

    void Write( void * pvBuffer, ULONGLONG oStart, DWORD cbToWrite )
    {
        _mmStream.Write( pvBuffer, oStart, cbToWrite );
    }

#if CIDBG == 1
    WCHAR const * GetPath() { return _mmStream.GetPath(); }
#endif

private:

    CLockingMmStream &  _mmStream;
};