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.
641 lines
17 KiB
641 lines
17 KiB
// --------------------------------------------------------------------------------
|
|
// 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);
|
|
}
|