zlib раздувает операцию распаковки

У меня есть буфер данных, который содержит несколько сжатых элементов, это может быть сжатый элемент deflate или zlib.

Я обнаружил, что вызов zlib inflate возвращает Z_STREAM_END после обработки первого сжатого блока. Здесь несколько сжатых элементов могут быть в любом количестве (здесь, в моем примере, их 3). Но эти данные поступают с других сторон, которые не сообщают подробности о количестве сжатых элементов в данных.

Итак, как я могу реализовать использование функциональности zlib inflate, чтобы она могла работать с несколькими сжатыми элементами?

Ниже приведен пример быстрого и грязного примера, в котором я пытаюсь разработать свою проблему. Это относится к библиотеке zlib 1.2.5.

/* example.c -- understanding zlib inflate/decompression operation
 */

#define CHECK_ERR(err, msg) { \
    if (err != Z_OK) { \
        std::cerr << msg << " error: " << err << std::endl; \
        exit(1); \
    } \
}

/* ===========================================================================
 * deflate() to create compressed data
 */
void test_deflate(std::vector<uint8_t> & input_data, std::vector<uint8_t>& compr)
{
    z_stream c_stream; /* compression stream */
    int err;

    compr.clear();

    c_stream.zalloc = (alloc_func)0;
    c_stream.zfree = (free_func)0;
    c_stream.opaque = (voidpf)0;

    err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
    CHECK_ERR(err, "deflateInit");

    c_stream.next_in  = &input_data[0];
    c_stream.avail_in = input_data.size();

    for (;;) {
        uint8_t c_buffer[10] = {};
        c_stream.next_out  = &c_buffer[0];
        c_stream.avail_out = 10;

        err = deflate(&c_stream, Z_FINISH);
        if (err == Z_STREAM_END)
        {
            for (int i = 0; i < (10 - c_stream.avail_out); i++)
                compr.push_back(c_buffer[i]);
            break;
        }
        CHECK_ERR(err, "deflate");
        for (int i = 0; i < (10 - c_stream.avail_out); i++)
            compr.push_back(c_buffer[i]);
    }

    std::cout << "Compressed data (size = " << std::dec << compr.size() << ") = ";
    for (int i = 0; i < compr.size(); i++)
        std::cout << (uint32_t) compr[i];
    std::cout << std::endl;

    err = deflateEnd(&c_stream);
    CHECK_ERR(err, "deflateEnd");
}

/* ===========================================================================
 * Test inflate()
 */
void test_inflate(std::vector<uint8_t> &compr,
                  std::vector<uint8_t> &uncompr)
{
    int err;
    z_stream d_stream; /* decompression stream */

    uncompr.clear();

    d_stream.zalloc = Z_NULL;
    d_stream.zfree = Z_NULL;
    d_stream.opaque = Z_NULL;
    d_stream.avail_in = 0;
    d_stream.next_in = Z_NULL;
    err = inflateInit(&d_stream);
    CHECK_ERR(err, "inflateInit");

    d_stream.avail_in = compr.size();
    d_stream.next_in  = &compr[0];

    for(;;) {
        uint8_t d_buffer[10] = {};
        d_stream.next_out = &d_buffer[0];
        d_stream.avail_out = 10;

        err = inflate(&d_stream, Z_NO_FLUSH);

        if (err == Z_STREAM_END) {
            for (int i = 0; i < (10 - d_stream.avail_out); i++)
                uncompr.push_back(d_buffer[i]);
            if (d_stream.avail_in == 0)
                break;
        }

        CHECK_ERR(err, "inflate");
        for (int i = 0; i < (10 - d_stream.avail_out); i++)
            uncompr.push_back(d_buffer[i]);
    }
    err = inflateEnd(&d_stream);
    CHECK_ERR(err, "inflateEnd");

    std::cout << "Uncompressed data (size = " << std::dec << uncompr.size() << ") = ";
    for (int i = 0; i < uncompr.size(); i++)
        std::cout << (uint32_t) uncompr[i];
    std::cout << std::endl;
}


/* ===========================================================================
 * Usage:  example
 */

