home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / C++-7 / DISK10 / MFC / SRC / ARCHIVE.CP$ / archive
Encoding:
Text File  |  1992-03-10  |  12.1 KB  |  505 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library. 
  2. // Copyright (C) 1992 Microsoft Corporation 
  3. // All rights reserved. 
  4. //  
  5. // This source code is only intended as a supplement to the 
  6. // Microsoft Foundation Classes Reference and Microsoft 
  7. // QuickHelp documentation provided with the library. 
  8. // See these sources for detailed information regarding the 
  9. // Microsoft Foundation Classes product. 
  10.  
  11. #include "afx.h"
  12. #include "afxcoll.h"
  13. #pragma hdrstop
  14.  
  15. #include <malloc.h>
  16.  
  17. #ifdef AFX_CORE_SEG
  18. #pragma code_seg(AFX_CORE_SEG)
  19. #endif
  20.  
  21.  
  22. #ifndef min
  23. #define min(a,b)        (((a) < (b)) ? (a) : (b))
  24. #endif
  25.  
  26. #ifdef _DEBUG
  27. #undef THIS_FILE
  28. static char BASED_CODE THIS_FILE[] = __FILE__;
  29. #endif
  30.  
  31. #define new DEBUG_NEW
  32.  
  33. ////////////////////////////////////////////////////////////////////////////
  34. // Serialize member functions for low level classes put here
  35. // for code swapping improvements
  36.  
  37. // CString serialization code
  38. // String format: if < 255 chars: len:BYTE, characters in bytes
  39. //              if >= 255 characters: 0xff, len:WORD, characters in bytes
  40.  
  41. CArchive&
  42. operator <<(CArchive& ar, const CString& string)
  43. {
  44.     if (string.m_nDataLength < 255)
  45.     {
  46.         ar << (BYTE) string.m_nDataLength;
  47.     }
  48.     else
  49.     {
  50.         ar << (BYTE) 0xff;
  51.         ar << (WORD) string.m_nDataLength;
  52.     }
  53.     ar.Write(string.m_pchData, string.m_nDataLength);
  54.     return ar;
  55. }
  56.  
  57. CArchive&
  58. operator >>(CArchive& ar, CString& string)
  59. {
  60.     string.Empty();
  61.  
  62.     BYTE bLen;
  63.     ar >> bLen;
  64.  
  65.     WORD nNewLen;
  66.     if (bLen == 0xff)
  67.         // read word of length
  68.         ar >> nNewLen;
  69.     else
  70.         nNewLen = bLen;
  71.  
  72.     // read in as normal characters
  73.     if (nNewLen != 0)
  74.     {
  75.         string.AllocBuffer(nNewLen);
  76.         if (ar.Read(string.m_pchData, nNewLen) != nNewLen)
  77.             AfxThrowArchiveException(CArchiveException::endOfFile);
  78.     }
  79.     return ar;
  80. }
  81.  
  82. // Runtime class serialization code
  83. CRuntimeClass*
  84. CRuntimeClass::Load(CArchive& ar, WORD* pwSchemaNum)
  85. {
  86.     WORD nLen;
  87.     char szClassName[64];
  88.     CRuntimeClass* pClass;
  89.  
  90.     ar >> (*pwSchemaNum) >> nLen;
  91.  
  92.     if (nLen >= sizeof(szClassName) || ar.Read(szClassName, nLen) != nLen)
  93.         return NULL;
  94.     szClassName[nLen] = '\0';
  95.  
  96.     for (pClass = pFirstClass; pClass != NULL; pClass = pClass->m_pNextClass)
  97.     {
  98.         if (strcmp(szClassName, pClass->m_pszClassName) == 0)
  99.             return pClass;
  100.     }
  101.  
  102.     return NULL;
  103. }
  104.  
  105. void
  106. CRuntimeClass::Store(CArchive& ar)
  107.     // Stores a class ref
  108. {
  109.     WORD nLen = (WORD)strlen(m_pszClassName);
  110.  
  111.     ar << m_wSchema << nLen;
  112.     ar.Write(m_pszClassName, nLen);
  113. }
  114.  
  115. ////////////////////////////////////////////////////////////////////////////
  116. ////////////////////////////////////////////////////////////////////////////
  117. // Archive object input/output
  118.  
  119.     // amount to grow m_loadArray upon insert
  120.     enum { nGrowSize = 10 };
  121.  
  122.     // minimum buffer size
  123.     enum { nBufSizeMin = 128 };
  124.  
  125. ////////////////////////////////////////////////////////////////////////////
  126. // Pointer mapping constants
  127. #define wNullTag      ((WORD)0)
  128. #define wNewClassTag  ((WORD)-1)
  129. #define wOldClassTag  ((WORD)-32768) /* 0x8000 or the class index with this */
  130. #define nMaxMapCount  ((WORD)32766)  /* 0x7FFE last valid mapCount */
  131.  
  132.  
  133. // TRY/CATCH cannot be used with /Ox
  134. #pragma optimize("elg", off)
  135.  
  136. CArchive::CArchive(CFile* pFile, 
  137.         UINT nMode,
  138.         int nBufSize /* = 512 */,
  139.         void FAR* lpBuf /* = NULL */)
  140. {
  141.     ASSERT_VALID(pFile);
  142.  
  143.     m_nMode = nMode;
  144.  
  145.     // initialize the buffer.  minimum size is 128
  146.     m_lpBufStart = (BYTE FAR*)lpBuf;
  147.  
  148.     if (nBufSize < nBufSizeMin)
  149.     {
  150.         // force use of private buffer of minimum size
  151.         m_nBufSize = nBufSizeMin;
  152.         m_lpBufStart = NULL; 
  153.     }
  154.     else
  155.         m_nBufSize = nBufSize;
  156.  
  157.     if (m_lpBufStart == NULL)
  158.     {
  159.         m_lpBufStart = (BYTE FAR*)_fmalloc(m_nBufSize);
  160.         m_bUserBuf = FALSE;
  161.     }
  162.     else
  163.         m_bUserBuf = TRUE;
  164.  
  165.     ASSERT(m_lpBufStart != NULL);
  166.     ASSERT(AfxIsValidAddress(m_lpBufStart, m_nBufSize));
  167.  
  168.     m_lpBufMax = m_lpBufStart + m_nBufSize;
  169.     m_lpBufCur = (IsLoading()) ? m_lpBufMax : m_lpBufStart;
  170.     m_pFile = pFile;
  171.  
  172.     // allocate the load/store map/array fail gracefully if OOM
  173.     TRY
  174.     {
  175.         if (nMode == CArchive::load) 
  176.             m_pLoadArray = new CPtrArray;
  177.         else
  178.             m_pStoreMap = new CMapPtrToWord;
  179.     }
  180.     CATCH(CMemoryException, e)
  181.     {
  182.         if (!m_bUserBuf)
  183.             _ffree(m_lpBufStart);
  184.         THROW_LAST();
  185.     }
  186.     END_CATCH
  187.  
  188.     if (nMode == CArchive::load) 
  189.     {
  190.         ASSERT(IsLoading());
  191.         ASSERT(nGrowSize > 0);
  192.         m_pLoadArray->SetSize(nGrowSize, nGrowSize); 
  193.         ASSERT(wNullTag == 0);
  194.         m_pLoadArray->SetAt(wNullTag, NULL);
  195.         m_nMapCount = 1;
  196.     }
  197.     else
  198.     {
  199.         ASSERT(IsStoring());
  200.  
  201.         m_pStoreMap->SetAt(NULL, wNullTag);
  202.         m_nMapCount = 1;
  203.     }
  204.  
  205. }
  206. #pragma optimize("", on)
  207.  
  208.  
  209. CArchive::~CArchive()
  210. {
  211.     ASSERT(AfxIsValidAddress(m_lpBufStart, (UINT)(m_lpBufMax - m_lpBufStart)));
  212.     ASSERT(AfxIsValidAddress(m_lpBufCur, (UINT)(m_lpBufMax - m_lpBufCur)));
  213.     ASSERT(m_lpBufStart != NULL);
  214.  
  215.     // Close makes m_pFile NULL.  If it is not NULL, we must Close the
  216.     // CArchive.
  217.     if (m_pFile)
  218.         Close();
  219.  
  220.     if (!m_bUserBuf)
  221.         _ffree(m_lpBufStart);
  222.  
  223.     if (m_nMode == CArchive::load)
  224.         delete m_pLoadArray;
  225.     else
  226.         delete m_pStoreMap;
  227. }
  228.  
  229. void
  230. CArchive::Close()
  231. {
  232.     ASSERT_VALID(m_pFile);
  233.  
  234.     Flush();
  235.     m_pFile = NULL;
  236. }
  237.  
  238. void
  239. CArchive::WriteObject(const CObject* cpOb)
  240. {
  241.     // object can be NULL
  242.     ASSERT(IsStoring());    // proper direction
  243.     ASSERT(m_lpBufStart != NULL);
  244.     ASSERT(m_lpBufCur != NULL);
  245.  
  246.     CObject* pOb = (CObject*)cpOb;
  247.     WORD nObIndex;
  248.  
  249.     ASSERT(sizeof(nObIndex) == 2);
  250.     ASSERT(sizeof(wNullTag) == 2);
  251.     ASSERT(sizeof(wNewClassTag) == 2);
  252.  
  253.     if (pOb == NULL)
  254.         *this << wNullTag;
  255.     else if (!(cpOb->IsSerializable()))
  256.         AfxThrowNotSupportedException();
  257.     else if ((nObIndex = (*m_pStoreMap)[pOb]) != 0) //ASSUME: initialized to 0 map
  258.         *this << nObIndex;
  259.     else
  260.     {
  261.         CRuntimeClass* pClassRef = pOb->GetRuntimeClass();
  262.         WORD nClassIndex;
  263.  
  264.         // write out class id of pOb, with high bit set to indicate
  265.         // new object follows
  266.  
  267.         // ASSUME: initialized to 0 map
  268.         if ((nClassIndex = (*m_pStoreMap)[pClassRef]) != 0) 
  269.         {
  270.             // previously seen class, write out the index tagged by high bit
  271.             *this << (WORD)(wOldClassTag | nClassIndex);
  272.         }
  273.         else
  274.         {
  275.             // new class
  276.             *this << wNewClassTag;
  277.             pClassRef->Store(*this);
  278.  
  279.             (*m_pStoreMap)[pClassRef] = (WORD) m_nMapCount++;
  280.             if (m_nMapCount > nMaxMapCount)
  281.                 AfxThrowArchiveException(CArchiveException::badIndex);
  282.         }
  283.         // enter in stored object table and output
  284.         (*m_pStoreMap)[pOb] = (WORD)m_nMapCount++;
  285.         if (m_nMapCount > nMaxMapCount)
  286.             AfxThrowArchiveException(CArchiveException::badIndex);
  287.  
  288.         pOb->Serialize(*this);
  289.     }
  290. }
  291.  
  292.  
  293.  
  294. CObject*
  295. CArchive::ReadObject(const CRuntimeClass* pClassRefRequested)
  296. {
  297.     ASSERT(pClassRefRequested == NULL || AfxIsValidAddress(pClassRefRequested, sizeof(struct CRuntimeClass)));
  298.     ASSERT(IsLoading());    // proper direction
  299.     ASSERT(wNullTag == 0);
  300.     ASSERT(m_lpBufStart != NULL);
  301.     ASSERT(m_lpBufCur != NULL);
  302.  
  303.     CRuntimeClass* pClassRef;
  304.     WORD obTag;
  305.     WORD wSchema;
  306.  
  307.     if (pClassRefRequested && (pClassRefRequested->m_wSchema == 0xFFFF))
  308.         AfxThrowNotSupportedException();
  309.         
  310.     *this >> obTag;
  311.  
  312.     //NOTE: this relies on signed testing of the tag values
  313.     if ((short)obTag >= (short)wNullTag)
  314.     {
  315.         if (obTag > (WORD)m_pLoadArray->GetUpperBound())
  316.             AfxThrowArchiveException(CArchiveException::badIndex);
  317.  
  318.         CObject* pOb = (CObject*)m_pLoadArray->GetAt(obTag);
  319.  
  320.         if (pOb != NULL && pClassRefRequested && !pOb->IsKindOf(pClassRefRequested))
  321.             AfxThrowArchiveException(CArchiveException::badClass);
  322.         return pOb;
  323.     }
  324.  
  325.  
  326.     if (obTag == wNewClassTag)
  327.     {
  328.         // new object follows a new class id
  329.         if (m_nMapCount > nMaxMapCount)
  330.             AfxThrowArchiveException(CArchiveException::badIndex);
  331.  
  332.         if ((pClassRef = CRuntimeClass::Load(*this, &wSchema)) == NULL)
  333.         {
  334.             AfxThrowArchiveException(CArchiveException::badClass);
  335.             return NULL;
  336.         }
  337.         if (pClassRef->m_wSchema != wSchema)
  338.         {
  339.             AfxThrowArchiveException(CArchiveException::badSchema);
  340.             return NULL;
  341.         }
  342.         m_pLoadArray->InsertAt(m_nMapCount++, pClassRef, 1);
  343.         ASSERT(m_nMapCount < (UINT)0x7FFF);
  344.     } 
  345.     else
  346.     {
  347.         // existing class index in obTag followed by new object
  348.  
  349.         WORD nClassIndex = (WORD)(obTag & (WORD)~wOldClassTag);
  350.         ASSERT(sizeof(nClassIndex) == 2);
  351.  
  352.         if (nClassIndex & 0x8000 || 
  353.                 nClassIndex > (WORD)m_pLoadArray->GetUpperBound())
  354.             AfxThrowArchiveException(CArchiveException::badIndex);
  355.  
  356.         pClassRef = (CRuntimeClass*)m_pLoadArray->GetAt(nClassIndex);
  357.     }
  358.  
  359.     // allocate a new object based on the class just acquired
  360.     CObject* pOb = pClassRef->CreateObject();
  361.     ASSERT(pOb != NULL);
  362.  
  363.     // Add to mapping array BEFORE de-serializing
  364.     m_pLoadArray->InsertAt(m_nMapCount++, pOb, 1);
  365.  
  366.     pOb->Serialize(*this);
  367.  
  368.     ASSERT(pOb != NULL);
  369.     if (pClassRefRequested && !pOb->IsKindOf(pClassRefRequested))
  370.         AfxThrowArchiveException(CArchiveException::badClass);
  371.  
  372.     return pOb;
  373. }
  374.  
  375.  
  376. UINT
  377. CArchive::Read(void FAR* lpBuf, UINT nMax)
  378. {
  379.     ASSERT_VALID(m_pFile);
  380.     ASSERT(lpBuf != NULL);
  381.     ASSERT(m_lpBufStart != NULL);
  382.     ASSERT(m_lpBufCur != NULL);
  383.     ASSERT(AfxIsValidAddress(lpBuf, nMax));
  384.     ASSERT(AfxIsValidAddress(m_lpBufStart, (UINT)(m_lpBufMax - m_lpBufStart)));
  385.     ASSERT(AfxIsValidAddress(m_lpBufCur, (UINT)(m_lpBufMax - m_lpBufCur)));
  386.     ASSERT(IsLoading());
  387.  
  388.     register UINT nRead = 0;
  389.  
  390.     if (nMax == 0)
  391.         return 0;
  392.  
  393.     while (nMax > 0)
  394.     {
  395.         UINT nCopy = min(nMax, (UINT)(m_lpBufMax - m_lpBufCur));
  396.         _fmemcpy(lpBuf, m_lpBufCur, nCopy);
  397.         m_lpBufCur += nCopy;
  398.         lpBuf = ((BYTE FAR*)lpBuf) + nCopy;
  399.         nMax -= nCopy;
  400.         nRead += nCopy;
  401.         if (nMax != 0)
  402.             FillBuffer(min(nMax, (UINT)m_nBufSize));
  403.     }
  404.     return nRead;
  405. }
  406.  
  407. void
  408. CArchive::Write(const void FAR* lpBuf, UINT nMax)
  409. {
  410.     ASSERT_VALID(m_pFile);
  411.     ASSERT(m_lpBufStart != NULL);
  412.     ASSERT(m_lpBufCur != NULL);
  413.     ASSERT(AfxIsValidAddress(lpBuf, nMax));
  414.     ASSERT(AfxIsValidAddress(m_lpBufStart, (UINT)(m_lpBufMax - m_lpBufStart)));
  415.     ASSERT(AfxIsValidAddress(m_lpBufCur, (UINT)(m_lpBufMax - m_lpBufCur)));
  416.     ASSERT(IsStoring());
  417.  
  418.     register void FAR* lpBufT = (void FAR*)lpBuf;
  419.  
  420.     while (nMax > 0)
  421.     {
  422.         UINT nCopy = min(nMax, (UINT)(m_lpBufMax - m_lpBufCur));
  423.         _fmemcpy(m_lpBufCur, lpBufT, nCopy);
  424.         m_lpBufCur += nCopy;
  425.         lpBufT = ((BYTE FAR*)lpBufT) + nCopy;
  426.         nMax -= nCopy;
  427.         if (nMax != 0)
  428.         {
  429.             // write out the current buffer to file
  430.             if (m_lpBufCur != m_lpBufStart)
  431.                 m_pFile->Write(m_lpBufStart, m_lpBufCur - m_lpBufStart);
  432.  
  433.             // restore buffer to initial state
  434.             m_lpBufCur = m_lpBufStart;
  435.         }
  436.     }
  437. }
  438.  
  439.  
  440. void
  441. CArchive::Flush()
  442. {
  443.     ASSERT(m_lpBufStart != NULL);
  444.     ASSERT(m_lpBufCur != NULL);
  445.     ASSERT_VALID(m_pFile);
  446.     ASSERT(m_lpBufStart != NULL);
  447.     ASSERT(m_lpBufCur != NULL);
  448.     ASSERT(AfxIsValidAddress(m_lpBufStart, (UINT)(m_lpBufMax - m_lpBufStart)));
  449.     ASSERT(AfxIsValidAddress(m_lpBufCur, (UINT)(m_lpBufMax - m_lpBufCur)));
  450.  
  451.     if (IsLoading())
  452.     {
  453.         // unget the characters in the buffer, seek back unused amount
  454.         m_pFile->Seek(-(m_lpBufMax - m_lpBufCur), CFile::current);
  455.         m_lpBufCur = m_lpBufMax;    // empty
  456.     }
  457.     else
  458.     {
  459.         // write out the current buffer to file
  460.         if (m_lpBufCur != m_lpBufStart)
  461.         {
  462.             m_pFile->Write(m_lpBufStart, m_lpBufCur - m_lpBufStart);
  463.             m_pFile->Flush();
  464.         }
  465.  
  466.         // restore buffer to initial state
  467.         m_lpBufCur = m_lpBufStart;
  468.     }
  469. }
  470.  
  471. void 
  472. CArchive::FillBuffer(UINT nBytesNeeded)
  473. {
  474.     ASSERT(IsLoading());
  475.     ASSERT_VALID(m_pFile);
  476.     ASSERT(m_lpBufStart != NULL);
  477.     ASSERT(m_lpBufCur != NULL);
  478.     ASSERT(nBytesNeeded > 0);
  479.     ASSERT(AfxIsValidAddress(m_lpBufStart, (UINT)(m_lpBufMax - m_lpBufStart)));
  480.     ASSERT(AfxIsValidAddress(m_lpBufCur, (UINT)(m_lpBufMax - m_lpBufCur)));
  481.  
  482.  
  483.     // fill up the current buffer from file
  484.     if (m_lpBufCur > m_lpBufStart)
  485.     {
  486.         // there is at least some room to fill
  487.         UINT nUnused = 0; // bytes remaining in buffer
  488.         UINT nActual = 0; // bytes read from file
  489.  
  490.         if ((nUnused = m_lpBufMax - m_lpBufCur) > 0)
  491.         {
  492.             _fmemcpy(m_lpBufStart, m_lpBufCur, m_lpBufMax - m_lpBufCur);    // copy unused
  493.         }
  494.  
  495.         nActual = m_pFile->Read(m_lpBufStart+nUnused, m_nBufSize-nUnused);
  496.  
  497.         if (nActual < nBytesNeeded)
  498.             // not enough data to fill request
  499.             AfxThrowArchiveException(CArchiveException::endOfFile);
  500.  
  501.         m_lpBufCur = m_lpBufStart;
  502.         m_lpBufMax = m_lpBufStart + nUnused + nActual;
  503.     }
  504. }
  505.