7z文件格式及其源码的分析(六)-完结篇

2条回复

上一篇在这里 7z文件格式及其源码的分析(五)

这一篇主要介绍7z的流式压缩和解压原理.

对于流式处理来讲, 主要问题是要处理好文件头的问题. 因为是流式的, 所以,不可能走回头路. 也就不可能说压缩完了之后回到开头来修改头的信息.

7z文件的文件头其实是分为两部分的, 这两部分文件头在前面的几篇分析中都有介绍:

  1. 头Header, 就是写在文件前面的 Header. 这个头比较小, 主要记录了文件的版本信息, 以及尾header的位置偏移和校验和等等. 其主要作用是文件类型识别和查找到尾Header

  2. 尾Header, 就是写在文件末尾的 Header. 这里记录了 7z 文件的全部压缩信息, 解压主要就靠这里面的信息了.

7z 常见的压缩过程是: 先把 头Header 的一些位置保留, 然后压缩数据. 压缩完之后, 尾Header 写完之后, 再把 尾Header 的大小,偏移,校验和写回到 头Header 中去.

在流式环境下, 最后一步回头写 头Header 的过程显然不可能完成. 因为已经流过了.

那 7z 是怎么处理这个问题的呢? 7z 支持流式压缩吗?

7z 的文档里,并没有明确的回答这个问题. 我们还是让它的代码回答吧.

我们找到 7z 的解压代码, 7zIn.cpp 文件. 找到 HRESULT CInArchive::ReadDatabase2() 函数. 大概在 1136行.

这个函数就是在读取 头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)

如果这几个都读不到怎么办.

看代码, 接下来, 它会从文件末尾开始, 倒着向前查找特征字符:

if (buf[i] == 0x17 && buf[i + 1] == 0x6 || buf[i] == 0x01 && buf[i + 1] == 0x04)

这就是在找 0x17 0x06 或者 0x01 0x04 这两个特征串.

代码已经很明确了:

如果找到这其中之一, 就算是找到 尾Header 了.

为什么能这么做呢, 这两个串代表什么呢?

通过查看7z的文档(事实上, 前面讲的 尾Header 的结构的时候也能发现)知道.

  1. 0x17 是代表压缩Header的标记 kEncodedHeader. 后面的 0x06 是 packstream 的标记 kPackInfo.
  2. 0x01 是代表普通Header的标记 kHeader. 而 0x04 是 mainstream 的标记 kMainStreamsInfo.

这正是 7z 的两种 尾Header 的格式.

到这里, 可以总结一下了:

7z 流式压缩
压缩时可以把 头Header 中的 偏移量和 crc 和等 留空.
在解压时, 如果检测到这些留空了, 则会从文件末尾开始查找 尾Header 特征串.

这种方法可以一定程度实现 流式压缩. 虽然 7z 的文件结构本身,限制它不适合流式压缩的.

欢迎大家讨论交流: Neil 的博客

byNeil
byNeil.com

From Blog by Neil, post 7z文件格式及其源码的分析(六)-完结篇

原文来自 Blog by Neil, post 7z文件格式及其源码的分析(六)-完结篇 转载请注明出处。本站保留一切权力

