7z文件格式及其源码的分析(六)-完结篇
7z文件格式及其源碼的分析(六)-完結(jié)篇
2條回復(fù)
上一篇在這里?7z文件格式及其源碼的分析(五)
這一篇主要介紹7z的流式壓縮和解壓原理.
對(duì)于流式處理來講, 主要問題是要處理好文件頭的問題. 因?yàn)槭橇魇降? 所以,不可能走回頭路. 也就不可能說壓縮完了之后回到開頭來修改頭的信息.
7z文件的文件頭其實(shí)是分為兩部分的, 這兩部分文件頭在前面的幾篇分析中都有介紹:
頭Header, 就是寫在文件前面的 Header. 這個(gè)頭比較小, 主要記錄了文件的版本信息, 以及尾header的位置偏移和校驗(yàn)和等等. 其主要作用是文件類型識(shí)別和查找到尾Header
尾Header, 就是寫在文件末尾的 Header. 這里記錄了 7z 文件的全部壓縮信息, 解壓主要就靠這里面的信息了.
7z 常見的壓縮過程是: 先把?頭Header?的一些位置保留, 然后壓縮數(shù)據(jù). 壓縮完之后,?尾Header?寫完之后, 再把?尾Header?的大小,偏移,校驗(yàn)和寫回到?頭Header?中去.
在流式環(huán)境下, 最后一步回頭寫?頭Header?的過程顯然不可能完成. 因?yàn)橐呀?jīng)流過了.
那 7z 是怎么處理這個(gè)問題的呢? 7z 支持流式壓縮嗎?
7z 的文檔里,并沒有明確的回答這個(gè)問題. 我們還是讓它的代碼回答吧.
我們找到 7z 的解壓代碼,?7zIn.cpp?文件. 找到?HRESULT CInArchive::ReadDatabase2()?函數(shù). 大概在?1136行.
這個(gè)函數(shù)就是在讀取?頭Header?信息, 并查找?尾Header?位置.
我們截取它開頭的一部分代碼片段:
HRESULT CInArchive::ReadDatabase2(DECL_EXTERNAL_CODECS_LOC_VARSCArchiveDatabaseEx &db#ifndef _NO_CRYPTO, ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined#endif) {db.Clear();db.ArchiveInfo.StartPosition = _arhiveBeginStreamPosition;db.ArchiveInfo.Version.Major = _header[6];db.ArchiveInfo.Version.Minor = _header[7];if (db.ArchiveInfo.Version.Major != kMajorVersion)ThrowUnsupportedVersion();UInt32 crcFromArchive = Get32(_header + 8);UInt64 nextHeaderOffset = Get64(_header + 0xC);UInt64 nextHeaderSize = Get64(_header + 0x14);UInt32 nextHeaderCRC = Get32(_header + 0x1C);UInt32 crc = CrcCalc(_header + 0xC, 20);#ifdef FORMAT_7Z_RECOVERYif (crcFromArchive == 0 && nextHeaderOffset == 0 && nextHeaderSize == 0 && nextHeaderCRC == 0){UInt64 cur, cur2;RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &cur));const int kCheckSize = 500;Byte buf[kCheckSize];RINOK(_stream->Seek(0, STREAM_SEEK_END, &cur2));int checkSize = kCheckSize;if (cur2 - cur < kCheckSize)checkSize = (int)(cur2 - cur);RINOK(_stream->Seek(-checkSize, STREAM_SEEK_END, &cur2));RINOK(ReadStream_FALSE(_stream, buf, (size_t)checkSize));int i;for (i = (int)checkSize - 2; i >= 0; i--)if (buf[i] == 0x17 && buf[i + 1] == 0x6 || buf[i] == 0x01 && buf[i + 1] == 0x04)break;if (i < 0)return S_FALSE;nextHeaderSize = checkSize - i;nextHeaderOffset = cur2 - cur + i;nextHeaderCRC = CrcCalc(buf + i, (size_t)nextHeaderSize);RINOK(_stream->Seek(cur, STREAM_SEEK_SET, NULL));}else#endif{if (crc != crcFromArchive)ThrowIncorrect();}db.ArchiveInfo.StartPositionAfterHeader = _arhiveBeginStreamPosition + kHeaderSize;(...)}可以看到, 下面這幾句是在從?頭Header?上讀取?尾Header?信息.
UInt32 crcFromArchive = Get32(_header + 8);UInt64 nextHeaderOffset = Get64(_header + 0xC);UInt64 nextHeaderSize = Get64(_header + 0x14);UInt32 nextHeaderCRC = Get32(_header + 0x1C);UInt32 crc = CrcCalc(_header + 0xC, 20);緊接著, 后面又有一句判斷:
if (crcFromArchive == 0 && nextHeaderOffset == 0 && nextHeaderSize == 0 && nextHeaderCRC == 0)如果這幾個(gè)都讀不到怎么辦.
看代碼, 接下來, 它會(huì)從文件末尾開始, 倒著向前查找特征字符:
if (buf[i] == 0x17 && buf[i + 1] == 0x6 || buf[i] == 0x01 && buf[i + 1] == 0x04)這就是在找?0x17 0x06?或者?0x01 0x04?這兩個(gè)特征串.
代碼已經(jīng)很明確了:
如果找到這其中之一, 就算是找到?尾Header?了.
為什么能這么做呢, 這兩個(gè)串代表什么呢?
通過查看7z的文檔(事實(shí)上, 前面講的?尾Header?的結(jié)構(gòu)的時(shí)候也能發(fā)現(xiàn))知道.
這正是 7z 的兩種?尾Header?的格式.
到這里, 可以總結(jié)一下了:
7z 流式壓縮
壓縮時(shí)可以把?頭Header?中的 偏移量和 crc 和等 留空.
在解壓時(shí), 如果檢測(cè)到這些留空了, 則會(huì)從文件末尾開始查找?尾Header?特征串.
這種方法可以一定程度實(shí)現(xiàn) 流式壓縮. 雖然 7z 的文件結(jié)構(gòu)本身,限制它不適合流式壓縮的.
歡迎大家討論交流:?Neil 的博客
byNeil
byNeil.com
From?Blog by Neil,?post?7z文件格式及其源碼的分析(六)-完結(jié)篇
原文來自?Blog by Neil,?post?7z文件格式及其源碼的分析(六)-完結(jié)篇?轉(zhuǎn)載請(qǐng)注明出處。本站保留一切權(quán)力
總結(jié)
以上是生活随笔為你收集整理的7z文件格式及其源码的分析(六)-完结篇的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win11怎么连接宽带?
- 下一篇: eclipse import netsc