C ++ lzma сжатие и распаковка большого потока по частям

, мне нужно выполнять сжатие и распаковку lzma на лету. Я получаю большой файл через qnetworkmanager в Qt, и мне нужно распаковать его по мере загрузки потока данных.

Когда я получаю часть потока данных, мне нужно распаковать его, добавить в файл и затем освободить любую используемую память во время процесса. Как лучше всего это сделать?

Теперь я пытаюсь использовать xz-utils, чистый c api, может быть, кто-нибудь может предложить лучший способ?

следующий код, основанный на этом примере

UPD2:

extern "C" void *lz_alloc(void *opaque, size_t nmemb, size_t size)
{
void *p = NULL;
try{
    p = new char [size];
}
catch(std::bad_alloc &ba)
{
    p = NULL;
}
return p;
}

extern "C" void lz_free(void *opaque, void *ptr)
{
delete [] (char*)ptr;
}


QByteArray lzCompress(QByteArray data)
{
QByteArray arr;
lzma_check check = LZMA_CHECK_CRC64;
lzma_stream strm = LZMA_STREAM_INIT; /* alloc and init lzma_stream struct */
lzma_allocator al;
al.alloc = lz_alloc;
al.free = lz_free;
strm.allocator = &al;
byte *in_buf;
byte out_buf [OUT_BUF_MAX];
size_t in_len;  /* length of useful data in in_buf */
size_t out_len; /* length of useful data in out_buf */
lzma_ret ret_xz;

/* initialize xz encoder */
ret_xz = lzma_easy_encoder (&strm, 9 | LZMA_PRESET_EXTREME, check);
if (ret_xz != LZMA_OK) {
    return QByteArray();
}

in_len = data.size();
in_buf = (byte*)data.data();
strm.next_in = in_buf;
strm.avail_in = in_len;

do {
    strm.next_out = out_buf;
    strm.avail_out = OUT_BUF_MAX;
    ret_xz = lzma_code (&strm, LZMA_FINISH);

    out_len = OUT_BUF_MAX - strm.avail_out;
    arr.append((char*)out_buf, out_len);
    out_buf[0] = 0;
} while (strm.avail_out == 0);
lzma_end (&strm);
return arr;
}

Я проспал несколько часов, и теперь я думаю более ясно, исправил свой неправильный код, обновил его (он ведет себя так же, как работает qCompress)

UPD3:

код декомпрессии (поведение qUncompress)

QByteArray lzUncompress(QByteArray data)
{
lzma_stream strm = LZMA_STREAM_INIT; /* alloc and init lzma_stream struct */
const uint32_t flags = LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED;
const uint64_t memory_limit = UINT64_MAX; /* no memory limit */
byte *in_buf;
uint8_t out_buf [OUT_BUF_MAX];
size_t in_len;  /* length of useful data in in_buf */
size_t out_len; /* length of useful data in out_buf */
lzma_ret ret_xz;
QByteArray arr;

ret_xz = lzma_stream_decoder (&strm, memory_limit, flags);
if (ret_xz != LZMA_OK) {
    return QByteArray();
}

in_len = data.size();
in_buf = (byte*)data.data();

strm.next_in = in_buf;
strm.avail_in = in_len;
do {
    strm.next_out = out_buf;
    strm.avail_out = OUT_BUF_MAX;
    ret_xz = lzma_code (&strm, LZMA_FINISH);

    out_len = OUT_BUF_MAX - strm.avail_out;
    arr.append((char*)out_buf, out_len);
    out_buf[0] = 0;
} while (strm.avail_out == 0);
lzma_end (&strm);
return arr;
}

UPD4:

базовый класс декомпрессии потока, следующий код просто распаковывает поток xz, загруженный с http-сервера на лету, именно то, что мне нужно:

class lz_stream_decompressor : public QObject
{
Q_OBJECT
public:
lz_stream_decompressor(QNetworkReply *r, QNetworkAccessManager *q, const QString &str, unsigned long sz): flags(LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED), memory_limit(UINT64_MAX), state(0), total_upd_size(sz)
{
    repl = r;
    qnm = q;
    path = str;
    strm.next_in = NULL;
    strm.avail_in = 0;
    strm.total_in = 0;
    strm.next_out = NULL;
    strm.avail_out = 0;
    strm.total_out = 0;
    strm.allocator = NULL;
    strm.internal = NULL;
    strm.reserved_ptr1 = NULL;
    strm.reserved_ptr2 = NULL;
    strm.reserved_ptr3 = NULL;
    strm.reserved_ptr4 = NULL;
    strm.reserved_int1 = 0;
    strm.reserved_int2 = 0;
    strm.reserved_int3 = 0;
    strm.reserved_int4 = 0;
    strm.reserved_enum1 = LZMA_RESERVED_ENUM;
    strm.reserved_enum2 = LZMA_RESERVED_ENUM;
    ret_xz = lzma_stream_decoder (&strm, memory_limit, flags);
    if (ret_xz != LZMA_OK)
    {
        state = -1;
        repl->abort();
    }
    else
    {
        connect(repl, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handle_new_data(qint64,qint64)));
        connect(q, SIGNAL(finished(QNetworkReply*)), SLOT(compressed_file_request_finished(QNetworkReply*)));
        QFile(path).rename(path + ".tmp");
    }
}
~lz_stream_decompressor()
{
/*        if(repl)
        delete repl; */
    lzma_end (&strm);
}
const short get_state()
{
    return state;
}
signals:
void finished();

