Leaked source code of windows server 2003
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

#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_)