我正在尝试使用zlib解压缩(膨胀)通过gzip压缩的一些IP数据包有效负载数据。但是,我有些难以理解zlib提供的一些涵盖通货膨胀的文档。我有一个我的程序填充的字符数组,但我似乎无法使用以下代码对其进行充气:
const u_char payload; /*contains gzip data,
captured prior to this point in the program*/
/*read compressed contents*/
int ret; //return val
z_stream stream;
unsigned char out[MEM_CHUNK]; //output array, MEM_CHUNK defined as 65535
/* allocate inflate state */
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
stream.avail_in = size_payload; // size of input
stream.next_in = (Bytef *)payload; // input char array
stream.avail_out = (uInt)sizeof(out); // size of output
stream.next_out = (Bytef *)out; // output char array
ret = inflateInit(&stream);
inflate(&stream, Z_NO_FLUSH);
inflateEnd(&stream);
printf("Inflate: %s\n\n", out);
在zlib documentation中,他们通过do / while循环不断调用膨胀,检查Z_STREAM_END标志。我在这里有点困惑,因为看起来他们正在从文件中工作而我不是。我是否也需要这个循环,或者我是否能够提供一个char数组而不需要循环膨胀?
这里的任何指导都会非常感激。我对使用压缩和C ++都很陌生。
感谢。
答案 0 :(得分:4)
假设您正在为inflate
提供适当且完整的“压缩流”,并且有足够的空间来输出数据,您只需要拨打inflate
一次。
编辑:它没有像zlib documentation那样清楚地写出来,但确实说:
inflate
解压缩尽可能多的数据,并在停止时停止 输入缓冲区变空或输出缓冲区变满。有可能 引入一些输出延迟(读取输入而不产生任何输出 输出)除非被迫冲洗。
当然,对于任何尚未“在内存中并且已完成”的流,您希望逐块运行它,因为这将减少总运行时间(您可以在接收数据时进行解压缩[来自网络或文件系统预取缓存]用于下一个块)。
这是示例代码中的整个函数。我已从页面中删除文本组件以集中代码,并使用字母// A
,// B
等标记部分,然后标记为尝试解释以下部分。
int inf(FILE *source, FILE *dest)
{
int ret;
unsigned have;
z_stream strm;
unsigned char in[CHUNK]; // A
unsigned char out[CHUNK];
/* allocate inflate state */
strm.zalloc = Z_NULL; // B
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit(&strm); // C
if (ret != Z_OK)
return ret;
/* decompress until deflate stream ends or end of file */
do {
strm.avail_in = fread(in, 1, CHUNK, source); // D
if (ferror(source)) {
(void)inflateEnd(&strm); // E
return Z_ERRNO;
}
if (strm.avail_in == 0) // F
break;
strm.next_in = in; // G
/* run inflate() on input until output buffer not full */
do {
strm.avail_out = CHUNK; // H
strm.next_out = out;
ret = inflate(&strm, Z_NO_FLUSH); // I
assert(ret != Z_STREAM_ERROR); /* state not clobbered */
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR; /* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
(void)inflateEnd(&strm);
return ret;
}
have = CHUNK - strm.avail_out; // J
if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
(void)inflateEnd(&strm);
return Z_ERRNO;
}
} while (strm.avail_out == 0); // K
/* done when inflate() says it's done */
} while (ret != Z_STREAM_END); // L
/* clean up and return */
(void)inflateEnd(&strm);
return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}
A :in
是输入缓冲区(我们从一个文件中读取到此缓冲区中,然后稍后将其传递给inflate
。out
是输出缓冲区,inflate
用于存储输出数据。
B :设置名为z_stream
的{{1}}对象。这包含各种字段,其中大部分都不重要(因此设置为Z_NULL)。重要的是strm
和avail_in
以及next_in
和avail_out
(稍后设置)。
C :启动通胀流程。这将设置一些内部数据结构,并使next_out
函数本身“准备好运行”。
D :从文件中读取“CHUNK”数据量。存储inflate
中读取的字节数,实际数据进入strm.avail_in
。
E :如果我们出错,请致电in
完成inflate
。任务完成。
F :没有可用的数据,我们已经完成了。
G :设置我们的数据来源(inflateEnd
设置为输入缓冲区next_in
)。
H :我们现在处于循环中以膨胀事物。这里我们设置输出缓冲区:in
和next_out
分别指示输出的位置和空间大小。
我:致电avail_out
。这将解压缩输入缓冲区的一部分,直到输出满。
J :计算此步骤中可用的数据量(inflate
是字节数)。
K :在have
完成后我们还剩下空间 - 这表示inflate
缓冲区中的数据输出已完成,而不是输出缓冲区。所以是时候从输入文件中读取更多数据了。
L :如果来自in
来电的错误代码为“满意”,请再次转发。
现在,很明显,如果您正在从网络中阅读并解压缩到内存中,则需要将inflate
和fread
替换为适当的fwrite
和read from network
改为打电话。我无法确切地告诉您这些是什么,因为您没有提供任何解释数据来源的内容 - 您是在呼叫memcpy
还是recv
还是read
,还是其他什么? - 它会去哪里?