public slots:
void handle_new_data(qint64 bytesReceived, qint64 bytesTotal);
void compressed_file_request_finished(QNetworkReply*);
private:
QNetworkReply *repl;
QNetworkAccessManager *qnm;
lzma_stream strm;
const uint32_t flags;
const uint64_t memory_limit; /* no memory limit */
short state;
byte *in_buf;
byte out_buf [OUT_BUF_MAX];
size_t in_len;  /* length of useful data in in_buf */
size_t out_len; /* length of useful data in out_buf */
lzma_ret ret_xz;
QString path;
unsigned long &total_upd_size;
};

и реализация:

void lz_stream_decompressor::handle_new_data(qint64 bytesReceived, qint64 bytesTotal)
{
if(repl->error() != QNetworkReply::NoError)
{//TODO: handle error here
    QFile(path).remove();
    QFile(path + ".tmp").rename(path);
    return;
}
total_upd_size -= repl->bytesAvailable();
QByteArray data = repl->readAll();
in_len = data.size();
in_buf = (byte*)data.data();
strm.next_in = in_buf;
strm.avail_in = in_len;

do {
    strm.next_out = out_buf;
    strm.avail_out = OUT_BUF_MAX;
    ret_xz = lzma_code (&strm, LZMA_RUN);
    out_len = OUT_BUF_MAX - strm.avail_out;
    QFile file(path);
    if(file.open(QIODevice::WriteOnly | QIODevice::Append))
    {
        file.write(QByteArray((char*)out_buf, (int)out_len));
        file.close();
    }
    out_buf[0] = 0;
} while (strm.avail_out == 0);
}

void lz_stream_decompressor::compressed_file_request_finished(QNetworkReply* repl)
{
if(repl->error() != QNetworkReply::NoError)
{//TODO: handle error here
    QFile(path).remove();
    QFile(path + ".tmp").rename(path);
    emit finished();
    return;
}
total_upd_size -= repl->bytesAvailable();
QByteArray data = repl->readAll();
in_len = data.size();
in_buf = (byte*)data.data();
strm.next_in = in_buf;
strm.avail_in = in_len;

do {
    strm.next_out = out_buf;
    strm.avail_out = OUT_BUF_MAX;
    ret_xz = lzma_code (&strm, LZMA_FINISH);
    out_len = OUT_BUF_MAX - strm.avail_out;
    QFile file(path);
    if(file.open(QIODevice::WriteOnly | QIODevice::Append))
    {
        file.write(QByteArray((char*)out_buf, (int)out_len));
        file.close();
    }
    out_buf[0] = 0;
} while (strm.avail_out == 0);
repl->deleteLater();
QFile(path + ".tmp").remove();
emit finished();
}

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

Я хотел бы увидеть любые предложения по этому коду

вам также необходимо подключить слот compressed_file_request_finished к сигналу завершения qnetworkmanager, чтобы завершить несжатие data.

UPD5:

исправлены lzCompress и lzUncompress, похоже, теперь работает нормально, а не сюрприз e об использовании LZMA_FULL_FLUSH в handle_new_data, поскольку я читал, что это то, что мне нужно, но все еще не уверен, теперь я адаптирую существующий код для использования этого ...

UPD6:

вам также нужно что-то вроде этого:

/* read/write buffer sizes */
#define IN_BUF_MAX  409600
#define OUT_BUF_MAX 409600
/* analogous to xz CLI options: -0 to -9 */
#define COMPRESSION_LEVEL 7

/* boolean setting, analogous to xz CLI option: -e */
#define COMPRESSION_EXTREME true

в видимом диапазоне для работы этого кода.

UPD7:

обновленный код, весь протестированный и работающий, я обнаружил, что liblzma не является полностью поточно-ориентированной, я попытался сделать многопоточное сжатие списка файлов. и он очень часто дает сбой.

17
задан simonc 21 February 2013 в 16:21
поделиться