Zlib单字节分块解压报错Z_BUF_ERROR问题求助
分块解压Zlib数据时Z_BUF_ERROR问题排查与解决
问题描述
我有一个通过Zlib压缩的数据缓冲区,希望分块(每次1字节)完成解压,而非一次性分配足够大的输出缓冲区来容纳所有解压后的数据。参照zpipe.c的思路实现代码后,inflate始终抛出Z_BUF_ERROR错误,无法将数据解压到1字节缓冲区中。
原代码
#include <iostream> #include <string> #include <zlib.h> static std::string in_buff_string; static std::string out_buff_string; static z_stream stream; int my_uncompress_func__from_memory(unsigned long CHUNK_SIZE, char* char_p__to_compressed_data, unsigned long size__compressed_data) { //char_p__to_compressed_data - buffer with compresed data //size__compressed_data - size data in buffer "char_p__to_compressed_data " CHUNK_SIZE = 1; //1 byte //------------------------------------- in_buff_string.resize(CHUNK_SIZE); out_buff_string.resize(CHUNK_SIZE); int err; //------------------------------------- stream = { 0 }; //------------------------------------- err = inflateInit2(&stream, 15); if (err != Z_OK && err != Z_STREAM_END) { return 1; } //-------------------------------------- //---------------------------------------------------- int flush; size_t cntr = 0; //---------------------------------------------------- do { if (cntr != size__compressed_data) { //---------------------------------------------- memcpy(&in_buff_string[0], char_p__to_compressed_data + cntr, CHUNK_SIZE); stream.avail_in = CHUNK_SIZE; stream.next_in = (Bytef*)&in_buff_string[0]; cntr++; //---------------------------------------------- flush = Z_NO_FLUSH; } else { flush = Z_FINISH; } do { stream.avail_out = CHUNK_SIZE; stream.next_out = (Bytef*)&out_buff_string[0]; //------------------------------------------------ err = inflate(&stream, flush); if (err == Z_BUF_ERROR) { std::cout << "Z_BUF_ERROR" << std::endl; return 1; } //------------------------------------------------ int nbytes = CHUNK_SIZE - stream.avail_out; if (nbytes != 0) { std::cout << out_buff_string << std::endl; } } while (stream.avail_out == 0); } while (flush != Z_FINISH); inflateEnd(&stream); return 0; }
原因分析
- 错误的
Z_BUF_ERROR处理逻辑:Z_BUF_ERROR不是致命错误,它仅表示当前输入或输出缓冲区无法满足解压需求——要么需要更多输入数据,要么需要更大的输出空间。原代码直接在收到该错误时退出,中断了解压流程。 - 1字节输入的局限性:Zlib的deflate算法需要累积一定量的压缩数据才能解码出输出,单次仅喂入1字节输入时,可能无法立即生成输出,此时
inflate返回Z_BUF_ERROR提示需要更多输入,而非操作失败。 - 循环逻辑漏洞:内层循环仅在输出缓冲区被填满时继续,当输入不足无法产生输出时,内层循环跳出,但外层循环没有处理这种需要继续补充输入的情况,直接进入下一轮,导致流程异常。
解决办法
- 修正
Z_BUF_ERROR的判断逻辑:仅当inflate返回Z_BUF_ERROR,且已无剩余输入数据(stream.avail_in == 0)且处于Z_FINISH阶段时,才判定为真正的错误。否则应继续补充输入或重试解压。 - 调整循环结构:确保在
inflate返回Z_BUF_ERROR但还有未处理的输入数据时,继续向流中喂入下一块输入,而非直接退出。 - 正确处理
Z_FINISH阶段:只有所有输入数据都已喂入后,才使用Z_FINISH标记,此时若仍出现Z_BUF_ERROR,则说明压缩数据损坏或解压逻辑有误。
修改后的代码
#include <iostream> #include <string> #include <zlib.h> static std::string in_buff_string; static std::string out_buff_string; static z_stream stream; int my_uncompress_func__from_memory(unsigned long CHUNK_SIZE, char* char_p__to_compressed_data, unsigned long size__compressed_data) { CHUNK_SIZE = 1; // 强制1字节分块 in_buff_string.resize(CHUNK_SIZE); out_buff_string.resize(CHUNK_SIZE); int err; stream = { 0 }; err = inflateInit2(&stream, 15); if (err != Z_OK) { return 1; } size_t cntr = 0; int flush = Z_NO_FLUSH; do { // 仅当当前输入缓冲区为空时,补充新的输入数据 if (stream.avail_in == 0 && cntr < size__compressed_data) { memcpy(&in_buff_string[0], char_p__to_compressed_data + cntr, CHUNK_SIZE); stream.avail_in = CHUNK_SIZE; stream.next_in = (Bytef*)&in_buff_string[0]; cntr++; } // 所有输入都已喂入,切换到FINISH模式 else if (cntr >= size__compressed_data) { flush = Z_FINISH; } stream.avail_out = CHUNK_SIZE; stream.next_out = (Bytef*)&out_buff_string[0]; // 执行解压 err = inflate(&stream, flush); // 处理错误:仅当FINISH阶段且无输入时的Z_BUF_ERROR才是真错误 if (err == Z_BUF_ERROR) { if (flush == Z_FINISH && stream.avail_in == 0) { std::cout << "Fatal Z_BUF_ERROR: corrupted data or invalid stream" << std::endl; inflateEnd(&stream); return 1; } // 非致命情况,继续循环补充输入或重试 continue; } // 处理其他致命错误 if (err != Z_OK && err != Z_STREAM_END) { std::cout << "Inflate error: " << err << std::endl; inflateEnd(&stream); return 1; } // 输出解压得到的数据 int nbytes = CHUNK_SIZE - stream.avail_out; if (nbytes != 0) { std::cout.write(out_buff_string.data(), nbytes); std::cout << std::endl; } } while (!(err == Z_STREAM_END && flush == Z_FINISH)); inflateEnd(&stream); return 0; }
内容的提问来源于stack exchange,提问作者Serg_
相关产品推荐
相关产品推荐

