您的位置:首页 > 其它

zlib学习小结

2016-05-01 01:26 1201 查看
官方手册 : 
http://www.zlib.net/manual.html
参考连接 :
http://blog.csdn.net/zhoudaxia/article/details/8039519 http://www.oschina.net/code/snippet_65636_22542 http://blog.163.com/bh_binghu/blog/static/94553512011626102054571/
请多多支持以上作者...

另外,推荐下载源码同步学习.

研究了好久的zlib,今天终于可以拿得出手了.这里分享一下使用的经验.

基础数据结构

z_stream : 压缩算法,压缩程度以及输入输出buffer和长度等都保存在这里,可以理解为压缩上下文.

常用的函数

deflateInit : 参数比较少,里面的实现其实是调用的deflateInit2

deflateInit2 : 压缩初始化的基础函数,有很多参数,下面会重点介绍

deflate : 压缩函数.

deflateEnd : 压缩完成以后,释放空间,但是注意,仅仅是释放deflateInit中申请的空间,自己申请的空间还是需要自己释放.

inflateInit : 解压初始化函数,内部调用的inflateInit2.

inflateInit2 : 解压初始化的基础函数.后面重点介绍.

infalte : 解压函数

inflateEnd : 同deflateEnd作用类似.

compress : 全部附加选项默认压缩,内部调用compress2

compress2 : 带level的压缩方式.

uncompress : 解压缩.

压缩函数介绍

deflateInit2 :

函数原型为 : int ZEXPORT deflateInit2(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy)

z_stream : 这个是压缩上下文,你在调用deflateInit2之前首先要做的就是初始化这个结构体,很简单,个里面的三个回调赋值就行了.连官方给出的例子也是置成NULL.所以这里直接这样初始化,strm.zalloc = NULL; strm.zfree = NULL; strm.opaque = NULL.

level : 这个参数目前可选值有四个(好吧,在我看来,有两个没啥用嘛),如下:

Z_NO_COMPRESSION : 不压缩

Z_BEST_SPEED : 速度优先,可以理解为最低限度的压缩.

Z_BEST_COMPRESSION : 压缩优先,但是速度会有些慢.
Z_DEFAULT_COMPRESSION : 没仔细看源码.compress里面用的就是这个选项.

method : 就压缩嘛,所以选项就只有Z_DEFLATED

windowBits : 这个选项可就有点复杂了,也很重要,取值与压缩方式如下:

  * -(15 ~ 8) : 纯deflate压缩

  * 15 ~ 8 : 带zlib头和尾

  * > 16 : 带gzip头和尾

所以,无论是zlib的压缩还是gzip的压缩,再或者是纯deflate流,zlib都能搞定.验证方式 : 

1. zlib的话可以使用命令 cat 压缩文件名 | zlib-deflate -uncompress验证(家里的电脑没有这个命令,忘记什么包里面的了.)

2. 如果是纯deflate的压缩数据,只能字节写解压的代码.注意调用inflateInit2的时候这个值要对应.

3. 如果是gzip的方式,就直接用gunzip验证.

memLevel : 目前只有一个选项,MAX_MEM_LEVEL,无非是运行过程中对内存使用的限制.

strategy : 直接给默认就行Z_DEFAULT_STRATEGY.

deflate :

函数原型 : ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));

strm : 这个参数就是上面调用了deflateInit2初始化过后的变量.调用deflate之前,需要为它指定四个成员.

strm.next_in = 你的待压缩数据

strm.next_out = 压缩以后数据存储的buffer

strm.avail_in = 待压缩数据的长度

strm.avail_out = 压缩数据存储buffer的长度.

这个next_out 没有固定的长度,并不是说因为是压缩,压缩以后的数据一定比原来的短,所以就可以少给,一般建议起码给到next_in的长度.

flush : 没有用过别的选项,

Z_NO_FLUSH : 如果是一个大文件,没有办法一次加载进内存,那么这个选项就派上用场了.它告诉zlib我还会再给你数据的.

这个参数对应的deflate的返回值是Z_OK.

Z_FINISH : 这个标记才是告诉zlib,我没有数据了,这次是最后一个.

这个参数对应的deflate的返回值是Z_STREAM_END

deflateEnd

上面的描述已经足够清楚,它没有更多的作用了.

compress2

函数原型 : ZEXTERN int ZEXPORT compress2 OF((Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen, int level));

如果你看了它的内部实现,就会知道,这其实就是上面三个函数的封装.供使用者偷懒用的,谁不喜欢一步到位呢.不过要明确它的缺点 :

a) 它的确很方便,但是却限制了所有的可能性,因为它只能压缩一次,也就是说内部直接调用的Z_FINISH

b) 不要觉得保存的是压缩的数据就给小buffer,有一篇博客给出的值是输入buffer的四倍.这个亏我是吃过的,前几天用这个函数,给了输入buffer的大小,我觉得足够了.但是始终返回Z_BUF_ERROR.后来看到那篇博客才恍然大悟.

解压函数介绍

