zlib

zlib是一个通用数据压缩库,数据格式被ietf标准化,在很多其他格式中(png http jar linux kernel)中都有应用,算是事实上的压缩标准了。源码。比较新的压缩算法比如7-Zip使用LZMA & LZMA2 算法,bzip2 使用Burrows–Wheeler算法,google zopfli 优化的deflate算法,都是在压缩率方面进行了优化,但cpu消耗也增加了,可以参见这个benchmark。defalte算法本身很快,linux kernel在启动时采用压缩的vmlinuz,虽然解压需要消耗cpu时间,但和压缩后减少的io时间比,启动速度还是有增加的。

历史

关于 .zip .z .gz .bz 等格式的历史 参见。按照时间线大概是这样。

compress: 1985年 开始在unix系统上使用,针对单个文件压缩,压缩后的文件后缀为.Z。压缩算法是用LZW,在当时该算法是有专利的,导致使用起来有风险。

gif:1987年 图片压缩格式,同样使用了LZW压缩算法,也面临专利风险。

zip: 1989 Phil Katz开发的使用delfate(LZ77+Huffman)压缩算法,.zip文件格式支持目录压缩。Phil Katz对压缩算法的实现保留了专利,但压缩解压程序是共享软件并开放了文件格式。1990年Info-ZIP group实现了开源的zip实现。

gzip: 1992年,出于专利风险,Gnu基于deflate算法,添加了头尾包装了deflate数据定义了新的文件格式.gz,用来代替有专利风险的compress程序。gzip针对单个文件使用,对于目录配合tar文件使用。

png: 1996年,因为gif的专利风险,创建了替代gif文件的新格式png,底层压缩算法使用了deflate算法,这样把gzip中专注于压缩的部分提取出来组成了zlib,然后gzip和libpng都使用zlib作为其核心算法实现。

数据/文件格式

ietf 定义了三种格式:

接口函数及关系

zlib包含几套不同的逻辑接口,核心压缩方法都是deflate,差异只是文件头尾,不同的封装等。

deflatecompressgzip的底层实现。

int deflateInit2(z_streamp strm, int level, int method, int windowBits,int memLevel,int strategy);

在调用init接口前strm的三个成员zalloc, zfree, opaque 需要先初始化,使用默认的内存分配函数直接用Z_NULL初始化。

z_stream c_stream; /* compression stream */
c_stream.zalloc = Z_NULL;
c_stream.zfree = Z_NULL;
c_stream.opaque = Z_NULL;

level 表示压缩的程度,可以设置的值为[0,9].0:表示不进行压缩,1:表示速度最快,9:表示压缩率最高。Z_DEFAULT_COMPRESSION=6,是速度和压缩率平衡的推荐值。

#define Z_NO_COMPRESSION         0
#define Z_BEST_SPEED             1
#define Z_BEST_COMPRESSION       9
#define Z_DEFAULT_COMPRESSION  (-1)

method 参数只能设置为#define Z_DEFLATED 8在使用deflate接口时。

windowBits history buffer历史窗口大小,在LZ77算法中在查找重复出现的字符串时,会从这个大小的窗口中查找。实际窗口大小为 2windowBits byte,该指默认为define MAX_WBITS 15,即窗口大小为32k,该值的合法大小为[8,15]。该值越大压缩效果越好。该值可以设置为负值,绝对值表示窗口大小,负值表示在压缩结果中不添加zlib头和结尾的checksum,如果该值大于16,表示使用gzip的头尾,参见附录中的代码。

memLevel [1,9] 默认值为define DEF_MEM_LEVEL 8 表示在压缩的过程中应该使用多少内存,1表示最小内存,同时压缩率也最低,define MAX_MEM_LEVEL 9 使用内存最多,速度也比较快。

strategy 对压缩的策略进行调节

#define Z_FILTERED            1 //between Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY
#define Z_HUFFMAN_ONLY        2 //只使用Huffman编码,不使用LZ77
#define Z_RLE                 3 //limit match distances to one
#define Z_FIXED               4 //静态Huffman编码
#define Z_DEFAULT_STRATEGY    0 //默认既使用LZ77 也使用Huffman

Z_FIXED是使用固定的Huffman码表,码表在rfc1951里定义

                Lit Value    Bits        Codes
               ---------    ----        -----
                 0 - 143     8          00110000 through
                                        10111111
               144 - 255     9          110010000 through
                                        111111111
               256 - 279     7          0000000 through
                                        0010111
               280 - 287     8          11000000 through
                                        11000111

