# hpatchlite-wrapper **Repository Path**: RT-Thread-Mirror/hpatchlite-wrapper ## Basic Information - **Project Name**: hpatchlite-wrapper - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2025-01-03 - **Last Updated**: 2025-10-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # hpatchlite-wrapper 一个为 [HPatchLite](https://github.com/sisong/HPatchLite) 设计的、轻量级且用户友好的封装层。 ## 项目简介 本项目提供了一个简化的API,旨在帮助开发者将功能强大的 `HPatchLite` 二进制差分库快速集成到嵌入式系统中,尤其是在 RT-Thread 生态下。本封装层处理了复杂的内存管理和不同解压插件的抽象,让您能更专注于上层业务逻辑。 ## 使用说明 - 本项目使用 `HPatchLite` 作为Git子模块。在克隆主仓库后,请使用以下命令来拉取子模块的完整代码: > git submodule update --init --recursive ## API 接口 ```c /** * @brief 使用 HPatchLite 库执行差分合并操作 * * @param listener 指向监听器结构的指针,同时也作为用户上下文句柄。 * @param patch_cache_size 用于核心差分算法的内部缓存大小。 * @param decompress_cache_size 用于解压数据流的缓存大小。 * @param _do_read_diff 读取差分补丁数据流的回调函数。 * @param _do_read_old 读取旧版本固件数据的回调函数。 * @param _do_write_new 写入新合成的固件数据的回调函数。 * @return hpi_patch_result_t 成功时返回 HPATCHI_SUCCESS,失败时返回对应的错误码。 */ hpi_patch_result_t hpi_patch(hpatchi_listener_t *listener, int patch_cache_size, int decompress_cache_size, hpi_TInputStream_read _do_read_diff, read_old_t _do_read_old, write_new_t _do_write_new); ``` ## 使用示例 ```c #include "hpatch_impl.h" // 定义一个上下文结构体,用于保存所有必要的状态和数据 typedef struct hpatchi_instance_t { hpatchi_listener_t parent; // 必须作为第一个成员 // 在此处定义您自己的状态变量 const fal_partition_t patch_part; const fal_partition_t old_part; int patch_read_pos; // ... 等等 } hpatchi_instance_t; // 实现所需的回调函数 hpi_BOOL _do_read_old(struct hpatchi_listener_t *listener, hpi_pos_t addr, hpi_byte *data, hpi_size_t size) { // 实现从旧固件分区读取的逻辑 ... } hpi_BOOL _do_read_patch(hpi_TInputStreamHandle input_stream, hpi_byte *data, hpi_size_t *size) { // 实现从差分补丁数据流读取的逻辑 ... } hpi_BOOL _do_write_new(struct hpatchi_listener_t *listener, const hpi_byte *data, hpi_size_t size) { // 实现原地升级的逻辑 (例如,使用交换缓冲区) ... } void demo_patch_update(void) { // 初始化您的实例,包含所有必要的分区信息和状态 hpatchi_instance_t instance = { .patch_part = fal_partition_find("download"), .old_part = fal_partition_find("app"), .patch_read_pos = 0, // ... 初始化其他成员 }; // 使用您的实例和回调函数来调用差分合并接口 hpi_patch_result_t result = hpi_patch(&instance.parent, 4096, // patch_cache_size 4096, // decompress_cache_size _do_read_patch, _do_read_old, _do_write_new); if (result == HPATCHI_SUCCESS) { // 差分合并成功! } else { // 差分合并失败,处理错误 } } ``` ## 补丁文件结构 由 `hdiffi` 工具生成的差分补丁文件主要由两部分构成:HPatchLite 主文件头,以及紧随其后的数据流。数据流本身根据压缩类型的不同,可能还会包含自己的子文件头。 ### 原始十六进制数据示例 ``` 68 49 01 53 10 23 02 36 cd b9 69 00 00 ff 03 82 ... ``` ### 逐字节分析 文件被按顺序解析。 #### 1. HPatchLite 主文件头 (由 `hpatch_lite_open` 解析) | 字节 | 十六进制 | 值/ASCII | 解释 | |:-----|:---------|:---------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------| | 0 | `68` | `'h'` | **HPatchLite 魔法数 1** | | 1 | `49` | `'I'` | **HPatchLite 魔法数 2** | | 2 | `01` | `1` | **压缩类型 (Compress Type)**:`1` 对应 `hpi_compressType_tuz`,表示本补丁使用了 `tuz` 压缩。 | | 3 | `53` | `83` | **版本与长度编码字节**:二进制为 `01010011`。
- **高2位 (`01`)**: 版本号 `1`。
- **中3位 (`010`)**: `uncompressSize` 字段占 **2** 个字节。
- **低3位 (`011`)**: `newSize` 字段占 **3** 个字节。 | | 4-6 | `10 23 02` | - | **newSize (3 字节)**:小端(Little-Endian)编码的值。
`0x022310` = **140048**。这个值是最终生成的新固件 `new.bin` 的总大小。 | | 7-8 | `36 cd` | - | **uncompressSize (2 字节)**:小端编码。
`0xCD36` = **52534**。这个值是未经压缩的、原始的差分指令流的大小。 | **结论**: 主文件头的长度是**可变的**。解析完这部分后,数据流指针将指向下一个部分,准备交给相应的解压器处理。 #### 2. 解压器子文件头 (以 `tuz` 为例) 紧跟在主文件头之后的数据属于特定的解压器。对于 `tuz` 来说,这部分以字典大小信息开头。 | 字节 | 十六进制 | 值 | 解释 | |:-----|:---------|:---------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------| | 9-10 | `b9 69` | - | **字典大小信息**: 这是 `tuz` 专有的头部数据,由 `_tuz_TStream_getReservedMemSize` 函数读取。在此示例中,小端编码的 `0x69B9` 等于 **27065**。这个值是解压器**实际需要的总内存**,不一定等于您在打包时指定的字典大小。 | ## 压缩功能 为了显著减小补丁包的体积,可以使用 `-c` 参数在生成补丁时启用压缩功能。 ### 使用 `tuz` 压缩 `tuz` 是一种轻量级压缩算法,非常适合在嵌入式系统上使用。 * **打包示例**: 以下命令创建了一个使用 `tuz` 压缩的补丁。由于未指定字典大小,`hdiffi` 会使用默认的 **32KB** 字典。 ```sh # -c-tuz 参数启用了 tuz 压缩 .\hdiffi.exe -c-tuz .\old.bin .\new.bin .\patch_tuz_de.bin ``` * **输出日志解读**: ```sh old : ".\old.bin" new : ".\new.bin" out : ".\patch_tuz_de.bin" hdiffi run with compress plugin: "tuz" oldDataSize : 140048 newDataSize : 140048 (used one tinyuz dictSize: 32768 (input data: 52534)) diffDataSize: 76 ... hpatchi run with decompresser: "tuz" requirements memory size: (must) 27065 + (custom cache) 32768 ... ``` * **关键信息解读**: 1. `used one tinyuz dictSize: 32768`: 这行日志确认了PC端的压缩器确实按照要求,**使用了32KB的窗口**来寻找重复数据。 2. `requirements memory size: (must) 27065`: **这是对嵌入式设备至关重要的信息。** 它指明了在单片机上执行解压时,`tuz` 解压器将通过 `hpi_malloc` **强制要求分配的RAM大小**。这个大小包括了字典缓冲区和解压器自身需要的状态内存。 3. **结论**: 打包时指定的字典大小 (`-dictSize=32k`) 是一个**上限和参考**。设备端实际需要的内存(`27065`)是由压缩后的数据流本身决定的。您**必须确保**您的设备有足够的堆内存来满足这个 "must" 的要求,否则升级将会因内存分配失败而终止。 ## 联系方式 - sulfurandcu@gmail.com - https://github.com/sulfurandcu/hpatchlite-wrapper ## 参考链接 - https://sulfurandcu.github.io/sulfurandcu.io/cloidefbf00hzv0rqa7zg722r.html