zlib解压缩操作的inflate解压缩操作

3
我有一个数据缓冲区,其中包含多个压缩成员,可以是deflate或zlib压缩成员。
我发现zlib的inflate调用在处理第一个压缩块后返回Z_STREAM_END。这里多个压缩成员的数量可以是任意的(在我的示例中为3)。但是此数据来自未详细说明数据中压缩成员数量的其他方面。
那么,我该如何实现使用zlib inflate功能,以使其能够处理多个压缩成员?
以下是一个简单粗暴的示例,我试图阐述我的问题。这涉及到zlib 1.2.5库的情况。
/* example.c -- understanding zlib inflate/decompression operation
 */

#define CHECK_ERR(err, msg) { \
    if (err != Z_OK) { \
        std::cerr << msg << " error: " << err << std::endl; \
        exit(1); \
    } \
}

/* ===========================================================================
 * deflate() to create compressed data
 */
void test_deflate(std::vector<uint8_t> & input_data, std::vector<uint8_t>& compr)
{
    z_stream c_stream; /* compression stream */
    int err;

    compr.clear();

    c_stream.zalloc = (alloc_func)0;
    c_stream.zfree = (free_func)0;
    c_stream.opaque = (voidpf)0;

    err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
    CHECK_ERR(err, "deflateInit");

    c_stream.next_in  = &input_data[0];
    c_stream.avail_in = input_data.size();

    for (;;) {
        uint8_t c_buffer[10] = {};
        c_stream.next_out  = &c_buffer[0];
        c_stream.avail_out = 10;

        err = deflate(&c_stream, Z_FINISH);
        if (err == Z_STREAM_END)
        {
            for (int i = 0; i < (10 - c_stream.avail_out); i++)
                compr.push_back(c_buffer[i]);
            break;
        }
        CHECK_ERR(err, "deflate");
        for (int i = 0; i < (10 - c_stream.avail_out); i++)
            compr.push_back(c_buffer[i]);
    }

    std::cout << "Compressed data (size = " << std::dec << compr.size() << ") = ";
    for (int i = 0; i < compr.size(); i++)
        std::cout << (uint32_t) compr[i];
    std::cout << std::endl;

    err = deflateEnd(&c_stream);
    CHECK_ERR(err, "deflateEnd");
}

/* ===========================================================================
 * Test inflate()
 */
void test_inflate(std::vector<uint8_t> &compr,
                  std::vector<uint8_t> &uncompr)
{
    int err;
    z_stream d_stream; /* decompression stream */

    uncompr.clear();

    d_stream.zalloc = Z_NULL;
    d_stream.zfree = Z_NULL;
    d_stream.opaque = Z_NULL;
    d_stream.avail_in = 0;
    d_stream.next_in = Z_NULL;
    err = inflateInit(&d_stream);
    CHECK_ERR(err, "inflateInit");

    d_stream.avail_in = compr.size();
    d_stream.next_in  = &compr[0];

    for(;;) {
        uint8_t d_buffer[10] = {};
        d_stream.next_out = &d_buffer[0];
        d_stream.avail_out = 10;

        err = inflate(&d_stream, Z_NO_FLUSH);

        if (err == Z_STREAM_END) {
            for (int i = 0; i < (10 - d_stream.avail_out); i++)
                uncompr.push_back(d_buffer[i]);
            if (d_stream.avail_in == 0)
                break;
        }

        CHECK_ERR(err, "inflate");
        for (int i = 0; i < (10 - d_stream.avail_out); i++)
            uncompr.push_back(d_buffer[i]);
    }
    err = inflateEnd(&d_stream);
    CHECK_ERR(err, "inflateEnd");

    std::cout << "Uncompressed data (size = " << std::dec << uncompr.size() << ") = ";
    for (int i = 0; i < uncompr.size(); i++)
        std::cout << (uint32_t) uncompr[i];
    std::cout << std::endl;
}


/* ===========================================================================
 * Usage:  example
 */

int main(int argc, char **argv)
{
    std::vector<uint8_t> input_data;
    std::vector<uint8_t> compr, multiple_compr;
    std::vector<uint8_t> uncompr;

    std::cout << "Input Data (in hex) = ";
    for (int i=0; i<32; i++) {
        input_data.push_back((uint8_t)i);
        if( i && (i % 2 == 0))
            std::cout << " ";
        std::cout << std::hex << (uint32_t)input_data[i];
    }
    std::cout << std::endl;

    // create compressed buffer-1 from input data
    test_deflate(input_data, compr);

    // copy compressed buffer-1 data into multiple compressed member buffer
    multiple_compr = compr;
    compr.clear();

    // create compressed buffer-2 from input data
    test_deflate(input_data, compr);

    // append data of compressed buffer-2 into multiple compressed member buffer
    for(int i=0; i< compr.size(); i++)
    {
        multiple_compr.push_back(compr[i]);
    }

    // create decompressed output
    test_inflate(multiple_compr, uncompr);

    // compare decompressed data with input data
    std::vector<uint8_t> final_data;
    final_data.push_back(input_data);
    final_data.push_back(input_data);
    if (final_data == uncompr)
       std::cout << "Matched" << std::endl;
    else
       std::cout << "Not Matched" << std::endl;

    return 0;
}

1) 第二次调用inflate返回错误,但我希望它能成功执行,为什么会这样?

2) 当我在inflate调用参数中使用Z_FINISH时,它返回错误,为什么我不能在这里使用Z_FINISH?

请修正我的示例,并提出一些优化的方法来完成相同的任务。


有点不太清楚。您得到块数据并对其进行解压缩。但是您不知道可能会获得多少块?这似乎与您包装在周围的整个zlib故事无关。 - Jongware
可能是在zlib inflate调用中发现的,但我想知道是否有像我将数据缓冲区分配给avail_in这样的机制。如果存在多个压缩成员(即3个),则在解压缩第一个成员后,avail_in仍应具有其他压缩成员(即2个)的数据。我不能继续进行吗?由于我正在学习zlib使用方法,所以想法还不太清楚。如果您可以分享一些zlib inflate的正确使用示例,那就更好了。在参考zlib手册之后,对我来说仍然不太清楚。 - ronex dicapriyo
啊,等等。所以在解压之后,你的输入缓冲区中可能会剩余数据。请参见http://www.zlib.net/zlib_how.html,其中在一半左右提到了这一点。(我自己从未使用过这个,所以我要退出了。) - Jongware
1个回答

2

只需对剩余数据重复执行inflate操作即可。

使用inflateReset()代替inflateEnd()inflateInit()可以节省一些不必要的free和malloc。您可能还有上次inflate留下的一些数据在next_inavail_in中,因此先使用这些数据,然后重新加载。


这似乎可以完美地工作,如果可能的话,您能否请提供一个示例代码。实际上,对于压缩数据,我已经使用了一些预构建的压缩缓冲区,是否有可能我可以使用defalte操作生成多个压缩成员数据的示例代码,并在该结果上进行解压缩,从而创建包含先前多个压缩输出数据的未压缩数据的单个解压缩缓冲区。这将是理解zlib的好的快速入门。感谢您的回答。 - ronex dicapriyo
我无法理解你的问题。至于示例代码,你可以查看zpipe.c的这个带注释源代码 - Mark Adler
我没有时间去审核别人的代码。你可以尝试访问http://codereview.stackexchange.com/,看看是否有人可以帮你审核代码。 - Mark Adler
好的,我能理解。谢谢你提供的所有帮助。 - ronex dicapriyo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接