8

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;
}

しかし、iOS で実行されているこのメソッドが膨張に失敗している(Python のgzip モジュールを使用して Linux マシンで収縮された) データの例をいくつか見つけました。何が起こっているかは次のとおりです。

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 でエンコードされ、HTTP 経由で転送される前に URL がエンコードされます。ViewController.m に URL エンコードされた base64 文字列を埋め込みました。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

4

2 に答える 2

2

はい、バグのようです。zlib サイトのこの注釈付きの例によるとZ_BUF_ERROR、inflate() に追加の入力が提供されない限り、それ以上の出力がないことを示すだけであり、それ自体がインフレ ループを異常終了させる理由ではありません。

実際、リンクされたサンプルはZ_BUF_ERRORまったく同じように処理されているようZ_OKです。

于 2013-07-23T21:08:03.283 に答える