有压缩自然也得有解压,并且都是一一对应的.

inflateInit2 : 初始化

函数原型 : inflateInit2(z_streamp strm, int windowBits)

strm : 和deflate一样,初始化三个回调以后即可.有的参考文档说还需要初始化第四个选项,具体记不清哪个了.不过我试过以后发现貌似不用.

windownBits : 含义和deflateInit2一样,而且一定要对应起来.

inflate : 解压

函数原型 : ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));

strm : 四个参数.

strm.next_in = 你的待解压数据

strm.next_out = 解压以后数据存储的buffer

strm.avail_in = 待解压数据的长度

strm.avail_out = 解压数据存储buffer的长度.

flush : 和deflate一样,如果是Z_NO_FLUSH说明还有数据没有解压,如果是Z_FINISH说明这是最后一包待解压数据.

inflateEnd : 释放上面两步骤里面申请的资源.

uncompress :

函数原型 : ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen));

dest : 解压以后的数据存在这里

destLen : dest 大小

source : 待解压数据buffer

sourceLen : 待解压数据长度

其实这个函数就是简单封装了inflateInit, inflate, inflateEnd.同样,这个函数只适合单独解压场景,不适合需要多次传入的场景.

关于返回值

现在看来,除了deflate和inflate成功会有两个返回值以外,其他的函数返回值成功的话都是Z_OK.

如果 flush == Z_NO_FLUSH, 这两个函数返回值是Z_OK

如果flush == Z_FINISH, 这两个函数的返回值是Z_STREAM_END

一定要区分.

一个简单的例子

这个例子太像官方提供的例子了.不过还是贴出来,加了几句注释,希望能够帮助理解.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <zlib.h>
#include <time.h>
#include <errno.h>
#include <assert.h>

#define READ_LEN 8192

// gzip头
#define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */
// 目前只有这种压缩方式
#define DEFLATED 8

// 标记压缩为快速还是高质量
#define FAST 4
#define SLOW 2

// gzip格式
// (u16)MAGIC + (u8)DEFLATE + (u8)flag + (u32)time + (u8)deflate_flag + (u8)OS_CODE

int main(int argc, char **argv)
{
if (argc < 3)
{
printf("usage : %s src_file dst_file\n", argv[1]);

return -1;
}

int ret = 0;
int rfd = 0;
uint64_t read_len = 0;
char *read_buf = NULL;

int wfd = 0;
uint64_t write_len = 0;
char *write_buf = NULL;

rfd = open(argv[1], O_RDONLY);
if (-1 == rfd)
{
perror("\n");
return -1;
}

wfd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0666);
if (wfd < 0)
{
perror("\n");
return -1;
}

read_buf = (char *)malloc(READ_LEN);
if (NULL == read_buf)
{
return -1;
}

write_len = READ_LEN * 4;
write_buf = (char *)malloc(write_len);
if (NULL == write_buf)
{
return -1;
}
uint8_t *ptr = (uint8_t*)write_buf;

z_stream stream;
stream.zalloc = NULL;
stream.zfree = NULL;
stream.opaque = NULL;
deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY);

AGAIN:
while ((read_len = read(rfd, read_buf, READ_LEN)) >= 0)
{
int flush = Z_NO_FLUSH;
if (read_len == 0)
{
flush = Z_FINISH;
}

stream.next_in = (uint8_t*)read_buf;
stream.avail_in = read_len;

do
{
write_len = 4 * READ_LEN;
stream.next_out = (uint8_t*)write_buf;
stream.avail_out = write_len;
ret = deflate(&stream, flush);
if (flush == Z_NO_FLUSH && ret != Z_OK)
{
goto ERROR;
}

if (flush == Z_FINISH && ret != Z_STREAM_END)
{
goto ERROR;
}

int avail = 4 * READ_LEN - stream.avail_out;
write(wfd, write_buf, avail);
}while (stream.avail_out == 0); // 如果avail_out不够用了,说明输入的buffer还有没有压缩的部分,那么要继续压缩.

// 如果输入长度不是0,说明压缩不完整,可以直接退出.
assert(stream.avail_in == 0);

if (read_len == 0)
{
break;
}
}

if (read_len < 0)
{
if (errno == EINTR)
{
goto AGAIN;
}
}

return 0;
ERROR:
deflateEnd(&stream);
close(rfd);
close(wfd);

return -1;
}这段代码,我使用的是gzip的压缩方式,所以如果要解压,不需要在单独写一份代码了.只要gunzip命令就可以验证程序是否正常.

后记 : 

关于三种压缩格式的区分.

deflate : 说他是压缩格式有点不确切,其实它就是压缩的核心算法.

zlib : zlib header + deflate + zlib tailer

gzip : gzip header + deflate + gzip tailer

另外我封装了一套单buffer的压缩和解压函数.也放在这里,有需要的同学可以参考一下.

compress.h

#ifndef MYZIP_H

#include <unistd.h>
#include <stdint.h>
#include <zlib.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef z_streamp PZSTREAM;
typedef z_stream ZSTREAM;

