当前位置: 首页 > 面试题库 >

这是gzip inflate方法中的错误吗?

冯浩旷
2023-03-14
问题内容

在iOS上搜索如何对gzip压缩数据进行充气时,以下方法会出现在结果数量中:

- (NSData *)gzipInflate
{
    if ([self length] == 0) return self;

    unsigned full_length = [self length];
    unsigned half_length = [self length] / 2;

    NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
    BOOL done = NO;
    int status;

    z_stream strm;
    strm.next_in = (Bytef *)[self bytes];
    strm.avail_in = [self length];
    strm.total_out = 0;
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;

    if (inflateInit2(&strm, (15+32)) != Z_OK) return nil;
    while (!done)
    {
        // Make sure we have enough room and reset the lengths.
        if (strm.total_out >= [decompressed length])
            [decompressed increaseLengthBy: half_length];
        strm.next_out = [decompressed mutableBytes] + strm.total_out;
        strm.avail_out = [decompressed length] - strm.total_out;

        // Inflate another chunk.
        status = inflate (&strm, Z_SYNC_FLUSH);
        if (status == Z_STREAM_END) done = YES;
        else if (status != Z_OK) break;
    }
    if (inflateEnd (&strm) != Z_OK) return nil;

    // Set real length.
    if (done)
    {
        [decompressed setLength: strm.total_out];
        return [NSData dataWithData: decompressed];
    }
    else return nil;
}

但是我遇到了一些数据示例(在带有Python的gzip模块的Linux机器上定义),该数据在iOS上运行时无法膨胀。这是正在发生的事情:

在while循环的最后一次迭代中,inflate()返回Z_BUF_ERROR并退出循环。但是在循环之后调用的inflateEnd()返回Z_OK。然后,该代码假定由于inflate()从未返回Z_STREAM_END,所以膨胀失败并返回null。

根据此页面,http:
//www.zlib.net/zlib_faq.html#faq05
Z_BUF_ERROR不是致命错误,我的有限示例测试显示,即使inflateEnd()返回Z_OK,数据也成功膨胀最后一次调用inflate()不会返回Z_OK。好像inflateEnd()完成了对最后一块数据的膨胀。

我对压缩以及gzip的工作方式了解不多,所以我很犹豫在不完全了解其功能的情况下对此代码进行更改。我希望对这个主题有更多了解的人可以对上面代码中的潜在逻辑缺陷有所了解,并提出解决方法。

可以在这里找到Google出现的另一种方法,该方法似乎也遇到了相同的问题:https
:
//github.com/nicklockwood/GZIP/blob/master/GZIP/NSData%2BGZIP.m

编辑:

因此,这是一个错误!现在,我们如何解决它?以下是我的尝试。代码审查,有人吗?

- (NSData *)gzipInflate
{
    if ([self length] == 0) return self;

    unsigned full_length = [self length];
    unsigned half_length = [self length] / 2;

    NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
    int status;

    z_stream strm;
    strm.next_in = (Bytef *)[self bytes];
    strm.avail_in = [self length];
    strm.total_out = 0;
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;

    if (inflateInit2(&strm, (15+32)) != Z_OK) return nil;

    do
    {
        // Make sure we have enough room and reset the lengths.
        if (strm.total_out >= [decompressed length])
            [decompressed increaseLengthBy: half_length];
        strm.next_out = [decompressed mutableBytes] + strm.total_out;
        strm.avail_out = [decompressed length] - strm.total_out;

        // Inflate another chunk.
        status = inflate (&strm, Z_SYNC_FLUSH);

        switch (status) {
            case Z_NEED_DICT:
                status = Z_DATA_ERROR;     /* and fall through */
            case Z_DATA_ERROR:
            case Z_MEM_ERROR:
            case Z_STREAM_ERROR:
                (void)inflateEnd(&strm);
                return nil;
        }
    } while (status != Z_STREAM_END);

    (void)inflateEnd (&strm);

    // Set real length.
    if (status == Z_STREAM_END)
    {
        [decompressed setLength: strm.total_out];
        return [NSData dataWithData: decompressed];
    }
    else return nil;
}

编辑2:

