c++ qt lzma

C++ lzma compresión y descompresión de gran flujo por partes



qt (1)

Necesito hacer lzma de compresión y descompresión sobre la marcha. Estoy recibiendo un archivo grande a través de qnetworkmanager en Qt y necesito descomprimirlo mientras se descarga la secuencia de datos.

Cuando recibo parte del flujo de datos, necesito descomprimirlo, adjuntarlo al archivo y liberar toda la memoria utilizada durante el proceso. ¿Cuál es la mejor manera de hacer esto?

Ahora estoy probando con xz-utils, pure c api, ¿quizás alguien pueda sugerir una mejor manera?

siguiente código basado en this ejemplo

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

He dormido algunas horas, y ahora estoy pensando con más claridad, arreglé mi código incorrecto, lo actualicé (se comporta como funciona qCompress)

UPD3:

código de descompresión (qUncompress como comportamiento)

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:

Clase de descompresión de flujo básico, el siguiente código simplemente descomprime el flujo Xz descargado desde el servidor http sobre la marcha, exactamente lo que necesito

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

y realización:

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

Todo esto, basado en el ejemplo del primer enlace, debe reemplazar las partes de código comentadas con su código para hacer algo con datos no comprimidos.

Me gustaría ver alguna sugerencia sobre este código.

también necesita conectar la ranura "comprimido_archivo_preferencia_finalizada" a la señal finalizada de qnetworkmanager para finalizar los datos sin comprimir.

UPD5:

Arreglado lzCompress y lzUncompress, parece funcionar bien ahora, no estoy seguro de usar LZMA_FULL_FLUSH en handle_new_data, ya que leí esto es lo que necesito, pero aún no estoy seguro, ahora estoy adaptando el código existente para usar este ...

UPD6:

También necesitas algo como esto:

/* 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

en rango visible para que este código funcione.

UPD7:

código actualizado, todo probado y en funcionamiento, he encontrado que liblzma no es completamente seguro para subprocesos, traté de hacer una compresión de múltiples archivos de listas de archivos. y se estrella muy a menudo.


En esta página, encontrará el SDK de lzma que proporciona códigos fuente en diferentes idiomas y algunos binarios: http://www.7-zip.org/sdk.html

Tienes dos soluciones:

  • Utilice el código fuente de C ++ para descomprimir el flujo entrante
  • Utilice los binarios del decodificador como una herramienta externa en su aplicación