Windows2003-3790/inetcore/outlookexpress/inetcomm/mimeole/vstream.cpp
2020-09-30 16:53:55 +02:00

642 lines
17 KiB
C++

// --------------------------------------------------------------------------------
// Vstream.cpp
// Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
// Ronald E. Gray
// --------------------------------------------------------------------------------
#include "pch.hxx"
#include "vstream.h"
#include "dllmain.h"
#include "demand.h"
// --------------------------------------------------------------------------------
// Utilities
// --------------------------------------------------------------------------------
inline ULONG ICeil(ULONG x, ULONG interval)
{
return (x ? (((x-1)/interval) + 1) * interval : 0);
}
// --------------------------------------------------------------------------------
// CVirtualStream::CVirtualStream
// --------------------------------------------------------------------------------
CVirtualStream::CVirtualStream(void)
{
m_cRef = 1;
m_cbSize = 0;
m_cbCommitted = 0;
m_cbAlloc = 0;
m_dwOffset = 0;
m_pstm = NULL;
m_pb = 0;
m_fFileErr = FALSE;
InitializeCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CVirtualStream::~CVirtualStream
// --------------------------------------------------------------------------------
CVirtualStream::~CVirtualStream(void)
{
if (m_pb)
VirtualFree(m_pb, 0, MEM_RELEASE);
if (m_pstm)
m_pstm->Release();
DeleteCriticalSection(&m_cs);
}
// --------------------------------------------------------------------------------
// CVirtualStream::QueryInterface
// --------------------------------------------------------------------------------
STDMETHODIMP CVirtualStream::QueryInterface(REFIID riid, LPVOID *ppv)
{
// check params
if (ppv == NULL)
return TrapError(E_INVALIDARG);
// Init
*ppv = NULL;
// Find IID
if ( (IID_IUnknown == riid)
|| (IID_IStream == riid)
|| (IID_IVirtualStream == riid))
*ppv = (IStream *)this;
else
{
*ppv = NULL;
return TrapError(E_NOINTERFACE);
}
// AddRef It
AddRef();
// Done
return (ResultFromScode(S_OK));
}
// --------------------------------------------------------------------------------
// CVirtualStream::AddRef
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CVirtualStream::AddRef(void)
{
return InterlockedIncrement((LONG*)&m_cRef);
}
// --------------------------------------------------------------------------------
// CVirtualStream::SyncFileStream
// --------------------------------------------------------------------------------
HRESULT CVirtualStream::SyncFileStream()
{
LARGE_INTEGER li;
HRESULT hr;
// figure out where to set the file stream be subtracting the memory portion
// of the stream from the offset
#ifdef MAC
if (m_dwOffset < m_cbAlloc)
LISet32(li, 0);
else
{
LISet32(li, m_dwOffset);
li.LowPart -= m_cbAlloc;
}
#else // !MAC
if (m_dwOffset < m_cbAlloc)
li.QuadPart = 0;
else
li.QuadPart = m_dwOffset - m_cbAlloc;
#endif // MAC
// seek in the stream
hr = m_pstm->Seek(li, STREAM_SEEK_SET, NULL);
// reset the file err member based on the current error
m_fFileErr = !!hr;
return hr;
}
// --------------------------------------------------------------------------------
// CVirtualStream::Release
// --------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CVirtualStream::Release(void)
{
ULONG cRef = InterlockedDecrement((LONG*)&m_cRef);
if (0 != cRef)
{
#ifdef DEBUG
return cRef;
#else
return 0;
#endif
}
delete this;
return 0;
}
// --------------------------------------------------------------------------------
// CVirtualStream::Read
// --------------------------------------------------------------------------------
#ifndef WIN16
STDMETHODIMP CVirtualStream::Read(LPVOID pv, ULONG cb, ULONG *pcbRead)
#else
STDMETHODIMP CVirtualStream::Read(VOID HUGEP *pv, ULONG cb, ULONG *pcbRead)
#endif // !WIN16
{
// Locals
HRESULT hr = ResultFromScode(S_OK);
ULONG cbGet = 0;
// Check
AssertWritePtr(pv, cb);
// Thread Safety
EnterCriticalSection(&m_cs);
// if the steam pointer is possibly out of sync
// resync
if (m_fFileErr)
{
hr = SyncFileStream();
if (hr) goto err;
}
// make sure there's something to read
if (m_dwOffset < m_cbSize)
{
// figure out what we're getting out of memory
if (m_dwOffset < m_cbCommitted)
{
if (m_cbSize > m_cbCommitted)
cbGet = min(cb, m_cbCommitted - m_dwOffset);
else
cbGet = min(cb, m_cbSize - m_dwOffset);
// copy the memory stuff
CopyMemory((LPBYTE)pv, m_pb + m_dwOffset, cbGet);
}
// if we still have stuff to read
// and we've used all of the memory
// and we do have a stream, try to get the rest of the data out of the stream
if ( (cbGet != cb)
&& (m_cbCommitted == m_cbAlloc)
&& m_pstm)
{
ULONG cbRead;
#ifdef DEBUG
LARGE_INTEGER li = {0, 0};
ULARGE_INTEGER uli = {0, 0};
if (!m_pstm->Seek(li, STREAM_SEEK_CUR, &uli))
#ifdef MAC
Assert(((m_dwOffset + cbGet) - m_cbAlloc) == uli.LowPart);
#else // !MAC
Assert(((m_dwOffset + cbGet) - m_cbAlloc) == uli.QuadPart);
#endif // MAC
#endif
hr = m_pstm->Read(((LPBYTE)pv) + cbGet, cb - cbGet, &cbRead);
if (hr)
{
m_fFileErr = TRUE;
goto err;
}
cbGet += cbRead;
}
m_dwOffset += cbGet;
}
if (pcbRead)
*pcbRead = cbGet;
err:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CVirtualStream::SetSize
// --------------------------------------------------------------------------------
HRESULT CVirtualStream::SetSize(ULARGE_INTEGER uli)
{
// Locals
HRESULT hr = ResultFromScode(S_OK);
ULONG cbDemand = uli.LowPart;
ULONG cbCommit = ICeil(cbDemand, g_dwSysPageSize);
if (uli.HighPart != 0)
return(ResultFromScode(STG_E_MEDIUMFULL));
// Thread Safety
EnterCriticalSection(&m_cs);
// if we haven't initialized memory, do it now
if (!m_cbAlloc)
{
LPVOID pv;
ULONG cb = 32 * g_dwSysPageSize; // use 32 pages
while ((!(pv = VirtualAlloc(NULL, cb, MEM_RESERVE, PAGE_READWRITE)))
&& (cb > g_dwSysPageSize))
{
cb /= 2;
}
if (!pv)
{
hr = ResultFromScode(E_OUTOFMEMORY);
goto err;
}
m_cbAlloc = cb;
m_pb = (LPBYTE)pv;
}
if (cbCommit < m_cbCommitted)
{
// shrink the stream
LPBYTE pb =m_pb;
ULONG cb;
// figure out the begining of the last page in the range not used
pb += cbCommit;
// figure out the size of the range being decommitted
cb = m_cbCommitted - cbCommit;
#ifndef MAC
VirtualFree(pb, cb, MEM_DECOMMIT);
#endif // !MAC
// figure out what we have left committed
m_cbCommitted = cbCommit;
}
else if (cbCommit > m_cbCommitted)
{
LPBYTE pb;
// figure out how much memory to commit
cbCommit = (cbDemand <= m_cbAlloc)
? ICeil(cbDemand, g_dwSysPageSize)
: m_cbAlloc;
if (cbCommit > m_cbCommitted)
{
#ifndef MAC
if (!VirtualAlloc(m_pb, cbCommit, MEM_COMMIT, PAGE_READWRITE))
{
hr = ResultFromScode(E_OUTOFMEMORY);
goto err;
}
#endif // !MAC
}
m_cbCommitted = cbCommit;
// Wow, we've used all of memory, start up the disk
if (cbDemand > m_cbAlloc)
{
ULARGE_INTEGER uliAlloc;
// no stream? better create it now
if (!m_pstm)
{
hr = CreateTempFileStream(&m_pstm);
if (hr) goto err;
}
uliAlloc.LowPart = cbDemand - m_cbAlloc;
uliAlloc.HighPart = 0;
hr = m_pstm->SetSize(uliAlloc);
if (hr) goto err;
// if the current offset beyond the end of the memory allocation,
// initialize the stream pointer correctly
if (m_dwOffset > m_cbAlloc)
{
hr = SyncFileStream();
if (hr) goto err;
}
}
}
m_cbSize = cbDemand;
err:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
}
// --------------------------------------------------------------------------------
// CVirtualStream::QueryStat
// --------------------------------------------------------------------------------
STDMETHODIMP CVirtualStream::Stat(STATSTG *pStat, DWORD grfStatFlag)
{
// Invalid Arg
if (NULL == pStat)
return TrapError(E_INVALIDARG);
// Fill pStat
pStat->type = STGTY_STREAM;
pStat->cbSize.HighPart = 0;
pStat->cbSize.LowPart = m_cbSize;
// Done
return S_OK;
}
// --------------------------------------------------------------------------------
// CVirtualStream::QueryStat
// --------------------------------------------------------------------------------
void CVirtualStream::QueryStat(ULARGE_INTEGER *puliOffset, ULARGE_INTEGER *pulSize)
{
#ifdef MAC
if (puliOffset)
ULISet32(*puliOffset, m_dwOffset);
if (pulSize)
ULISet32(*pulSize, m_cbSize);
#else // !MAC
if (puliOffset)
puliOffset->QuadPart = (LONGLONG)m_dwOffset;
if (pulSize)
pulSize->QuadPart = (LONGLONG)m_cbSize;
#endif // MAC
}
// --------------------------------------------------------------------------------
// CVirtualStream::Seek
// --------------------------------------------------------------------------------
STDMETHODIMP CVirtualStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
{
// Locals
HRESULT hr = ResultFromScode(S_OK);
BOOL fForward;
ULONG ulOffset;
#ifdef MAC
ULONG llCur;
#else // !MAC
LONGLONG llCur;
#endif // MAC
// Thread Safety
EnterCriticalSection(&m_cs);
// look for starting position
if (dwOrigin == STREAM_SEEK_CUR)
llCur = m_dwOffset;
else if (dwOrigin == STREAM_SEEK_END)
llCur = m_cbSize;
else
llCur = 0;
#ifdef MAC
Assert(0 == dlibMove.HighPart);
llCur += dlibMove.LowPart;
#else // !MAC
llCur += dlibMove.QuadPart;
#endif // MAC
// limit to 4 Gig
if (llCur > 0xFFFFFFFF)
goto seekerr;
// if we have a stream and
// we are currently in the file stream or the new seek seeks into the
// stream and the seek will not grow the stream, reseek in the stream
if ( m_pstm
&& ( (m_dwOffset > m_cbAlloc)
|| (llCur > m_cbAlloc))
&& (llCur <= m_cbSize))
{
LARGE_INTEGER li;
#ifdef MAC
LISet32(li ,llCur < m_cbAlloc ? 0 : llCur - m_cbAlloc);
#else // !MAC
li.QuadPart = llCur < m_cbAlloc ? 0 : llCur - m_cbAlloc;
#endif // MAC
hr = m_pstm->Seek(li, STREAM_SEEK_SET, NULL);
if (hr)
{
m_fFileErr = TRUE;
goto err;
}
}
m_dwOffset = (ULONG)llCur;
if (plibNewPosition)
#ifdef MAC
LISet32(*plibNewPosition, llCur);
#else // !MAC
plibNewPosition->QuadPart = llCur;
#endif // MAC
err:
// Thread Safety
LeaveCriticalSection(&m_cs);
return hr;
seekerr:
hr = ResultFromScode(STG_E_MEDIUMFULL);
goto err;
// Done
}
// --------------------------------------------------------------------------------
// CVirtualStream::Write
// --------------------------------------------------------------------------------
#ifndef WIN16
STDMETHODIMP CVirtualStream::Write(const void *pv, ULONG cb, ULONG *pcbWritten)
#else
STDMETHODIMP CVirtualStream::Write(const void HUGEP *pv, ULONG cb, ULONG *pcbWritten)
#endif // !WIN16
{
// Locals
HRESULT hr = ResultFromScode(S_OK);
ULONG cbNew;
ULONG cbWrite = 0;
// Thread Safety
EnterCriticalSection(&m_cs);
// figure out where we'll end up
cbNew = cb + m_dwOffset;
// make sure that we won't wrap
if (cbNew < m_dwOffset)
goto stmfull;
// if that is past the end of the stream, make more stream
if (cbNew > m_cbSize)
{
ULARGE_INTEGER uli = {cbNew, 0};
hr = SetSize(uli);
if (hr) goto err;
}
// figure out what we're putting into memory
if (m_dwOffset < m_cbCommitted)
{
cbWrite = min(cb, m_cbCommitted - m_dwOffset);
// copy the memory stuff
CopyMemory(m_pb + m_dwOffset, (LPBYTE)pv, cbWrite);
}
// if we still have stuff to write, dump to the file
if (cbWrite != cb)
{
ULONG cbWritten;
Assert(m_pstm);
#ifdef DEBUG
LARGE_INTEGER li = {0, 0};
ULARGE_INTEGER uli = {0, 0};
if (!m_pstm->Seek(li, STREAM_SEEK_CUR, &uli))
#ifdef MAC
Assert(0 == uli.HighPart);
Assert(((m_dwOffset + cbWrite) - m_cbAlloc) == uli.LowPart);
#else // !MAC
Assert(((m_dwOffset + cbWrite) - m_cbAlloc) == uli.QuadPart);
#endif // MAC
#endif
hr = m_pstm->Write(((LPBYTE)pv) + cbWrite, cb - cbWrite, &cbWritten);
if (hr)
{
m_fFileErr = TRUE;
goto err;
}
cbWrite += cbWritten;
}
m_dwOffset += cbWrite;
if (pcbWritten)
*pcbWritten = cbWrite;
err:
// Thread Safety
LeaveCriticalSection(&m_cs);
// Done
return hr;
stmfull:
hr = ResultFromScode(STG_E_MEDIUMFULL);
goto err;
}
STDMETHODIMP CVirtualStream::CopyTo(LPSTREAM pstmDst,
ULARGE_INTEGER uli,
ULARGE_INTEGER* puliRead,
ULARGE_INTEGER* puliWritten)
{
HRESULT hr = 0;
UINT cbBuf;
ULONG cbRemain;
ULONG cbReadMem = 0;
ULONG cbWriteMem = 0;
#ifdef MAC
ULARGE_INTEGER uliRead = {0, 0};
ULARGE_INTEGER uliWritten = {0, 0};
#else // !MAC
ULARGE_INTEGER uliRead = {0};
ULARGE_INTEGER uliWritten = {0};
#endif // MAC
// Initialize the outgoing params
if (puliRead)
{
ULISet32((*puliRead), 0);
}
if (puliWritten)
{
ULISet32((*puliWritten), 0);
}
if (!m_cbSize)
goto err;
// if the request is greater than the max ULONG, bring the request down to
// the max ULONG
if (uli.HighPart)
#ifdef MAC
ULISet32(uli, ULONG_MAX);
#else // !MAC
uli.QuadPart = 0xFFFFFFFF;
#endif // MAC
if (m_dwOffset < m_cbCommitted)
{
if (m_cbSize < m_cbAlloc)
cbReadMem = (ULONG)min(uli.LowPart, m_cbSize - m_dwOffset);
else
cbReadMem = (ULONG)min(uli.LowPart, m_cbAlloc - m_dwOffset);
hr = pstmDst->Write(m_pb + m_dwOffset, cbReadMem, &cbWriteMem);
if (!hr && (cbReadMem != cbWriteMem))
hr = ResultFromScode(E_OUTOFMEMORY);
if (hr) goto err;
uli.LowPart -= cbReadMem;
}
// if we didn't get it all from memory and there is information in
// the file stream, read from the file stream
if ( uli.LowPart
&& (m_cbSize > m_cbAlloc)
&& m_pstm)
{
hr = m_pstm->CopyTo(pstmDst, uli, &uliRead, &uliWritten);
if (hr)
{
m_fFileErr = TRUE;
goto err;
}
}
m_dwOffset += uliRead.LowPart + cbReadMem;
// Total cbReadMem and ulRead because we have them both.
#ifdef MAC
if (puliRead)
{
ULISet32(*puliRead, uliRead.LowPart);
Assert(INT_MAX - cbReadMem >= puliRead->LowPart);
puliRead->LowPart += cbReadMem;
}
if (puliWritten)
puliWritten->LowPart = uliWritten.LowPart + cbWriteMem;
#else // !MAC
if (puliRead)
puliRead->QuadPart = cbReadMem + uliRead.LowPart;
// Add in cbWriteMem because any written from the file stream was
// already set
if (puliWritten)
puliWritten->QuadPart = uliWritten.LowPart + cbWriteMem;
#endif // MAC
err:
return (hr);
}