这是一个示例Xcode项目,它说明了我正在遇到的问题。放气发生在服务器端,数据经过base64和url编码后才通过HTTP传输。我已经将URL编码的base64字符串嵌入到ViewController.m中。url-
decode和base64-decode以及您的gzipInflate方法位于NSDataExtension.m中

https://dl.dropboxusercontent.com/u/38893107/gzip/GZIPTEST.zip

这是python gzip库定义的二进制文件:

https://dl.dropboxusercontent.com/u/38893107/gzip/binary.zip

这是通过HTTP传输的URL编码的base64字符串:https
:
//dl.dropboxusercontent.com/u/38893107/gzip/urlEncodedBase64.txt


问题答案:

是的,这是一个错误。

实际上是正确的,如果inflate()不返回Z_STREAM_END,则说明您还没有完成通货膨胀。
inflateEnd()返回Z_OK实际上并没有多大意义,只是返回了一个有效的状态并能够释放内存。

因此inflate()必须最终返回,Z_STREAM_END然后才能声明成功。但是,Z_BUF_ERROR这不是放弃的理由。在这种情况下,您只需inflate()使用更多的输入或更多的输出空间再次调用即可。然后您将得到Z_STREAM_END

从zlib.h中的文档中:

/* ...
Z_BUF_ERROR if no progress is possible or if there was not enough room in the
output buffer when Z_FINISH is used.  Note that Z_BUF_ERROR is not fatal, and
inflate() can be called again with more input and more output space to
continue decompressing.
... */

更新:

由于那里有许多错误代码,下面是实现所需方法的正确代码。该代码处理不完整的gzip流,串联的gzip流和非常大的gzip流。对于非常大的gzip流,当编译为64位可执行文件时,中的unsigned长度z_stream不够大。
NSUInteger是64位,而是unsigned32位。在这种情况下,您必须循环输入才能将其提供给inflate()

本示例仅返回nil任何错误。return nil;如果需要更高级的错误处理,请在每个注释后的注释中注明错误的性质。

