【问题标题】:zlib inflate stream and avail_inzlib 膨胀流和avail_in
【发布时间】:2015-03-16 21:34:55
【问题描述】:

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

但我的理解是每次调用inflate 都应该完全清空输入流,这意味着当我再次调用inflate 时,如果最后一次调用没有结束,我不必担心。我的理解在这里正确吗?还是我总是需要检查strm.avail_in 以查看是否有待处理的输入?

另外,为什么会有待处理的输入?为什么inflate 不简单地在每次调用时消耗所有可用的输入?

【问题讨论】:

    标签: c++ zlib


    【解决方案1】:

    inflate() 可以返回,因为它已填满输出缓冲区,但未消耗所有输入数据。如果发生这种情况,您需要提供一个新的输出缓冲区并再次调用inflate() 直到m_strm.avail.in == 0

    zlib 手册有这样说...

    详细语义如下。 inflate 执行以下一项或两项 以下操作:

    从 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); 调用以提供更多缓冲区空间...

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        猜你喜欢
        • 2021-05-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多