这些参数都是在压缩时使用的,虽然在zlib的头文件里保存了levelwindowBits,但这些参数不是解压必须的。

接口使用示例 参见官方的示例

deflate

deflate规范

deflate规范rfc1951

压缩后的文件结构1,block外部包围的zlib格式的头和尾。 压缩数据由一系列的block组成。block的顺序和输入数据顺序一致。除了不压缩只存储block(block类型后续详细介绍)最大限制为65535外,其他类型没有大小限制。每个block之间相互独立。每个block使用,LZ77和Huffman编码进行压缩,如下图2来源参考连接地址 在LZ77算法中,每个重复出现的位置最远可以距当前位置32k,也就是滑动窗口的大小,每个重复段的最大大小为258,这些值都是经验值,太长连续出现的序列可能性不大,太远的距离太耗计算资源。

int deflateInit2(z_streamp strm, int level, int method, int windowBits,int memLevel,int strategy)=>
int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
                  version, stream_size){
    s->w_bits = (uInt)windowBits;//define MAX_WBITS   15
    s->w_size = 1 << s->w_bits;//32k
    s->w_mask = s->w_size - 1;
    s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));//s->window包括了search buffer和lookahead buffer共64k
}

Huffman编码

Huffman编码不是唯一的,首先每个分支的0、1可以互换而不影响编码长度,另一个相同码长的字符可以互换编码,而不影响编码总编码长度。这些编码只是形式不同,实际都是一样的。

为了能减少存储编码树的存储空间,Phil Katz觉得既然每一种编码都可以,那就选个比较特殊的一种,这样能减少编码树的占用空间大小。Phil Katz在Huffman编码的基础上增加了两条约束,来保证编码的唯一性,称作 deflate 格式的Huffman编码。

参见这个例子

                      /\              Symbol    Code
                     0  1             ------    ----
                    /    \                A      00
                   /\     B               B       1
                  0  1                    C     011
                 /    \                   D     010
                A     /\
                     0  1
                    /    \
                   D      C

这是合法的Huffman编码,但不符合deflate规范,采用deflate格式后

                      /\              Symbol    Code
                     0  1             ------    ----
                    /    \                A      10
                   B     /\               B       0
                        0  1              C     110
                       /    \             D     111
                      A     /\
                           0  1
                          /    \
                         C      D   

采用deflate规范后,Huffman码表存储可以存储每个码点的编码,转为存储编码的长度。

        Symbol  Code   Code Length
        ------  ----   ----
        A       10      2
        B       0       1
        C       110     3
        D       111     3

通过存储码长,码点的长度进一步减小了,码长位于[1,maxCodeLength]之间。Phil Katz可能基于zip block的大小,也可能基于利用16位寄存器的考虑,设计了maxCodeLength为15,具体什么原因也无从考证了。

具体编码过程

deflate的block分三种格式,采用block最开始的3 bit标示,都是在rfc1951中定义

        first bit       BFINAL
        next 2 bits     BTYPE

其中BTYPE定义为

        00 - no compression
        01 - compressed with fixed Huffman codes
        10 - compressed with dynamic Huffman codes
        11 - reserved (error)

关于字节存储顺序

     +--------+
     |76543210|
     +--------+

         0        1
     +--------+--------+
     |00001000|00000010|
     +--------+--------+
      ^        ^
      |        |
      |        + more significant byte = 2 x 256
      + less significant byte = 8

存储block(BTYPE=00)格式

          0   1   2   3   4...
        +---+---+---+---+================================+
        |  LEN  | NLEN  |... LEN bytes of literal data...|
        +---+---+---+---+================================+
TIMFENG-MB0:zlib fengguangtu$ ./mytest 
hello world!
hello world, hello!:20
68 65 6C 6C 6F 20 77 6F 72 6C 64 2C 20 68 65 6C
6C 6F 21 00
default result::19
CB 48 CD C9 C9 57 28 CF 2F CA 49 D1 51 C8 00 71
14 19 00
store block result::25
01 14 00 EB FF 68 65 6C 6C 6F 20 77 6F 72 6C 64
2C 20 68 65 6C 6C 6F 21 00

静态Huffman编码 block(BTYPE=01)格式

