zlib - gzip 的零压缩率

zlib - Zero compression rate for gzip

本文关键字:压缩率 gzip zlib      更新时间:2023-10-16

我有以下C++代码,它使用 zlib 压缩内存 缓冲区到 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()调用返回的内容,以确定压缩结果的大小。这可以是dest.size() - strm.avail_out,也可以是strm.total_out

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

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