Распаковать связанные потоки zlib без чтения следующих байтов - PullRequest
1 голос
/ 29 сентября 2019

У меня есть файл, который я не могу изменить, который состоит из 3 сцепленных данных zlib. Данные не очень большие (несколько сотен килобайт). Как я могу их прочитать? Есть функция Qt qUncompress() (отредактированная), но она принимает длину в качестве аргумента, и я не знаю, какова фактическая длина потока.

Решение 1: Когдачтение данных через поток, код, который я вижу для выполнения этого блока чтения данных и остановки при возникновении ошибки. Проблема заключается в том, что «чтение фиксированного фрагмента данных» будет использовать этот фрагмент, и поток будет поврежден, если размер потока не точно кратен N.

Псевдокод:

while (no error) {
   read N bytes
   decompress_next(these N bytes)
}

... Here there may be up to N-1 totally skipped bytes...

Это работает, когда N = 1, но я чувствую себя немного хакером. Есть ли лучшая альтернатива?

Решение 2: Распакуйте поток, сожмите снова и получите размер первого куска. Перейдите к смещению, а затем прочитайте и т. Д. (Это не должно применяться, когда входной поток недоступен для записи, но это должно работать в моей ситуации)код нетривиален.

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

Изменить: Утилита zlib-flate похоже, чтобы сделать это для решения № 2, так что, по-видимому, это возможно

1 Ответ

0 голосов
/ 11 октября 2019

Вы можете использовать библиотеку zlib напрямую для достижения этой цели. Вот пример кода:

#include <iostream>
#include <zlib.h>
#include <vector>
#include <algorithm>
#include <numeric>
#include <cassert>
#include <string>
#include <array>

std::vector<uint8_t> my_compress(std::string const& in_data)
{
    auto bufsize = compressBound(in_data.size());

    std::vector<uint8_t> outbuf(bufsize);

    auto srcptr = reinterpret_cast<uint8_t const*>(in_data.data());
    size_t destlen = outbuf.size();
    auto result = compress(outbuf.data(), &destlen, srcptr, in_data.size());
    assert(result == Z_OK);

    outbuf.resize(destlen);

    return outbuf;
}

std::vector<uint8_t> generate_concatenated_data()
{
    std::vector<uint8_t> outbuf;
    std::vector<std::string> strings =
    {
        "zlib is a widely-used library",
        "remember to check your error returns",
        "the quick brown fox jumps over the lazy dog",
        "Never tell me the odds."
    };

    for(auto const& s: strings)
    {
        auto compressed = my_compress(s);

        // append each compressed stream to the end of the buffer
        outbuf.insert(end(outbuf), begin(compressed), end(compressed));
    }

    return outbuf;
}

void print_buffer(std::vector<char> const& buf)
{
    std::cout << "buffer contains: ";
    for(char c: buf)
    {
        std::cout << c;
    }
    std::cout << '\n';
}

int main()
{

    std::vector<uint8_t> concat_data = generate_concatenated_data();


    std::array<uint8_t, 1024> scratch = {}; //scratch buffer for decompressing the data. (size doesn't matter )


    z_stream s{};

    // standard zlib init procedure

    s.zalloc = nullptr;
    s.zfree = nullptr;
    s.opaque = nullptr;
    int init_result = inflateInit(&s);
    // insert error checking here.
    assert(init_result == Z_OK);



    s.next_in = concat_data.data();
    s.avail_in = concat_data.size();

    while(s.avail_in > 0)
    {
        // output destination buffer
        std::vector<char> out_data;


        int inflate_ret = 0;
        while(true)
        {

            s.next_out = scratch.data();
            s.avail_out = scratch.size();
            inflate_ret = inflate(&s, Z_NO_FLUSH);

            //make sure we decoded right
            assert(inflate_ret == Z_OK || inflate_ret == Z_STREAM_END);

            auto bytes_decoded = scratch.size() - s.avail_out;


            // there are definitely more efficient ways to append to a vector.
            for(int i = 0; i < bytes_decoded; i++)
            {
                out_data.push_back(scratch[i]);
            }

            // is this stream done?
            if(inflate_ret == Z_STREAM_END) break;

        }
        assert(inflate_ret == Z_STREAM_END);


        // get ready for the next stream
        auto reset_result = inflateReset(&s);
        assert(reset_result == Z_OK);


        // do something with the data.
        print_buffer(out_data);
    }


    //cleanup
    inflateEnd(&s);
    std::cout << "end.\n";

}

Вы получите следующий вывод:

buffer contains: zlib is a widely-used library
buffer contains: remember to check your error returns
buffer contains: the quick brown fox jumps over the lazy dog
buffer contains: Never tell me the odds.
end.

Документация для библиотеки zlib доступна здесь: https://zlib.net/manual.html

...