静态的Huffman编码编码是固定的,因此不需要存储码表。 固定的Huffman码表,码表在rfc1951里定义

                Lit Value    Bits        Codes
               ---------    ----        -----
                 0 - 143     8          00110000 through
                                        10111111
               144 - 255     9          110010000 through
                                        111111111
               256 - 279     7          0000000 through
                                        0010111
               280 - 287     8          11000000 through
                                        11000111

数据首先经过LZ77编码,重复出现的字符串转化为(distance,length)对,之后按固定的Huffman表进行编码,distance 按使用固定的5 bits编码,30 31从未使用到,extra 按照和动态Huffman编码 block(BTYPE=10)的distance编码相同。

              Extra           Extra               Extra
         Code Bits Dist  Code Bits   Dist     Code Bits Distance
         ---- ---- ----  ---- ----  ------    ---- ---- --------
           0   0    1     10   4     33-48    20    9   1025-1536
           1   0    2     11   4     49-64    21    9   1537-2048
           2   0    3     12   5     65-96    22   10   2049-3072
           3   0    4     13   5     97-128   23   10   3073-4096
           4   1   5,6    14   6    129-192   24   11   4097-6144
           5   1   7,8    15   6    193-256   25   11   6145-8192
           6   2   9-12   16   7    257-384   26   12  8193-12288
           7   2  13-16   17   7    385-512   27   12 12289-16384
           8   3  17-24   18   8    513-768   28   13 16385-24576
           9   3  25-32   19   8   769-1024   29   13 24577-32768

注意:编码后的Huffman编码结果采用most-significant在字节低位的方式,直观感受是需要把编码后结果倒转之后放入字节流中。

TIMFENG-MB0:zlib fengguangtu$ ./mytest 
hello world!
hello world, hello!:20
68 65 6C 6C 6F 20 77 6F 72 6C 64 2C 20 68 65 6C
6C 6F 21 00
default result::19
CB 48 CD C9 C9 57 28 CF 2F CA 49 D1 51 C8 00 71
14 19 00
store block result::25
01 14 00 EB FF 68 65 6C 6C 6F 20 77 6F 72 6C 64
2C 20 68 65 6C 6C 6F 21 00
fixed block result::19
CB 48 CD C9 C9 57 28 CF 2F CA 49 D1 51 C8 00 71
14 19 00

按照固定码表得到转换关系

ASCii bits
---- ----
68 h 19
65 e a9
6c l 39
6c l 39
6f o f9
20   0a
77 w e5
6f o f9
72 r 45
6c l 39
64 d 29
2c , 3a
20   0a
68 h 19
102  40(7)//len:258
7(5)0(2) (7) //distance 13
21 ! 8a
00   0c
100  00(7)

组合在一起

   1     9  BTYPE BFINAL
  000-1 1001    01    1 ->CB

  a      9
  101-0 1001 000 ->48

  3      9
  001-1 1001 101 ->CD

  ...

  0      a
  000-0 1010

  1      9
  000-1 1001 000 ->C8

  4      0
  01-00 000 000 ->00

  7-0
  0-0111 00 01 ->71

  8      a
  1-000 1010 0 ->14

  0      c
  0-000 1100 1 ->19

  0     0
  000 0000 0 ->00

动态Huffman编码 block(BTYPE=10)格式

这是deflate的主要格式,和静态Huffman比多存储了Huffman码表。 在压缩的过程中,经过LZ77处理后重复字符(最少3个)转化为(distance,length)对,distance:[1-32768],length:[3,258],length和原始字符使用同一个Huffman码表(literal/length alphabet),256编码表示结束符,distance使用一个码表(distance alphabet)。

针对huffma表,采用之前介绍的,不是直接存储Huffman码表,而是存储码表中每个编码的长度。码表按顺序存储,这时整个block中不存在的字符会比较多,对应的就是码表中会存在比较多的0,这时码表采用游程编码进一步减小码表的体积。

游程编码规则为

           0 - 15: Represent code lengths of 0 - 15
               16: Copy the previous code length 3 - 6 times.
                   The next 2 bits indicate repeat length
                         (0 = 3, ... , 3 = 6)
                      Example:  Codes 8, 16 (+2 bits 11),
                                16 (+2 bits 10) will expand to
                                12 code lengths of 8 (1 + 6 + 5)
               17: Repeat a code length of 0 for 3 - 10 times.
                   (3 bits of length)
               18: Repeat a code length of 0 for 11 - 138 times
                   (7 bits of length)