/* ===========================================================================
* 指定压缩(解压)类型
* 官方文档提到 :
* -(15 ~ 8) : raw deflate压缩
* 15 ~ 8 : 带zlib头和尾
* > 16 : 带gzip头和尾
*/
typedef enum
{
E_ZIP_RAW = -MAX_WBITS,
E_ZIP_ZLIB = MAX_WBITS,
E_ZIP_GZIP = MAX_WBITS + 16
} zip_type;

// 压缩回调函数
typedef void (*flate_cb)(void *buf, int len);

/******************************************************************************
** 执行deflate压缩
** NOTE : 1. 这组函数仅仅适用于小buffer
2. 在调用deflate_beg之前,需要自己初始化strm
3. 如果deflating失败,要执行deflate_end释放资源
4. 明确一点,deflate_end只做了stream内部资源释放工作,如果stream是动态分配的,需要自己free
******************************************************************************/
int deflate_beg(PZSTREAM strm, zip_type type);
int deflating(PZSTREAM strm, uint8_t *inbuf, uint32_t in_len, uint8_t *outbuf, uint32_t *out_len);
int deflate_end(PZSTREAM strm);

/******************************************************************************
** 执行inflate解压
** NOTE : 1. 这组函数仅仅适用于小buffer
2. 在调用inflate_beg之前,需要自己初始化strm
3. 如果inflating失败,要执行inflate_end释放资源
4. 明确一点,inflate_end只做了stream内部资源释放工作,如果stream是动态分配的,需要自己free
******************************************************************************/
int inflate_beg(PZSTREAM strm, zip_type type);
int inflating(PZSTREAM strm, uint8_t *inbuf, uint32_t in_len, uint8_t *outbuf, uint32_t *outlen);
int inflate_end(PZSTREAM strm);

#ifdef __cplusplus
}
#endif

#endif
compress.c

#include <unistd.h>
#include "compress.h"

/// strm需要在传入之前自己初始化
int deflate_beg(PZSTREAM strm, zip_type type)
{
int ret = 0;
int window_bits;

if (NULL == strm)
{
goto ERROR;
}

strm->zalloc = Z_NULL;
strm->zfree = Z_NULL;
strm->opaque = Z_NULL;
strm->next_in = Z_NULL;
strm->avail_in = 0;

window_bits = (int)type;
ret = deflateInit2(strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY);
if (ret != Z_OK)
{
goto ERROR;
}

return 0;

ERROR:
return -1;
}

int deflating(PZSTREAM strm, uint8_t *inbuf, uint32_t in_len, uint8_t *outbuf, uint32_t *out_len)
{
int ret = 0;

if (NULL == strm || NULL == inbuf || NULL == outbuf || NULL == out_len)
{
goto ERROR;
}

ret = deflateReset(strm);
if (ret != Z_OK)
{
goto ERROR;
}

strm->next_in = inbuf;
strm->avail_in = in_len;
strm->next_out = outbuf;
strm->avail_out = *out_len;

// 此函数的应用场景都是小buffer场景,所以这里inbuf的传入都是完整需要压缩的,
// 所以这里直接调用Z_FINISH
ret = deflate(strm, Z_FINISH);
if (ret != Z_STREAM_END)
{
goto ERROR;
}

*out_len = strm->total_out;

return 0;

ERROR:
return -1;
}

int deflate_end(PZSTREAM strm)
{
int ret = 0;

ret = deflateEnd(strm);
if (ret != Z_OK)
{
goto ERROR;
}

return 0;

ERROR:
return -1;
}

int inflate_beg(PZSTREAM strm, zip_type type)
{
int ret = 0;
int window_bits = 0;

if (NULL == strm)
{
goto ERROR;
}

strm->zalloc = (alloc_func)0;
strm->zfree = (free_func)0;
strm->opaque = (voidpf)0;

window_bits = (int)type;
ret = inflateInit2(strm, window_bits);
if (ret != Z_OK)
{
goto ERROR;
}

return 0;

ERROR:
return -1;
}

int inflating(PZSTREAM strm, uint8_t *inbuf, uint32_t in_len, uint8_t *outbuf, uint32_t *outlen)
{
int ret = 0;

if (NULL == strm || NULL == inbuf || NULL == outbuf || NULL == outlen)
{
goto ERROR;
}

ret = inflateReset(strm);
if (ret != Z_OK)
{
goto ERROR;
}

strm->next_in = inbuf;
strm->avail_in = in_len;
strm->next_out = outbuf;
strm->avail_out = *outlen;

ret = inflate(strm, Z_FINISH);
if (Z_STREAM_END != ret)
{
goto ERROR;
}

*outlen = strm->total_out;

return 0;

ERROR:
return -1;
}

int inflate_end(PZSTREAM strm)
{
int ret = 0;

ret = inflateEnd(strm);
if (ret != Z_OK)
{
goto ERROR;
}

return 0;

ERROR:
return -1;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c zlib