zlib 膨胀流和 avail_in
zlib inflate stream and avail_in
我正在处理的应用程序的一部分涉及通过套接字逐段接收 zlib (deflate) 格式的压缩数据流。该例程基本上是接收块中的压缩数据,并在更多数据可用时将其传递给 inflate
。当 inflate
returns Z_STREAM_END
我们知道完整的对象已经到达。
基本C++ inflater函数的一个非常简化的版本如下:
void inflater::inflate_next_chunk(void* chunk, std::size_t size)
{
m_strm.avail_in = size;
m_strm.next_in = chunk;
m_strm.next_out = m_buffer;
int ret = inflate(&m_strm, Z_NO_FLUSH);
/* ... check errors, etc. ... */
}
奇怪的是,每次点赞... 40 次左右,inflate
都会失败并显示 Z_DATA_ERROR
。
根据 zlib manual,Z_DATA_ERROR
表示 "corrupt or incomplete" 流。显然,在我的应用程序中,数据可能会被破坏的方式有很多种,这超出了这个问题的范围——但经过一番修改后,我意识到对 inflate
的调用会 return Z_DATA_ERROR
如果 m_strm.avail_in
在我将其设置为 size
之前 而不是 0
。换句话说,似乎 inflate
失败了,因为在我设置 avail_in
之前,流 中已经有 数据。
但我的理解是每次调用 inflate
都应该完全清空输入流,这意味着当我再次调用 inflate
时,如果它没有完成我不必担心最后一次通话。我的理解在这里正确吗?或者我是否总是需要检查 strm.avail_in
以查看是否有待处理的输入?
此外,为什么会有待处理的输入?为什么 inflate
不在每次调用时简单地消耗所有可用输入?
inflate()
可以 return 因为它已经填满了输出缓冲区但没有消耗所有的输入数据。如果发生这种情况,您需要提供一个新的输出缓冲区并再次调用 inflate()
直到 m_strm.avail.in == 0
.
zlib 手册是这样说的...
The detailed semantics are as follows. inflate performs one or both of
the following actions:
Decompress more input starting at next_in and update next_in and
avail_in accordingly. If not all input can be processed (because there
is not enough room in the output buffer), next_in is updated and
processing will resume at this point for the next call of inflate().
您似乎假设您的压缩输入将始终适合您的输出缓冲区space,但情况并非总是如此...
我的包装器代码如下所示...
bool CDataInflator::Inflate(
const BYTE * const pDataIn,
DWORD &dataInSize,
BYTE *pDataOut,
DWORD &dataOutSize)
{
if (pDataIn)
{
if (m_stream.avail_in == 0)
{
m_stream.avail_in = dataInSize;
m_stream.next_in = const_cast<BYTE * const>(pDataIn);
}
else
{
throw CException(
_T("CDataInflator::Inflate()"),
_T("No space for input data"));
}
}
m_stream.avail_out = dataOutSize;
m_stream.next_out = pDataOut;
bool done = false;
do
{
int result = inflate(&m_stream, Z_BLOCK);
if (result < 0)
{
ThrowOnFailure(_T("CDataInflator::Inflate()"), result);
}
done = (m_stream.avail_in == 0 ||
(dataOutSize != m_stream.avail_out &&
m_stream.avail_out != 0));
}
while (!done && m_stream.avail_out == dataOutSize);
dataInSize = m_stream.avail_in;
dataOutSize = dataOutSize - m_stream.avail_out;
return done;
}
注意循环以及调用者依赖 dataInSize
来了解所有当前输入数据何时被消耗的事实。如果输出 space 已满,则调用者使用 Inflate(0, 0, pNewBuffer, newBufferSize);
再次调用以提供更多缓冲区 space...
考虑将 inflate()
调用包装在 do-while
循环中,直到流的 avail_out
不为空(即已提取一些数据):
m_strm.avail_in = fread(compressed_data_buffer, 1, some_chunk_size / 8, some_file_pointer);
m_strm.next_in = compressed_data_buffer;
do {
m_strm.avail_out = some_chunk_size;
m_strm.next_out = inflated_data_buffer;
int ret = inflate(&m_strm, Z_NO_FLUSH);
/* error checking... */
} while (m_strm.avail_out == 0);
inflated_bytes = some_chunk_size - m_strm.avail_out;
如果不调试 inflate()
的内部工作,我怀疑它有时可能只需要 运行 多次就可以提取可用数据。
我正在处理的应用程序的一部分涉及通过套接字逐段接收 zlib (deflate) 格式的压缩数据流。该例程基本上是接收块中的压缩数据,并在更多数据可用时将其传递给 inflate
。当 inflate
returns Z_STREAM_END
我们知道完整的对象已经到达。
基本C++ inflater函数的一个非常简化的版本如下:
void inflater::inflate_next_chunk(void* chunk, std::size_t size)
{
m_strm.avail_in = size;
m_strm.next_in = chunk;
m_strm.next_out = m_buffer;
int ret = inflate(&m_strm, Z_NO_FLUSH);
/* ... check errors, etc. ... */
}
奇怪的是,每次点赞... 40 次左右,inflate
都会失败并显示 Z_DATA_ERROR
。
根据 zlib manual,Z_DATA_ERROR
表示 "corrupt or incomplete" 流。显然,在我的应用程序中,数据可能会被破坏的方式有很多种,这超出了这个问题的范围——但经过一番修改后,我意识到对 inflate
的调用会 return Z_DATA_ERROR
如果 m_strm.avail_in
在我将其设置为 size
之前 而不是 0
。换句话说,似乎 inflate
失败了,因为在我设置 avail_in
之前,流 中已经有 数据。
但我的理解是每次调用 inflate
都应该完全清空输入流,这意味着当我再次调用 inflate
时,如果它没有完成我不必担心最后一次通话。我的理解在这里正确吗?或者我是否总是需要检查 strm.avail_in
以查看是否有待处理的输入?
此外,为什么会有待处理的输入?为什么 inflate
不在每次调用时简单地消耗所有可用输入?
inflate()
可以 return 因为它已经填满了输出缓冲区但没有消耗所有的输入数据。如果发生这种情况,您需要提供一个新的输出缓冲区并再次调用 inflate()
直到 m_strm.avail.in == 0
.
zlib 手册是这样说的...
The detailed semantics are as follows. inflate performs one or both of the following actions:
Decompress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in is updated and processing will resume at this point for the next call of inflate().
您似乎假设您的压缩输入将始终适合您的输出缓冲区space,但情况并非总是如此...
我的包装器代码如下所示...
bool CDataInflator::Inflate(
const BYTE * const pDataIn,
DWORD &dataInSize,
BYTE *pDataOut,
DWORD &dataOutSize)
{
if (pDataIn)
{
if (m_stream.avail_in == 0)
{
m_stream.avail_in = dataInSize;
m_stream.next_in = const_cast<BYTE * const>(pDataIn);
}
else
{
throw CException(
_T("CDataInflator::Inflate()"),
_T("No space for input data"));
}
}
m_stream.avail_out = dataOutSize;
m_stream.next_out = pDataOut;
bool done = false;
do
{
int result = inflate(&m_stream, Z_BLOCK);
if (result < 0)
{
ThrowOnFailure(_T("CDataInflator::Inflate()"), result);
}
done = (m_stream.avail_in == 0 ||
(dataOutSize != m_stream.avail_out &&
m_stream.avail_out != 0));
}
while (!done && m_stream.avail_out == dataOutSize);
dataInSize = m_stream.avail_in;
dataOutSize = dataOutSize - m_stream.avail_out;
return done;
}
注意循环以及调用者依赖 dataInSize
来了解所有当前输入数据何时被消耗的事实。如果输出 space 已满,则调用者使用 Inflate(0, 0, pNewBuffer, newBufferSize);
再次调用以提供更多缓冲区 space...
考虑将 inflate()
调用包装在 do-while
循环中,直到流的 avail_out
不为空(即已提取一些数据):
m_strm.avail_in = fread(compressed_data_buffer, 1, some_chunk_size / 8, some_file_pointer);
m_strm.next_in = compressed_data_buffer;
do {
m_strm.avail_out = some_chunk_size;
m_strm.next_out = inflated_data_buffer;
int ret = inflate(&m_strm, Z_NO_FLUSH);
/* error checking... */
} while (m_strm.avail_out == 0);
inflated_bytes = some_chunk_size - m_strm.avail_out;
如果不调试 inflate()
的内部工作,我怀疑它有时可能只需要 运行 多次就可以提取可用数据。