新闻详情

新闻详情

首页 / 资讯中心 / 详情

Flipper Zero 固件中的 heatshrink 嵌入式压缩库:LZSS 原理、配置参数与 FURI 封装实战

发布时间:2026/9/14 11:29:44来源:尧图网络
Flipper Zero 固件中的 heatshrink 嵌入式压缩库:LZSS 原理、配置参数与 FURI 封装实战
Flipper Zero 固件中的 heatshrink 嵌入式压缩库LZSS 原理、配置参数与 FURI 封装实战【免费下载链接】flipperzero-firmwareFlipper Zero firmware source code项目地址: https://gitcode.com/GitHub_Trending/fl/flipperzero-firmwareheatshrink 是一个面向嵌入式/硬实时系统的轻量级 LZSS 数据压缩库在 Flipper Zero 固件仓库flipperzero-firmware中既作为独立第三方库完整保留在 lib/heatshrink又被lib/toolbox层封装成CompressAPI广泛用于.ths备份/OTA 压缩归档与图像资源的运行时解压。本文基于 lib/heatshrink/README.md 展开结合仓库中的构建脚本、FURI封装源码与单元测试完整讲解它的状态机用法、三个核心配置参数的取舍以及它在 Flipper 生态中的实际落地方式。定位与核心特性根据 lib/heatshrink/README.md 的说明heatshrink 的设计目标是为嵌入式/实时系统提供数据压缩/解压其四大特性是低内存占用最低约 50 字节 300 字节可覆盖大多数通用场景增量式、CPU 占用有界可以把输入数据切成任意小的片段喂给状态机这是硬实时环境下最有价值的属性——压缩/解压工作可以分摊到多个调度切片中不会产生不可预测的长耗时峰值内存分配方式自由库本身不约束内存管理既可动态分配也可静态分配ISC 许可证可免费用于商业目的。算法层面heatshrink 基于 LZSSLempel-Ziv-Storer-Szymanski选择它正是因为该算法在小内存下依然可行。README 中还提到一个可选项启用小型倒排索引index可显著加快压缩速度代价是内存增加 2^(window size1) 字节且在构建索引时临时占用约 512 字节栈空间不启用索引时压缩器可以运行在 100 字节以内的内存中。当前仓库集成的版本为0.4.1作者为 Scott Vokes见 heatshrink_common.h/* Version 0.4.1 */ #define HEATSHRINK_VERSION_MAJOR 0 #define HEATSHRINK_VERSION_MINOR 4 #define HEATSHRINK_VERSION_PATCH 1快速上手CLI 工具与库两种用法README 指出项目包含一个独立的命令行程序heatshrink源码见 heatshrink.c同时编码器和解码器也可以作为互相独立的库使用。若只想把库拷进自己的工程复制以下文件即可heatshrink_common.h、heatshrink_config.h公共部分heatshrink_encoder.c或heatshrink_decoder.c及各自头文件二选一或都要。库构建的静态库使用静态或动态分配两种方式均可。动态分配是默认行为在嵌入式上下文中通常希望静态分配做法是在 heatshrink_config.h 中将HEATSHRINK_DYNAMIC_ALLOC置 0。该配置文件同时提供静态模式所需的三个参数与开关#if HEATSHRINK_DYNAMIC_ALLOC #define HEATSHRINK_MALLOC(SZ) malloc(SZ) #define HEATSHRINK_FREE(P, SZ) free(P) #else #define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 32 #define HEATSHRINK_STATIC_WINDOW_BITS 8 #define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4 #endif #define HEATSHRINK_DEBUGGING_LOGS 0 /* Use indexing for faster compression. (This requires additional space.) */ #define HEATSHRINK_USE_INDEX 1注意HEATSHRINK_USE_INDEX默认为 1即默认启用加速索引HEATSHRINK_DEBUGGING_LOGS可打开调试日志。状态机基本用法四步流程README 的核心实操是基本用法一节编码器/解码器都是状态机用法固定为四步创建状态机动态分配时用heatshrink_encoder_alloc/heatshrink_decoder_alloc静态分配时直接定义结构体并调用heatshrink_encoder_reset/heatshrink_decoder_reset初始化。sink灌入输入input_size出参指示实际消费了多少输入字节若为 0 说明内部缓冲区已满需要等待输出后再继续。poll取出输出output_size出参指示实际输出字节数返回码指示是否还有更多输出。状态机可能在收到足够输入前不产生任何输出因此第 2、3 步要循环交替执行——由于是压缩输入输出尺寸差异很大必须循环缓冲。finish通知输入结束返回码指示是否仍有剩余输出若有则继续poll并持续finishpoll直到finish表示输出耗尽。README 特别警告调用finish之后如果不reset就无法再sink新数据。完整 API 签名与返回值heatshrink_encoder.h 与 heatshrink_decoder.h 给出了各函数的 Doxygen 注释与完整签名两个方向完全对称/* 编码器 */ heatshrink_encoder *heatshrink_encoder_alloc(uint8_t window_sz2, uint8_t lookahead_sz2); void heatshrink_encoder_free(heatshrink_encoder *hse); /* 仅动态分配时存在 */ void heatshrink_encoder_reset(heatshrink_encoder *hse); HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, uint8_t *in_buf, size_t size, size_t *input_size); HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, uint8_t *out_buf, size_t out_buf_size, size_t *output_size); HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse); /* 解码器 */ heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2); void heatshrink_decoder_free(heatshrink_decoder *hsd); void heatshrink_decoder_reset(heatshrink_decoder *hsd); HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, uint8_t *in_buf, size_t size, size_t *input_size); HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, uint8_t *out_buf, size_t out_buf_size, size_t *output_size); HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd);各返回码的语义摘自头文件注释编码器含义HSER_SINK_OK数据已灌入输入缓冲区HSER_POLL_EMPTY/HSER_POLL_MORE输入已耗尽 / 还有更多输出需再次 pollHSER_FINISH_DONE/HSER_FINISH_MORE编码完成 / 仍有剩余输出继续 poll解码器含义HSDR_SINK_OK/HSDR_SINK_FULL数据已灌入可 poll / 内部缓冲区无空间HSDR_POLL_EMPTY/HSDR_POLL_MORE输入已耗尽 / 还有数据用新的输出缓冲区再次调用HSDR_FINISH_DONE/HSDR_FINISH_MORE输出完成 / 仍有输出两种模式下的结构体内存布局差异也能直接印证 README 中动态/静态分配的说法在 heatshrink_decoder.h 中动态模式使用柔性数组uint8_t buffers[]先输入缓冲区后 2^W 的展开窗口静态模式则展开为编译期定长数组#else /* Input buffer, then expansion window buffer */ uint8_t buffers[(1 HEATSHRINK_DECODER_WINDOW_BITS(_)) HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)]; #endif即解码器静态占用 2^W窗口 输入缓冲区大小编码器则因需要前/后两帧缓冲静态时为2 WINDOW_BITS见 heatshrink_encoder.h 中buffer[2 HEATSHRINK_ENCODER_WINDOW_BITS(_)]。CLI 参数速查heatshrink.c 中的usage()给出了完整命令行格式heatshrink [-h] [-e|-d] [-v] [-w SIZE] [-l BITS] [IN_FILE] [OUT_FILE]-e编码默认/-d解码-v打印输入输出大小、压缩率等-w SIZELZSS 滑动窗口大小的以 2 为底的指数即window_sz2-l BITS回指长度lookahead所用位数即lookahead_sz2未指定IN_FILE/OUT_FILE时默认走 stdin/stdout-。源码中的默认值为DEF_WINDOW_SZ2 11、DEF_LOOKAHEAD_SZ2 4、解码器输入缓冲区 256 字节heatshrink.c 第 13-15 行。注意 usage 文本中的建议是嵌入式系统用-w 8其他场景用-w 10-l推荐 4。配置参数详解window_sz2、lookahead_sz2、input_buffer_sizeREADME 的 Configuration 一节是本文重点三个参数直接影响资源占用和压缩效果动态分配时在alloc调用中传入静态分配时写在heatshrink_config.h中。1. window_sz2CLI: -w——滑动窗口大小窗口大小为2^W 字节决定编码器能回溯多远寻找重复模式例window_sz2 8只用 256 字节2^8window_sz2 10用 1024 字节内存更多但可能检测到更多重复而压缩得更有效当前取值范围4 至 15。这一点与源码约束一致heatshrink_common.h 定义HEATSHRINK_MIN_WINDOW_BITS 4、HEATSHRINK_MAX_WINDOW_BITS 15heatshrink_encoder.c 的heatshrink_encoder_alloc会校验window_sz2越界即返回 NULL。2. lookahead_sz2CLI: -l——前瞻/重复模式最大长度决定能表示的重复模式最大长度lookahead_sz2 4时一段 50 字节连续的 a 会被拆成若干个 16 字节2^4的重复引用更大的值可以一次表示更长的模式但前瞻位数是固定计入每个回指的过大的 lookahead 会给小模式附加无用的长度位反而降低压缩率当前取值范围3 至 window_sz2 − 1。源码校验同样印证heatshrink_encoder.calloc中lookahead_sz2 HEATSHRINK_MIN_LOOKAHEAD_BITS(3)或lookahead_sz2 window_sz2都会分配失败。3. input_buffer_size——解码器输入缓冲区决定解码器单步能处理多少数据更大缓冲用更多内存极端小的缓冲如 1 字节会因大量挂起/恢复调用而增加开销但不会改变压缩效果。官方推荐默认值README 给出的嵌入式默认值建议window_sz2在810之间是低内存场景的好起点具体取决于内存紧张程度更小或更大的窗口在特定数据下可能有更好的权衡必须用代表性数据实测lookahead_sz2从window_sz2 / 2附近起步例如-w 8 -l 4或-w 10 -l 5可以用 CLI 工具对不同测试数据实测压缩率。在 Flipper 仓库中的构建与自测静态库构建固件构建系统通过 lib/heatshrink.scons 将heatshrink_*.c编译成名为heatshrink的静态库并安装到LIB_DIST_DIR同时把#/lib/heatshrink加入全局包含路径。因此任何 FURI 代码都可以通过#include lib/heatshrink/heatshrink_encoder.h直接引用。自带的测试与基准lib/heatshrink目录保留了上游完整的自测资产可用于独立验证test_heatshrink_static.c、test_heatshrink_dynamic.c静态/动态分配两种模式下的编解码一致性测试test_heatshrink_dynamic_theft.c针对动态分配的内存窃取压力测试Makefile 与benchmark脚本独立构建 CLI、运行测试和做压缩率基准。这呼应了 README 中静态库会分别构建静态/动态分配两种版本的说法。FURI 封装层toolbox/compress 与 CompressIcon固件并没有直接裸调 heatshrink API而是在 lib/toolbox/compress.c / lib/toolbox/compress.h 中封装了一层更易用的接口头文件注释称其为 LZSS based compression HAL API把 heatshrink 状态机循环封装成了同步的encode/decode调用。配置结构与默认值typedef struct { uint16_t window_sz2; uint16_t lookahead_sz2; uint16_t input_buffer_sz; } CompressConfigHeatshrink; extern const CompressConfigHeatshrink compress_config_heatshrink_default;默认配置定义在 compress.c#define COMPRESS_EXP_BUFF_SIZE_LOG (8u) /* window_sz2 */ #define COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG (4u) /* lookahead_sz2 */ #define COMPRESS_ICON_ENCODED_BUFF_SIZE (256u) /* input_buffer_sz */ const CompressConfigHeatshrink compress_config_heatshrink_default { .window_sz2 COMPRESS_EXP_BUFF_SIZE_LOG, .lookahead_sz2 COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG, .input_buffer_sz COMPRESS_ICON_ENCODED_BUFF_SIZE, };即window_sz28、lookahead_sz24、input_buffer_sz256——正好落在 README 推荐区间-w 8 -l 4内注释标明Used for image assets。带 4 字节头的容器格式与压不缩就存原文策略compress_encode会在压缩流前面加一个 4 字节头compress.ctypedef struct { uint8_t is_compressed; uint8_t reserved; uint16_t compressed_buff_size; } CompressHeader;关键行为见compress_encode_internal若编码失败或压缩后尺寸不小于原文 1 字节则放弃压缩改存data_out[0] 0x00 原始数据否则写入is_compressed 1的头部。compress_decode依据头部的is_compressed标志走解压或直通拷贝两条路径——这种设计对熵很高的数据如随机字节、位图不会越压越大。该封装还提供两组流式 APIcompress_decode_streamed(compress, read_cb, write_cb)通过读/写回调对压缩流做边读边解压内部循环调用heatshrink_decoder_sink/poll/finish正是 README 四步流程的工程化实现compress.cCompressStreamDecoder可read/seek/tell/rewind的流解码器。注意seek只能向前通过解码并丢弃数据实现rewind需要调用方自行把读回调重新定位到起点见 compress.h 中的注释与_Static_assert风格的契约。图像资源专用接口CompressIconCompressIcon是图标场景的轻量封装compress.ccompress_icon_alloc(decode_buf_size)内部用固定参数输入缓冲 256、窗口 8、lookahead 4创建解码器compress_icon_decode检查 4 字节头压缩数据则原地解压到内部缓冲返回的指针在下一次调用前有效未压缩数据则直接指向原文偏移 1 处——避免了未压缩小图的一次拷贝。核心落地场景.ths 压缩归档heatshrink 在固件中最重量级的用途是Heatshrink 压缩的 tar 归档扩展名.ths用于固件 OTA 与 SD 卡备份的体积优化。格式规范见 documentation/file_formats/TarHeatshrinkFormat.md7 字节头4 字节 magic0x48 0x53 0x44 0x53ASCII HSDSHeatShrink DataStream 1 字节 version当前0x01 1 字节 window size 1 字节 lookahead size头后直接跟压缩数据。之所以需要自定义头是因为 heatshrink 规范本身不定义携带压缩参数的容器格式。解码侧实现见 lib/toolbox/tar/tar_archive.c/* HSDS heatshrink data stream header magic */ static const uint32_t HEATSHRINK_MAGIC 0x53445348; /* 小端存储为 H D S H 字节序 */ typedef struct { uint32_t magic; uint8_t version; uint8_t window_sz2; uint8_t lookahead_sz2; } FURI_PACKED HeatshrinkStreamHeader; _Static_assert(sizeof(HeatshrinkStreamHeader) 7, Invalid HeatshrinkStreamHeader size);工作流程tar_archive_opentar_archive_get_mode_for_path按扩展名判断.ths→TarOpenModeReadHeatshrink打开文件后读 7 字节头校验 magic从头部取window_sz2/lookahead_sz2作为解码参数输入缓冲固定 512 字节FILE_BLOCK_SIZE据此创建CompressStreamDecoder将CompressStreamDecoder的 read/seek 包成 microtar 的 ops于是 lib/microtar 对上层看到的仍是一个普通 tar 流压缩完全透明热缩流是只读的heatshrink_ops.write NULL普通.tar则走filesystem_ops读写。压缩侧由构建工具链完成scripts/hs.py 是fbt的子命令fbt hs基于heatshrink2Python 包提供compress/decompress/info/tar四个子命令写出同样的 7 字节 HSDS 头HeatshrinkDataStreamHeader.pack()。注意其默认参数与运行时图标压缩不同DEFAULT_WINDOW 13、DEFAULT_LOOKAHEAD 6——归档文件一次性处理、内存不受设备端 50 字节级别的约束因此可以用更大的窗口换取更高压缩率。单元测试验证设备端测试 applications/debug/unit_tests/tests/compress/compress_test.c 覆盖四条用例可作为正确用法的参照compress_test_reference_comp_decomp用默认配置对参考数据做编解码结果必须与预存的compressed.bin/uncompressed.bin逐字节一致锁定压缩输出是确定性的compress_test_random_comp_decomp随机数据编解码往返一致性并验证输出缓冲不足时正确返回失败compress_test_heatshrink_stream以window_sz29, lookahead_sz24, input_buffer_sz128调用compress_decode_streamed解压后 MD5 必须等于预期值测试数据由heatshrink2 compress -w 9 -l 4生成compress_test_heatshrink_tar打开test.ths归档并全量解包校验条目数与所有文件 MD5 的 XOR 值。参数选择小结把 README 的建议与仓库中三处真实配置放在一起可以形成一个可复用的决策参考使用场景window_sz2lookahead_sz2input_buffer_sz依据官方嵌入式建议8 ~ 10≈ window/2—README Recommended Defaults图像资源运行时解压设备端84256compress.ccompress_config_heatshrink_default流式单文件解压测试94128compress_test.ccompress_test_heatshrink_stream构建期归档压缩宿主端136—scripts/hs.pyHSWrapper默认值CLI 独立工具默认114256heatshrink.cDEF_*宏几个值得记住的工程结论编解码两端参数必须一致解码器的窗口/lookahead 必须与压缩时使用的一致heatshrink_decoder.halloc注释明确说明这正是 HSDS 头要把两个参数写进文件的原因window 越大压缩越好但内存线性增长解码器窗口缓冲即 2^W 字节编码器静态模式还要乘 2window_sz2上限 15 意味着最大 32 KiB 窗口在设备端要谨慎lookahead 不宜盲目加大每个回指固定占用 w l 位数据以短模式为主时过大的 l 会反向降低压缩率README 原文overly large lookahead size can reduce compression by adding unused size bits to small patterns单步工作量有界sink/poll的语义允许把一次大 IO 拆成任意多次小切片input_buffer_size只影响单步吞吐不影响压缩率——这是它在硬实时系统里可用的根本原因。延伸阅读格式规范TarHeatshrinkFormat.md、AppManifests.md库源码heatshrink_encoder.c、heatshrink_decoder.c、heatshrink_common.h封装与集成toolbox/compress.h、tar_archive.c、heatshrink.scons上游项目说明含算法 blog 与索引设计背景lib/heatshrink/README.md。【免费下载链接】flipperzero-firmwareFlipper Zero firmware source code项目地址: https://gitcode.com/GitHub_Trending/fl/flipperzero-firmware创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