0-15表示自己,16表示重复前一个编码3-6次后面跟两个bit表示具体是3-6中的3 4 5 6,依次类推。

Phil Katz觉得length越大(连续重复字符越多)出现的概率越低,过于大的length没必要占一个码位。经过思考后设置长度对应的编码表如下,这体现了工程和理论的差别,理论上最优编码为Huffman编码,但在实际使用上,往往不是理论上最优取得的效果更好。

             Extra               Extra               Extra
        Code Bits Length(s) Code Bits Lengths   Code Bits Length(s)
        ---- ---- ------     ---- ---- -------   ---- ---- -------
         257   0     3       267   1   15,16     277   4   67-82
         258   0     4       268   1   17,18     278   4   83-98
         259   0     5       269   2   19-22     279   4   99-114
         260   0     6       270   2   23-26     280   4  115-130
         261   0     7       271   2   27-30     281   5  131-162
         262   0     8       272   2   31-34     282   5  163-194
         263   0     9       273   3   35-42     283   5  195-226
         264   0    10       274   3   43-50     284   5  227-257
         265   1  11,12      275   3   51-58     285   0    258
         266   1  13,14      276   3   59-66

同理distance也采用了类似的思路

              Extra           Extra               Extra
         Code Bits Dist  Code Bits   Dist     Code Bits Distance
         ---- ---- ----  ---- ----  ------    ---- ---- --------
           0   0    1     10   4     33-48    20    9   1025-1536
           1   0    2     11   4     49-64    21    9   1537-2048
           2   0    3     12   5     65-96    22   10   2049-3072
           3   0    4     13   5     97-128   23   10   3073-4096
           4   1   5,6    14   6    129-192   24   11   4097-6144
           5   1   7,8    15   6    193-256   25   11   6145-8192
           6   2   9-12   16   7    257-384   26   12  8193-12288
           7   2  13-16   17   7    385-512   27   12 12289-16384
           8   3  17-24   18   8    513-768   28   13 16385-24576
           9   3  25-32   19   8   769-1024   29   13 24577-32768

这里举几个(distance,length)例子

distance和length Huffman码表使用游程编码后,两个码表作为原始数据再次进行Huffman编码,共用一个Huffman表,编码前的数据为[0-18],Phil Katz认为这个编码code length最大长度不会超过7(可以用3位bit表示)。对于hello world示例里进行二次编码后 Huffman码表为

    n   0   l  2 c    0 (0) 
    n   1   l  3 c    1 (4) 
    n   3   l  3 c    5 (5) 
    n   4   l  2 c    2 (1) 
    n  17   l  3 c    3 (6) 

该表中也可能存在大量连续的0,0集中在中部,在上面的例子中只有2和3其他都是0。 所以Phil Katz将最终的码表按16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15这个顺序存储,每位占3bits,记录最后一个不是0的长度,后面的零都不存储。

到了这一步Phil Katz,终于认为可以不用再折腾。

最终的block的结构为

           5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286)
           5 Bits: HDIST, # of Distance codes - 1        (1 - 32)
           4 Bits: HCLEN, # of Code Length codes - 4     (4 - 19)
           (HCLEN + 4) x 3 bits: code lengths for the code length
              alphabet given just above, in the order: 16, 17, 18,
              0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15

              These code lengths are interpreted as 3-bit integers
              (0-7); as above, a code length of 0 means the
              corresponding symbol (literal/length or distance code
              length) is not used.

           HLIT + 257 code lengths for the literal/length alphabet,
              encoded using the code length Huffman code

           HDIST + 1 code lengths for the distance alphabet,
              encoded using the code length Huffman code

           The actual compressed data of the block,
              encoded using the literal/length and distance Huffman
              codes

           The literal/length symbol 256 (end of data),
              encoded using the literal/length Huffman code

    code-ascii  len    huff
      ---     ---     ----
    n   0   l  4 c    6 (6) 
    n  32   l  3 c    0 (0) 
    n  33 ! l  4 c    e (7) 
    n  44 , l  4 c    1 (8) 
    n 100 d l  4 c    9 (9) 
    n 101 e l  4 c    5 (a) 
    n 104 h l  4 c    d (b) 
    n 108 l l  3 c    4 (1) 
    n 111 o l  3 c    2 (2) 
    n 114 r l  4 c    3 (c) 
    n 119 w l  4 c    b (d) 
    n 256   l  4 c    7 (e) 
    n 258   l  4 c    f (f) 

