下面的ChunkBuffer
代码的目的是从给定的输入文件中指定一个“块”,并循环该块(如果需要的话)。
它的操作类似于以下伪代码,其重要区别在于真正的代码使用堆分配的缓冲区来提高性能:
chunk c;
chunk_init(&c, input_file, begin_offset, size_of_chunk);
while (/* still need to read bytes */)
{
byte b = chunk_next_byte(&c); // may loop to `begin_offset`
}
ChunkBuffer
代码是不可移植的,适合在Windows程序中使用,并且是以接近Windows的样式编写的。
我关于这个代码评审的主要目标是:
// enable Unicode support within the Windows API
#define UNICODE
#define _UNICODE
// skip the inclusion of certain internal headers
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef struct
{
BOOL fError; ///< ChunkBuffer's error flag.
HANDLE hHeap; ///< Handle of the heap to allocate memory from.
HANDLE hFile; ///< Handle of the file to be read.
LPBYTE lpbBuffer; ///< Buffer of bytes read.
LARGE_INTEGER liOffset; ///< User-specified start offset in the file where the chunk begins.
LARGE_INTEGER liLength; ///< User-specified chunk size in bytes.
SIZE_T cbBufferSize; ///< User-specified size for the internal byte buffer.
SIZE_T index; ///< Index of the current byte.
SIZE_T cbAvailable; ///< Bytes available in the internal byte buffer.
LARGE_INTEGER liRemaining; ///< Bytes remaining in the chunk.
} CHUNKBUFFER, *PCHUNKBUFFER;
typedef const CHUNKBUFFER * PCCHUNKBUFFER;
void ChunkBuffer_Init(
PCHUNKBUFFER pCB,
HANDLE hHeap, HANDLE hFile,
LARGE_INTEGER liOffset,
LARGE_INTEGER liLength,
SIZE_T cbBufferSize);
void ChunkBuffer_Free(PCHUNKBUFFER pCB);
BOOL ChunkBuffer_Error(PCCHUNKBUFFER pCB);
BYTE ChunkBuffer_NextByte(PCHUNKBUFFER pCB);
///
/// @brief Initializes a ChunkBuffer.
/// @param [out] pCB ChunkBuffer to initialize.
/// @param hHeap Handle of the heap to allocate memory from.
/// @param hFile Handle of the file to read data from.
/// @param liOffset User-specified start offset in the file where the chunk begins.
/// @param liLength User-specified chunk size in bytes.
/// @param cbBufferSize User-specified size for the internal byte buffer.
///
void ChunkBuffer_Init(
PCHUNKBUFFER pCB,
HANDLE hHeap,
HANDLE hFile,
LARGE_INTEGER liOffset,
LARGE_INTEGER liLength,
SIZE_T cbBufferSize)
{
pCB->hHeap = hHeap;
pCB->hFile = hFile;
pCB->liOffset = liOffset;
pCB->liLength = liLength;
pCB->cbBufferSize = cbBufferSize;
pCB->fError = FALSE;
pCB->lpbBuffer = NULL;
pCB->index = 0;
pCB->cbAvailable = 0;
pCB->liRemaining = liLength;
}
///
/// @brief Frees the resources allocated by a ChunkBuffer.
/// @note This function does not close the handle of the file
/// associated with the ChunkBuffer; this is on purpose.
/// @param [in,out] pCB ChunkBuffer to deinitialize.
///
void ChunkBuffer_Free(PCHUNKBUFFER pCB)
{
if (pCB->lpbBuffer != NULL)
HeapFree(pCB->hHeap, 0, pCB->lpbBuffer);
pCB->hHeap = INVALID_HANDLE_VALUE;
pCB->hFile = INVALID_HANDLE_VALUE;
pCB->lpbBuffer = NULL;
}
///
/// @brief Returns the ChunkBuffer's error flag.
/// @param [in] pCB ChunkBuffer to inspect.
/// @returns Whether or not the ChunkBuffer is in an error state.
/// @retval TRUE An error occurred.
/// @retval FALSE No errors occurred.
///
BOOL ChunkBuffer_Error(PCCHUNKBUFFER pCB)
{
return pCB->fError;
}
///
/// @brief Retrieves the next byte in the ChunkBuffer.
/// @param [in,out] pCB ChunkBuffer to read.
/// @returns The next byte.
///
BYTE ChunkBuffer_NextByte(PCHUNKBUFFER pCB)
{
#define SET_FERROR_IF(x) if (x) { \
pCB->fError = TRUE; \
return 0; \
} else (void)0
if (pCB->fError) // the ChunkBuffer is in an error state
return 0;
// allocate memory for the internal byte buffer, if needed;
// and if so, then also read data from the file
if (pCB->lpbBuffer == NULL)
{
const LPBYTE lpbTemp = HeapAlloc(pCB->hHeap, 0, pCB->cbBufferSize);
const SIZE_T cbMinChunk = (SIZE_T)min((LONGLONG)pCB->cbBufferSize, pCB->liLength.QuadPart);
SET_FERROR_IF(lpbTemp == NULL);
pCB->lpbBuffer = lpbTemp;
SET_FERROR_IF(!SetFilePointerEx(pCB->hFile, pCB->liOffset, NULL, FILE_BEGIN));
SET_FERROR_IF(!ReadFile(pCB->hFile, pCB->lpbBuffer, cbMinChunk, &pCB->cbAvailable, NULL));
pCB->liRemaining.QuadPart -= pCB->cbAvailable;
}
if (pCB->index == pCB->cbAvailable) // may need to read from the file again
{
// only read new data if the user-specified chunk length is greater than
// that of the internal buffer; otherwise all data is already available
if ((LONGLONG)pCB->cbBufferSize < pCB->liLength.QuadPart)
{
if (pCB->liRemaining.QuadPart == 0) // reached the end of the user-specified chunk
{
SET_FERROR_IF(!SetFilePointerEx(pCB->hFile, pCB->liOffset, NULL, FILE_BEGIN));
pCB->liRemaining = pCB->liLength;
}
const SIZE_T cbMinSize = (SIZE_T)min((LONGLONG)pCB->cbBufferSize, pCB->liRemaining.QuadPart);
SET_FERROR_IF(!ReadFile(pCB->hFile, pCB->lpbBuffer, cbMinSize, &pCB->cbAvailable, NULL));
if (pCB->cbAvailable == 0) // end of key file was reached
{
SET_FERROR_IF(!SetFilePointerEx(pCB->hFile, pCB->liOffset, NULL, FILE_BEGIN));
SET_FERROR_IF(!ReadFile(pCB->hFile, pCB->lpbBuffer, cbMinSize, &pCB->cbAvailable, NULL));
}
pCB->liRemaining.QuadPart -= pCB->cbAvailable;
}
pCB->index = 0;
}
return pCB->lpbBuffer[pCB->index++];
#undef SET_FERROR_IF
}
//
// Basic usage preparations
//
#if 0
CHUNKBUFFER cb;
HANDLE heap = GetProcessHeap();
HANDLE input_file = CreateFile(/* ... */);
LARGE_INTEGER begin; // ...
LARGE_INTEGER size; // ...
#define BUFFER_SIZE 4096
ChunkBuffer_Init(&cb, heap, input_file, begin, size, BUFFER_SIZE);
BYTE b = ChunkBuffer_NextByte(&cb);
ChunkBuffer_Free(&cb);
#endif
发布于 2017-01-15 02:11:14
这一行不正确:
// only read new data if the user-specified chunk length is greater than // that of the internal buffer; otherwise all data is already available if ((LONGLONG)pCB->cbBufferSize < pCB->liLength.QuadPart)
您在这里假设,如果缓冲区大小大于所请求的长度,那么您将已经读取了全部数量。但是,如果初始ReadFile()
调用返回的字节少于请求的字节(即cbAvailable
小于liLength.QuadPart
),那么您将永远不会读取文件的其余部分。我相信您可以删除这个if
语句,因为稍后对cbRemaining
的检查足以处理所有情况。
https://codereview.stackexchange.com/questions/152599
复制相似问题