最近项目中遇到OTA固件升级,压缩由上位机实现,解压缩在boot启动时,读取固件内容后边解压边加载数据的方式启动。
前期预研了三种压缩方案,分别是lz4、zip、7z,代码仓库放在下面,三者各自的原理可自行百度了解。
- lz4
- zip
- 7z
一、lzma的使用
由于项目中要求对所升级的固件压缩比的最大化,因此选择了7z中的lzma算法,版本为1900
LzmaUtil.c文件中完整实现了针对一个流式文件的压缩以及解压缩。实验环境为Ubuntu20.04。
- 编译
修改Lzma文件夹下的makefile.gcc,将编译器修改为gcc。在当前目录下输入make -f makefile.gcc
编译生成lzma可执行文件
PROG = lzma CXX = gcc LIB = RM = rm -f CFLAGS = -c -O2 -Wall -D_7ZIP_ST
-
压缩
./lzma e *要压缩的文件* *目标文件*
第二个参数(e)不区分大小写./lzma e makefile.gcc makefile_compr
-
解压
./lzma d *要解压的文件* *目标文件*
同样,第二个参数不区分大小写./lzma d makefile_compr makefile_decompr
解压后可以对比makefile_decompr与原始文件makefile.gcc内容是否一致
二、lzma的深入理解
- lzma压缩文件的特征
Offset Size Description 0 1 Special LZMA properties (lc,lp, pb in encoded form) 1 4 Dictionary size (little endian) 5 8 Uncompressed size (little endian). -1 means unknown size 13 Compressed data
以二进制的方式打开一个用lzma压缩算法压缩后的文件,其文件格式为文件头(13bytes)+ 压缩数据大小
,第一个字节是压缩文件的属性,后四个字节是字典的大小,再往后8个字节是未压缩数据的大小,它们都用小端(little endian)表示。
第一个字节属性,三个取值分别如下:
name Range Description lc [0, 8] the number of "literal context" bits lp [0, 4] the number of "literal pos" bits pb [0, 4] the number of "pos" bits
- 代码详解
简单了解一下lzma压缩后的文件特征,现在回到LzmaUtil.c来看看具体的压缩与解压缩是怎样实现的。
main2()
CFileSeqInStream inStream; CFileOutStream outStream;
这两个结构体可以等同于文件IO中的fread和fwrite函数来理解,用来打开一个文件和向文件中写入内容。接下来是关于入参的一些判断很好理解。通过encodeMode
的真假来决定是压缩还是解压缩。
Encode()
压缩
static SRes Encode(ISeqOutStream *outStream, ISeqInStream *inStream, UInt64 fileSize, char *rs)
*outStream:压缩后的数据
*inStream:源数据
fileSize:源数据的大小
lzma属性相关参数结构体:
typedef struct _CLzmaEncProps {
int level; /* 0 <= level <= 9 */ UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version (1 << 12) <= dictSize <= (3 << 29) for 64-bit version default = (1 << 24) */ int lc; /* 0 <= lc <= 8, default = 3 */ int lp; /* 0 <= lp <= 4, default = 0 */ int pb; /* 0 <= pb <= 4, default = 2 */ int algo; /* 0 - fast, 1 - normal, default = 1 */ int fb; /* 5 <= fb <= 273, default = 32 */ int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ int numHashBytes; /* 2, 3 or 4, default = 4 */ UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ int numThreads; /* 1 or 2, default = 2 */ UInt64 reduceSize; /* estimated size of data that will be compressed. default = (UInt64)(Int64)-1. Encoder uses this value to reduce dictionary size */ } CLzmaEncProps;
Encode
中设置了lzma的属性,并将其写入文件头然后压缩,这里贴出属性的初始化函数的实现(属性设置的太长了,大家可以自行查看,实现原理很简单)。
void LzmaEncProps_Init(CLzmaEncProps *p) {
p->level = 5; p->dictSize = p->mc = 0; p->reduceSize = (UInt64)(Int64)-1; p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; p->writeEndMark = 0; }
压缩所用到的结构体CLzmaEnc
大小为123k,因此在单片机上实现压缩功能是很困难
Decode()
解压缩
static SRes Decode(ISeqOutStream *outStream, ISeqInStream *inStream)
*outStream:解压后的数据
*inStream:源数据
该函数会首先解析压缩文件的文件头,然后申请解压所需的内存空间,接下来进入Decode2
开始解压缩。
static SRes Decode2(CLzmaDec *state, ISeqOutStream *outStream, ISeqInStream *inStream, UInt64 unpackSize)
Decode2
中解压缩的逻辑不难理解,可以根据自己的硬件资源调整两个缓存的大小,同时需要关注属性设置时字典的大小避免资源不足无法完成解压。
最后,可根据自己项目的实际情况更改文件读写的接口、lzma的属性等参数来实现压缩、解压缩。
今天的文章
7z压缩算法_安卓解压缩7z分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/80987.html