File:  [WindowsNT SDKs] / mstools / mfc / src / archive.cpp
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 18:21:01 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: ntsdk-oct-1992, ntsdk-jun-1992, ntsdk-jul-1993, HEAD
Microsoft Windows NT Build 297 06-28-1992

// This is a part of the Microsoft Foundation Classes C++ library. 
// Copyright (C) 1992 Microsoft Corporation 
// All rights reserved. 
//  
// This source code is only intended as a supplement to the 
// Microsoft Foundation Classes Reference and Microsoft 
// QuickHelp documentation provided with the library. 
// See these sources for detailed information regarding the 
// Microsoft Foundation Classes product. 

#include "afx.h"
#include "afxcoll.h"
#pragma hdrstop

#include <malloc.h>

#ifdef AFX_CORE_SEG
#pragma code_seg(AFX_CORE_SEG)
#endif


#ifndef min
#define min(a,b)        (((a) < (b)) ? (a) : (b))
#endif

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

#define new DEBUG_NEW

////////////////////////////////////////////////////////////////////////////
// Serialize member functions for low level classes put here
// for code swapping improvements

// CString serialization code
// String format: if < 255 chars: len:BYTE, characters in bytes
//              if >= 255 characters: 0xff, len:WORD, characters in bytes

CArchive&
operator <<(CArchive& ar, const CString& string)
{
	if (string.m_nDataLength < 255)
	{
		ar << (BYTE) string.m_nDataLength;
	}
	else
	{
		ar << (BYTE) 0xff;
		ar << (WORD) string.m_nDataLength;
	}
	ar.Write(string.m_pchData, string.m_nDataLength);
	return ar;
}

CArchive&
operator >>(CArchive& ar, CString& string)
{
	string.Empty();

	BYTE bLen;
	ar >> bLen;

	WORD nNewLen;
	if (bLen == 0xff)
		// read word of length
		ar >> nNewLen;
	else
		nNewLen = bLen;

	// read in as normal characters
	if (nNewLen != 0)
	{
		string.AllocBuffer(nNewLen);
		if (ar.Read(string.m_pchData, nNewLen) != nNewLen)
			AfxThrowArchiveException(CArchiveException::endOfFile);
	}
	return ar;
}

// Runtime class serialization code
CRuntimeClass*
CRuntimeClass::Load(CArchive& ar, UINT* pwSchemaNum)
{
	WORD nLen;
	char szClassName[64];
	CRuntimeClass* pClass;

	ar >> (WORD&)(*pwSchemaNum) >> nLen;

	if (nLen >= sizeof(szClassName) || ar.Read(szClassName, nLen) != nLen)
		return NULL;
	szClassName[nLen] = '\0';

	for (pClass = pFirstClass; pClass != NULL; pClass = pClass->m_pNextClass)
	{
		if (strcmp(szClassName, pClass->m_pszClassName) == 0)
			return pClass;
	}

	return NULL;
}

void
CRuntimeClass::Store(CArchive& ar)
	// Stores a class ref
{
	WORD nLen = (WORD)strlen(m_pszClassName);

	ar << (WORD)m_wSchema << nLen;
	ar.Write(m_pszClassName, nLen);
}

////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// Archive object input/output

	// amount to grow m_loadArray upon insert
	enum { nGrowSize = 10 };

	// minimum buffer size
	enum { nBufSizeMin = 128 };

////////////////////////////////////////////////////////////////////////////
// Pointer mapping constants
#define wNullTag      ((WORD)0)
#define wNewClassTag  ((WORD)-1)
#define wOldClassTag  ((WORD)-32768) /* 0x8000 or the class index with this */
#define nMaxMapCount  ((WORD)32766)  /* 0x7FFE last valid mapCount */


// TRY/CATCH cannot be used with /Ox
#pragma optimize("elg", off)

