zlib膨胀流和avail_in

时间:2015-03-16 21:34:56

标签: c++ zlib

我正在处理的应用程序的一部分涉及以套接字的形式逐个接收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 manualZ_DATA_ERROR表示"损坏或不完整"流。显然,在我的应用程序中数据可能会被破坏的方式超出了这个问题的范围 - 但经过一些修补,我意识到对inflate的调用会返回{{1}在我将Z_DATA_ERROR设置为m_strm.avail_in之前,如果0 size。换句话说,似乎inflate失败了,因为在我设置avail_in之前,流中已有数据

但我的理解是,每次拨打inflate都应该完全清空输入流,这意味着当我再次拨打inflate时,我不应该担心,如果它没有最后一个电话结束。我的理解在这里是否正确?或者我是否始终需要检查strm.avail_in以查看是否有待处理的输入?

另外,为什么会有待处理的输入?为什么inflate只是在每次通话时都消耗所有可用的输入?

2 个答案:

答案 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()的内部工作原理,我怀疑它有时可能需要多次运行才能提取可用数据。