同理distance编码为

    n   0   l  1 c    0 (0) 
    n   7   l  1 c    1 (1) 

pkzip要求至少编码一个distance,并且至少要编码一位,第一行的0是补足用的。

码表和码表长度的格式是

literal/length alphabet Huffman total:258+1=259 ->HLIT=259-257=2
origin  : 0 .  32 33 . 44 . 100 101 . 104 . 108 . 111 . 114 . 119 . 256 . 258
code-hex: 6 .  0  e  . 1  . 9    5  . d   . 4   . 2   . 3   . b   . 7   . f
code len: 4 .  3  4  . 4  . 4    4  . 4   . 3   . 3   . 4   . 4   . 4   . 4
sqencode: 4 18 3  4 17 4 18 4    4 00 4 17  3 0 0 3 0 0 4 17  4 18  4  0  4
huffcode: 2 7  5  2 3  2 7  2    2 00 2 3   5 0 0 5 0 0 2 3   2 7   2  0  2      
extra.  : 7-0010100 3-111 7-0101100     3-000             3-001 7-1111101 

distance alphabet Huffman total:7+1=8 ->HDIST=8-1=7
origin  : 0 .  7
code-hex: 0 .  1
code len: 1 .  1
sqencode: 1 17 1
huffcode: 1 3  1
extra.  :  011

对lit&dist sqencode 再次huffman编码得到

n   0   l  2 c    0 (0) 
n   1   l  3 c    1 (4) 
n   3   l  3 c    5 (5) 
n   4   l  2 c    2 (1) 
n  17   l  3 c    3 (6) 
n  18   l  3 c    7 (7) 

index: 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
value: 0 , 3 , 3 , 2, 0, 0, 0, 0, 0 , 0, 0 , 2, 0 , 3, 0 , 0,  0, 3, 0,
total= 19-1=18 -> HCLEN=18-4=14

  HLIT=2  BTYPE BFINAL
  00010    10    1 ->15

  HCLEN=14 HDIST=7
  1-110     00111  ->C7

  3    3   0    
  01-1 011 000 1   ->B1

  0   2    
  000 010 01   ->09

  0    0   0
  0-00 000 000 ->00

  0    0   0
  00-0 000 000 0->00

  0    2  
  000 010 00 ->08

  0    0   3
  0-00 000 011 ->03

  3   0  HCLEN bits end 
  011 000 0 ->30

           7   2
  001-0100 111 1-0  ->4F

  2  5
  10 101 001 ->A9

  2      3
  10 111 011 ->BB

           7 
  01-01100 111 ->67

  0   2  2
  00  10 10 01 ->29

       3    2  0
  00-0 011  10 00 ->38

  0   0  5
  0-0 00 101  00 ->14

  0  0  5
  00 00 101 0 ->0A

      3   2
  001 011 10 ->2E

           7   2
  1111-101 111 10 ->BE

  0  2
  00 10 1111 ->2F

  2  HLIT bits end 
  10

      3   1 
  011 011 001 10 ->66

  h-d4  1 //  HDIST bits end
  11-01 001 011 ->4B

  l-43  e-54 
  1-00  0101 11->17

       o-23 l-43
  00-0 010  100 1 ->29

  ....
TIMFENG-MB0:zlib fengguangtu$ ./mytest 
hello world!
hello world, hello!:20
68 65 6C 6C 6F 20 77 6F 72 6C 64 2C 20 68 65 6C
6C 6F 21 00
dyn block result::31
15 C7 B1 09 00 00 08 03 30 4F A9 BB 67 29 38 14
0A 2E BE 2F 66 4B 17 29 AC 86 19 E8 8F DB 01

总的流程图如下

作者

