我正在处理的应用程序的一部分涉及以套接字的形式逐个接收zlib(deflate)格式的压缩数据流。该例程基本上是以块的形式接收压缩数据,并在有更多数据可用时将其传递给inflate
。当inflate
返回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
表示"损坏或不完整"流。显然,在我的应用程序中数据可能会被破坏的方式超出了这个问题的范围 - 但经过一些修补,我意识到对inflate
的调用会返回{{1}在我将Z_DATA_ERROR
设置为m_strm.avail_in
之前,如果0
不 size
。换句话说,似乎inflate
失败了,因为在我设置avail_in
之前,流中已有数据。
但我的理解是,每次拨打inflate
都应该完全清空输入流,这意味着当我再次拨打inflate
时,我不应该担心,如果它没有最后一个电话结束。我的理解在这里是否正确?或者我是否始终需要检查strm.avail_in
以查看是否有待处理的输入?
另外,为什么会有待处理的输入?为什么inflate
只是在每次通话时都消耗所有可用的输入?
答案 0 :(得分:1)
inflate()
可以返回,因为它已填充输出缓冲区但未消耗所有输入数据。如果发生这种情况,您需要提供新的输出缓冲区并再次致电inflate()
,直至m_strm.avail.in == 0
。
zlib手册可以说......
详细的语义如下。膨胀执行一个或两个 以下行动:
从next_in开始解压缩更多输入并更新next_in和 因此,avail_in。如果不是所有输入都可以处理(因为那里 在输出缓冲区中没有足够的空间),next_in被更新并且 处理将在此时恢复以进行下一次inflate()调用。
您似乎假设您的压缩输入始终适合您的输出缓冲区空间,并非总是如此......
我的包装代码看起来像这样......
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
来了解所有当前输入数据何时被消耗的事实。如果输出空间已填满,则调用者再次使用Inflate(0, 0, pNewBuffer, newBufferSize);
调用以提供更多缓冲区空间...
答案 1 :(得分:0)
考虑将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()
的内部工作原理,我怀疑它有时可能需要多次运行才能提取可用数据。