這是7z文件格式及其源碼的分析系列的第四篇. 上一篇講到了7z文件靜態結構的尾header部分.這一篇開始,將從7z實際壓縮流程開始詳細介紹7z文件尾header的詳細結構.html
一, 第一個概念: coder.算法
在7z的壓縮過程當中, 一個很是核心的概念就是coder. 一個coder表明一個算法, 一般是指一個壓縮或解壓算法(也包括過濾算法和加密算法等). 例如, 在7z中lzma算法就是一個coder, deflate算法也是一個coder. 7z中用於加密的AES256算法也是一個coder. 數組
因此概念上講, 能處理一個文件流的算法就是一個coder. 這個"處理"的概念能夠是壓縮/解壓, 加密等等.ui
(圖1)加密
一般來說, 一個coder只能處理一個輸入流, 而且只有一個輸出流. 好比把一個文件流壓縮成一個輸出流. 可是, 7z中有的coder能夠把一個輸入流處理成多個輸出流, 反過來也能夠把多個流處理成一個流. 好比7z的 BCJ2 coder, 它是一個過濾coder, 能夠把一個exe文件過濾成四個輸出流. 這樣的話, 7z的coder概念獲得了擴展. 就是可能同時處理多個輸入流, 而且可能輸出多個流:spa
(圖2)3d
這裏能夠先簡單的體驗一下壓縮的過程了:code
1. 簡單壓縮過程, 把文件流交給lzma coder壓縮.orm
(圖3)htm
2. 多coder串聯, 理論上能夠串聯任何兩個coder, 並且串聯的級數也是沒有限制的, 能夠串聯任意多級. 固然, 因爲熵的存在, 串聯過個壓縮coder是沒有意義的.
這裏示例的是最經常使用的一種方式,就是壓縮而且加密.
(圖4)
注意上圖中的o1, 和 i2. o1是第一個coder的輸出流, i2是第二個coder的輸入流. 在實際操做中, 這兩個流實際上是同一個流, 直接把第一個的輸出當作第二個的輸入.
解壓的過程就是上面的逆過程.
上圖就是比較完整的一次壓縮過程了.
二, 第二個概念 Folder, 不是文件夾.
這裏的Folder要特別注意, 它不是咱們一般指的文件夾. 它也不是任何物理上存在的東西.
7z在開始壓縮以前, 會把文件分類, 大致上是按文件類型以及文件是否須要加密來分類的. 好比說, 把全部的exe文件分紅一類(一個Folder), 或者把全部須要加密的文件分在一塊兒. 等等. 具體分類方法之後再說. 這個分類方法並不重要, 7z的實現用的方法比較簡單. 實際上若是要實現7z的壓縮器的話, 這個分類方法你說了算. 你能夠給每一個文件劃分紅一個Folder.
咱們看一個例子:
(圖5)
在這個例子中, 咱們共有5個文件須要壓縮.
1. 首先, 經過必定的分組方法, 咱們分紅了兩個Folder, 第一個Folder包括: a.exe, b.exe 和 c.dll 三個文件. 第二個Folder包括:a.txt 和 b.txt.
2. 對Folder1來講, 它包含三個文件, Folder1就簡單的把三個文件串聯起來,當作一個大文件, 做爲輸入流 i1 給Coder1 用. 後面的過程就就是上面的 圖4 的內容了.
在7z源碼的: \CPP\7zip\Archive\7z\ 這個目錄下,有 7zFolderInStream.h 和7zFolderInStream.cpp 專門處理把多個文件串聯假裝成一個文件的任務. 從Coder1 的角度看, 它只知道有個文件流 i1, 並不知道這個i1 是一個真實的文件 仍是由一個Folder假裝的.
實際上, 7z概念上最小的壓縮單位不是文件, 而是Folder, 它會先把全部的文件都歸到一個相應的Folder中, 而後 讓這個Folder做爲文件流, 流過若干個Coder. 咱們再抽象一下上面的壓縮過程:
(圖 6)
上圖中的字母 'i' 表示輸入的意思, 'o' 表示輸出. 後面的數字表示序號. 簡單解釋一下, 這個Folder流最初是做爲Coder1 的輸入流i1. Coder1 的輸出流是o1. 這個o1又做爲 i2 輸入給Coder2用, 而後又是Coder3.
值得注意的是最後一個coder的輸出流 o3. 它就是壓縮的最終輸出結果了. 它在7z中叫作一個PackedStream. 就是打包的流. 咱們叫作p1吧. 若是有多個Folder, 那每一個Folder就會有一個或多個PackedStream. 因此全部文件壓縮以後就會有 pn. 這n個packedStream會被按順序存儲在 7z的文件主體, 就是上一篇文章中介紹的第二部分.
每一個Folder 包含了哪些文件, 每一個文件大小等等這些詳細信息都存貯在7z的尾文件頭中了. 在7zformat.txt中有這一段:
NumFolders Folders[NumFolders] { NumCoders CodersInfo[NumCoders] { ID NumInStreams; //表示這個coder 所接受的輸入流的個數, 通常是1個 NumOutStreams; //表示這個coder的輸出流的個數, 通常是1個. PropertiesSize //一個int值, 表示後面Properties的字節長度 Properties[PropertiesSize] // 字節數組, 表示這個coder的一些設置信息, 好比壓縮級別, 或者AES加密的IV等等. } NumBindPairs // 表示bindpair 的個數. bindpair表示輸入流和輸出流的綁定關係. 例如上面的圖6中, o1和i2是綁定的, o2和i3是綁定的. BindPairsInfo[NumBindPairs] //bindpair的數組, 記錄每個bindpair. { InIndex; //這個綁定的輸入index, 就是上圖中對應的 i後面的序號. (很差意思, 畫圖的時候沒注意,圖上下表是從1開始的,可是實際上,你懂的, 都是從0開始的.全部上面圖中的下標都要減一.) OutIndex; //綁定對應的輸出index, 就是對應上圖中o後面的序號. 同上. } PackedIndices //這表示這個folder最終輸出的packstream在全部packstream中的序號. } UnPackSize[Folders][Folders.NumOutstreams] // 這是一個二位數組, 記錄每一個Folder對應的輸出流的個數. CRCs[NumFolders] //這是一個Crc的數組, 沒個folder 流的crc, 7z目前沒有使用這一個字段.
稍微解釋一下上面的結構:
1. NumFolders, 顯示一個int32值, 它記錄了7z文件中共有多少個Folder.
2. 後面那是folder數組, 一次排布每一個Folder. 每一個Folder結構以下:
3. NumCoders, int32值, 記錄了這個Folder總共進過了幾個coder.
4. 後面就是它的全部Coder的數組, 每一個coder的結構: 顯示一個coder 的id. 就是coder的惟一標示符. 這個id的定義在: DOC/目錄下的 methods.txt.
5. 更詳細的信息, 請看上面代碼後面的註釋吧.
6. 我再強調一點,我畫圖的時候沒有注意,因此圖中的i和o後面的序號都是從1開始的, 實際上,你懂的, 每一個存儲的序號都是從0開始的, 沒有例外. 若是你發現哪裏的序號和我說的不同, 請檢查這個. 沒有例外, 全部的序號都是從0開始的. 包括之後我可能會畫的圖. 記住都是從0開始的.
7, 再有一點就是, 好比上圖中的folder通過了 coder1, coder2 和coder3 這三個coder. 實際才存儲這三個coder的時候, 是按逆序存儲的, 就是先存Coder3, 而後是coder2, 最後是coder1. 這是爲了方便解壓.
上面的圖6就是一次比較完整的壓縮流程, 解壓的流程就是反過來, 先分別構建coder1, coder2 和coder3, 而後逆向流動就最終解壓了.
每一個Folder都會通過一次完整的壓縮過程.
好了, 主要的壓縮過程和結構已經介紹完了. 下一篇將給你們介紹剩下的文件詳細信息的存儲方式, 以及最終的Header的生成方式.
最後仍是歡迎你們訪問個人獨立博客: http://byNeil.com
寫這麼多字, 畫圖都不容易, 幫頂一下吧, 小夥伴們.