C++ lzma大流分部分压缩解压
我需要即时进行 lzma 压缩和解压缩。我通过 Qt 中的 qnetworkmanager 接收一个大文件,我需要在下载数据流时解压缩它。
当我收到部分数据流时,我需要将其解压缩,附加到文件,然后释放在此过程中使用的任何内存。最好的方法是什么?
现在我正在尝试使用 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一样)
< strong>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:
基本流解压缩类,以下代码只是解压缩从http服务器动态下载的xz流,正是我所需要的:
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 的完成信号以完成未压缩的数据。
UPD5:
修复了lzCompress和lzUncompress,看起来现在工作正常,不确定是否在handle_new_data中使用LZMA_FULL_FLUSH,因为我读到这是我需要的,但仍然不确定,现在我调整现有代码来使用它...
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不完全线程安全,我尝试对文件列表进行多线程压缩。而且经常崩溃。
I need to do lzma compression and decompression on the fly. I'm receiving a large file via qnetworkmanager in Qt and I need to decompress it as the data stream is downloading.
When I receive part of the data stream I need to decompress it, append to file and than free any used memory during the process. What is the best way to do this?
Now I'm trying with xz-utils, pure c api, maybe someone can suggest a better way?
following code based on this example
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;
}
i have sleeped few hours, and now i thinking more clearly, fixed my wrong code, updated it(it's behave just as qCompress works)
UPD3:
decompression code (qUncompress like behavior )
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:
basic stream decompression class, following code just decompress xz stream downloaded from http server on the fly, exactly what i need:
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;
};
and realisation:
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();
}
all this based on example from first link, you need to replace commented code parts with your code to do something with uncompressed data.
i would like to see any suggestions to this code
you also need to connect "compressed_file_request_finished" slot to finished signal of qnetworkmanager to finish uncompressed data.
UPD5:
fixed lzCompress and lzUncompress, looks like working fine now, not sure about using LZMA_FULL_FLUSH in handle_new_data, as i read this is what i need, but still not sure, now i adapting existing code to use this...
UPD6:
you also need something like this:
/* 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
in visible range for this code to work.
UPD7:
updated code, all tested and working, i have found that liblzma not completely thread-safe, i tried to make multi-threaded compression of filelist. and it crashing very often.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在此页面上,您将找到lzma SDK,它提供了不同语言的源代码和一些二进制文件:http://www.7 -zip.org/sdk.html
您有两种解决方案:
On this page, you will find the lzma SDK which provides source codes in different languages and some binaries: http://www.7-zip.org/sdk.html
You have two solutions: