首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Windows程序的文件块缓冲区

Windows程序的文件块缓冲区
EN

Code Review用户
提问于 2017-01-13 23:24:28
回答 1查看 161关注 0票数 4

下面的ChunkBuffer代码的目的是从给定的输入文件中指定一个“块”,并循环该块(如果需要的话)。

它的操作类似于以下伪代码,其重要区别在于真正的代码使用堆分配的缓冲区来提高性能:

代码语言:javascript
运行
复制
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的样式编写的。

我关于这个代码评审的主要目标是:

  1. 查找编码错误(不正确的缓冲、内存泄漏等)
  2. 增强代码的Windows样式。
代码语言:javascript
运行
复制
// 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
EN

回答 1

Code Review用户

发布于 2017-01-15 02:11:14

Bug

这一行不正确:

// 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的检查足以处理所有情况。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/152599

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档