7z文件格式及其源码的分析(六)-完结篇相关推荐

  1. 7z文件格式及其源码的分析

    本文是一个系列. 主要是分享我最近一年做7z文件开发的经验. 主要包括7z官方源码的结构分析, 以及7z文件格式的分析. 其中涉及到7z源码结构的各个细节, 以及7z文件格式的具体细节. 本文适合对象 ...

  2. 【Java 虚拟机原理】Class 字节码二进制文件分析 六 ( 属性类型 | Code 属性 | 属性名称索引 | 属性长度 | 操作数栈最大深度 | 局部变量存储空间 | 字节码长度 )

    文章目录 前言 一.属性类型 二.Code 属性表数据结构 三.属性名称索引 四.属性长度 五.操作数栈最大深度 六.局部变量存储空间 七.字节码长度 八.存储字节码指令的一系列字节流 前言 上一篇博 ...

  3. FPGA解析B码----连载7(完结篇)

    前言 上篇完结篇介绍了程序的整体架构和B码错误保护程序,这篇主要介绍下1PPS的产生. 写到这里想先聊聊现在的软件,用的是QII,不知道这个软件还能免费用多久.现在国外的软件慢慢的都不能用了,只能用国 ...

  4. FPGA解析B码----连载8(完结篇)

    前言 前两个完结篇介绍了B码的结构,B码保护程序和B码的1PPS产生程序,下面介绍B码的UTC时间产生.当然B码中含有UTC时间和UTC时间的关键信息.程序的整体思路是差不多的,翻过来调过去也就是那点 ...

  5. Fabric源码流程分析之Orderer篇

    导言: 本文使用fabric1.1版本,此时有小朋友会问了,fabric都出1.4.2了你怎么还在看1.1呢!首先fabric自1.0以后大的架构基本没有变化,小版本升级只是功能性上更加丰满了,当然最 ...

  6. Python-LBM(格子玻尔兹曼)程序源码实例分析—圆柱绕流篇

    初次学习LBM计算方法,找到一个比较优秀的用python语言编写的圆柱绕流的实例,对每段代码详细添加了注释,帮助自己总结,也为初学的朋友们提供一点帮助(全部代码在文章最后). 先放一张结果图像: 1. ...

  7. Foxdisk-代码仓库介绍暨完结篇

    (请保留-> 作者: 罗冰 https://blog.csdn.net/luobing4365) Foxdisk之代码仓库介绍暨完结篇 1 Foxdisk1 2 Foxdisk2 3 Foxdi ...

  8. UPX源码分析——加壳篇

    0x00 前言 UPX作为一个跨平台的著名开源压缩壳,随着Android的兴起,许多开发者和公司将其和其变种应用在.so库的加密防护中.虽然针对UPX及其变种的使用和脱壳都有教程可查,但是至少在中文网 ...

  9. <2021SC@SDUSC>【Overload游戏引擎】OvCore源码模块分析(六)——SceneSystem

    <2021SC@SDUSC>[Overload游戏引擎]OvCore源码模块分析(六)--SceneSystem 前言 SceneSystem Scene SceneManager 总结 ...

  10. <2021SC@SDUSC>【Overload游戏引擎】OvUI源码模块分析(六)——Widgets

    <2021SC@SDUSC>[Overload游戏引擎]OvUI源码模块分析(六)--Widgets Button Button namespace OvUI::Widgets::Butt ...

最新文章

  1. msflexgrid允许大选择_工程中要用多大的电线电缆?一文教你怎么算
  2. 尊重个体多样性,科学人文终统一
  3. 【NLP】使用BERT完成NLP任务
  4. QT学习:字符串类QString
  5. 从服务器上的数据库备份到本地
  6. 从ACL2021看对比学习在NLP中的应用
  7. 7-3 方格取数 (15 分)
  8. css几个居中的方法
  9. python学习:用两种思路计算质数与合数
  10. 智能车摄像头组怎么在OLED屏画出中线
  11. QUIC 技术创新 让视频和图片分发再提速
  12. 微信小游戏《飞机打方块》源码分享
  13. GNU通用公共许可协议--GPLV3中文翻译
  14. html 样式重叠问题,css怎么解决网页重叠问题
  15. 基于STM32的汇编程序
  16. 论文复现:模拟风电不确定性——拉丁超立方抽样生成及缩减场景(Matlab全代码)
  17. 亚马逊 Amazon Kindle Book 代购 英文原版 正版书 图书 电 子 书-淘宝网
  18. 日本人骑着22的自行车就上了高速!
  19. 【小白】学深度学习 发现一个褥百度GPU羊毛的机会
  20. Spring学习笔记09 - 对象的生命周期

热门文章

  1. linux热迁移137error VMware vCenter Converter 问题小结
  2. P3369 (Splay树模板)
  3. LCOI出题组加团规则
  4. 计算机科学的拉丁文,拉丁字母A-Z在计算机中对应的二进制编码
  5. Xcode 8 过滤系统输出
  6. 辩证法——自然观、自然科学方法论和科学观
  7. unity与Android交互的currentActivity和入口Activity
  8. 《正在爆发的互联网革命》作者个人出资10000美金,面向全球征召六度分割理论实验对象!...
  9. SSM框架-Spring(一)
  10. [OCCT] OCC官方示例介绍