You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1280 lines
32 KiB
1280 lines
32 KiB
#ifndef _ASTREAM_H_
|
|
#define _ASTREAM_H_
|
|
|
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
//
|
|
// ASTREAM.H
|
|
//
|
|
// Async streams header.
|
|
//
|
|
#include <ex\refcnt.h>
|
|
#include <ex\refhandle.h>
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS IAsyncReadObserver
|
|
//
|
|
// Passed to IAsyncStream::AsyncRead() and called when the asynchronous
|
|
// operation completes.
|
|
//
|
|
class IAsyncReadObserver
|
|
{
|
|
// NOT IMPLEMENTED
|
|
//
|
|
IAsyncReadObserver& operator=( const IAsyncReadObserver& );
|
|
|
|
public:
|
|
// CREATORS
|
|
//
|
|
virtual ~IAsyncReadObserver() = 0 {}
|
|
|
|
// MANIPULATORS
|
|
//
|
|
virtual VOID ReadComplete( UINT cbRead, HRESULT hr ) = 0;
|
|
};
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS IAsyncWriteObserver
|
|
//
|
|
// Passed to IAsyncStream::AsyncWrite() and called when the asynchronous
|
|
// operation completes.
|
|
//
|
|
class IAsyncWriteObserver : public IRefCounted
|
|
{
|
|
// NOT IMPLEMENTED
|
|
//
|
|
IAsyncWriteObserver& operator=( const IAsyncWriteObserver& );
|
|
|
|
public:
|
|
// CREATORS
|
|
//
|
|
virtual ~IAsyncWriteObserver() = 0 {}
|
|
|
|
// MANIPULATORS
|
|
//
|
|
virtual VOID WriteComplete( UINT cbWritten, HRESULT hr ) = 0;
|
|
};
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS IAsyncFlushObserver
|
|
//
|
|
// Passed to IAsyncStream::AsyncFlush() and called when the asynchronous
|
|
// operation completes.
|
|
//
|
|
class IAsyncFlushObserver : public IRefCounted
|
|
{
|
|
// NOT IMPLEMENTED
|
|
//
|
|
IAsyncFlushObserver& operator=( const IAsyncFlushObserver& );
|
|
|
|
public:
|
|
// CREATORS
|
|
//
|
|
virtual ~IAsyncFlushObserver() = 0 {}
|
|
|
|
// MANIPULATORS
|
|
//
|
|
virtual VOID FlushComplete( HRESULT hr ) = 0;
|
|
};
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS IAsyncCopyToObserver
|
|
//
|
|
// Passed to IAsyncStream::AsyncCopyTo() and called when the asynchronous
|
|
// operation completes.
|
|
//
|
|
class IAsyncCopyToObserver
|
|
{
|
|
// NOT IMPLEMENTED
|
|
//
|
|
IAsyncCopyToObserver& operator=( const IAsyncCopyToObserver& );
|
|
|
|
public:
|
|
// CREATORS
|
|
//
|
|
virtual ~IAsyncCopyToObserver() = 0 {}
|
|
|
|
// MANIPULATORS
|
|
//
|
|
virtual VOID CopyToComplete( UINT cbCopied, HRESULT hr ) = 0;
|
|
};
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS IDavStream
|
|
//
|
|
// Interface for sync streams.
|
|
//
|
|
class IDavStream
|
|
{
|
|
// NOT IMPLEMENTED
|
|
//
|
|
IDavStream& operator=( const IDavStream& );
|
|
|
|
protected:
|
|
// CREATORS
|
|
//
|
|
// Only create this object through it's descendents!
|
|
//
|
|
IDavStream() {};
|
|
|
|
public:
|
|
// DESTRUCTORS
|
|
//
|
|
// Out of line virtual destructor necessary for proper
|
|
// deletion of objects of derived classes via this class
|
|
//
|
|
virtual ~IDavStream() = 0
|
|
{
|
|
}
|
|
|
|
// ACCESSORS
|
|
//
|
|
|
|
// DwLeft() is the function that must be implemented by the descendants of this
|
|
// interface if they are to be passed to
|
|
//
|
|
// IHybridStream::ScCopyFrom(const IDavStream * stmSrc,
|
|
// IAsyncWriteObserver * pobsAsyncWrite)
|
|
//
|
|
// In that case it must return the number of bytes left in the stream to drain.
|
|
// That size can be an estimated one, not necessarily exact. That has to be
|
|
// implemented, that way, as the stream size that we are getting from the store
|
|
// when we open the stream on the property may be not correct (from the
|
|
// experience in that I can tell that still in most cases that data is
|
|
// correct, except for PR_BODY). Also for example in conversion streams we will
|
|
// not know the stream size in advance (we have not read all data yet and do not
|
|
// know in what converted size it will result in). Descendants that are not intended to
|
|
// be passed to the call above may choose to implement DwLeft() to trap or return 0.
|
|
// Of course in that case they should not rely on information comming back from
|
|
// that call. Also as soon as it is finally determined that we have reached the
|
|
// end of the stream (i.e. we read a piece of data, and we read less than we asked for),
|
|
// the function should always return 0.
|
|
//
|
|
virtual DWORD DwLeft() const = 0;
|
|
|
|
// FEnd() is the function that must return TRUE in the case the whole stream has been
|
|
// already drained/consumed. FALSE must be returned in all other cases. Child classes
|
|
// may implement it to return FALSE always if they are always sure that ScCopyFrom
|
|
// operations from this stream will always be given amount of bytes to copy that is
|
|
// equal or less than actual amount of bytes still remaining in the stream. Of course
|
|
// if they choose always to return FLASE they should not use the function to determine
|
|
// if they reached the end of the stream.
|
|
//
|
|
virtual BOOL FEnd() const
|
|
{
|
|
TrapSz("IDavStream::FEnd() not implemented");
|
|
return FALSE;
|
|
}
|
|
|
|
// ScRead() reading from the stream
|
|
//
|
|
virtual SCODE ScRead( BYTE * pbBuf,
|
|
UINT cbToRead,
|
|
UINT * pcbRead ) const
|
|
{
|
|
TrapSz("IDavStream::ScRead() not implemented");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
// MANIPULATORS
|
|
//
|
|
virtual SCODE ScWrite( const BYTE * pbBuf,
|
|
UINT cbToWrite,
|
|
UINT * pcbWritten )
|
|
{
|
|
TrapSz("IDavStream::ScWrite() not implemented");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
virtual SCODE ScCopyTo( IDavStream& stmDst,
|
|
UINT cbToCopy,
|
|
UINT * pcbCopied )
|
|
{
|
|
TrapSz("IDavStream::ScCopyTo() not implemented");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
virtual SCODE ScFlush()
|
|
{
|
|
return S_OK;
|
|
}
|
|
};
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS IAsyncStream
|
|
//
|
|
// Interface for async streams.
|
|
//
|
|
// AsyncRead() -
|
|
// Asynchronously reads bytes from a stream and notifies an
|
|
// observer when the I/O completes. IAsyncStream provides
|
|
// a default implementation that notifies the observer with
|
|
// 0 bytes read and an HRESULT of E_NOTIMPL.
|
|
//
|
|
// AsyncWrite()
|
|
// Asynchronously writes bytes to a stream and notifies an
|
|
// observer when the I/O completes. IAsyncStream provides
|
|
// a default implementation that notifies the observer with
|
|
// 0 bytes written and an HRESULT of E_NOTIMPL.
|
|
//
|
|
// AsyncCopyTo()
|
|
// Asynchronously copies bytes from this stream to another
|
|
// IAsyncStream and notifies an observer when the I/O completes.
|
|
// IAsyncStream provides a default implementation that notifies
|
|
// the observer with 0 bytes copied and an HRESULT of E_NOTIMPL.
|
|
//
|
|
// AsyncFlush()
|
|
// To be used with buffered writable streams. Asynchronously
|
|
// flushes accumulated data written in previous calls to
|
|
// AsyncWrite() and notifies an observer when the I/O completes.
|
|
// IAsyncStream provides a default implementation that notifies
|
|
// the observer with an HRESULT of E_NOTIMPL.
|
|
//
|
|
// !!!IMPORTANT!!!
|
|
// Despite the refcounted base class of IAsyncWriteObserver and IAsyncFlushObserver,
|
|
// it is the CALLER's sole responsbility to guarantee the lifetime of the stream
|
|
// and observers through completion of any async I/O call.
|
|
//
|
|
class IAsyncStream
|
|
{
|
|
// NOT IMPLEMENTED
|
|
//
|
|
IAsyncStream& operator=( const IAsyncStream& );
|
|
|
|
public:
|
|
// DESTRUCTORS
|
|
//
|
|
virtual ~IAsyncStream() = 0
|
|
{
|
|
}
|
|
|
|
// ACCESSORS
|
|
//
|
|
virtual UINT CbReady() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// MANIPULATORS
|
|
//
|
|
virtual VOID AsyncRead( BYTE * pbBuf,
|
|
UINT cbToRead,
|
|
IAsyncReadObserver& obsAsyncRead )
|
|
{
|
|
obsAsyncRead.ReadComplete( 0, E_NOTIMPL );
|
|
}
|
|
|
|
virtual VOID AsyncWrite( const BYTE * pbBuf,
|
|
UINT cbToWrite,
|
|
IAsyncWriteObserver& obsAsyncWrite )
|
|
{
|
|
obsAsyncWrite.WriteComplete( 0, E_NOTIMPL );
|
|
}
|
|
|
|
virtual VOID AsyncCopyTo( IAsyncStream& stmDst,
|
|
UINT cbToCopy,
|
|
IAsyncCopyToObserver& obsAsyncCopyTo )
|
|
{
|
|
obsAsyncCopyTo.CopyToComplete( 0, E_NOTIMPL );
|
|
}
|
|
|
|
virtual VOID AsyncFlush( IAsyncFlushObserver& obsAsyncFlush )
|
|
{
|
|
obsAsyncFlush.FlushComplete( E_NOTIMPL );
|
|
}
|
|
};
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS IHybridStream
|
|
//
|
|
// Interface for a hybrid sync/async stream. The main difference
|
|
// between this interface and IAsyncStream is that the calls here
|
|
// do not always complete via async observer notification. If a
|
|
// call executes synchronously, it fills in all return values and
|
|
// returns status via an SCODE. If a call executes asynchronously,
|
|
// it immediately returns E_PENDING and calls the completion observer
|
|
// with return values and status when the asynchronous I/O completes.
|
|
//
|
|
// Callers cannot control whether a call executes synchronously or
|
|
// asynchronously.
|
|
//
|
|
// !!!IMPORTANT!!!
|
|
// Despite the refcounted base class of IAsyncWriteObserver and IAsyncFlushObserver,
|
|
// it is the CALLER's sole responsbility to guarantee the lifetime of the stream
|
|
// and observers through completion of any async I/O call.
|
|
//
|
|
class IHybridStream
|
|
{
|
|
// NOT IMPLEMENTED
|
|
//
|
|
IHybridStream& operator=( const IHybridStream& );
|
|
|
|
public:
|
|
// CREATORS
|
|
//
|
|
virtual ~IHybridStream() = 0
|
|
{
|
|
}
|
|
|
|
// ACCESSORS
|
|
//
|
|
virtual UINT CbSize() const
|
|
{
|
|
TrapSz("IHybridStream::CbSize() not implemented");
|
|
return 0;
|
|
}
|
|
|
|
// MANIPULATORS
|
|
//
|
|
virtual SCODE ScRead( BYTE * pbToRead,
|
|
DWORD cbToRead,
|
|
DWORD * pcbRead,
|
|
IAsyncReadObserver * pobsAsyncRead )
|
|
{
|
|
TrapSz("IHybridStream::ScRead() not implemented");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
virtual SCODE ScWrite( const BYTE * pbToWrite,
|
|
DWORD cbToWrite,
|
|
DWORD * pcbWritten,
|
|
IAsyncWriteObserver * pobsAsyncWrite )
|
|
{
|
|
TrapSz("IHybridStream::ScWrite() not implemented");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
virtual SCODE ScCopyFrom( const IDavStream * pdsToCopy,
|
|
const DWORD cbToCopy,
|
|
DWORD * pcbCopied,
|
|
IAsyncWriteObserver * pobsAsyncWrite )
|
|
{
|
|
TrapSz("IHybridStream::ScCopyFrom() not implemented");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
virtual SCODE ScCopyFrom( const IDavStream * pdsToCopy,
|
|
IAsyncWriteObserver * pobsAsyncWrite )
|
|
{
|
|
DWORD cbCopied;
|
|
return ScCopyFrom( pdsToCopy,
|
|
pdsToCopy->DwLeft(),
|
|
&cbCopied,
|
|
pobsAsyncWrite);
|
|
}
|
|
|
|
virtual SCODE ScCopyTo( IHybridStream& stmDst,
|
|
DWORD cbToCopy,
|
|
DWORD * pcbCopied,
|
|
IAsyncCopyToObserver * pobsAsyncCopyTo )
|
|
{
|
|
TrapSz("IHybridStream::ScCopyTo() not implemented");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
virtual SCODE ScFlush( IAsyncFlushObserver * pobsAsyncFlush )
|
|
{
|
|
TrapSz("IHybridStream::ScFlush() not implemented");
|
|
return E_NOTIMPL;
|
|
}
|
|
};
|
|
|
|
// ========================================================================
|
|
//
|
|
// TEMPLATE CLASS CBufferedStream
|
|
//
|
|
// Inline buffering stream implementation. See !!! IMPORTANT !!! section
|
|
// below for limitations and other considerations.
|
|
//
|
|
// Template parameters:
|
|
//
|
|
// _RawStream
|
|
// Raw stream type. _RawStream must implement ScReadRaw() for
|
|
// CBufferedStream::ScRead(), if it is to be used, and ScWriteRaw()
|
|
// for CBufferedStream::ScWrite() and CBufferedStream::ScFlush()
|
|
// if they are to be used. The prototypes are:
|
|
//
|
|
// SCODE ScReadRaw( BYTE * pbToRead,
|
|
// DWORD cbToRead,
|
|
// DWORD * pcbRead,
|
|
// IAsyncReadObserver * pobsAsyncRead );
|
|
//
|
|
// SCODE ScWriteRaw( const BYTE * pbToWrite,
|
|
// DWORD cbToWrite,
|
|
// DWORD * pcbWritten,
|
|
// IAsyncWriteObserver * pobsAsyncWrite );
|
|
//
|
|
// These functions read and write from the raw stream. The I/O
|
|
// they implement can be synchronous or asynchronous or both.
|
|
//
|
|
// CB_BUF
|
|
// Size (in bytes) of the buffer to use. The buffer is a direct
|
|
// member of CBufferedStream; no allocation is done.
|
|
//
|
|
//
|
|
// !!! IMPORTANT !!!
|
|
//
|
|
// Reading and writing:
|
|
// There is no restriction on the amount of data that can be read
|
|
// or written at once, but data is buffered CB_BUF bytes at a time.
|
|
// This means that a request to write, for example, 128K of data
|
|
// will incur two buffer flushes when CB_BUF is 64K. The same
|
|
// is true of reads and buffer fills. Buffer flushes/fills are
|
|
// typically the expensive I/O operations, so choose a CB_BUF
|
|
// that works well with the particular I/O (e.g. 64K for file I/O).
|
|
//
|
|
// Flushing:
|
|
// There is an assumption in ScFlush() that the stream being flushed
|
|
// to is not a buffered stream; ScFlush() does not flush the raw stream.
|
|
//
|
|
// Class size:
|
|
// Since the buffer is inline (i.e. not allocated), instances of this class
|
|
// can potentially be large. Whenever such an instance is used as a
|
|
// direct member of another class, it should be the last such member so as
|
|
// to maximize data locality when accessing other members of the class.
|
|
//
|
|
template<class _RawStream, UINT CB_BUF>
|
|
class CBufferedStream :
|
|
private IAsyncReadObserver,
|
|
private IAsyncWriteObserver
|
|
{
|
|
// Amount of the buffer used.
|
|
//
|
|
UINT m_cbBufUsed;
|
|
|
|
// Index of next byte to read from buffer.
|
|
//
|
|
UINT m_ibBufCur;
|
|
|
|
// Per read/write request state. These members are used
|
|
// to keep track of state across various async I/O calls.
|
|
//
|
|
const IDavStream * m_pdsRequest;
|
|
LPBYTE m_pbRequest;
|
|
DWORD m_cbRequest;
|
|
DWORD m_cbRequestDone;
|
|
|
|
// Caller-supplied observers. Used to notify the caller
|
|
// when I/O completes.
|
|
//
|
|
IAsyncReadObserver * m_pobsRead;
|
|
IAsyncWriteObserver * m_pobsWrite;
|
|
IAsyncFlushObserver * m_pobsFlush;
|
|
|
|
// Pointer to the raw stream. Used in buffer filling/flushing.
|
|
//
|
|
_RawStream * m_pstmRaw;
|
|
|
|
// The buffer. The CB_BUF size is a template parameter.
|
|
//
|
|
BYTE m_rgbBuf[CB_BUF];
|
|
|
|
// Internal I/O routines
|
|
//
|
|
inline SCODE ScReadInt();
|
|
inline SCODE ScWriteInt();
|
|
inline SCODE ScCopyFromInt();
|
|
|
|
// Raw stream I/O completion routines
|
|
//
|
|
inline VOID RawReadComplete(UINT cbReadRaw);
|
|
inline VOID RawWriteComplete(UINT cbWrittenRaw);
|
|
|
|
// Buffer filling and flushing utilities
|
|
//
|
|
inline SCODE ScFillBuffer();
|
|
inline SCODE ScFlushBuffer();
|
|
|
|
// NOT IMPLEMENTED
|
|
//
|
|
CBufferedStream( const CBufferedStream& );
|
|
CBufferedStream& operator=( const CBufferedStream& );
|
|
|
|
public:
|
|
// CREATORS
|
|
//
|
|
CBufferedStream() :
|
|
m_cbBufUsed(0),
|
|
m_ibBufCur(0),
|
|
m_pobsRead(NULL),
|
|
m_pobsWrite(NULL),
|
|
m_pobsFlush(NULL)
|
|
{
|
|
}
|
|
|
|
// ACCESSORS
|
|
//
|
|
ULONG CbBufUsed() const
|
|
{
|
|
return m_cbBufUsed;
|
|
}
|
|
|
|
// MANIPULATORS
|
|
//
|
|
inline
|
|
SCODE ScRead( _RawStream& stmRaw,
|
|
BYTE * pbToRead,
|
|
DWORD cbToRead,
|
|
DWORD * pcbRead,
|
|
IAsyncReadObserver * pobsReadExt );
|
|
|
|
inline
|
|
SCODE ScWrite( _RawStream& stmRaw,
|
|
const BYTE * pbToWrite,
|
|
DWORD cbToWrite,
|
|
DWORD * pcbWritten,
|
|
IAsyncWriteObserver * pobsWriteExt );
|
|
|
|
inline
|
|
SCODE ScCopyFrom( _RawStream& stmRaw,
|
|
const IDavStream * pdsToCopy,
|
|
const DWORD cbToCopy,
|
|
DWORD * pcbCopied,
|
|
IAsyncWriteObserver * pobsWriteExt );
|
|
|
|
inline
|
|
SCODE ScFlush( _RawStream& stmRaw,
|
|
IAsyncFlushObserver * pobsFlushExt );
|
|
|
|
// IAsyncReadObserver/IAsyncWriteObserver
|
|
//
|
|
// Note: these functions are not really inlined -- they are declared
|
|
// virtual in the observer interface classes. However we must declare
|
|
// them inline so that the compiler will generate one instance of each
|
|
// function rather than one instance per function per module. This is
|
|
// the member function equivalent of DEC_CONST.
|
|
//
|
|
inline
|
|
VOID ReadComplete( UINT cbReadRaw,
|
|
HRESULT hr );
|
|
|
|
inline
|
|
VOID WriteComplete( UINT cbWrittenRaw,
|
|
HRESULT hr );
|
|
|
|
//$REVIEW
|
|
//
|
|
// IAsyncWriteObserver and IAsyncReadObserver are both refcounted
|
|
// interfaces and don't need to be. Caller assumes all responsibility
|
|
// for keeping stream and observer objects alive through any stream
|
|
// call.
|
|
//
|
|
void AddRef()
|
|
{
|
|
TrapSz("CBufferedStream::AddRef() is not implemented!");
|
|
}
|
|
|
|
void Release()
|
|
{
|
|
TrapSz("CBufferedStream::Release() is not implemented!");
|
|
}
|
|
};
|
|
|
|
template<class _RawStream, UINT CB_BUF>
|
|
SCODE
|
|
CBufferedStream<_RawStream, CB_BUF>::ScRead(
|
|
_RawStream& stmRaw,
|
|
BYTE * pbToRead,
|
|
DWORD cbToRead,
|
|
DWORD * pcbRead,
|
|
IAsyncReadObserver * pobsReadExt )
|
|
{
|
|
// Check parameters
|
|
//
|
|
Assert(cbToRead > 0);
|
|
Assert(!IsBadWritePtr(pbToRead, cbToRead));
|
|
Assert(!IsBadWritePtr(pcbRead, sizeof(UINT)));
|
|
Assert(!pobsReadExt || !IsBadReadPtr(pobsReadExt, sizeof(IAsyncReadObserver)));
|
|
|
|
// We had better not be in any I/O of any sort.
|
|
//
|
|
Assert(!m_pobsRead);
|
|
Assert(!m_pobsWrite);
|
|
Assert(!m_pobsFlush);
|
|
|
|
// Set up state for a new read
|
|
//
|
|
m_pstmRaw = &stmRaw;
|
|
m_pdsRequest = NULL;
|
|
m_pbRequest = pbToRead;
|
|
m_cbRequest = cbToRead;
|
|
m_pobsRead = pobsReadExt;
|
|
m_cbRequestDone = 0;
|
|
|
|
// Issue the read
|
|
//
|
|
SCODE sc = ScReadInt();
|
|
|
|
// If the read didn't pend then clear out the observer
|
|
// and return the amount of data read.
|
|
//
|
|
if (E_PENDING != sc)
|
|
{
|
|
m_pobsRead = NULL;
|
|
*pcbRead = m_cbRequestDone;
|
|
}
|
|
|
|
// Return the result of the I/O, which may be S_OK, E_PENDING
|
|
// or any other error.
|
|
//
|
|
return sc;
|
|
}
|
|
|
|
template<class _RawStream, UINT CB_BUF>
|
|
SCODE
|
|
CBufferedStream<_RawStream, CB_BUF>::ScReadInt()
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
// Loop around alternately filling and reading from the
|
|
// buffer until we finish the request or until a fill pends.
|
|
//
|
|
while ( m_cbRequestDone < m_cbRequest )
|
|
{
|
|
// If we have read everything from the buffer then try
|
|
// to refill the buffer from the raw stream.
|
|
//
|
|
if (m_ibBufCur == m_cbBufUsed)
|
|
{
|
|
sc = ScFillBuffer();
|
|
if (FAILED(sc))
|
|
{
|
|
if (E_PENDING != sc)
|
|
DebugTrace("CBufferedStream::ScReadInt() - ScFillBuffer() failed 0x%08lX\n", sc);
|
|
|
|
break;
|
|
}
|
|
|
|
// If the buffer is still empty then we have
|
|
// exhausted the stream, so we are done.
|
|
//
|
|
if (0 == m_cbBufUsed)
|
|
break;
|
|
}
|
|
|
|
// The buffer should have data available to be read
|
|
// so read it.
|
|
//
|
|
Assert(m_ibBufCur < m_cbBufUsed);
|
|
DWORD cbToRead = min(m_cbBufUsed - m_ibBufCur,
|
|
m_cbRequest - m_cbRequestDone);
|
|
|
|
memcpy(m_pbRequest + m_cbRequestDone,
|
|
&m_rgbBuf[m_ibBufCur],
|
|
cbToRead);
|
|
|
|
m_ibBufCur += cbToRead;
|
|
m_cbRequestDone += cbToRead;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
template<class _RawStream, UINT CB_BUF>
|
|
SCODE
|
|
CBufferedStream<_RawStream, CB_BUF>::ScFillBuffer()
|
|
{
|
|
// We better have a stream to fill from
|
|
//
|
|
Assert(m_pstmRaw);
|
|
|
|
// Assert that we are not in any write/copy/flush I/O
|
|
//
|
|
Assert(!m_pobsWrite);
|
|
Assert(!m_pobsFlush);
|
|
|
|
// We should only try to refill the buffer after all
|
|
// of the data in it has been consumed.
|
|
//
|
|
Assert(m_ibBufCur == m_cbBufUsed);
|
|
|
|
// Reset the buffer back to the beginning.
|
|
//
|
|
m_cbBufUsed = 0;
|
|
m_ibBufCur = 0;
|
|
|
|
// Read data in from the raw stream. If reading pends on I/O
|
|
// then we will resume processing in CBufferedStream::ReadComplete()
|
|
// when the I/O completes.
|
|
//
|
|
DWORD cbRead = 0;
|
|
SCODE sc = m_pstmRaw->ScReadRaw(m_rgbBuf,
|
|
CB_BUF,
|
|
&cbRead,
|
|
this);
|
|
if (SUCCEEDED(sc))
|
|
{
|
|
// ScReadRaw() did not pend, so update our internal state and continue.
|
|
//
|
|
RawReadComplete(cbRead);
|
|
}
|
|
else if (E_PENDING != sc)
|
|
{
|
|
DebugTrace("CBufferedStream::ScFillBuffer() - m_pstmRaw->ScReadRaw() failed 0x%08lX\n", sc);
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
template<class _RawStream, UINT CB_BUF>
|
|
VOID
|
|
CBufferedStream<_RawStream, CB_BUF>::ReadComplete( UINT cbReadRaw,
|
|
HRESULT hr )
|
|
{
|
|
// Update our internal state
|
|
//
|
|
RawReadComplete(cbReadRaw);
|
|
|
|
// If I/O succeeded then continue reading where we left off.
|
|
// We are done reading only when ScReadInt() returns S_OK
|
|
// or any error other than E_PENDING.
|
|
//
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ScReadInt();
|
|
if (E_PENDING == hr)
|
|
return;
|
|
|
|
if (FAILED(hr))
|
|
DebugTrace("CBufferedStream::ReadComplete() - ScReadInt() failed 0x%08lX\n", hr);
|
|
}
|
|
|
|
// Pull the external read observer from where we saved it
|
|
//
|
|
Assert(m_pobsRead);
|
|
IAsyncReadObserver * pobsReadExt = m_pobsRead;
|
|
m_pobsRead = NULL;
|
|
|
|
// Complete the read by calling the client back with
|
|
// total amount read for the request.
|
|
//
|
|
// Note that m_cbRequestDone != m_cbRequest only when there
|
|
// is an error.
|
|
//
|
|
Assert(FAILED(hr) || m_cbRequestDone == m_cbRequest);
|
|
pobsReadExt->ReadComplete(m_cbRequestDone, hr);
|
|
}
|
|
|
|
template<class _RawStream, UINT CB_BUF>
|
|
VOID
|
|
CBufferedStream<_RawStream, CB_BUF>::RawReadComplete(UINT cbReadRaw)
|
|
{
|
|
Assert(0 == m_cbBufUsed);
|
|
Assert(0 == m_ibBufCur);
|
|
|
|
// Update the number of bytes read
|
|
//
|
|
m_cbBufUsed = cbReadRaw;
|
|
}
|
|
|
|
template<class _RawStream, UINT CB_BUF>
|
|
SCODE
|
|
CBufferedStream<_RawStream, CB_BUF>::ScWrite(
|
|
_RawStream& stmRaw,
|
|
const BYTE * pbToWrite,
|
|
DWORD cbToWrite,
|
|
DWORD * pcbWritten,
|
|
IAsyncWriteObserver * pobsWriteExt )
|
|
{
|
|
// Check parameters
|
|
//
|
|
Assert(cbToWrite > 0);
|
|
Assert(!IsBadReadPtr(pbToWrite, cbToWrite));
|
|
Assert(!IsBadWritePtr(pcbWritten, sizeof(UINT)));
|
|
Assert(!pobsWriteExt || !IsBadReadPtr(pobsWriteExt, sizeof(IAsyncWriteObserver)));
|
|
|
|
// We had better not be in any I/O of any sort.
|
|
//
|
|
Assert(!m_pobsRead);
|
|
Assert(!m_pobsWrite);
|
|
Assert(!m_pobsFlush);
|
|
|
|
// Set up state for a new write. Casting away const-ness is OK;
|
|
// we don't write to m_pbRequest on writes.
|
|
//
|
|
m_pstmRaw = &stmRaw;
|
|
m_pdsRequest = NULL;
|
|
m_pbRequest = const_cast<BYTE *>(pbToWrite);
|
|
m_cbRequest = cbToWrite;
|
|
m_pobsWrite = pobsWriteExt;
|
|
m_cbRequestDone = 0;
|
|
|
|
// Issue the write
|
|
//
|
|
SCODE sc = ScWriteInt();
|
|
|
|
// If the write didn't pend then clear out the observer
|
|
// and return the amount of data written.
|
|
//
|
|
if (E_PENDING != sc)
|
|
{
|
|
m_pobsWrite = NULL;
|
|
*pcbWritten = m_cbRequestDone;
|
|
}
|
|
|
|
// Return the result of the I/O, which may be S_OK, E_PENDING
|
|
// or any other error.
|
|
//
|
|
return sc;
|
|
}
|
|
|
|
template<class _RawStream, UINT CB_BUF>
|
|
SCODE
|
|
CBufferedStream<_RawStream, CB_BUF>::ScCopyFrom(
|
|
_RawStream& stmRaw,
|
|
const IDavStream * pdsToCopy,
|
|
const DWORD cbToCopy,
|
|
DWORD * pcbCopied,
|
|
IAsyncWriteObserver * pobsWriteExt )
|
|
{
|
|
// Check parameters
|
|
//
|
|
Assert(cbToCopy >= 0);
|
|
Assert(!IsBadReadPtr(pdsToCopy, sizeof(IDavStream)));
|
|
Assert(!IsBadWritePtr(pcbCopied, sizeof(UINT)));
|
|
Assert(!pobsWriteExt || !IsBadReadPtr(pobsWriteExt, sizeof(IAsyncWriteObserver)));
|
|
|
|
// We had better not be in any I/O of any sort.
|
|
//
|
|
Assert(!m_pobsRead);
|
|
Assert(!m_pobsWrite);
|
|
Assert(!m_pobsFlush);
|
|
|
|
// Set up state for a new write. Casting away const-ness is OK;
|
|
// we don't write to m_pbRequest on writes.
|
|
//
|
|
m_pstmRaw = &stmRaw;
|
|
m_pdsRequest = pdsToCopy;
|
|
m_pbRequest = NULL;
|
|
m_cbRequest = cbToCopy;
|
|
m_pobsWrite = pobsWriteExt;
|
|
m_cbRequestDone = 0;
|
|
|
|
// Issue the write
|
|
//
|
|
SCODE sc = ScCopyFromInt();
|
|
|
|
// If the write didn't pend then clear out the observer
|
|
// and return the amount of data written.
|
|
//
|
|
if (E_PENDING != sc)
|
|
{
|
|
m_pobsWrite = NULL;
|
|
*pcbCopied = m_cbRequestDone;
|
|
}
|
|
|
|
// Return the result of the I/O, which may be S_OK, E_PENDING
|
|
// or any other error.
|
|
//
|
|
return sc;
|
|
}
|
|
|
|
template<class _RawStream, UINT CB_BUF>
|
|
SCODE
|
|
CBufferedStream<_RawStream, CB_BUF>::ScWriteInt()
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
// Loop around alternately filling and flushing the buffer until
|
|
// we finish the request or until a buffer flush pends.
|
|
//
|
|
while ( m_cbRequestDone < m_cbRequest )
|
|
{
|
|
// If there is no room left to write to the buffer then flush
|
|
// the buffer to the raw stream.
|
|
//
|
|
if (CB_BUF == m_cbBufUsed)
|
|
{
|
|
sc = ScFlushBuffer();
|
|
if (FAILED(sc))
|
|
{
|
|
if (E_PENDING != sc)
|
|
DebugTrace("CBufferedStream::ScWriteInt() - ScFlushBuffer() failed 0x%08lX\n", sc);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// There is room left in the buffer so copy over
|
|
// as much data from the request as will fit.
|
|
//
|
|
Assert(m_cbBufUsed < CB_BUF);
|
|
DWORD cbToWrite = min(CB_BUF - m_cbBufUsed,
|
|
m_cbRequest - m_cbRequestDone);
|
|
|
|
Assert(m_pbRequest);
|
|
memcpy(&m_rgbBuf[m_cbBufUsed],
|
|
m_pbRequest + m_cbRequestDone,
|
|
cbToWrite);
|
|
|
|
m_cbBufUsed += cbToWrite;
|
|
m_cbRequestDone += cbToWrite;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
template<class _RawStream, UINT CB_BUF>
|
|
SCODE
|
|
CBufferedStream<_RawStream, CB_BUF>::ScCopyFromInt()
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
// Loop around alternately filling and flushing the buffer until
|
|
// we finish the request or until a buffer flush pends.
|
|
//
|
|
while ( m_cbRequestDone < m_cbRequest )
|
|
{
|
|
// If there is no room left to write to the buffer then flush
|
|
// the buffer to the raw stream.
|
|
//
|
|
if (CB_BUF == m_cbBufUsed)
|
|
{
|
|
sc = ScFlushBuffer();
|
|
if (FAILED(sc))
|
|
{
|
|
if (E_PENDING != sc)
|
|
DebugTrace("CBufferedStream::ScCopyFromInt() - ScFlushBuffer() failed 0x%08lX\n", sc);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// There is room left in the buffer so copy over
|
|
// as much data from the request as will fit.
|
|
//
|
|
Assert(m_cbBufUsed < CB_BUF);
|
|
UINT cbCopied = 0;
|
|
DWORD cbToCopy = min(CB_BUF - m_cbBufUsed,
|
|
m_cbRequest - m_cbRequestDone);
|
|
|
|
Assert(m_pdsRequest);
|
|
sc = m_pdsRequest->ScRead(&m_rgbBuf[m_cbBufUsed],
|
|
cbToCopy,
|
|
&cbCopied);
|
|
if (FAILED(sc))
|
|
{
|
|
Assert(E_PENDING != sc);
|
|
DebugTrace("CBufferedStream::ScCopyFromInt() - ScRead() from source buffer failed 0x%08lX\n", sc);
|
|
break;
|
|
}
|
|
|
|
m_cbBufUsed += cbCopied;
|
|
m_cbRequestDone += cbCopied;
|
|
|
|
// Even if the clients requested to read certain amount of bytes to be read,
|
|
// they may be wrong in their estimates, so let us be really smart about the
|
|
// case and check if the end of the stream has been reached
|
|
//
|
|
if (m_pdsRequest->FEnd())
|
|
{
|
|
// Make sure that we certainly go out of the loop in the case we finished
|
|
//
|
|
m_cbRequest = m_cbRequestDone;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
template<class _RawStream, UINT CB_BUF>
|
|
VOID
|
|
CBufferedStream<_RawStream, CB_BUF>::WriteComplete( UINT cbWritten,
|
|
HRESULT hr )
|
|
{
|
|
// Update our internal state with the amount of buffered data that we
|
|
// flushed. We only want to do this IF the call was successful....
|
|
// RawWriteComplete() asserts that cbWritten == m_cbBufUsed, which will
|
|
// not be true if the write failed.
|
|
//
|
|
if (SUCCEEDED(hr))
|
|
RawWriteComplete(cbWritten);
|
|
|
|
// I/O is complete. Either the write that just completed
|
|
// failed or the subsequent write completed synchronously.
|
|
// Notify the appropriate observer that we are done.
|
|
//
|
|
if (m_pobsWrite)
|
|
{
|
|
// I/O was a write, not a flush
|
|
//
|
|
Assert(!m_pobsFlush);
|
|
|
|
// If I/O succeeded then continue writing where we left off.
|
|
// We are done writing only when ScWriteInt() returns S_OK
|
|
// or any error other than E_PENDING.
|
|
//
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ScWriteInt();
|
|
if (E_PENDING == hr)
|
|
return;
|
|
|
|
if (FAILED(hr))
|
|
DebugTrace("CBufferedStream::WriteComplete() - ScWriteInt() failed 0x%08lX\n", hr);
|
|
}
|
|
|
|
// Pull the external write observer from where we saved it
|
|
//
|
|
IAsyncWriteObserver * pobsWriteExt = m_pobsWrite;
|
|
m_pobsWrite = NULL;
|
|
|
|
// Complete the write by calling the client back with
|
|
// total amount written for the request.
|
|
//
|
|
// Note that m_cbRequestDone != m_cbRequest only when there
|
|
// is an error.
|
|
//
|
|
Assert(FAILED(hr) || m_cbRequestDone == m_cbRequest);
|
|
pobsWriteExt->WriteComplete(m_cbRequestDone, hr);
|
|
}
|
|
else
|
|
{
|
|
// I/O was a flush, not a write
|
|
//
|
|
Assert(m_pobsFlush);
|
|
|
|
// The buffer should be empty after flushing
|
|
//
|
|
Assert(0 == m_cbBufUsed);
|
|
|
|
// Pull the external flush observer from where we saved it
|
|
//
|
|
IAsyncFlushObserver * pobsFlushExt = m_pobsFlush;
|
|
m_pobsFlush = NULL;
|
|
|
|
// Tell it that we are done.
|
|
//
|
|
pobsFlushExt->FlushComplete(hr);
|
|
}
|
|
}
|
|
|
|
template<class _RawStream, UINT CB_BUF>
|
|
SCODE
|
|
CBufferedStream<_RawStream, CB_BUF>::ScFlushBuffer()
|
|
{
|
|
// We better have a stream to flush to.
|
|
//
|
|
Assert(m_pstmRaw);
|
|
|
|
// We better have something to flush.
|
|
//
|
|
Assert(m_cbBufUsed > 0);
|
|
|
|
// Write out all buffered data to the raw stream. If writing
|
|
// pends on I/O then we will resume processing in
|
|
// CBufferedStream::WriteComplete() when the I/O completes.
|
|
//
|
|
DWORD cbWritten = 0;
|
|
SCODE sc = m_pstmRaw->ScWriteRaw(m_rgbBuf,
|
|
m_cbBufUsed,
|
|
&cbWritten,
|
|
this);
|
|
if (SUCCEEDED(sc))
|
|
{
|
|
// ScWriteRaw() did not pend, so update our internal state and continue.
|
|
//
|
|
RawWriteComplete(cbWritten);
|
|
}
|
|
else if (E_PENDING != sc)
|
|
{
|
|
DebugTrace("CBufferedStream::ScFlushBuffer() - m_pstmRaw->ScWriteRaw() failed 0x%08lX\n", sc);
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
template<class _RawStream, UINT CB_BUF>
|
|
VOID
|
|
CBufferedStream<_RawStream, CB_BUF>::RawWriteComplete(UINT cbWrittenRaw)
|
|
{
|
|
// Verify that we wrote the entire buffer
|
|
//
|
|
Assert(cbWrittenRaw == m_cbBufUsed);
|
|
|
|
// Start the buffer again from the beginning
|
|
//
|
|
m_cbBufUsed = 0;
|
|
}
|
|
|
|
template<class _RawStream, UINT CB_BUF>
|
|
SCODE
|
|
CBufferedStream<_RawStream, CB_BUF>::ScFlush( _RawStream& stmRaw,
|
|
IAsyncFlushObserver * pobsFlushExt )
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
// Check parameters
|
|
//
|
|
Assert(!pobsFlushExt || !IsBadReadPtr(pobsFlushExt, sizeof(IAsyncFlushObserver)));
|
|
|
|
// We had better not be in any I/O of any sort.
|
|
//
|
|
Assert(!m_pobsRead);
|
|
Assert(!m_pobsFlush);
|
|
Assert(!m_pobsWrite);
|
|
|
|
// If there's nothing to flush then we're done.
|
|
//
|
|
if (m_cbBufUsed)
|
|
{
|
|
// Set up state for a flush
|
|
//
|
|
m_pstmRaw = &stmRaw;
|
|
m_pobsFlush = pobsFlushExt;
|
|
|
|
// Flush buffered data to the raw stream.
|
|
//
|
|
sc = ScFlushBuffer();
|
|
|
|
// If the flush didn't pend then clear out the observer.
|
|
//
|
|
if (E_PENDING != sc)
|
|
m_pobsFlush = NULL;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS CFileStreamImp
|
|
//
|
|
// Base implementation class for a file stream.
|
|
//
|
|
template<class _RawStream, class _OVL>
|
|
class CFileStreamImp
|
|
{
|
|
//
|
|
// File handle
|
|
//
|
|
auto_ref_handle m_hf;
|
|
|
|
//
|
|
// File pointer
|
|
//
|
|
_OVL m_ovl;
|
|
|
|
//
|
|
// Implementation stream is a buffered stream with a buffer size
|
|
// of 64K to optimize for file I/O.
|
|
//
|
|
// Note: this data member is declared LAST because it contains
|
|
// an internal 64K buffer that we don't want sitting between
|
|
// other member variables.
|
|
//
|
|
CBufferedStream<_RawStream, 64 * 1024> m_BufferedStream;
|
|
|
|
// NOT IMPLEMENTED
|
|
//
|
|
CFileStreamImp( const CFileStreamImp& );
|
|
CFileStreamImp& operator=( const CFileStreamImp& );
|
|
|
|
public:
|
|
// CREATORS
|
|
//
|
|
CFileStreamImp(const auto_ref_handle& hf) :
|
|
m_hf(hf)
|
|
{
|
|
memset(&m_ovl, 0, sizeof(m_ovl));
|
|
}
|
|
|
|
// ACCESSORS
|
|
//
|
|
HANDLE HFile() const
|
|
{
|
|
return m_hf.get();
|
|
}
|
|
|
|
_OVL * POverlapped()
|
|
{
|
|
return &m_ovl;
|
|
}
|
|
|
|
// MANIPULATORS
|
|
//
|
|
SCODE ScRead( _RawStream& stmRaw,
|
|
BYTE * pbToRead,
|
|
DWORD cbToRead,
|
|
DWORD * pcbRead,
|
|
IAsyncReadObserver * pobsAsyncRead )
|
|
{
|
|
return m_BufferedStream.ScRead( stmRaw,
|
|
pbToRead,
|
|
cbToRead,
|
|
pcbRead,
|
|
pobsAsyncRead );
|
|
}
|
|
|
|
SCODE ScWrite( _RawStream& stmRaw,
|
|
const BYTE * pbToWrite,
|
|
DWORD cbToWrite,
|
|
DWORD * pcbWritten,
|
|
IAsyncWriteObserver * pobsAsyncWrite )
|
|
{
|
|
return m_BufferedStream.ScWrite( stmRaw,
|
|
pbToWrite,
|
|
cbToWrite,
|
|
pcbWritten,
|
|
pobsAsyncWrite );
|
|
}
|
|
|
|
SCODE ScCopyFrom( _RawStream& stmRaw,
|
|
const IDavStream * pdsToCopy,
|
|
const DWORD cbToCopy,
|
|
DWORD * pcbCopied,
|
|
IAsyncWriteObserver * pobsAsyncWrite )
|
|
{
|
|
return m_BufferedStream.ScCopyFrom( stmRaw,
|
|
pdsToCopy,
|
|
cbToCopy,
|
|
pcbCopied,
|
|
pobsAsyncWrite );
|
|
}
|
|
|
|
|
|
SCODE ScFlush( _RawStream& stmRaw,
|
|
IAsyncFlushObserver * pobsFlush )
|
|
{
|
|
return m_BufferedStream.ScFlush( stmRaw, pobsFlush );
|
|
}
|
|
|
|
//
|
|
// Update the current file position
|
|
//
|
|
VOID UpdateFilePos(UINT cbIO)
|
|
{
|
|
//
|
|
// Check for overflow of the low 32 bits of the offset. If we are
|
|
// going to overflow then increment the high part of the offset.
|
|
//
|
|
if (m_ovl.Offset + cbIO < m_ovl.Offset)
|
|
{
|
|
++m_ovl.OffsetHigh;
|
|
|
|
//
|
|
// OffsetHigh should NEVER overflow
|
|
//
|
|
Assert(m_ovl.OffsetHigh);
|
|
}
|
|
|
|
//
|
|
// Update the low 32 bits of the offset
|
|
//
|
|
m_ovl.Offset += cbIO;
|
|
}
|
|
};
|
|
|
|
#endif // !defined(_ASTREAM_H_)
|