SpringBoot线程池配置优化与实战指南 2026/9/14 12:08:54

SpringBoot线程池配置优化与实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Spring Boot端口异常问题排查与字节序转换原理 2026/9/14 12:08:54

Spring Boot端口异常问题排查与字节序转换原理

1. 问题现象与初步排查那天下午,我正在调试一个基于Spring Boot的Web服务,按照惯例将服务端口设置为8080。启动日志显示服务正常监听8080端口,但当我用浏览器访问http://localhost:8080时,却收到了"无法连接"的错误。更…

阅读更多 →
ms-swift Megatron-SWIFT 实战:DeepSeek-V4 微调训练、精度对齐与 FP8/vLLM 部署全流程 2026/9/14 12:08:54

ms-swift Megatron-SWIFT 实战:DeepSeek-V4 微调训练、精度对齐与 FP8/vLLM 部署全流程

ms-swift Megatron-SWIFT 实战:DeepSeek-V4 微调训练、精度对齐与 FP8/vLLM 部署全流程 【免费下载链接】swift Use PEFT or Full-parameter to CPT/SFT/DPO/GRPO 600 LLMs (Qwen3.6, DeepSeek-V4, GLM-5.1, InternLM3, Llama4, ...) and 300 MLLMs (Qwen3-VL, Qwen…

阅读更多 →
外点法MATLAB入门:罚函数构造与约束优化实现 2026/9/14 12:08:54

外点法MATLAB入门:罚函数构造与约束优化实现

简介:压缩包内为基于Matlab实现的外点法程序实例,通过源代码直观演示外点法求解含约束非线性规划问题的完整流程,面向相关课程学生、科研人员与优化算法初学者,也适合中高级研究者快速复现算法。资源共9个文件,全部为M…

阅读更多 →
无线网卡工作原理深度解析:从射频前端到协议栈 2026/9/14 12:08:54

无线网卡工作原理深度解析:从射频前端到协议栈

1. 从“插上就用”到“看不见的对话”:无线网卡不是USB闪存盘很多人第一次接触无线网卡,是在笔记本电脑找不到Wi-Fi图标、手机热点连不上打印机、或者台式机想装个路由器却被告知“得先配个无线网卡”的时候。它长得像一个U盘,插进USB口&…

阅读更多 →
Easy-Vibe 调试艺术:从错误堆栈到 AI 辅助的系统化调试方法论 2026/9/14 12:05:53

Easy-Vibe 调试艺术:从错误堆栈到 AI 辅助的系统化调试方法论

Easy-Vibe 调试艺术:从错误堆栈到 AI 辅助的系统化调试方法论 【免费下载链接】easy-vibe 💻 vibe coding 101|The first course for AI-native product builders. 项目地址: https://gitcode.com/GitHub_Trending/ea/easy-vibe 本文是…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