

Программирование на языке 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 занимает то положение, которое и должен занимать!
Похожие статьи: Load
