Программирование на языке MFC

Мой второй блог в серии программирования

Метод FillBuffer


Первым делом оператор проверяет, хватит ли распределенной под буфер памяти для того, чтобы записать еще одно слово. Если памяти не хватает, то вызывается метод FillBuffer(), которому в качестве аргу­мента передается количество байтов, недостаточных для нормаль­ного выполнения операции записи. Исходный код метода FillBuffer() можно найти в файле агссоге.срр. Я прошу читателя запастись терпе­нием, потому что код метода достаточно объемен:

void CArchive::FillBuffer(UINT nBytesNeeded) {

ASSERT_VALID(m_pFile); ASSERT (IsLoadingO ) ;

ASSERT(m_bDirectBuffer || m_lpBufStart !=NULL); ASSERT(m_bDirectBuffer || m_lpBufCur !=NULL); ASSERT(nBytesNeeded > 0);

ASSERT(nBytesNeeded <= (UINT)m_nBufSize); ASSERT(m_lpBufStart == NULL ||

AfxIsValidAddress(m_lpBufStart,

m_lpBufMax – m_lpBufStart, FALSE)); ASSERT(m_lpBufCur == NULL ||

AfxIsValidAddress(m_lpBufCur,

m_lpBufMax – m_lpBufCur, FALSE));

UINT nUnused = m_lpBufMax – m_lpBufCur;

ULONG nTotalNeeded = ( (ULONG)nBytesNeeded) + nUnused;

// fill up the current buffer from file if (!m_bDirectBuffer) {

ASSERT(m_lpBufCur != NULL); ASSERT(m_lpBufStart != NULL); ASSERT(m_lpBufMax !=NULL);

if (m_lpBufCur > m_lpBufStart) {

// copy unused

if ( (int)nUnused > 0) {

memmove(m_lpBufStart, m_lpBufCur, nUnused); m_lpBufCur = m_lpBufStart; m_lpBufMax = m_lpBufStart + nUnused;

}

// read to satisfy nBytesNeeded or nLeft if possible UINT nRead = nUnused; UINT nLeft = m_nBufSize-nUnused; UINT nBytes;

BYTE* lpTemp = m_lpBufStart + nUnused;

do

{

nBytes = m_pFile->Read(lpTemp, nLeft); lpTemp = lpTemp + nBytes; nRead += nBytes; nLeft -= nBytes;

}

while (nBytes > 0 && nLeft > 0 && nRead < nBytesNeeded);

m_lpBufCur = m_lpBufStart; m_lpBufMax = m_lpBufStart + nRead;

}

}

else {

// seek to unused portion and get the buffer

I/ starting there if (nUnused != 0)

m_pFile->Seek(-(LONG)nUnused, CFile::current); UINT nActual = m_pFile->GetBufferPtr(CFile::bufferRead,

m_nBufSize, (void**)&m_lpBufStart, (void**)&m_lpBufMax) ; ASSERT(nActual == (UINT)(m_lpBufMax – m_lpBufStart)); m_lpBufCur = m_lpBufStart;

}

// not enough data to fill request?

if ( (ULONG) (m_lpBufMax – m_lpBufCur) < nTotalNeeded) AfxThrowArchiveException(CArchiveException::endOfFile);

}

После проверки и, при необходимости, выдачи всех предупреж­дений метод определяет прежде всего число неиспользуемых бай­тов в буфере. Обратите внимание на то, что это значение вычис­ляется как разность между значениями полей mJpBufMax и mJpBufCur, а не (mJpBufStart + m_nBufSize – mJpBufCur). Почему? Пока непонятно… Затем определяется, сколько всего бай­тов необходимо для того, чтобы операция чтения могла завер­шиться нормально. Естественно, число необходимых байтов равно сумме числа неиспользуемых байтов в буфере и числа недоста­ющих байтов, переданных методу в качестве аргумента. Далее про­исходит следующее. Если у нас буфер занят частично, т. е. число неиспользуемых байтов не равно нулю, то мы копируем свободный участок памяти в начало буфера. При этом указатель на текущую позицию в буфере устанавливается на начало буфера. Это понятно. Но непонятно следующее – почему указатель m JpBufMax устанав­ливается на конец переписанного участка? Подождите, пожалуйста, осталось совсем недолго…

Идем дальше. И вот здесь… Посмотрите: значение переменной nRead делается равным числу неиспользованных байтов! Опять за­гадка. .. Определяем, сколько байтов еще необходимо считать для того, чтобы заполнить буфер до конца, читаем из файла в буфер… Полю mJpBufCur присваиваивается значение указателя на начало буфера… Вот оно! В поле mJpBufMax мы записываем число считан­ных байтов! Т. е. поле mJpBufMax указывает на конец тех данных, которые мы считали! Обычно поле mJpBufMax будет указывать на считывании достигли конца файла! В этом случае поле mJpBufMax будет указывать куда-то на середину буфера, на последний байт считанных данных! Т. е. поле mJpBufMax указывает на конец считанных данных!

Понятно теперь, почему неиспользованным участком считается участок от указателя на текущую позицию в буфере до указателя mJpBufMax. Конечно же! Ведь после предыдущих операций чтения мы могли выполнять операции записи, т. е. указатель на текущую позицию в файле мог сместиться в сторону уменьшения. Но ведь в этом участке находятся данные, которые мы уже считывали! Зачем нам осуществлять считывание одних и тех же данных повторно? Естественно, нам легче их скопировать из одного места буфера в другое, чем осуществлять лишние обращения к диску!

Схематично взаимоотношения буфера и его полей показаны на 9.

Подводя итог сказанному выше, можно сделать вывод о том, что метод FHIBufferQ осуществляет заполнение выделенного ар­хиву буфера данными из файла, осуществляя буферированное чтение данных.

Обратите внимание, что указатель m JpBufCur после осуществ­ления считывания данных указывает на начало буфера. А теперь, если мы вернемся в код перегруженного оператора «»», мы уви­дим, что сразу после вызова FillBuffer() оператор записывает по указанной ему ссылке считанное слово и увеличивает указатель mJpBufCur на величину, равную размеру считанного данного. Ука­затель mJpBufCur занимает то положение, которое и должен за­нимать!

Похожие статьи: