程序调试--基本数据类型向CMemFile的串行化

程序代码:

#include <afxwin.h> int _tmain(int argc, _TCHAR* argv[]) { CMemFile file; CArchive ar(&file, CArchive::store); LONG a = 100, b = 200; ar<<a<<b; return 0; }  

 

1、CMemFile构造。

CMemFile::CMemFile(UINT nGrowBytes) { m_nGrowBytes = nGrowBytes; // 增长量4096 m_nPosition = 0; // 当前位置 m_nBufferSize = 0; // 缓冲区大小 m_nFileSize = 0; // 已提交的文件长度。 m_lpBuffer = NULL; // 缓冲区指针 m_bAutoDelete = TRUE; // 是否自动删除 }  

 

m_nPosition是缓冲区当前指针位置,m_nFileSize是缓冲区已提交的大小,m_nPosition可能大于m_nFileSize,但这没有关系,只有m_nFileSize真正反应缓冲区中有多少数据。在下次CMemFile::Write或使用CMemFile::bufferCommit调用CMemFile::GetBufferPtr()时,m_nFileSize将被修改。

 

2、CArchive构造(代码有删减)。

CArchive::CArchive(CFile* pFile, UINT nMode, int nBufSize, void* lpBuf) { // initialize members not dependent on allocated buffer m_nMode = nMode; // 模式值 CArchive::store m_pFile = pFile; // 文件指针 &file // initialize the buffer. minimum size is 128 m_lpBufStart = (BYTE*)lpBuf; // 缓冲区首址 NULL m_bDirectBuffer = FALSE; // 是否为直接缓冲 if (nBufSize < nBufSizeMin) { // force use of private buffer of minimum size m_nBufSize = nBufSizeMin; m_lpBufStart = NULL; } else m_nBufSize = nBufSize; // m_nBufSize = 4096 nBufSize = m_nBufSize; // 4096 if (m_lpBufStart == NULL) { // check for CFile providing buffering support // 调用CMemFile::GetBufferPtr()函数,判断是否为直接缓冲。 m_bDirectBuffer = m_pFile->GetBufferPtr(CFile::bufferCheck)&CFile::bufferDirect; if (!m_bDirectBuffer) // m_bDirectBuffer = TRUE { // no support for direct buffering, allocate new buffer m_lpBufStart = new BYTE[m_nBufSize]; m_bUserBuf = FALSE; } else { // CFile* supports direct buffering! nBufSize = 0; // will trigger initial FillBuffer } } m_lpBufMax = m_lpBufStart + nBufSize; // NULL m_lpBufCur = (IsLoading()) ? m_lpBufMax : m_lpBufStart; // NULL }  

 

构造完成的时候,m_ lpBufStart = NULL, m_lpBufMax = NULL, m_lpBufCur = NULL, m_ nBufSize = 4096, m_bDirectBuffer = TRUE.

    关于直接缓冲:CMemFile中是一大片缓冲区,比如是0~12040这么大范围。而CArchive看到的只是其中一部分2048~6144这么大范围(6144 2048 = m_nBufSize)CArchive每次直接向它看到的缓冲区写入数据,当写满的时候,就通过CMemFile::bufferCommit参数调用CMemFile::GetBufferPtr()提交缓冲区,CMemFile此时更新文件大小和起始指针。CArchive的当前缓冲区被提交之后,它继续向CMemFile申请一块缓冲区,然后继续写数据。

 

3、写入数据。

CArchive& CArchive::operator<<(LONG l) { if(!IsStoring()) AfxThrowArchiveException(CArchiveException::readOnly,m_strFileName); if (m_lpBufCur + sizeof(LONG) > m_lpBufMax) Flush(); *(UNALIGNED LONG*)m_lpBufCur = l; m_lpBufCur += sizeof(LONG); return *this; }  

因为此时m_lpBufCur和m_lpBufMax都是NULL,所以执行Flush()。在这个函数中不仅使CMemFile申请了缓冲区,也让CArchive获得了缓冲区界面。

 

4、第一次执行CArchive::Flush()刷新缓冲区。每次Flush的时候,根据CArchive中的指针更新CMemFile中的位置信息,然后分配更大的内存区。

void CArchive::Flush() { if (!m_bDirectBuffer) // m_bDirectBuffer = TRUE { // write out the current buffer to file if (m_lpBufCur != m_lpBufStart) m_pFile->Write(m_lpBufStart, ULONG(m_lpBufCur - m_lpBufStart)); } else { // commit current buffer if (m_lpBufCur != m_lpBufStart) // FALSE m_pFile->GetBufferPtr(CFile::bufferCommit, ULONG(m_lpBufCur - m_lpBufStart)); // get next buffer VERIFY(m_pFile->GetBufferPtr(CFile::bufferWrite, m_nBufSize, (void**)&m_lpBufStart, (void**)&m_lpBufMax) == (UINT)m_nBufSize); } m_lpBufCur = m_lpBufStart; }  

通过CFile::bufferCommit参数调用CMemFile::GetBufferPtr()提交缓冲区,但其实没有提交任何数据。

然后,第一次通过CFile::bufferWrite参数调用CMemFile::GetBufferPtr()时,CMemFile分配了缓冲区内存,CArchive获得了缓冲区界面。

 

4、CMemFile::GetBufferPtr()函数。

 

1、 nCommand4种可能,定义在CFile中,分别是:bufferReadbufferWritebufferCommitbufferCheck

bufferCheck用于获取当前缓冲区是直接缓冲bufferDirect,还是块缓冲bufferBlocking

bufferCommit用于提交缓冲区,此时nCount用于表明提交字节数。

bufferWrite用于向外界提供一块缓冲区界面。nCount表明外界申请的字节数,ppBufStart返回该缓冲区界面首地址,ppBufMax返回该缓冲区界面最大地址,函数返回值表明缓冲区容量,可能小于nCount

bufferRead是向外界提供一块数据缓冲区,用于从其中读取数据。ppBufStart用于返回缓冲区界面首地址,ppBufMax用于返回缓冲区界面末地址。

UINT CMemFile::GetBufferPtr(UINT nCommand, UINT nCount, void** ppBufStart, void**ppBufMax) { if (nCommand == bufferCheck) { // only allow direct buffering if we're // growable if (m_nGrowBytes > 0) return bufferDirect; else return 0; } if (nCommand == bufferCommit) { // commit buffer ASSERT(ppBufStart == NULL); ASSERT(ppBufMax == NULL); m_nPosition += nCount; if (m_nPosition > m_nFileSize) m_nFileSize = m_nPosition; return 0; } if (ppBufStart == NULL || ppBufMax == NULL) { return 0; } // when storing, grow file as necessary to satisfy buffer request if (nCommand == bufferWrite) { if (m_nPosition + nCount < m_nPosition || m_nPosition + nCount < nCount) { AfxThrowInvalidArgException(); } if (m_nPosition + nCount > m_nBufferSize) // 超出缓冲区容量 { GrowFile(m_nPosition + nCount); } } // store buffer max and min *ppBufStart = m_lpBuffer + m_nPosition; // end of buffer depends on whether you are reading or writing if (nCommand == bufferWrite) *ppBufMax = m_lpBuffer + min(m_nBufferSize, m_nPosition + nCount); else { if (nCount == (UINT)-1) nCount = UINT(m_nBufferSize - m_nPosition); *ppBufMax = m_lpBuffer + min(m_nFileSize, m_nPosition + nCount); m_nPosition += LPBYTE(*ppBufMax) - LPBYTE(*ppBufStart); } // return number of bytes in returned buffer space (may be <= nCount) return ULONG(LPBYTE(*ppBufMax) - LPBYTE(*ppBufStart)); }  

该函数中的GrowFile函数很关键,它负责在缓冲区剩余空间不足时申请缓冲区。

 

5、1、CMemFile::GrowFile()增长CMemFile文件大小。如果dwNewLen大于当前缓冲区大小,将重新分配内存。如果m_nGrowBytes == 0表明缓冲区不可增长,GrowFile函数会抛出一个内存异常。然后,GrowFile会在当前m_nBufferSize的基础上不断增加m_nGorwBytes,直到大于dwNewLen,这样便计算出来了新缓冲区大小。接下来判断是否已经分配过缓冲区,如果没有则调用Alloc分配缓冲,否则调用ReAlloc在原始缓冲区的基础上分配新缓冲区。

 

 

 

 

 

void CMemFile::GrowFile(SIZE_T dwNewLen) { if (dwNewLen > m_nBufferSize) { // grow the buffer SIZE_T dwNewBufferSize = m_nBufferSize; // watch out for buffers which cannot be grown! if (m_nGrowBytes == 0) AfxThrowMemoryException(); // determine new buffer size while (dwNewBufferSize < dwNewLen) dwNewBufferSize += m_nGrowBytes; // allocate new buffer BYTE* lpNew; if (m_lpBuffer == NULL) lpNew = Alloc(dwNewBufferSize); // 分配内存 else lpNew = Realloc(m_lpBuffer, dwNewBufferSize); if (lpNew == NULL) AfxThrowMemoryException(); m_lpBuffer = lpNew; m_nBufferSize = dwNewBufferSize; } }  

 

 

你可能感兴趣的:(程序调试--基本数据类型向CMemFile的串行化)