【问题标题】:C++ Inflate gzip char arrayC ++膨胀gzip char数组
【发布时间】:2013-06-24 21:54:34
【问题描述】:

我正在尝试使用 zlib 解压缩(膨胀)一些通过 gzip 压缩的 IP 数据包负载数据。但是,我很难理解 zlib 提供的一些涵盖通货膨胀的文档。我有一个我的程序填充的 char 数组,但我似乎无法使用以下代码对其进行扩充:

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 循环不断调用inflate,检查Z_STREAM_END 标志。我在这里有点困惑,因为似乎他们正在处理文件,而我却没有。我是否也需要这个循环,还是我能够提供一个 char 数组而不循环 inflate?

这里的任何指导将不胜感激。我对使用压缩和 C++ 都很陌生。

谢谢。

【问题讨论】:

  • 我立即看到的最明显的错误是payloadconst u_charBytef* 的演员表指针 类型。我认为这注定是一个核心转储。还是应该是一个数组或一些动态分配的数据缓冲区?
  • 编辑:@WhozCraig 我对它的理解是它应该是一个动态数据数组,但我不完全确定,我正在使用一些 sample code 来实现它.

标签: c++ gzip zlib inflate


【解决方案1】:

假设您给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;
}

Ain 是输入缓冲区(我们从文件中读取到这个缓冲区中,然后稍后将其传递给inflateout 是输出缓冲区,即被inflate用来存储输出数据。

B:设置一个名为strmz_stream 对象。这包含各种字段,其中大部分在这里并不重要(因此设置为 Z_NULL)。重要的是avail_innext_in 以及avail_outnext_out(稍后设置)。

C:开始充气过程。这设置了一些内部数据结构,只是使inflate 函数本身“准备好运行”。

D:从文件中读取“CHUNK”数量的数据。将读取的字节数存入strm.avail_in,实际数据进入in

E:如果我们出错了,请通过调用inflateEnd 完成inflate。任务完成。

F:没有可用的数据,我们结束了。

G:设置我们的数据来自哪里(next_in 设置为输入缓冲区,in)。

H:我们现在正处于循环膨胀的过程中。在这里我们设置了输出缓冲区:next_outavail_out 分别指示输出的去向和有多少空间。

:自己打电话给inflate。这将解压缩输入缓冲区的一部分,直到输出已满。

J:计算这一步有多少数据可用(have是字节数)。

K:直到inflate 完成时还有剩余空间 - 这表示in 缓冲区中的数据的输出已完成,而不是输出缓冲区中的空间不足。所以是时候从输入文件中读取更多数据了。

L:如果来自inflate 调用的错误代码是“happy”,请再转一圈。

现在,显然,如果您正在从网络读取数据并解压缩到内存中,则需要将 freadfwrite 替换为一些合适的 read from networkmemcpy 类型调用。我不能确切地告诉你这些是什么,因为你没有提供任何东西来解释你的数据来自哪里——你打电话给recvreadWSARecv,还是别的什么? - 它要去哪里?

【讨论】:

  • 我正在尝试逐块读取数据,我不应该制作 z_stream 并以不同的方式调用 inflate() 吗?
  • 对,那么你应该在循环中调用inflate,每次它“饿了”时都会给出一个新块(也就是说,它完成了前一个块)。 [显然,等待该块中的数据完成,否则会出错]。出于测试目的,首先收集所有数据然后解压缩是有意义的,只是为了简化过程[您还可以将该数据保存为文件并“手动”解压缩以查看是否获得相同的数据如你所愿]
  • 查看zlib documentation,我看到所有对 inflate() 的调用都需要一个 z_stream 参数,我想我感到困惑的是这与我的输入字符数组对应的位置在哪里?跨度>
  • 我已根据您链接中的示例代码扩展了我的答案。由于您的代码不包含任何内容来指示数据如何到达函数,因此我无法说明您需要做什么才能使其与您的数据流一起使用。
  • 请注意,这里的示例代码是从this page复制而来的,其中对zlib的使用进行了广泛的评论。每次调用inflate()之前,在z_stream结构体中分别设置或更新输入或输出指针处的指针和数据量或空间量。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-24
  • 1970-01-01
  • 2012-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多