- (NSData *) gzipInflate
{
    z_stream strm;

    // Initialize input
    strm.next_in = (Bytef *)[self bytes];
    NSUInteger left = [self length];        // input left to decompress
    if (left == 0)
        return nil;                         // incomplete gzip stream

    // Create starting space for output (guess double the input size, will grow
    // if needed -- in an extreme case, could end up needing more than 1000
    // times the input size)
    NSUInteger space = left << 1;
    if (space < left)
        space = NSUIntegerMax;
    NSMutableData *decompressed = [NSMutableData dataWithLength: space];
    space = [decompressed length];

    // Initialize output
    strm.next_out = (Bytef *)[decompressed mutableBytes];
    NSUInteger have = 0;                    // output generated so far

    // Set up for gzip decoding
    strm.avail_in = 0;
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    int status = inflateInit2(&strm, (15+16));
    if (status != Z_OK)
        return nil;                         // out of memory

    // Decompress all of self
    do {
        // Allow for concatenated gzip streams (per RFC 1952)
        if (status == Z_STREAM_END)
            (void)inflateReset(&strm);

        // Provide input for inflate
        if (strm.avail_in == 0) {
            strm.avail_in = left > UINT_MAX ? UINT_MAX : (unsigned)left;
            left -= strm.avail_in;
        }

        // Decompress the available input
        do {
            // Allocate more output space if none left
            if (space == have) {
                // Double space, handle overflow
                space <<= 1;
                if (space < have) {
                    space = NSUIntegerMax;
                    if (space == have) {
                        // space was already maxed out!
                        (void)inflateEnd(&strm);
                        return nil;         // output exceeds integer size
                    }
                }

                // Increase space
                [decompressed setLength: space];
                space = [decompressed length];

                // Update output pointer (might have moved)
                strm.next_out = (Bytef *)[decompressed mutableBytes] + have;
            }

            // Provide output space for inflate
            strm.avail_out = space - have > UINT_MAX ? UINT_MAX :
                             (unsigned)(space - have);
            have += strm.avail_out;

            // Inflate and update the decompressed size
            status = inflate (&strm, Z_SYNC_FLUSH);
            have -= strm.avail_out;

            // Bail out if any errors
            if (status != Z_OK && status != Z_BUF_ERROR &&
                status != Z_STREAM_END) {
                (void)inflateEnd(&strm);
                return nil;                 // invalid gzip stream
            }

            // Repeat until all output is generated from provided input (note
            // that even if strm.avail_in is zero, there may still be pending
            // output -- we're not done until the output buffer isn't filled)
        } while (strm.avail_out == 0);

        // Continue until all input consumed
    } while (left || strm.avail_in);

    // Free the memory allocated by inflateInit2()
    (void)inflateEnd(&strm);

    // Verify that the input is a valid gzip stream
    if (status != Z_STREAM_END)
        return nil;                         // incomplete gzip stream

    // Set the actual length and return the decompressed data
    [decompressed setLength: have];
    return decompressed;
}


 类似资料:
  • 问题内容: 注意到今天在我们的代码库中有一行代码,我认为肯定会因语法错误而使构建失败,但是测试通过了,显然它实际上是有效的python(在2.x和3中)。 条件表达式有时不需要空格: 如果LHS是变量,则不起作用: 但是它似乎仍然可以与其他类型的文字一起使用: 这是怎么回事,出于某种原因,它是否有意成为语法的一部分?这个奇怪的怪癖是已知/记录的行为吗? 问题答案: 令牌之间的空白 除逻辑行的开头或

  • 问题内容: 一切都很好,我能够运行这个JSP项目,突然发生了一些事情,并且大多数我的servlet都出现了无法解决的错误。 我知道这是因为找不到特定的 JAR 文件进行编译..但是我的“ buildpath ”很好,我没有进行任何更改。 我陷入这种情况… 试过了 多次清理项目 删除并添加了JRE库 删除并添加了服务器(Tomcat 7.0.23) 问题答案: 您必须将Web项目的运行时设置为正在使

  • 问题内容: 我正在尝试使用杰克逊对POJO进行序列化和反序列化。从POJO到JSON可以完美地工作,而从另一个方向去则不行。 我有一个POJO 并运行和测试我运行了calendar.model包; 引发异常 我已经尽力将JSON转换为POJO了,但是没有。如果我从JSON映射到Map类型,它确实可以工作。 谢谢您的帮助 编辑 这是我依赖中的杰克逊的grep 看起来除了jackson2之外,没有其他

  • 问题内容: 我被困在保留关键字“ this”的错误上。在下面的我的React Component中,我将状态从我的主要组件“ App.js”传递到了“ RecipeList.js”组件,然后映射数据并呈现每个RecipeItem组件。我只是不明白为什么我会收到此错误 React.js-语法错误:这是保留字 该错误在render return方法内的RecipeList中调用。如果有人可以帮助,那就

  • 我试图创建一个svg点,我可以扩大使用笔画宽度。

  • 问题内容: 我在chrome和firefox中测试过,这就是问题所在。Date对象的方法有错误?当我在某天设置位置日期时,例如 然后,js代码为: 结果为“ 9”,为什么? 我发现当月有31天时,请运行该方法,该方法将返回错误的值 为什么? 问题答案: 让我们分解一下: 只有当天的值大于传递给的当月的最大天数时,这才是问题。否则,它会按您期望的那样工作。

  • 问题内容: 这是在我正在使用的名为Mirth的应用程序中,但它似乎来自Apache Commons库内部,该方法来自于检查某种东西是否确实是Base64编码的方法。所有文档都说唯一的回报是对还是错,那么我如何得到-61? 问题答案: 可能是。该假想是指数。 从http://kickjava.com/src/org/apache/commons/codec/binary/Base64.java.ht

  • 问题内容: T_PAAMAYIM_NEKUDOTAYIM听起来真的很异国情调,但对我来说绝对是胡说八道。我将其全部追溯到以下代码行: 在构造函数中,我创建一个Config对象。这是课程: 不知道为什么这不起作用/错误是什么意思… 问题答案: T_PAAMAYIM_NEKUDOTAYIM是PHP使用的双冒号范围解析–:: 快速浏览一下您的代码,我认为这一行: 应该 第一种是静态调用方法的方式-如果$