home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / dbmsg / mapi / remote.xp / xpstream.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-11  |  10.5 KB  |  360 lines

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. //  File Name
  4. //      XPSTREAM.CPP 
  5. //
  6. //  Description
  7. //      Wraps the StreamOnFile object to add buffering of the stream.
  8. //      The wrappered version uses an in-memory buffer, or cache, to
  9. //      reduce the number of actual Reads and Writes to the underlying
  10. //      stream.  The goal is, obviously, to improve performance.
  11. //      This code is not thread safe.  Also, the implementation
  12. //      is not optimized for streams that both read and write continuously
  13. //      because there will be too much flushing going on.
  14. //
  15. //  Author
  16. //      Irving De la Cruz
  17. //
  18. //  Note: This file is the C++ version of the buffered IStream wrapper
  19. //        developed originally for the C-based MSPEER transport in
  20. //        the MAPI SDK samples.
  21. //
  22. //  Revision: 1.7
  23. //
  24. // Written for Microsoft Windows Developer Support
  25. // Copyright (c) 1995-1996 Microsoft Corporation. All rights reserved.
  26. //
  27. #include "XPSTREAM.H"
  28. #include "TRACES.H"
  29. #include "COMWINDS.H"
  30.  
  31.  
  32. CCachedStream::CCachedStream (LPSTREAM pImpStream, ULONG ulFlags)
  33. {
  34.     m_cRef = 1;
  35.     m_fDirty = FALSE;
  36.     m_ulFlags = ulFlags;
  37.     m_libBuff = 0;
  38.     m_cbBuffMac = 0;
  39.     m_pvCache = HeapAlloc (GetProcessHeap(), 0, XPSOF_BUFF_MAX);
  40.     if (NULL == m_pvCache)
  41.     {
  42.         throw CException (E_OUTOFMEMORY);
  43.     }
  44.     m_pImpStream = pImpStream;
  45.     m_pImpStream->AddRef();
  46. }
  47.  
  48. CCachedStream::~CCachedStream()
  49. {
  50.     m_pImpStream->Release();
  51.     if (m_pvCache)
  52.     {
  53.         HeapFree (GetProcessHeap(), 0, m_pvCache);
  54.     }
  55. }
  56.  
  57. STDMETHODIMP CCachedStream::Read (LPVOID lpvData, ULONG cbSize, ULONG * lpcbRead)
  58. {
  59.     HRESULT hResult = S_OK;
  60.     ULONG cbRead = 0;
  61.     ULONG cbT;
  62.     LPVOID lpvRead = NULL;
  63.  
  64.     if (IsBadWritePtr (lpvData, cbSize) || (lpcbRead && IsBadWritePtr (lpcbRead, sizeof(ULONG))))
  65.     {
  66.         hResult = STG_E_INVALIDPARAMETER;
  67.         goto ret;
  68.     }
  69.     if (!(XPSOF_READ & m_ulFlags))
  70.     {
  71.         hResult = STG_E_ACCESSDENIED;
  72.         goto ret;
  73.     }
  74.     ASSERT (m_cbBuffMac >= m_libBuff);
  75.  
  76.     // First, flush the buffer if it has been written into.  This
  77.     // operation empties our buffer and zeros the offset and size.
  78.     // We do this because we also buffer writes and we need to force
  79.     // the underlying stream to point to where the caller expects.
  80.     if ((XPSOF_WRITE & m_ulFlags) && m_fDirty)
  81.     {
  82.         hResult = Commit (0);
  83.         if (hResult)
  84.         {
  85.             goto ret;
  86.         }
  87.     }
  88.  
  89.     // Determine if the buffer is empty (cbT == 0) or not (cbT != 0).
  90.     // We consider the buffer empty if we've read past the end of it
  91.     // or if m_cbBuffMac and m_libBuff are equal to zero.
  92.     cbT = m_cbBuffMac - m_libBuff;
  93.  
  94.     // If the buffer is empty and the caller wants to read less than
  95.     // the size of our buffer, then we'll fill the buffer from the
  96.     // underlying stream object.  Adjust our buffer offset and size.
  97.     if (!cbT && (cbSize < XPSOF_BUFF_MAX))
  98.     {
  99.         hResult = m_pImpStream->Read (m_pvCache, XPSOF_BUFF_MAX, &cbRead);
  100.         if (hResult)
  101.         {
  102.             goto ret;
  103.         }
  104.         m_libBuff = 0;
  105.         m_cbBuffMac = cbT = cbRead;
  106.     }
  107.  
  108.     // Now, if the buffer is *not* empty and the caller wants to read
  109.     // fewer bytes than what is in the buffer, then we read it from
  110.     // our buffer, fix-up our offset, set the count read and leave.
  111.     if (cbT && (cbSize <= cbT))
  112.     {
  113.         lpvRead = (LPVOID)((LPBYTE)m_pvCache + m_libBuff);
  114.         CopyMemory (lpvData, lpvRead, cbSize);
  115.         m_libBuff += cbSize;
  116.         cbRead = cbSize;
  117.         goto ret;
  118.     }
  119.  
  120.     // If we are here, then the caller has requested more bytes to be
  121.     // read than what can fit in our buffer.  In this case, we copy
  122.     // the remaining data from the buffer (if any) into lpvData and
  123.     // then go straight to the underlying stream for the remainder.
  124.     // Either way, our buffer is empty after this operation.
  125.     lpvRead = lpvData;
  126.     if (cbT)
  127.     {
  128.         CopyMemory (lpvRead, (LPVOID)((LPBYTE)m_pvCache + m_libBuff), cbT);
  129.         lpvRead = (LPBYTE)lpvRead + cbT;
  130.         m_libBuff = 0;
  131.         m_cbBuffMac = 0;
  132.     }
  133.  
  134.     hResult = m_pImpStream->Read (lpvRead, cbSize - cbT, &cbRead);
  135.     if (hResult)
  136.     {
  137.         goto ret;
  138.     }
  139.     cbRead += cbT;
  140.  
  141. ret:
  142.     if (lpcbRead)
  143.     {
  144.         *lpcbRead = cbRead;
  145.     }
  146.     TraceResult ("CCachedStream::Read", hResult);
  147.     return hResult;
  148. }
  149.  
  150. STDMETHODIMP CCachedStream::Write (const void * lpvData, ULONG cbSize, ULONG * lpcbWritten)
  151. {
  152.     HRESULT hResult = S_OK;
  153.     ULONG cbWritten = 0;
  154.     ULONG cbT;
  155.     LPVOID lpvWrite = NULL;
  156.  
  157.     if (IsBadReadPtr (lpvData, cbSize) || (lpcbWritten && IsBadWritePtr (lpcbWritten, sizeof(ULONG))))
  158.     {
  159.         hResult = STG_E_INVALIDPARAMETER;
  160.         goto ret;
  161.     }
  162.     if (!(XPSOF_WRITE & m_ulFlags))
  163.     {
  164.         hResult = STG_E_ACCESSDENIED;
  165.         goto ret;
  166.     }
  167.     ASSERT (m_cbBuffMac >= m_libBuff);
  168.  
  169.     // First, if we've been Reading, then we need to re-wind the file
  170.     // pointer in the underlying stream to compensate for the last
  171.     // buffered Read.  Our new vacancy = the Max Size of our buffer.
  172.     if (!m_fDirty)
  173.     {
  174.         if (m_libBuff != m_cbBuffMac)
  175.         {
  176.             hResult = RewindStream (m_cbBuffMac - m_libBuff);
  177.             if (hResult)
  178.             {
  179.                 goto ret;
  180.             }
  181.         }
  182.         m_libBuff = 0;
  183.         m_cbBuffMac = XPSOF_BUFF_MAX;
  184.     }
  185.  
  186.     // Determine the total vacancy of the buffer.
  187.     cbT = m_cbBuffMac - m_libBuff;
  188.  
  189.     // If the caller wants to Write more bytes than the current
  190.     // vacancy of the buffer, then commit the current buffer and
  191.     // Write the callers data directly to the stream.  If the
  192.     // buffer is not dirty, then the Commit call is a no-op.
  193.     if (cbSize > cbT)
  194.     {
  195.         hResult = Commit (0);
  196.         if (hResult)
  197.         {
  198.             goto ret;
  199.         }
  200.         hResult = m_pImpStream->Write (lpvData, cbSize, &cbWritten);
  201.         goto ret;
  202.     }
  203.  
  204.     // The callers data will fit in our current buffer.  Copy the
  205.     // data into the buffer, mark the buffer as dirty, and adjust
  206.     // the buffer offset.  Set cbWritten to cbSize and return.
  207.     lpvWrite = (LPVOID)((LPBYTE)m_pvCache + m_libBuff);
  208.     CopyMemory (lpvWrite, lpvData, cbSize);
  209.     m_fDirty = TRUE;
  210.     m_libBuff += cbSize;
  211.     cbWritten = cbSize;
  212.  
  213. ret:
  214.     if (lpcbWritten)
  215.     {
  216.         *lpcbWritten = cbWritten;
  217.     }
  218.     TraceResult ("CCachedStream::Write", hResult);
  219.     return hResult;
  220. }
  221.  
  222. STDMETHODIMP CCachedStream::Seek (LARGE_INTEGER liMove, DWORD dwMode, ULARGE_INTEGER * lpliPos)
  223. {
  224.     HRESULT hResult = S_OK;
  225.     if (lpliPos && IsBadWritePtr (lpliPos, sizeof(ULARGE_INTEGER)))
  226.     {
  227.         hResult = STG_E_INVALIDPARAMETER;
  228.         goto ret;
  229.     }
  230.     ASSERT (m_cbBuffMac >= m_libBuff);
  231.  
  232.     // If our buffer is dirty, then we've been writing into it and
  233.     // we need to flush it.  Else, if it isn't dirty and our offset
  234.     // and buffer size are not equal, then we've been reading and we
  235.     // need to rewind the underlying stream to match our position.
  236.     if (m_fDirty)
  237.     {
  238.         hResult = Commit (0);
  239.         if (hResult)
  240.         {
  241.             goto ret;
  242.         }
  243.     }
  244.     else
  245.     {
  246.         if ((dwMode == STREAM_SEEK_CUR) && (m_libBuff != m_cbBuffMac))
  247.         {
  248.             hResult = RewindStream (m_cbBuffMac - m_libBuff);
  249.             if (hResult)
  250.             {
  251.                 goto ret;
  252.             }
  253.         }
  254.         m_libBuff = 0;
  255.         m_cbBuffMac = 0;
  256.     }
  257.     // Now, call the real stream's Seek method.
  258.     hResult = m_pImpStream->Seek (liMove, dwMode, lpliPos);
  259. ret:
  260.     TraceResult ("CCachedStream::Seek", hResult);
  261.     return hResult;
  262. }
  263.  
  264. STDMETHODIMP CCachedStream::CopyTo (LPSTREAM lpStrmDst,
  265.                                     ULARGE_INTEGER cbCopy,
  266.                                     ULARGE_INTEGER * lpcbRead,
  267.                                     ULARGE_INTEGER * lpcbWritten)
  268. {
  269.     HRESULT hResult;
  270.     if (IsBadReadPtr (lpStrmDst, sizeof(LPVOID)) ||
  271.         IsBadWritePtr (lpcbRead, sizeof(ULARGE_INTEGER)) ||
  272.         IsBadWritePtr (lpcbWritten, sizeof(ULARGE_INTEGER)))
  273.     {
  274.         hResult = STG_E_INVALIDPARAMETER;
  275.         goto ret;
  276.     }
  277.     ASSERT (m_cbBuffMac >= m_libBuff);
  278.  
  279.     // If our buffer is dirty, then we've been writing into it and
  280.     // we need to flush it.  Else, if it isn't dirty and our offset
  281.     // and buffer size are not equal, then we've been reading and we
  282.     // need to rewind the underlying stream to match our position.
  283.     if (m_fDirty)
  284.     {
  285.         hResult = Commit (0);
  286.         if (hResult)
  287.         {
  288.             goto ret;
  289.         }
  290.     }
  291.     else
  292.     {
  293.         if (m_libBuff != m_cbBuffMac)
  294.         {
  295.             hResult = RewindStream (m_cbBuffMac - m_libBuff);
  296.             if (hResult)
  297.             {
  298.                 goto ret;
  299.             }
  300.         }
  301.         m_libBuff = 0;
  302.         m_cbBuffMac = 0;
  303.     }
  304.  
  305.     // Now, call the real streams CopyTo method.
  306.     hResult = m_pImpStream->CopyTo (lpStrmDst, cbCopy, lpcbRead, lpcbWritten);
  307. ret:
  308.     TraceResult ("CCachedStream::CopyTo", hResult);
  309.     return hResult;
  310. }
  311.  
  312. STDMETHODIMP CCachedStream::Commit (ULONG ulFlags)
  313. {
  314.     HRESULT hResult = S_OK;
  315.     // Flush my internal buffer if it is dirty.
  316.     if ((XPSOF_WRITE & m_ulFlags) && m_fDirty)
  317.     {
  318.         hResult = m_pImpStream->Write (m_pvCache, m_libBuff, NULL);
  319.         if (hResult)
  320.         {
  321.             goto ret;
  322.         }
  323.     }
  324.     // Mark my buffer as empty cal clean.
  325.     m_fDirty = FALSE;
  326.     m_libBuff = 0;
  327.     m_cbBuffMac = 0;
  328. ret:
  329.     TraceResult ("CCachedStream::Commit", hResult);
  330.     return hResult;
  331. }
  332.  
  333. ///////////////////////////////////////////////////////////////////////////////
  334. //    CCachedStream::RewindStream()
  335. //
  336. //    Parameters
  337. //      ib      Number of bytes to rewind
  338. //
  339. //    Purpose
  340. //      This gets called to back-up the file pointer when a Write operation
  341. //      follows a Read operation.  This is necessary because the file pointer
  342. //      is actually further ahead in the file than the buffered file pointer.
  343. //
  344. //    Return Value
  345. //      An HRESULT
  346. //
  347. HRESULT WINAPI CCachedStream::RewindStream (ULONG ib)
  348. {
  349.     if (ib)
  350.     {
  351.         LARGE_INTEGER liRewind;
  352.         liRewind.HighPart = 0xFFFFFFFF;
  353.         liRewind.LowPart = -((LONG)ib);
  354.         return m_pImpStream->Seek (liRewind, STREAM_SEEK_CUR, NULL);
  355.     }
  356.     return S_OK;
  357. }
  358.  
  359. // End of File for XPSTREAM.CPP
  360.