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