int main(int argc, char **argv)
{
    std::vector<uint8_t> input_data;
    std::vector<uint8_t> compr, multiple_compr;
    std::vector<uint8_t> uncompr;

    std::cout << "Input Data (in hex) = ";
    for (int i=0; i<32; i++) {
        input_data.push_back((uint8_t)i);
        if( i && (i % 2 == 0))
            std::cout << " ";
        std::cout << std::hex << (uint32_t)input_data[i];
    }
    std::cout << std::endl;

    // create compressed buffer-1 from input data
    test_deflate(input_data, compr);

    // copy compressed buffer-1 data into multiple compressed member buffer
    multiple_compr = compr;
    compr.clear();

    // create compressed buffer-2 from input data
    test_deflate(input_data, compr);

    // append data of compressed buffer-2 into multiple compressed member buffer
    for(int i=0; i< compr.size(); i++)
    {
        multiple_compr.push_back(compr[i]);
    }

    // create decompressed output
    test_inflate(multiple_compr, uncompr);

    // compare decompressed data with input data
    std::vector<uint8_t> final_data;
    final_data.push_back(input_data);
    final_data.push_back(input_data);
    if (final_data == uncompr)
       std::cout << "Matched" << std::endl;
    else
       std::cout << "Not Matched" << std::endl;

    return 0;
}

1) Здесь второй раз вызов надувания возвращает ошибку, но я хочу, чтобы он прошел успешно, почему он работает так?

2) Когда я использую Z_FINISH в аргументе вызова надувания, он возвращается с ошибкой, почему я не могу использовать Z_FINISH здесь?

Пожалуйста, исправьте мой пример и предложите оптимизированный подход, чтобы сделать то же самое.


person ronex dicapriyo    schedule 17.11.2013    source источник
comment
Немного непонятно. Вы получаете данные фрагментов и распаковываете их. Но вы не знаете, сколько сколько чанков вы можете получить? Кажется, это не связано со всей историей zlib, которую вы обернули вокруг нее.   -  person Jongware    schedule 17.11.2013
comment
Да, это может быть так, как я нашел в вызове zlib inflate. Тем не менее, я хочу знать, есть ли какой-либо механизм, например, я назначаю буфер данных для avail_in, если есть несколько сжатых элементов (т.е. 3), то после распаковки первого члена avail_in по-прежнему должен иметь данные о других сжатых элементах (т.е. 2). Могу ли я продолжить? Тем не менее, пока я изучаю идею использования zlib, я пока еще не совсем понятен. Поэтому было бы лучше, если бы вы могли поделиться некоторым примером правильного использования zlib inflate. После обращения к руководству по zlib мне все еще не очень ясно.   -  person ronex dicapriyo    schedule 17.11.2013
comment
А, подожди. Таким образом, после распаковки в вашем входном буфере могут остаться данные. См. zlib.net/zlib_how.html, это упоминается примерно на полпути. (Я никогда не использовал это сам, поэтому я собираюсь выручить здесь.)   -  person Jongware    schedule 17.11.2013


Ответы (1)


Просто повторите операцию надувания оставшихся данных.

Вы можете сэкономить некоторые ненужные free и malloc, используя inflateReset() вместо inflateEnd() и inflateInit(). У вас могут остаться некоторые оставшиеся данные от последнего заполнения в next_in и avail_in, поэтому сначала используйте их, а затем перезагрузите.

person Mark Adler    schedule 17.11.2013
comment
Кажется, это может работать отлично. Если возможно, не могли бы вы поделиться тем же с примером кода. На самом деле для сжатых данных я использовал некоторый предварительно созданный сжатый буфер. Возможно ли, что я могу создать свой пример кода с операцией по умолчанию (которая генерирует несколько сжатых данных-членов) и раздувать этот результат, который создает один распакованный буфер, содержащий несжатые данные предыдущих нескольких сжатых o/p данные по умолчанию. Это было бы хорошим и быстрым началом для понимания zlib. Спасибо за ответ. - person ronex dicapriyo; 17.11.2013
comment
Я не могу понять ваш вопрос. В качестве примера кода вы можете посмотреть этот аннотированный исходный код для zpipe.c. - person Mark Adler; 18.11.2013
comment
У меня нет времени просматривать чужой код. Вы можете попробовать codereview.stackexchange.com, чтобы узнать, проверит ли кто-нибудь его. - person Mark Adler; 25.11.2013
comment
Хорошо, я понимаю, спасибо за всю эту помощь. - person ronex dicapriyo; 25.11.2013