zlib - gzip 的零压缩率

zlib - Zero compression rate for gzip

我有以下使用 zlib 压缩内存的 C++ 代码 缓冲到 gzip 编码流中:

void compress(const std::vector<char>& src)
{
    static constexpr int DEFAULT_WINDOW_BITS = 15;
    static constexpr int GZIP_WINDOW_BITS = DEFAULT_WINDOW_BITS + 16;
    static constexpr int GZIP_MEM_LEVEL = 8;

    z_stream stream;

    const auto srcData = reinterpret_cast<unsigned char*>(const_cast<char*>(src.data()));

    stream.zalloc = Z_NULL;
    stream.zfree = Z_NULL;
    stream.opaque = Z_NULL;
    stream.next_in = srcData;
    stream.avail_in = src.size();

    auto result = deflateInit2(&stream,
                               Z_DEFAULT_COMPRESSION,
                               Z_DEFLATED,
                               GZIP_WINDOW_BITS,
                               GZIP_MEM_LEVEL,
                               Z_DEFAULT_STRATEGY);

    if (result == Z_OK)
    {
        std::vector<char> dest(deflateBound(&stream, stream.avail_in));
        const auto destData = reinterpret_cast<unsigned char*>(dest.data());

        stream.next_out = destData;
        stream.avail_out = dest.size();

        result = deflate(&stream, Z_FINISH);

        if (result == Z_STREAM_END)
        {
            std::cout << "Original: " << src.size() << "; compressed: " << dest.size() << std::endl;
        }
        else
        {
            std::cerr << "Error when compressing: code " << std::to_string(result);
        }

        result = deflateEnd(&stream);

        if (result != Z_OK)
        {
            std::cerr << "Error: Cannot destroy deflate stream: code " << std::to_string(result) << std::endl;
        }
    }
    else
    {
        std::cerr << "Error: Cannot initialize deflate stream: code " << std::to_string(result) << std::endl;
    }
}

虽然函数成功完成,但我没有得到任何压缩 根本。事实上,对于一个仅包含字符 'a' 的 3MB 文件 重复多次,我得到以下信息:

Original: 3205841; compressed: 3206843

我是不是做错了什么?

(请注意,这是原始代码的简化版本;实际上,我会使用 RAII 和异常来处理资源和错误)。

问题的评论就是答案,所以记录在这里以供后人使用...

dest.size() 没有,也不能被 deflate() 改变。您从 dest.size() 得到的只是输出缓冲区 压缩之前的大小。您需要查看从 deflate() 调用返回的 something 以确定压缩结果的大小。这可以是 dest.size() - strm.avail_out,或 strm.total_out

在单个调用中执行压缩意味着您需要将输入和输出缓冲区大小都放入 unsigned,通常为 32 位。因此,您只能压缩大约 4 GB 的数据。如果您可能需要做更多的事情,那么您将需要一个循环,为较小的块调用 deflate()。可能更小的块,以 10 或 100 千字节为单位。这是使用 deflate() 的常用方法,因为它占用的内存要少得多,并且可以防止您的例程成为这方面的资源消耗者。

deflateBound()专门用于支持使用单个deflate()调用。它提供了可能的压缩大小的上限,可以比输入数据大 一点点。当输入数据不可压缩时就是这种情况,例如已经压缩或随机。