茱莉亚·伊万斯

第15天:如何构建.gz文件,雷杜

所以我在朱莉娅完成了gunzip的实现!一路上我结识了很多很棒的人,因此,我最后多次试图解释fileformat是如何工作的,可能经常以相当混乱的方式(对不起!这里是解释gzip的另一个混乱尝试,主要是为了我自己的利益。

gzip(也称为DEFLATE算法)背后的基本思想是存在2遍:

  1. LL87 pass:用指向它们之前在文本中出现的位置的指针替换重复序列。在本页
  2. Huffman编码传递:使用Huffman编码以有效的方式表示文本和指针

对于上述页,因此我将继续讨论gzip文件格式。如果你最近5天没有全神贯注在gzip中,这可能是没有意义的。

这就是说,下面是gzip文件的布局,我编写了一些代码来对它进行解码。github存储库在这里:com/jvns/gzip.jl

Gzip头和元数据(大约20字节)

文件的这一部分没那么有趣。每个gzip文件都必须启动魔术位1F46.有一个9字节的报头,后面是长度可变的元数据,包括文件名和其他一些东西。我不确定为什么文件名包含在元数据中!!

显然,gunzip还知道如何压缩其他一些传统压缩格式,但是为了我们的目的压缩法总是.

以下是我用来存储头和元数据的Julia数据结构:

类型GzipHeader id::Vector{Uint8}#.2.on_method::Uint8 flags::GzipFlags mtime::Vector{Uint8}#.4._flags::Uint8 os::Uint8end
类型GzipMetadata header::GzipHeader xlen::Uint16..::ASCIIString fname::ASCIIString fcomment::ASCIIString crc16::Uint16end

阻碍!!

头和元数据之后的gzip文件的其余部分是一系列块,按顺序阅读。据我所知,并行解压缩gzip文件是不可能的——如果不完全解压缩,就无法知道块何时结束。

块报头(3位)

每个块以3位开始

  • 这个块是否是最后一个块(1位)
  • 如何压缩块(2位)。我的gunzip只支持一种压缩方法,尽管其中一个选项是未压缩的,所以很容易添加。

哈夫曼码的报头(14位)

有一个递归的事情发生,其中有一个哈夫曼树编码与其他哈夫曼树。这个头部是帮助您开始读取所有这些树的元数据。

  • 海伦第一个树中的代码数(减去4)
  • HLIT第二棵树中字面代码的数量
  • HDIST第二棵树中距离码的数目(减一)

这个减四减一的事情基本上把我逼疯了。但它们至少容易表示:

输入HuffmanHeader hlit::Uint8 hdist::Uint8 hclen::Uint8end

第一个Huffman树的代码长度((HCLEN + 4)* 3位)

下一步,这里有一系列从0到7的数字,可以用来组合哈夫曼树。我读了这些read_first_tree().

第二哈夫曼树的代码长度(未知位数)

第二哈夫曼树的代码长度使用第一哈夫曼树编码。

所以你必须阅读(258+hlit+hdist)代码长度,使用第一棵赫夫曼树作为您的向导。我读了这些read_._tree_codes()

你最后在这里得到了两棵哈夫曼树——它们叫做文字树距离树.文字树由第一件制成257+HLIT代码,和距离树由下一个制成HDIST + 1代码。这其实并不明显,我花了很长时间才弄明白。

压缩数据!(未知位数)

读取压缩数据,你必须

  1. 读代码
    1. 如果是停止码,住手!!
    2. 如果它表示一个字面字符(如“a”或“2”),只需将其添加到解码文本中
    3. 如果它反而表示指向某些先前文本的指针,找出长度和相对距离,然后复制文本。

下面是我为此编写的实际代码!!

函数.ate_block!(译码文本,BitStream:literal_tree::HuffmanTree,._tree::HuffmanTree)虽然true#Read来自文件的代码=read_huffman_bits(bs,literal_tree)如果code==256#Stop code;块结束!如果附加了代码<=255#ASCII字符,则中断结束!(译码文本,[转换](UIT8)(code)]).#Po.to.text len=read_._code(bs,code).=read_._code(bs,._tree)#将先前的文本复制到我们的解码文本copy_text!(译码文本,距离,len)结束返回deco._tex.

读块的主要功能!!

这里是主要的块读取功能!它做很多事情,但它以一种相当可读的方式出现。

它修改解码文本这是因为块可以引用前面块中的文本。所以需要某种共享状态。

函数.ate_block!(译码文本,BitStream)head=read(bs,HuffmanHeader) #读取第一个Huffman树first_tree=read_first_tree(bs,(head.hclen)#读取第二个Huffman树代码=read_._tree_codes(bs,头,first_tree)#将文字树(0-255)放在一起,停止码(256),以及长度代码(257-285.) literal_codes=code[1:257+head.hlit] ._code_table=create_code_table(literal_codes,[0:.(literal_codes)-1])literal_tree=create_huffman_tree(._code_table)#将距离码树(0-17)._codes=code[end-head.hdist:end]dist_code_table=create_code_table(._codes,[0:.(._codes)-1])._tree=create_huffman_tree(dist_code_table)返回.ate_block!(译码文本,bs,文字树距离_树)端