当前位置: 首页 > news >正文

[HDiffPatch] 补丁算法 | `patch_decompress_with_cache` | `getStreamClip` | RLE游程编码

第三章:补丁算法(hpatch)

欢迎回来

在第二章:差异算法(hdiff)中,我们探索了HDiffPatch如何智能比较数据的两个版本()以创建仅描述变更的紧凑"差异文件"。现在,我们将学习这项技术的另一半:补丁算法(hpatch)

问题:如何获取新版本?

假设我们是软件应用的用户。开发者发布了更新,他们没有让我们下载完整的新版本(可能高达GB级别!),而是发送了一个小型"更新文件"(差异文件),并要求我们将其应用到当前安装版本。

我们的计算机如何使用HDiffPatch,通过"旧"应用数据和微小的"差异文件"重建"新"更新版应用?

这正是补丁算法解决的问题。它是差异生成的反向过程:接收数据和差异指令,正确重建数据

解决方案:补丁算法(hpatch)

HDiffPatch的hpatch算法如同遵循精确手册的建筑师,主要职责是:

  1. 读取差异文件:理解hdiff算法生成的指令
  2. 访问数据:按差异文件指示获取原始数据片段
  3. 构建数据:拼接新版本,执行如"从旧数据复制此部分"和"添加这些新字节"等指令

沿用上章的乐高城堡类比:如果差异文件是从旧城堡到新城堡的指令清单,补丁器就是严格执行这些指令的人:

  • “从旧城堡数据)取这面墙放在新城堡数据)此处”(应用"匹配"或"覆盖")
  • “用这些新积木新数据差异字节)建造新塔”
  • “取这面现有墙,但通过微调(应用子差异)更换顶部的几块积木”

得益于第一章:基于流的输入输出,整个过程高效完成,仅读取必要的旧数据块并写入新数据块,适合处理超大文件。

补丁核心概念

  • 旧数据:已有的原始文件或目录
  • 差异文件(补丁)hdiff算法创建的小文件,包含将旧数据变为新数据的指令
  • 新数据:补丁器将创建的目标更新文件或目录
  • 覆盖(匹配):将数据特定位置的块直接复制到数据特定位置的指令
  • 差异(新增/子差异):写入全新字节(新数据差异)或基于数据计算微调字节(子差异)的指令

应用补丁:简单示例

假设有old_version.txt文件和patch.hdiff文件,要创建new_version.txt

使用hpatchz命令行工具,命令如下:

hpatchz old_version.txt patch.hdiff new_version.txt

预期输出:
成功后将创建new_version.txt,可能显示如下进度信息:

旧文件 : "old_version.txt"
差异文件: "patch.hdiff"
输出文件: "new_version.txt"输入旧数据大小: 1024差异数据大小: 128差异数据类型: HDiff保存旧数据大小: 1024保存新数据大小: 1050压缩类型: "zlib" (需解压4次)补丁进度: [==============================]  100.0%补丁成功!hpatchz耗时: 0.005秒

补丁的C++概念代码

hpatchz命令背后,HDiffPatch库使用patch_decompress_with_cache等函数。以下是简化示例:

#include "file_for_patch.h" // 流类型
#include "libHDiffPatch/HPatch/patch.h" // patch_decompress_with_cache
#include "decompress_plugin_demo.h" // 示例解压器,如zlibDecompressPluginvoid apply_my_patch(const char* oldFileName, const char* diffFileName, const char* outNewFileName) {hpatch_TFileStreamInput oldFileStream;hpatch_TFileStreamInput diffFileStream;hpatch_TFileStreamOutput newFileStream;// 初始化流对象(第一章内容)hpatch_TFileStreamInput_init(&oldFileStream);hpatch_TFileStreamInput_init(&diffFileStream);hpatch_TFileStreamOutput_init(&newFileStream);// 1. 打开输入(旧、差异)和输出(新)文件hpatch_TFileStreamInput_open(&oldFileStream, oldFileName);hpatch_TFileStreamInput_open(&diffFileStream, diffFileName);// 打开newFileStream前需从差异文件获取其最终大小// getCompressedDiffInfo读取差异文件头部获取该信息hpatch_compressedDiffInfo diffInfo;getCompressedDiffInfo(&diffInfo, &diffFileStream.base); hpatch_TFileStreamOutput_open(&newFileStream, outNewFileName, diffInfo.newDataSize);// 2. 准备解压插件(差异文件通常压缩)//    本例使用zlib,实际取决于差异文件的compressTypehpatch_TDecompress decompressPlugin = zlibDecompressPlugin; // 假设使用zlib// 3. 分配临时内存缓存const size_t cacheSize = (size_t)1 << 23; // 8MB缓存unsigned char* temp_cache = (unsigned char*)malloc(cacheSize);// 4. 调用核心补丁算法函数//    从oldFileStream和diffFileStream读取,写入newFileStreampatch_decompress_with_cache(&newFileStream.base, &oldFileStream.base,&diffFileStream.base, &decompressPlugin,temp_cache, temp_cache + cacheSize);// 5. 清理free(temp_cache);hpatch_TFileStreamOutput_close(&newFileStream);hpatch_TFileStreamInput_close(&diffFileStream);hpatch_TFileStreamInput_close(&oldFileStream);printf("补丁应用成功!\n");
}// 调用示例:
// apply_my_patch("old_version.txt", "patch.hdiff", "new_version.txt");

