在windows下使用UltraEdit打开后缀的.gz的文件显示的数据格式如下:
2 bytes GZIP标志字节:0x1f, 0x8b (\037 \213)
1 byte 压缩方法: (0..7 reserved, 8 = deflate)
1 byte 标志位:
bit 0 set: 文件可能是ASCII文本文件
bit 1 set: 附加多个gzip文件部分
bit 2 set: 存在有可选的附加 内容
bit 3 set: 提供了原始的文件名称
bit 4 set: 则提供有一个O-终结的文件内容
bit 5 set: 文件被加密
bit 6,7: 保留
4 bytes 文件更改时间(Unix时间)
1 byte 额外的标志,决定了压缩方法。 2:使用最大的压缩,最慢的算法
4:采用最快的算法
1 byte 这个标志指明了进行压缩时系统的类型。
0 - FAT filesystem (MS-DOS, OS/2, NT/Win32)
1 - Amiga
2 - VMS (or OpenVMS)
3 - Unix
4 - VM/CMS
5 - Atari TOS
6 - HPFS filesystem (OS/2, NT)
7 - Macintosh
8 - Z-System
9 - CP/M
10 - TOPS-20
11 - NTFS filesystem (NT)
12 - QDOS
13 - Acorn RISCOS
255 - unknown
2 bytes optional part number (second part=1) 可选的序号
2 bytes optional extra field length 可选的附加内容的长度
? bytes optional extra field 可选的附加内容
? bytes optional original file name, zero terminated
可选的原始文件名称,以'\0'结束
? bytes optional file comment, zero terminated
可选文件内容(这部分不被解释,而是可读的供人使用的,以'\0'结束
12 bytes optional encryption header
? bytes compressed data
4 bytes crc32 这个是未压缩数据的循环冗余校验值。
4 bytes uncompressed input size modulo 2^32 这是原始数据的长度以2的32次方为模的值。
设计了一种可以单向编码的格式,而不用反向查找,也不用预知未压缩数据及输出的
已压缩数据的大小。如果输入的数据不是一个文件,那么修改时间被设置为压缩的开
始时间。
The format was designed to allow single pass compression without any
backwards seek, and without a priori knowledge of the uncompressed
input size or the available size on the output media. If input does
not come from a regular disk file, the file modification time is set
to the time at which compression started.
时间戳主要是用在在网络上传输gzip文件的情况下。在这种情况下,它不需要保存所有
者的属性。在本地传输的时候,所有者的属性在压缩/解压缩时由gzip所保存。忽略值为
0的时间戳。
The time stamp is useful mainly when one gzip file is transferred over
a network. In this case it would not help to keep ownership
attributes. In the local case, the ownership attributes are preserved
by gzip when compressing/decompressing the file. A time stamp of zero
is ignored.
标志位中,值为0的位是可选的,它可以使我们对输入的数据做一个预先的了解。在不
确定的时候,要将标志位清除。对有不同文件格式(文本文件和二进制文件)的系统来说,
解码时,可以使用标志位来选择不同的格式。
Bit 0 in the flags is only an optional indication, which can be set by
a small lookahead in the input data. In case of doubt, the flag is
cleared indicating binary data. For systems which have different
file formats for ascii text and binary data, the decompressor can
use the flag to choose the appropriate format.
如果有附加内容,则它必须包含一个或多个子字段,每个子字段有如下格式:
The extra field, if present, must consist of one or more subfields,
each with the following format:
subfield id : 2 bytes 子字段ID
subfield size : 2 bytes (little-endian format)子字段长度(小端字节序)
subfield data 子字段内容
子字段ID可以包含两个可记住的字母。请发送一些这样的ID给
[email protected].
第二个字节为0的ID是被保留的。定义了如下的ID
The subfield id can consist of two letters with some mnemonic value.
Please send any such id to
[email protected]. Ids with a zero second
byte are reserved for future use. The following ids are defined:
Ap (0x41, 0x70) : Apollo file type information
子字段长度是子字段内容的长度,不包含ID及子字段长度这四字节。但是
前面所说的"可选的附加内容的长度"则包含了ID及子字段长度的四字节。
The subfield size is the size of the subfield data and does not
include the id and the size itself. The field 'extra field length' is
the total size of the extra field, including subfield ids and sizes.
必须可以在压缩数据中找到数据结束的位置,而不论数据的实际长度是多少。如果压缩
数据不能够放到一个文件中(如磁盘的情况),每一部分都要由一个头字段开始,但是只
有最后一部分中有CRC32和原始数据的长度。 解压程序应该可以提示输入另外的,存在
于多个压缩文件中的数据。这是必要,但不是绝对的,因为当一部分数据毁坏时,还需
要得到其它部分的内容。
It must be possible to detect the end of the compressed data with any
compression format, regardless of the actual size of the compressed
data. If the compressed data cannot fit in one file (in particular for
diskettes), each part starts with a header as described above, but
only the last part has the crc32 and uncompressed size. A decompressor
may prompt for additional data for multipart compressed files. It is
desirable but not mandatory that multiple parts be extractable
independently so that partial data can be recovered if one of the
parts is damaged. This is possible only if no compression state is
kept from one part to the other. The compression-type dependent flags
can indicate this.
如果压缩文件的系统对文件名的大小写不敏感,则原始文件名会会强制转换成小写。
如果是从标准输入读入的数据,则没有原始文件名。
If the file being compressed is on a file system with case insensitive
names, the original name field must be forced to lower case. There is
no original file name if the data was compressed from standard input.
即使压缩后的文件会比原来的文件大,压缩还是会完成的。
Compression is always performed, even if the compressed file is
slightly larger than the original. The worst case expansion is
a few bytes for the gzip file header, plus 5 bytes every 32K block,
or an expansion ratio of 0.015% for large files. Note that the actual
number of used disk blocks almost never increases.
The encryption is that of zip 1.9. For the encryption check, the
last byte of the decoded encryption header must be zero. The time
stamp of an encrypted file might be set to zero to avoid giving a clue
about the construction of the random header.
gzip-1.2.4程序分析
一点说明:
在gzip.c中:
DECLARE(uch, inbuf, INBUFSIZ +INBUF_EXTRA);
DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
DECLARE(ush, d_buf, DIST_BUFSIZE);
DECLARE(uch, window, 2L*WSIZE);
#ifndef MAXSEG_64K
DECLARE(ush, tab_prefix, 1L<-8,即bb中有完整的字节,则将此字节放回输入中。
3)输出解压得到的内容。
6/
==================================================================================
在文件gzip-1.2.4/inflate.c中:
函数: int inflate_block(e)
int *e; /* last block flag */
参数:如果是1,是说明当前块是最后一块。
功能: 1)得到第一位,这一位说明当前块是否为最后一块(0,不是;1,是)并相应的设置参数。
2)得到下两位的值:
0,本块没有压缩,
1,用固定的Huffman编码压缩,见RFC1951的3.2.6节。
2,用动态的Huffman编码压缩,见RFC1951的3.2.7节。
3)根据前面得到的值,调用不同的函数解压:
inflate_stored(); 对于未压缩的数据,调用这个函数。
inflate_fixed(); 对于用固定的Huffman编码压缩的数据,调用这个函数。
inflate_dynamic(); 对于用动态的Huffman编码压缩的数据,调用这个函数。
7/
==================================================================================
在文件gzip-1.2.4/inflate.c中:
函数: int inflate_stored()
功能: 处理非压缩的数据内容
1)丢弃不足一字节的位。由于非压缩的数据中,内容都是以字节为单位的,所以原来按
位读取的时候,会剩余不足一字节位内容,现在要去掉这些位。
2)读入两字节的内容,其值是未压缩的数据长度。再读入两字节的内容,其值应该是前
两字节所表示的长度的补码,若不是,则错误。
3)逐字节的读入内容,并输出到输出文件中。
8/
==================================================================================
在文件gzip-1.2.4/inflate.c中:
函数: int inflate_fixed()
功能: 用固定的Huffman编码压缩的数据
1) 为0至287的文字/length值设定编码长度:
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
2) 调用huft_build()建造文字/length值的Huffman树
3) 设置所有distance值(从0至29)的编码长度为5。
4) 调用huft_build()建造distance值的Huffman树
5) 调用函数inflate_codes()进行解码。
9/
==================================================================================
在文件gzip-1.2.4/inflate.c中:
函数: int inflate_dynamic()
功能: 用动态的Huffman编码压缩的数据
1)读入5位的值HLIT,算出nl = 257+HLIT。这是需要编码的最大值。
2)读入5位的值HDIST,算出nd = 1+HDIST。这是distance的最大值。
3)读入4位的值HCLEN,算出nb = 4+HCLEN。说明有多少种编码长度。
4)再读入3*nb位,每三位的值表示用多少位来表示所对应的编码长度。
5)调用huft_build()建造编码长度的Huffman树。
6)利用这个Huffman树,对接下来的若干位解码出nl+nd个值,这些值依次是0~nl-1
的编码长度(对于文字/length平说),及0~nd-1的编码长度(对于distance来说)。
7)利用上面解码出的两组长度值,两次调用huft_build()函数,建造两个Huffman树
(一个是为文字/length,另一个是为distance)。
8)调用函数inflate_codes()进行解码。
10/
==================================================================================
在文件gzip-1.2.4/inflate.c中:
函数: int inflate_codes(tl, td, bl, bd)
struct huft *tl, *td; /* literal/length and distance decoder tables */
int bl, bd; /* number of bits decoded by tl[] and td[] */
参数: tl,td是进行Huffman编码解码时用到的结构体,由于length和distance用不同的编码
方式,所以要有两个指针进行解码。
在两种编码中,用struct huft结构编码时,分别以bl,bd位进行编码。
功能: 用两个以经做好的链表来进行解码。
1) 解码一个值X,如果0<=X<=255,则X是一个字符,输出,循环1)。
2) 如果X==255,则说明块结束,函数返回。
3) X>255,则说明读到的是一个length值,根据这个值,及其后的附加位,得到真实的
length值。
4) 继续读入一个值,这个值是distance的标志值,根据这个值及其后的附加位得到真实
的distance。
5) 在已经输出的串中,向前查找distance个字节,拷贝length个字节到输出串的末尾。
6) 循环1)
11/
==================================================================================
在文件gzip-1.2.4/inflate.c中:
函数: int huft_build() 和函数int huft_free()比较独立,可以直接引用,不再分析。
功能: int huft_build() :建立Huffman解码链表。
int huft_free() :清除链表。
12/
==================================================================================
在文件gzip-1.2.4/zip.c中:
函数: int zip(in, out)
int in, out; /* input and output file descriptors */
参数:为输入、输出文件。
功能:
1) 向输出写入三字节:0x1F 0x8B 0x08。
2) 向输出写入一个含有8个标志位的字节。
3) 向输出写入4字节的系统时间。
4) 初始化CRC的值。
5) 调用bi_init(out)初始化读入位串的程序。
6) 调用ct_init()进行分配内存,初始化变量表,保存原始文件信息的
操作。
7) 调用lm_init()为新文件初始化"最长匹配"的程序。
8) 再向输出写入2字节,一个为额外的标志,一个为系统类型。
9) 如果需要,则保存原始文件名称。
10) 保存头部信息的长度。
11) 调用函数deflate()压缩。
12) 写入4字节的CRC值。
13) 写入4字节的原始内容长度值。
14)修改前面保存的头部信息长度的值。
13/
==================================================================================
在文件gzip-1.2.4/deflate.c中:
函数: ulg deflate()
功能: 压缩数据。此函数通过一些复杂的算法来进行压缩操作,可以直接引用。
1) 如果需要快速压缩,则调用函数deflate_fast(),然后返回。
2) 将当前内容插入到哈希表中,并查找最长匹配。
3) 若找到匹配内容,则输出对的编码,否则输出字符编码。
14/
==================================================================================
在文件gzip-1.2.4/deflate.c中:
函数: ulg deflate()
功能: 压缩数据。此函数通过一些稍简单一些的算法来进行压缩操作,可以直接引用。
1)将当前内容插入到哈希表中,并查找最长匹配。
2)若找到匹配内容,则输出对的编码,否则输出字符编码。