ZIP的作者是一个叫Phil Katz的人,这个人算是开源界的一个具有悲剧色彩的传奇人物。虽然二三十年前,开源这个词还没有现在这样风起云涌,但是总有一些具有黑客精神的牛人,内心里面充满了自由,无论他处于哪个时代。Phil Katz这个人是个牛逼程序员,成名于DOS时代,只是从书籍中得知,那个时代网速很慢,拨号使用的是只有几十Kb(比特不是字节)的猫,56Kb实际上是这种猫的最高速度,在ADSL出现之后,这种技术被迅速淘汰。当时记录文件的也是硬盘,Phil Katz上网的时候还不到1990年,WWW实际上就没出现,浏览器当然是没有的,当时上网干嘛呢?基本就是类似于网管敲各种命令,这样实际上也可以聊天、上论坛不是吗,传个文件不压缩的话肯定死慢死慢的,所以压缩在那个时代很重要。当时有个商业公司提供了一种称为ARC的压缩软件,可以让你在那个时代聊天更快,当然是要付费的,Phil Katz就感觉到不爽,于是写了一个PKARC,免费的,看名字知道是兼容ARC的,于是网友都用PKARC了,ARC那个公司自然就不爽,把哥们告上了法庭,说牵涉了知识产权等等,结果Phil Katz坐牢了。。。牛人就是牛人, 在牢里面冥思苦想,决定整一个超越ARC的牛逼算法出来,牢里面就是适合思考,用了两周就整出来的,称为PKZIP,不仅免费,而且这次还开源了,直接公布源代码,因为算法都不一样了,也就不涉及到知识产权了,于是ZIP流行开来,不过Phil Katz这个人没有从里面赚到一分钱,还是穷困潦倒,因为喝酒过多等众多原因,2000年的时候死在一个汽车旅馆里。英雄逝去,精神永存,现在我们用UE打开ZIP文件,我们能看到开头的两个字节就是PK两个字符的ASCII码。参考 3

测试代码

deflate zlib gzip 格式测试代码

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "zlib.h"

#define CHECK_ERR(err, msg) { \
    if (err != Z_OK) { \
        fprintf(stderr, "%s error: %d\n", msg, err); \
        exit(1); \
    } \
}
static z_const char hello[] = "hello world, hello!";
#define CHUNK 16384
void printHex(const char* text,const char*data,const int len){
    printf("%s:%d\n",text,len);
    for (int i = 0; i < len; i++){
        if(i==0){
        }else if((i&0xF) ==0){
            printf("\n");
        }else if((i&0x3) == 0){
            printf("\t");
        }else{
            printf(" ");
        }
        printf("%02X", data[i]&0xff);
    }
    printf("\n");
}

void testZlib(){
    int helloSize = sizeof(hello)/sizeof(hello[0]);
    printHex(hello,hello,helloSize);
    int ret, flush;
    unsigned have;
    z_stream strm;
    //unsigned char in[CHUNK];
    unsigned char out[CHUNK];
    /* allocate deflate state */
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;

    //no wrap
    ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,Z_DEFLATED,-MAX_WBITS,8,Z_DEFAULT_STRATEGY);
    CHECK_ERR(ret, "deflateInit2 no wrap error");
    strm.avail_in = helloSize;
    strm.next_in = hello;
    strm.avail_out = CHUNK;
    strm.next_out = out;
    deflate(&strm,Z_FINISH);
    printHex("no wrap result:",out,CHUNK - strm.avail_out);
    deflateEnd(&strm);

    ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,Z_DEFLATED,MAX_WBITS,8,Z_DEFAULT_STRATEGY);
    CHECK_ERR(ret, "deflateInit2 zlib error");
    strm.avail_in = helloSize;
    strm.next_in = hello;
    strm.avail_out = CHUNK;
    strm.next_out = out;
    deflate(&strm,Z_FINISH);
    printHex("zlib result:",out,CHUNK - strm.avail_out);
    deflateEnd(&strm);

    ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,Z_DEFLATED,MAX_WBITS+16,8,Z_DEFAULT_STRATEGY);
    CHECK_ERR(ret, "deflateInit2 gzip error");
    strm.avail_in = helloSize;
    strm.next_in = hello;
    strm.avail_out = CHUNK;
    strm.next_out = out;
    deflate(&strm,Z_FINISH);
    printHex("gzip result:",out,CHUNK - strm.avail_out);
    deflateEnd(&strm);

    int size = CHUNK;
    ret = compress(out,&size,hello,helloSize);
    CHECK_ERR(ret, "compress error");
    printHex("compress result:",out,size);

    gzFile fd= gzopen("test.gz","wb");
    gzwrite(fd,hello,helloSize);
    gzclose(fd);

    int fd1 = open("test.gz",O_RDONLY);
    int rsize = read(fd1,out,CHUNK);
    printHex("gzwrite result:",out,rsize);
    close(fd1);

}

int main(int argc,char**argv){
    printf("hello world!\n");
    testZlib();
    return 0;
}

  1. ZLIB Data Structure 

  2. Understanding zlib 

  3. ZIP压缩算法详细分析及解压实例解释