说明:

  • 再次使用第一章:基于流的输入输出的hpatch_TFileStreamInputhpatch_TFileStreamOutput高效处理文件
  • getCompressedDiffInfo至关重要,读取差异文件的元数据(特别是newDataSize)后才能创建输出文件
  • patch_decompress_with_cache是HDiffPatch自有差异格式的主函数,接收(输出)、(输入)、差异(输入)的流,解压插件(差异文件通常压缩)及临时内存缓存
  • temp_cache供补丁器内部缓冲数据,优化读写操作,可能缓存部分数据以加速访问

原理:补丁算法数据流

观察应用补丁时hpatch算法(特别是HDiffPatch自有格式的patch_decompress_with_cache)的工作流程。

在这里插入图片描述

探索:读取指令与重建数据

patch_decompress_with_cache(位于libHDiffPatch/HPatch/patch.c)的核心步骤:

  1. 读取差异头部(read_diffz_head):
    解析差异文件初始部分,获取关键信息如预期oldDataSizenewDataSize及差异文件各节使用的压缩类型,这对验证输入和设置解压至关重要。

    // libHDiffPatch/HPatch/patch.c(简化)
    hpatch_BOOL read_diffz_head(hpatch_compressedDiffInfo* out_diffInfo, _THDiffzHead* out_head,const hpatch_TStreamInput* compressedDiff) {TStreamCacheClip diffHeadClip; // 高效读取流的辅助工具// ... 初始化diffHeadClip ...// 读取版本类型、compressType// 解包newDataSize、oldDataSize、coverCount及压缩节大小// ...return hpatch_TRUE;
    }
    

    TStreamCacheClip是辅助工具(基于第一章概念),让补丁器分块从diffStream读取数据,按需更新内部缓冲区。

  2. 设置解压与流剪辑(getStreamClip):
    差异文件内部分为多节(如"覆盖数据"、“RLE控制数据”、“RLE编码数据”、“新数据差异”),每节可能单独压缩。getStreamClip为每节设置专用输入流,必要时自动处理解压。

    // libHDiffPatch/HPatch/patch.c(简化)
    hpatch_BOOL getStreamClip(TStreamCacheClip* out_clip, _TDecompressInputStream* out_stream,hpatch_StreamPos_t dataSize, hpatch_StreamPos_t compressedSize,const hpatch_TStreamInput* stream, hpatch_StreamPos_t* pCurStreamPos,hpatch_TDecompress* decompressPlugin, TByte* aCache, hpatch_size_t cacheSize) {// 如'compressedSize'为0,是未压缩数据;'out_clip'直接从'stream'读取// 如'compressedSize'>0,数据被压缩// 打开'decompressPlugin'在'out_clip'从'out_stream'读取时实时解压// 'pCurStreamPos'前进'dataSize'(未压缩)或'compressedSize'return hpatch_TRUE;
    }
    

    此函数创建TStreamCacheClip对象作为差异文件特定(可能压缩)节的"视图",抽象了解压和字节获取的复杂性。

  3. 核心补丁逻辑(patchByClip):
    实际重建发生在此。它遍历coverCount(来自差异头部)并处理每条指令。

    // libHDiffPatch/HPatch/patch.c(简化)
    static hpatch_BOOL patchByClip(_TOutStreamCache* outCache, // 输出新数据const hpatch_TStreamInput*  oldData, // 输入旧数据hpatch_TCovers* covers, // 覆盖指令流TStreamCacheClip* code_newDataDiffClip, // 新数据差异流struct _TBytesRle_load_stream* rle_loader, // RLE差异解码器TByte* temp_cache,hpatch_size_t cache_size) {hpatch_StreamPos_t newPosBack = 0;// 遍历每条覆盖指令while (covers->leave_cover_count(covers) > 0) {hpatch_TCover cover;covers->read_cover(covers, &cover); // 读取下条覆盖(oldPos, newPos, length)// 1. 处理覆盖间空隙的'新数据差异'if (newPosBack < cover.newPos) {hpatch_StreamPos_t copyLength = cover.newPos - newPosBack;_TOutStreamCache_copyFromClip(outCache, code_newDataDiffClip, copyLength);_rle_decode_skip(rle_loader, copyLength); // 跳过对应RLE数据}// 2. 应用'覆盖'(添加带RLE解码子差异的旧数据)_patch_add_old_with_rle(outCache, rle_loader, oldData, cover.oldPos, cover.length,temp_cache, cache_size);newPosBack = cover.newPos + cover.length;}// 处理文件末尾剩余的'新数据差异'// ..._TOutStreamCache_flush(outCache); // 将缓冲数据写入输出流return hpatch_TRUE;
    }
    
    • _TOutStreamCache_copyFromClip:将code_newDataDiffClip(可能压缩)的原始字节直接写入输出数据
    • _rle_decode_skip:使用_TBytesRle_load_stream处理与新数据差异节对应的RLE编码数据,本质上消费差异流以保持同步,但不应用到输出数据
    • _patch_add_old_with_rle:关键函数。从oldData流读取数据,然后通过加减RLE解码流(rle_loader)的值应用"子差异"变更,结果写入outCache
  4. RLE解码(_TBytesRle_load_stream):

    • HDiffPatch差异文件中的子差异新数据差异数据通常经过RLE(游程编码)进一步压缩。
    • _TBytesRle_load_stream管理此解码,可读取实际数据(新数据差异)或生成零/255字节序列,或单个重复字节(用于子差异操作如+0+5-10)。
    // libHDiffPatch/HPatch/patch.c(简化)
    static hpatch_BOOL _TBytesRle_load_stream_decode_add(_TBytesRle_load_stream* loader,TByte* out_data, hpatch_size_t decodeSize) {// 管理RLE解码内部状态(如相同字节序列的'memSetLength',直接复制字节的'memCopyLength')// 从内部剪辑解包RLE控制码和对应数据// 用重复值填充'out_data'或直接复制数据return hpatch_TRUE;
    }
    
