一直没找到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;
}
posted on 2014-04-26 16:02  淡菊  阅读(4834)  评论(1编辑  收藏  举报