一直没找到Qt中方便的gzip模块,于是自己动手,调用zlib模块实现了一份.
目标:
1.gzip的压缩与解压
2.内存中操作
3.方便的Qt接口
实现分析:
gzip
压缩算法为 deflate
inflateInit2时要求zlib库忽略zlib
header
gzip
在 deflate 的 raw data 前增加了 10 个字节的头,尾部添加了 8 个字节的校验字节(可选 crc32 和 adler32)
和长度标识字节
gzip
的 magic number 是 0x1f, 0x8b
zlib
windowBits MAX_WBITS + 16 的时候,zlib 自己会产生一个 gzip 的头和尾,这种情况下 OS_CODE 被设置为
255(unknown),尾部校验使用 crc32
接口:
QByteArray
gzipCompress(const QByteArray &in);
bool
gzipUncompress(const QByteArray &data, QByteArray &out);
QByteArray
gzipUncompress(const QByteArray &data);
实现:
int
httpgzdecompress(Byte *zdata, uLong nzdata, Byte *data, uLong *ndata)
{
int err = 0;
z_stream d_stream = {0}; /* decompression stream */
static char dummy_head[2] =
{
0x8 + 0x7 * 0x10,
(((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) &
0xFF,
};
d_stream.zalloc = (alloc_func)0;
d_stream.zfree = (free_func)0;
d_stream.opaque = (voidpf)0;
d_stream.next_in = zdata;
d_stream.avail_in = 0;
d_stream.next_out = data;
if(inflateInit2(&d_stream, 47) != Z_OK) return -1;
while (d_stream.total_out < *ndata && d_stream.total_in <
nzdata) {
d_stream.avail_in = d_stream.avail_out = 1; /* force small
buffers */
if((err = inflate(&d_stream, Z_NO_FLUSH)) ==
Z_STREAM_END) break;
if(err != Z_OK )
{
if(err == Z_DATA_ERROR)
{
d_stream.next_in = (Bytef*)
dummy_head;
d_stream.avail_in =
sizeof(dummy_head);
if((err =
inflate(&d_stream, Z_NO_FLUSH)) != Z_OK)
{
return -1;
}
}
else return -1;
}
}
if(inflateEnd(&d_stream) != Z_OK) return -1;
*ndata = d_stream.total_out;
return 0;
}
int
httpgzcompress(Bytef *data, uLong ndata, Bytef *zdata, uLong *nzdata)
{
z_stream c_stream;
int err = 0;
if(data && ndata > 0)
{
c_stream.zalloc = (alloc_func)0;
c_stream.zfree = (free_func)0;
c_stream.opaque = (voidpf)0;
if(deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION,
Z_DEFLATED,
MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK) return -1;
c_stream.next_in = data;
c_stream.avail_in = ndata;
c_stream.next_out = zdata;
c_stream.avail_out = *nzdata;
while (c_stream.avail_in != 0 && c_stream.total_out
< *nzdata)
{
if(deflate(&c_stream, Z_NO_FLUSH) !=
Z_OK) return -1;
}
if(c_stream.avail_in != 0) return c_stream.avail_in;
for (;;) {
if((err = deflate(&c_stream, Z_FINISH))
== Z_STREAM_END) break;
if(err != Z_OK) return -1;
}
if(deflateEnd(&c_stream) != Z_OK) return -1;
*nzdata = c_stream.total_out;
return 0;
}
return -1;
}
QByteArray
gzipCompress(const QByteArray &in)
{
QByteArray dest(compressBound(in.size()) + 7, 0);
uLongf destLen = dest.size();
int r1 = httpgzcompress((Bytef*)in.data(), in.size(),
(Bytef*)dest.data(),&destLen);
dest.resize(destLen);
return dest;
}
bool
gzipUncompress(const QByteArray &data, QByteArray &out)
{
if (data.size()<=8)
{
return false;
}
ulong orign_crc = *((ulong*)(data.data() + data.length() - 8));
ulong len =*((ulong*)(data.data() + data.length() - 4));
out.resize(len);
int ret = httpgzdecompress((Bytef *)data.data(), data.length(),(Bytef
*)out.data(), &len); //(Bytef *)(data.data()+10), data.length()-10-8);
if (ret!=Z_OK)
{
qWarning("gzipUncompress error: %d", ret);
return false;
}
uLong crc = crc32(0L, Z_NULL, 0);
crc = crc32(crc, (Bytef *)(out.data()), out.length());
if (crc==orign_crc)
{
return true;
}
else{
qWarning("gzipUncompress crc check sum fail");
out.clear();
return false;
}
}
QByteArray
gzipUncompress(const QByteArray &data)
{
QByteArray out;
gzipUncompress(out);
return out;
}
还有一份之前使用的qCompress的压缩实现(效率比较低)
QByteArray
gzip(const QByteArray &in)
{
QByteArray
uncompress=qCompress(in);
uncompress.remove(0,4);//去除qCompress头
uncompress.remove(0,
2); //去除zlib头
uncompress.remove(uncompress.size() - 4, 4);
//去除zlib尾
uint len=in.size();
unsigned long crc=GetCrc32((unsigned char *)in.data(),len);
const unsigned char
gzipheader[10]={0x1f,0x8b,8,0,0,0,0,0,2,255};
QByteArray
out;
out.append((char *)gzipheader,10);
out.append(uncompress);
out.append((char *)&crc,4);
out.append((char
*)&len,4);
return out;
}