🎢RLE(游程编码)

一种无损数据压缩技术,通过将连续重复的数据值替换为单个值和重复次数来减少存储空间

示例: 原始数据 AAAABBBCCD 压缩为 4A3B2C1D

支持其他差异格式

patch_decompress_with_cache用于HDiffPatch原生差异格式,但hpatchz命令行工具也能应用其他流行差异格式:

差异格式描述核心补丁函数
HDiffPatch原生HDiffPatch自身高度优化的格式,通常用插件(如Zlib、Zstd、LZMA)压缩。采用高级匹配和游程编码生成小差异文件。patch_decompress_with_cachepatch_single_compressed_diff
BSDIFF4广泛使用的二进制差异格式,内部节使用Bzip2压缩。HDiffPatch包含应用此类差异的封装器。bspatch_with_cache
VCDIFF标准二进制差异格式(RFC 3284),也被Xdelta3和Google的open-vcdiff等工具使用。HDiffPatch提供封装器支持常见变体。vcpatch_with_cache

hpatchz.c中的主hpatch函数通过读取差异文件头部智能检测格式,然后*分派到合适的补丁函数。 *

总结

补丁算法(hpatch)是差异生成过程的关键对应部分

严谨遵循紧凑差异文件的指令,结合现有旧数据,重建新的更新数据。HDiffPatch的hpatch设计高效,处理多种差异格式,并借助基于流的I/O和可选内存缓存扩展到超大文件。

理解了差异的创建和应用后,让我们探索HDiffPatch如何定制差异文件中使用的压缩算法。

下一章:压缩器/解压器插件

http://www.dtcms.com/a/548857.html

相关文章:

  • 【C++ vector 深度解析】:动态数组的使用与底层原理实战
  • 【0基础学算法】前缀和刷题日志(三):连续数组、矩阵区域和
  • 学习网站建设与管理汉沽集团网站建设
  • 10月30日
  • ESP32开发指南(基于IDF):连接AWS,乐鑫官方esp-aws-iot-master例程实验、跑通
  • Cocos资源加密方案解析
  • 从零开始的云原生之旅(六):DaemonSet 实战日志采集器
  • 网站建设人员任职要求电器类网站设计
  • [论文阅读] AI + Debug | 基于大语言模型的GitHub故障复现测试用例生成方法解析
  • 关于App的gradle降级问题的总结
  • 从零开始的云原生之旅(四):K8s 工作负载完全指南
  • 论文阅读笔记——自注意力机制
  • Xsens动作捕捉系统:训练、实时控制机器人
  • iOS 26 性能监控工具有哪些?多工具协同打造全方位性能分析体系
  • 做网站公司合同最新网站建设进程
  • 佳木斯做网站公司wordpress模板视频教程
  • viewerjs+vue3 using javascript
  • 2.游戏逆向-pxxx-分析UE源码中的GName
  • 编程与数学 03-007 《看潮资源管理器》项目开发 18 文件夹操作(2-2)
  • 服务器镜像安全:Docker 镜像漏洞扫描(Trivy)与基础镜像优化
  • Web安全深度实战:从漏洞挖掘到安全防护
  • 教育行业 | 信而泰推出Darsi通信网络与网络安全教育实验平台解决方案
  • 【Java笔记】定时器
  • ih5做的网站怎么上传seo企业网站优化
  • 刚建设的网站多久能在百度查到最美情侣高清视频播放
  • 【JavaEE初阶】TCP核心机制10——异常情况的处理
  • 阿里云ECS在线扩容磁盘
  • 私人兼职做网站开发麻章手机网站建设
  • 如何把ChatGPT嵌入到自己的应用中?
  • 豆包、元宝、Kimi等AI对话大模型会成为“带货”主流吗?