CArchive::CArchive(CFile* pFile, 
		UINT nMode,
		int nBufSize /* = 512 */,
		void FAR* lpBuf /* = NULL */)
{
	ASSERT_VALID(pFile);

	m_nMode = nMode;

	// initialize the buffer.  minimum size is 128
	m_lpBufStart = (BYTE FAR*)lpBuf;

	if (nBufSize < nBufSizeMin)
	{
		// force use of private buffer of minimum size
		m_nBufSize = nBufSizeMin;
		m_lpBufStart = NULL; 
	}
	else
		m_nBufSize = nBufSize;

	if (m_lpBufStart == NULL)
	{
		m_lpBufStart = (BYTE FAR*)_fmalloc(m_nBufSize);
		m_bUserBuf = FALSE;
	}
	else
		m_bUserBuf = TRUE;

	ASSERT(m_lpBufStart != NULL);
	ASSERT(AfxIsValidAddress(m_lpBufStart, m_nBufSize));

	m_lpBufMax = m_lpBufStart + m_nBufSize;
	m_lpBufCur = (IsLoading()) ? m_lpBufMax : m_lpBufStart;
	m_pFile = pFile;

	// allocate the load/store map/array fail gracefully if OOM
	TRY
	{
		if (nMode == CArchive::load) 
			m_pLoadArray = new CPtrArray;
		else
			m_pStoreMap = new CMapPtrToWord;
	}
	CATCH(CMemoryException, e)
	{
		if (!m_bUserBuf)
			_ffree(m_lpBufStart);
		THROW_LAST();
	}
	END_CATCH

	if (nMode == CArchive::load) 
	{
		ASSERT(IsLoading());
		ASSERT(nGrowSize > 0);
		m_pLoadArray->SetSize(nGrowSize, nGrowSize); 
		ASSERT(wNullTag == 0);
		m_pLoadArray->SetAt(wNullTag, NULL);
		m_nMapCount = 1;
	}
	else
	{
		ASSERT(IsStoring());

		m_pStoreMap->SetAt(NULL, wNullTag);
		m_nMapCount = 1;
	}

}
#pragma optimize("", on)


CArchive::~CArchive()
{
	ASSERT(AfxIsValidAddress(m_lpBufStart, (UINT)(m_lpBufMax - m_lpBufStart)));
	ASSERT(AfxIsValidAddress(m_lpBufCur, (UINT)(m_lpBufMax - m_lpBufCur)));
	ASSERT(m_lpBufStart != NULL);

	// Close makes m_pFile NULL.  If it is not NULL, we must Close the
	// CArchive.
	if (m_pFile)
		Close();

	if (!m_bUserBuf)
		_ffree(m_lpBufStart);

	if (m_nMode == CArchive::load)
		delete m_pLoadArray;
	else
		delete m_pStoreMap;
}

void
CArchive::Close()
{
	ASSERT_VALID(m_pFile);

	Flush();
	m_pFile = NULL;
}

void
CArchive::WriteObject(const CObject* cpOb)
{
	// object can be NULL
	ASSERT(IsStoring());    // proper direction
	ASSERT(m_lpBufStart != NULL);
	ASSERT(m_lpBufCur != NULL);

	CObject* pOb = (CObject*)cpOb;
	WORD nObIndex;

	ASSERT(sizeof(nObIndex) == 2);
	ASSERT(sizeof(wNullTag) == 2);
	ASSERT(sizeof(wNewClassTag) == 2);

	if (pOb == NULL)
		*this << wNullTag;
	else if (!(cpOb->IsSerializable()))
		AfxThrowNotSupportedException();
	else if ((nObIndex = (*m_pStoreMap)[pOb]) != 0) //ASSUME: initialized to 0 map
		*this << nObIndex;
	else
	{
		CRuntimeClass* pClassRef = pOb->GetRuntimeClass();
		WORD nClassIndex;

		// write out class id of pOb, with high bit set to indicate
		// new object follows

		// ASSUME: initialized to 0 map
		if ((nClassIndex = (*m_pStoreMap)[pClassRef]) != 0) 
		{
			// previously seen class, write out the index tagged by high bit
			*this << (WORD)(wOldClassTag | nClassIndex);
		}
		else
		{
			// new class
			*this << wNewClassTag;
			pClassRef->Store(*this);

			(*m_pStoreMap)[pClassRef] = (WORD) m_nMapCount++;
			if (m_nMapCount > nMaxMapCount)
				AfxThrowArchiveException(CArchiveException::badIndex);
		}
		// enter in stored object table and output
		(*m_pStoreMap)[pOb] = (WORD)m_nMapCount++;
		if (m_nMapCount > nMaxMapCount)
			AfxThrowArchiveException(CArchiveException::badIndex);

		pOb->Serialize(*this);
	}
}



