// -------------------------------------------------------------------------------- // 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); }