7z文件格式及其源码的分析(四)
這是7z文件格式及其源碼的分析系列的第四篇. 上一篇講到了7z文件靜態(tài)結(jié)構(gòu)的尾header部分.這一篇開始,將從7z實(shí)際壓縮流程開始詳細(xì)介紹7z文件尾header的詳細(xì)結(jié)構(gòu).
一, 第一個(gè)概念: coder.
在7z的壓縮過程中, 一個(gè)非常核心的概念就是coder. ?一個(gè)coder代表一個(gè)算法, 通常是指一個(gè)壓縮或解壓算法(也包括過濾算法和加密算法等). 例如, 在7z中l(wèi)zma算法就是一個(gè)coder, ?deflate算法也是一個(gè)coder. ?7z中用于加密的AES256算法也是一個(gè)coder. ?
所以概念上講, 能處理一個(gè)文件流的算法就是一個(gè)coder. ?這個(gè)"處理"的概念可以是壓縮/解壓, 加密等等.
(圖1)
通常來講, 一個(gè)coder只能處理一個(gè)輸入流, 并且只有一個(gè)輸出流. ?比如把一個(gè)文件流壓縮成一個(gè)輸出流. ?但是, 7z中有的coder可以把一個(gè)輸入流處理成多個(gè)輸出流, 反過來也可以把多個(gè)流處理成一個(gè)流. 比如7z的 BCJ2 coder, ?它是一個(gè)過濾coder, 可以把一個(gè)exe文件過濾成四個(gè)輸出流. ?這樣的話, 7z的coder概念得到了擴(kuò)展. ?就是可能同時(shí)處理多個(gè)輸入流, 并且可能輸出多個(gè)流:
(圖2)
這里可以先簡(jiǎn)單的體驗(yàn)一下壓縮的過程了:
1. 簡(jiǎn)單壓縮過程, ?把文件流交給lzma coder壓縮.
(圖3)
2. 多coder串聯(lián), 理論上可以串聯(lián)任何兩個(gè)coder, 而且串聯(lián)的級(jí)數(shù)也是沒有限制的, 可以串聯(lián)任意多級(jí). 當(dāng)然, 由于熵的存在, 串聯(lián)過個(gè)壓縮coder是沒有意義的.
這里示例的是最常用的一種方式,就是壓縮并且加密.
(圖4)
注意上圖中的o1, 和 i2. ? o1是第一個(gè)coder的輸出流, i2是第二個(gè)coder的輸入流. 在實(shí)際操作中, 這兩個(gè)流其實(shí)是同一個(gè)流, 直接把第一個(gè)的輸出當(dāng)做第二個(gè)的輸入.
解壓的過程就是上面的逆過程.
上圖就是比較完整的一次壓縮過程了.
?
二, 第二個(gè)概念 Folder, 不是文件夾.
這里的Folder要特別注意, ?它不是我們通常指的文件夾. ?它也不是任何物理上存在的東西. ?
7z在開始?jí)嚎s之前, 會(huì)把文件分類, ?大體上是按文件類型以及文件是否需要加密來分類的. ? 比如說, 把所有的exe文件分成一類(一個(gè)Folder), 或者把所有需要加密的文件分在一起. 等等. 具體分類方法以后再說. ?這個(gè)分類方法并不重要, 7z的實(shí)現(xiàn)用的方法比較簡(jiǎn)單. 實(shí)際上如果要實(shí)現(xiàn)7z的壓縮器的話, 這個(gè)分類方法你說了算. 你可以給每個(gè)文件劃分成一個(gè)Folder.
?我們看一個(gè)例子:
(圖5)
?在這個(gè)例子中, 我們共有5個(gè)文件需要壓縮.?
1. 首先, 通過一定的分組方法, 我們分成了兩個(gè)Folder, ?第一個(gè)Folder包括: a.exe, b.exe 和 c.dll 三個(gè)文件. ?第二個(gè)Folder包括:a.txt 和 b.txt.
2. 對(duì)Folder1來說, 它包含三個(gè)文件, Folder1就簡(jiǎn)單的把三個(gè)文件串聯(lián)起來,當(dāng)做一個(gè)大文件, 作為輸入流 i1 給Coder1 用. 后面的過程就就是上面的 圖4 的內(nèi)容了.
在7z源碼的:?\CPP\7zip\Archive\7z\ 這個(gè)目錄下,有?7zFolderInStream.h 和7zFolderInStream.cpp 專門處理把多個(gè)文件串聯(lián)偽裝成一個(gè)文件的任務(wù). 從Coder1 的角度看, 它只知道有個(gè)文件流 i1, 并不知道這個(gè)i1 是一個(gè)真實(shí)的文件 還是由一個(gè)Folder偽裝的.
?
實(shí)際上, 7z概念上最小的壓縮單位不是文件, 而是Folder, ?它會(huì)先把所有的文件都?xì)w到一個(gè)相應(yīng)的Folder中, 然后 讓這個(gè)Folder作為文件流, 流過若干個(gè)Coder. ?我們?cè)俪橄笠幌律厦娴膲嚎s過程:
(圖 6)
上圖中的字母 'i' 表示輸入的意思, 'o' 表示輸出. 后面的數(shù)字表示序號(hào). ? 簡(jiǎn)單解釋一下, 這個(gè)Folder流最初是作為Coder1 的輸入流i1. Coder1 的輸出流是o1. 這個(gè)o1又作為 i2 輸入給Coder2用, ?然后又是Coder3. ? ?
值得注意的是最后一個(gè)coder的輸出流 o3. ?它就是壓縮的最終輸出結(jié)果了. ?它在7z中叫做一個(gè)PackedStream. 就是打包的流. ?我們叫做p1吧. ?如果有多個(gè)Folder, 那每個(gè)Folder就會(huì)有一個(gè)或多個(gè)PackedStream. ?所以所有文件壓縮之后就會(huì)有 pn. ?這n個(gè)packedStream會(huì)被按順序存儲(chǔ)在 7z的文件主體, 就是上一篇文章中介紹的第二部分.
?
每個(gè)Folder 包含了哪些文件, 每個(gè)文件大小等等這些詳細(xì)信息都存貯在7z的尾文件頭中了. 在7zformat.txt中有這一段:
NumFoldersFolders[NumFolders]{NumCodersCodersInfo[NumCoders]{IDNumInStreams; //表示這個(gè)coder 所接受的輸入流的個(gè)數(shù), 一般是1個(gè)NumOutStreams; //表示這個(gè)coder的輸出流的個(gè)數(shù), 一般是1個(gè).PropertiesSize //一個(gè)int值, 表示后面Properties的字節(jié)長(zhǎng)度Properties[PropertiesSize] // 字節(jié)數(shù)組, 表示這個(gè)coder的一些設(shè)置信息, 比如壓縮級(jí)別, 或者AES加密的IV等等. }NumBindPairs // 表示bindpair 的個(gè)數(shù). bindpair表示輸入流和輸出流的綁定關(guān)系. 例如上面的圖6中, o1和i2是綁定的, o2和i3是綁定的.BindPairsInfo[NumBindPairs] //bindpair的數(shù)組, 記錄每一個(gè)bindpair.{InIndex; //這個(gè)綁定的輸入index, 就是上圖中對(duì)應(yīng)的 i后面的序號(hào). (不好意思, 畫圖的時(shí)候沒注意,圖上下表是從1開始的,但是實(shí)際上,你懂的, 都是從0開始的.所有上面圖中的下標(biāo)都要減一.)OutIndex; //綁定對(duì)應(yīng)的輸出index, 就是對(duì)應(yīng)上圖中o后面的序號(hào). 同上. }PackedIndices //這表示這個(gè)folder最終輸出的packstream在所有packstream中的序號(hào).}UnPackSize[Folders][Folders.NumOutstreams] // 這是一個(gè)二位數(shù)組, 記錄每個(gè)Folder對(duì)應(yīng)的輸出流的個(gè)數(shù).CRCs[NumFolders] //這是一個(gè)Crc的數(shù)組, 沒個(gè)folder 流的crc, 7z目前沒有使用這一個(gè)字段.稍微解釋一下上面的結(jié)構(gòu):
1.?NumFolders, 顯示一個(gè)int32值, 它記錄了7z文件中共有多少個(gè)Folder.
2. 后面那是folder數(shù)組, 一次排布每個(gè)Folder. 每個(gè)Folder結(jié)構(gòu)如下:
3.?NumCoders, int32值, 記錄了這個(gè)Folder總共進(jìn)過了幾個(gè)coder.
4. 后面就是它的所有Coder的數(shù)組, 每個(gè)coder的結(jié)構(gòu): ?顯示一個(gè)coder 的id. 就是coder的唯一標(biāo)示符. 這個(gè)id的定義在:?DOC/目錄下的 methods.txt.?
5. 更詳細(xì)的信息, 請(qǐng)看上面代碼后面的注釋吧.
6. 我再?gòu)?qiáng)調(diào)一點(diǎn),我畫圖的時(shí)候沒有注意,所以圖中的i和o后面的序號(hào)都是從1開始的, 實(shí)際上,你懂的, 每個(gè)存儲(chǔ)的序號(hào)都是從0開始的, 沒有例外. 如果你發(fā)現(xiàn)哪里的序號(hào)和我說的不一樣, 請(qǐng)檢查這個(gè). ?沒有例外, 所有的序號(hào)都是從0開始的. 包括以后我可能會(huì)畫的圖. 記住都是從0開始的.
?
7, 再有一點(diǎn)就是, 比如上圖中的folder經(jīng)過了 coder1, coder2 和coder3 這三個(gè)coder. 實(shí)際才存儲(chǔ)這三個(gè)coder的時(shí)候, 是按逆序存儲(chǔ)的, 就是先存Coder3, 然后是coder2, 最后是coder1. ?這是為了方便解壓.
?
?
上面的圖6就是一次比較完整的壓縮流程, ? ?解壓的流程就是反過來, 先分別構(gòu)建coder1, coder2 和coder3, 然后逆向流動(dòng)就最終解壓了.
每個(gè)Folder都會(huì)經(jīng)過一次完整的壓縮過程.
?
好了, 主要的壓縮過程和結(jié)構(gòu)已經(jīng)介紹完了. ? 下一篇將給大家介紹剩下的文件詳細(xì)信息的存儲(chǔ)方式, 以及最終的Header的生成方式.
最后還是歡迎大家訪問我的獨(dú)立博客: http://byNeil.com?
?
寫這么多字, 畫圖都不容易, ?幫頂一下吧, 小伙伴們.
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/shuidao/p/3307300.html
總結(jié)
以上是生活随笔為你收集整理的7z文件格式及其源码的分析(四)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 精英任务 | 第二期券商研报复现挑战赛
- 下一篇: 计算机控制技术于海生期末考试,计算机控制