CObject*
CArchive::ReadObject(const CRuntimeClass* pClassRefRequested)
{
	ASSERT(pClassRefRequested == NULL || AfxIsValidAddress(pClassRefRequested, sizeof(struct CRuntimeClass)));
	ASSERT(IsLoading());    // proper direction
	ASSERT(wNullTag == 0);
	ASSERT(m_lpBufStart != NULL);
	ASSERT(m_lpBufCur != NULL);

	CRuntimeClass* pClassRef;
	WORD obTag;
	WORD wSchema;

	if (pClassRefRequested && (pClassRefRequested->m_wSchema == 0xFFFF))
		AfxThrowNotSupportedException();
		
	*this >> obTag;

	//NOTE: this relies on signed testing of the tag values
	if ((short)obTag >= (short)wNullTag)
	{
		if (obTag > (WORD)m_pLoadArray->GetUpperBound())
			AfxThrowArchiveException(CArchiveException::badIndex);

		CObject* pOb = (CObject*)m_pLoadArray->GetAt(obTag);

		if (pOb != NULL && pClassRefRequested && !pOb->IsKindOf(pClassRefRequested))
			AfxThrowArchiveException(CArchiveException::badClass);
		return pOb;
	}


	if (obTag == wNewClassTag)
	{
		// new object follows a new class id
		if (m_nMapCount > nMaxMapCount)
			AfxThrowArchiveException(CArchiveException::badIndex);

		if ((pClassRef = CRuntimeClass::Load(*this, (UINT*)&wSchema)) == NULL)
		{
			AfxThrowArchiveException(CArchiveException::badClass);
			return NULL;
		}
		if (pClassRef->m_wSchema != wSchema)
		{
			AfxThrowArchiveException(CArchiveException::badSchema);
			return NULL;
		}
		m_pLoadArray->InsertAt(m_nMapCount++, pClassRef, 1);
		ASSERT(m_nMapCount < (UINT)0x7FFF);
	} 
	else
	{
		// existing class index in obTag followed by new object

		WORD nClassIndex = (WORD)(obTag & (WORD)~wOldClassTag);
		ASSERT(sizeof(nClassIndex) == 2);

		if (nClassIndex & 0x8000 || 
				nClassIndex > (WORD)m_pLoadArray->GetUpperBound())
			AfxThrowArchiveException(CArchiveException::badIndex);

		pClassRef = (CRuntimeClass*)m_pLoadArray->GetAt(nClassIndex);
	}

	// allocate a new object based on the class just acquired
	CObject* pOb = pClassRef->CreateObject();
	ASSERT(pOb != NULL);

	// Add to mapping array BEFORE de-serializing
	m_pLoadArray->InsertAt(m_nMapCount++, pOb, 1);

	pOb->Serialize(*this);

	ASSERT(pOb != NULL);
	if (pClassRefRequested && !pOb->IsKindOf(pClassRefRequested))
		AfxThrowArchiveException(CArchiveException::badClass);

	return pOb;
}


UINT
CArchive::Read(void FAR* lpBuf, UINT nMax)
{
	ASSERT_VALID(m_pFile);
	ASSERT(lpBuf != NULL);
	ASSERT(m_lpBufStart != NULL);
	ASSERT(m_lpBufCur != NULL);
	ASSERT(AfxIsValidAddress(lpBuf, nMax));
	ASSERT(AfxIsValidAddress(m_lpBufStart, (UINT)(m_lpBufMax - m_lpBufStart)));
	ASSERT(AfxIsValidAddress(m_lpBufCur, (UINT)(m_lpBufMax - m_lpBufCur)));
	ASSERT(IsLoading());

	register UINT nRead = 0;

	if (nMax == 0)
		return 0;

	while (nMax > 0)
	{
		UINT nCopy = min(nMax, (UINT)(m_lpBufMax - m_lpBufCur));
		_fmemcpy(lpBuf, m_lpBufCur, nCopy);
		m_lpBufCur += nCopy;
		lpBuf = ((BYTE FAR*)lpBuf) + nCopy;
		nMax -= nCopy;
		nRead += nCopy;
		if (nMax != 0)
			FillBuffer(min(nMax, (UINT)m_nBufSize));
	}
	return nRead;
}

void
CArchive::Write(const void FAR* lpBuf, UINT nMax)
{
	ASSERT_VALID(m_pFile);
	ASSERT(m_lpBufStart != NULL);
	ASSERT(m_lpBufCur != NULL);
	ASSERT(AfxIsValidAddress(lpBuf, nMax));
	ASSERT(AfxIsValidAddress(m_lpBufStart, (UINT)(m_lpBufMax - m_lpBufStart)));
	ASSERT(AfxIsValidAddress(m_lpBufCur, (UINT)(m_lpBufMax - m_lpBufCur)));
	ASSERT(IsStoring());

	register void FAR* lpBufT = (void FAR*)lpBuf;

	while (nMax > 0)
	{
		UINT nCopy = min(nMax, (UINT)(m_lpBufMax - m_lpBufCur));
		_fmemcpy(m_lpBufCur, lpBufT, nCopy);
		m_lpBufCur += nCopy;
		lpBufT = ((BYTE FAR*)lpBufT) + nCopy;
		nMax -= nCopy;
		if (nMax != 0)
		{
			// write out the current buffer to file
			if (m_lpBufCur != m_lpBufStart)
				m_pFile->Write(m_lpBufStart, m_lpBufCur - m_lpBufStart);

			// restore buffer to initial state
			m_lpBufCur = m_lpBufStart;
		}
	}
}


void
CArchive::Flush()
{
	ASSERT(m_lpBufStart != NULL);
	ASSERT(m_lpBufCur != NULL);
	ASSERT_VALID(m_pFile);
	ASSERT(m_lpBufStart != NULL);
	ASSERT(m_lpBufCur != NULL);
	ASSERT(AfxIsValidAddress(m_lpBufStart, (UINT)(m_lpBufMax - m_lpBufStart)));
	ASSERT(AfxIsValidAddress(m_lpBufCur, (UINT)(m_lpBufMax - m_lpBufCur)));

	if (IsLoading())
	{
		// unget the characters in the buffer, seek back unused amount
		m_pFile->Seek(-(m_lpBufMax - m_lpBufCur), CFile::current);
		m_lpBufCur = m_lpBufMax;    // empty
	}
	else
	{
		// write out the current buffer to file
		if (m_lpBufCur != m_lpBufStart)
		{
			m_pFile->Write(m_lpBufStart, m_lpBufCur - m_lpBufStart);
			m_pFile->Flush();
		}

		// restore buffer to initial state
		m_lpBufCur = m_lpBufStart;
	}
}

void 
CArchive::FillBuffer(UINT nBytesNeeded)
{
	ASSERT(IsLoading());
	ASSERT_VALID(m_pFile);
	ASSERT(m_lpBufStart != NULL);
	ASSERT(m_lpBufCur != NULL);
	ASSERT(nBytesNeeded > 0);
	ASSERT(AfxIsValidAddress(m_lpBufStart, (UINT)(m_lpBufMax - m_lpBufStart)));
	ASSERT(AfxIsValidAddress(m_lpBufCur, (UINT)(m_lpBufMax - m_lpBufCur)));


	// fill up the current buffer from file
	if (m_lpBufCur > m_lpBufStart)
	{
		// there is at least some room to fill
		UINT nUnused = 0; // bytes remaining in buffer
		UINT nActual = 0; // bytes read from file

		if ((nUnused = m_lpBufMax - m_lpBufCur) > 0)
		{
			_fmemcpy(m_lpBufStart, m_lpBufCur, m_lpBufMax - m_lpBufCur);    // copy unused
		}

		nActual = m_pFile->Read(m_lpBufStart+nUnused, m_nBufSize-nUnused);

		if (nActual < nBytesNeeded)
			// not enough data to fill request
			AfxThrowArchiveException(CArchiveException::endOfFile);

		m_lpBufCur = m_lpBufStart;
		m_lpBufMax = m_lpBufStart + nUnused + nActual;
	}
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.