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 定义了三种格式:
DEFLATE
rfc1951 raw压缩数据zlib
rfc1950 在raw压缩数据上增加 2bytes头 4bytes尾(png文件使用该格式)
A zlib stream has the following structure:
0 1
+---+---+
|CMF|FLG| (more-->)
+---+---+
(if FLG.FDICT set)
0 1 2 3
+---+---+---+---+
| DICTID | (more-->)
+---+---+---+---+
+=====================+---+---+---+---+
|...compressed data...| ADLER32 |
+=====================+---+---+---+---+
gzip
rfc1952 在raw压缩数据上增加了18+ bytes
Each member has the following structure:
+---+---+---+---+---+---+---+---+---+---+
|ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->)
+---+---+---+---+---+---+---+---+---+---+
(if FLG.FEXTRA set)
+---+---+=================================+
| XLEN |...XLEN bytes of "extra field"...| (more-->)
+---+---+=================================+
(if FLG.FNAME set)
+=========================================+
|...original file name, zero-terminated...| (more-->)
+=========================================+
(if FLG.FCOMMENT set)
+===================================+
|...file comment, zero-terminated...| (more-->)
+===================================+
(if FLG.FHCRC set)
+---+---+
| CRC16 |
+---+---+
+=======================+
|...compressed blocks...| (more-->)
+=======================+
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| CRC32 | ISIZE |
+---+---+---+---+---+---+---+---+
zlib包含几套不同的逻辑接口,核心压缩方法都是deflate
,差异只是文件头尾,不同的封装等。
核心的deflate
接口:
//压缩接口
int deflateInit(z_streamp strm, int level);
int deflateInit2(z_streamp strm, int level, int method, int windowBits,int memLevel,int strategy);
int deflate(z_streamp strm, int flush);
int deflateEnd(z_streamp strm);
//对应的解压接口
int inflateInit(z_streamp strm);
int inflateInit2(z_streamp strm, int windowBits);
int inflate(z_streamp strm, int flush);
int inflateEnd(z_streamp strm);
还有一些不常用的接口如deflateCopy
等这里并没列出。
compress接口
int compress(Bytef *dest, uLongf *destLen,const Bytef *source, uLong sourceLen);
int compress2(Bytef *dest, uLongf *destLen,const Bytef *source, uLong sourceLen,int level);
int uncompress(Bytef *dest,uLongf *destLen,const Bytef *source, uLong sourceLen));
int uncompress2(Bytef *dest,uLongf *destLen,const Bytef *source, uLong *sourceLen);
gzip接口
gzFile gzopen(cconst char *path, const char *mode);
gzFile gzdopen(int fd, const char *mode);
int gzread(gzFile file, voidp buf, unsigned len);
int gzwrite(gzFile file,voidpc buf, unsigned len);
z_off_t gzseek(gzFile file,z_off_t offset, int whence);
z_off_t gztell(gzFile file);
z_off_t gzoffset(gzFile file);
deflate
是compress
和gzip
的底层实现。
compress
相比deflate
可以单独使用,内部是使用了deflate
接口,输出使用zlib格式。gzip
同样使用了deflate
接口,其封装为类似文件读写的接口形式,同时增加了gzip的文件头和校验标志。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的头文件里保存了level
和windowBits
,但这些参数不是解压必须的。
接口使用示例 参见官方的示例。
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编码不是唯一的,首先每个分支的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
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
01
:最后一个block,no compression,其它为padding14 00
-> 0x0014->20该block含有20字节EB FF
->~ 14 00
静态的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
这是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。
#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;
}