目录

一、miniLZO介绍

二、miniLZO的使用

三、VS2013下文件压缩和解压测试

四、STM32F103ZET6平台下的测试

五、总结

六、参考资料


一、miniLZO介绍

miniLZO是一种轻量级的压缩和解压缩库,它是基于LZO压缩和解压缩算法实现的。LZO虽然功能强大,但是编译后的库文件较大,而minilzo编译后的库则小于5kb,因此miniLZO为那些仅需要简单压缩和解压缩功能的程序而设计,所以适用于单片机等嵌入式系统使用。另外miniLZO的压缩率并不是很高,LZO算法看重的是压缩和解压的速度。

miniLZO下载地址:http://www.oberhumer.com/opensource/lzo/

二、miniLZO的使用

miniLZO目录结构如下:

三个头文件加一个minilzo.c文件而已,其中testmini.c是一个测试demo,给出了miniLZO的使用方法示例,testmini.c的这个例子展示了将一段内存数据进行压缩和解压的用法。如下:

/* testmini.c -- very simple test program for the miniLZO libraryThis file is part of the LZO real-time data compression library.Copyright (C) 1996-2017 Markus Franz Xaver Johannes OberhumerAll Rights Reserved.The LZO library is free software; you can redistribute it and/ormodify it under the terms of the GNU General Public License aspublished by the Free Software Foundation; either version 2 ofthe License, or (at your option) any later version.The LZO library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with the LZO library; see the file COPYING.If not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.Markus F.X.J. Oberhumer<markus@oberhumer.com>http://www.oberhumer.com/opensource/lzo/*/#include <stdio.h>
#include <stdlib.h>/*************************************************************************
// This program shows the basic usage of the LZO library.
// We will compress a block of data and decompress again.
//
// For more information, documentation, example programs and other support
// files (like Makefiles and build scripts) please download the full LZO
// package from
//    http://www.oberhumer.com/opensource/lzo/
**************************************************************************//* First let's include "minizo.h". */#include "minilzo.h"/* We want to compress the data block at 'in' with length 'IN_LEN' to* the block at 'out'. Because the input block may be incompressible,* we must provide a little more output space in case that compression* is not possible.*/#define IN_LEN      (128*1024ul)
#define OUT_LEN     (IN_LEN + IN_LEN / 16 + 64 + 3)static unsigned char __LZO_MMODEL in  [ IN_LEN ];
static unsigned char __LZO_MMODEL out [ OUT_LEN ];/* Work-memory needed for compression. Allocate memory in units* of 'lzo_align_t' (instead of 'char') to make sure it is properly aligned.*/#define HEAP_ALLOC(var,size) \lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ]static HEAP_ALLOC(wrkmem, LZO1X_1_MEM_COMPRESS);/*************************************************************************
//
**************************************************************************/int main(int argc, char *argv[])
{int r;lzo_uint in_len;lzo_uint out_len;lzo_uint new_len;if (argc < 0 && argv == NULL)   /* avoid warning about unused args */return 0;printf("\nLZO real-time data compression library (v%s, %s).\n",lzo_version_string(), lzo_version_date());printf("Copyright (C) 1996-2017 Markus Franz Xaver Johannes Oberhumer\nAll Rights Reserved.\n\n");/** Step 1: initialize the LZO library*/if (lzo_init() != LZO_E_OK){printf("internal error - lzo_init() failed !!!\n");printf("(this usually indicates a compiler bug - try recompiling\nwithout optimizations, and enable '-DLZO_DEBUG' for diagnostics)\n");return 3;}/** Step 2: prepare the input block that will get compressed.*         We just fill it with zeros in this example program,*         but you would use your real-world data here.*/in_len = IN_LEN;lzo_memset(in,0,in_len);/** Step 3: compress from 'in' to 'out' with LZO1X-1*/r = lzo1x_1_compress(in,in_len,out,&out_len,wrkmem);if (r == LZO_E_OK)printf("compressed %lu bytes into %lu bytes\n",(unsigned long) in_len, (unsigned long) out_len);else{/* this should NEVER happen */printf("internal error - compression failed: %d\n", r);return 2;}/* check for an incompressible block */if (out_len >= in_len){printf("This block contains incompressible data.\n");return 0;}/** Step 4: decompress again, now going from 'out' to 'in'*/new_len = in_len;r = lzo1x_decompress(out,out_len,in,&new_len,NULL);if (r == LZO_E_OK && new_len == in_len)printf("decompressed %lu bytes back into %lu bytes\n",(unsigned long) out_len, (unsigned long) in_len);else{/* this should NEVER happen */printf("internal error - decompression failed: %d\n", r);return 1;}printf("\nminiLZO simple compression test passed.\n");return 0;
}/* vim:set ts=4 sw=4 et: */

miniLZO库使用非常简单,在压缩和解压缩之前先调用lzo_init函数进行初始化,如果该函数返回LZO_E_OK就表明没有问题可以继续操作。需要压缩数据调用lzo1x_1_compress函数,需要解压数据就调用lzo1x_decompress函数或lzo1x_decompress_safe函数。这两个函数的区别是lzo1x_decompress_safe函数会对要解压缩数据的有效性进行验证,如果验证通过才会进行解压缩操作而lzo1x_decompress函数则不会这么做,如果数据不是有效的则会产生“段错误”。建议使用lzo1x_decompress_safe函数。以下是这些函数的详细操作说明:

  • lzo1x_1_compress函数进行压缩数据操作,其需要5个参数分别是:要压缩的数据、要压缩的数据的大小(单位为字节)、压缩后数据的缓冲区、压缩后缓冲区的大小(值结果参数,调用成功之后存储实际的压缩后的数据大小)、压缩工作缓冲区。压缩数据成功之后会返回LZO_E_OK;
  • lzo1x_decompress和lzo1x_decompress_safe函数进行数据的解压缩操作,其也需要5个参数分别是:要解压缩的数据、要解压缩数据的大小(单位为字节)、解压缩后数据的存放缓冲区、原始数据(未压缩数据)大小(值结果参数,执行成功之后返回解压缩后数据的实际大小)、解压缩不需要工作缓冲区可以为NULL。执行成功返回LZO_E_OK且其第4个参数要和原始数据大小一致(这个参数以指针方式传入,传入的值不正确也会解压失败,实验过程中发现使用lzo1x_decompress函数这个值可以随便给,lzo1x_decompress_safe就不行)。
  • 压缩过程需要一个工作内存wrkmem,在例子中经过了一系列宏可以发现他被定义为了一个64KB大小的数组,需要注意的一点是这个内存要4字节对齐,例子上特意加了这么一条注释来提醒。

注意:

(1)lzo1x_1_compress函数的第4个参数是值结果参数,传进去的值是用来指示存放压缩后数据的缓冲区大小,执行成功之后通过指针返回的结果是压缩后的数据实际使用的缓冲区大小,即压缩后的数据大小。压缩后需要的数据的缓冲区的大小上限是可以根据未压缩数据大小进行计算的公式:output_block_size = input_block_size + (input_block_size / 16) + 64 + 3;

(2)lzo1x_1_compress函数的第5个参数是压缩的时候需要使用的工作缓冲区,缓冲的生成在miniLZO库的提供测试例程(testmini.c)中有相关的宏生成该缓冲区,而解压的时候就不需要缓冲区;

(3)lzo1x_decompress和lzo1x_decompress_safe解压的时候需要的第4个参数是值结果参数,传进去的值是原始的未压缩数据的大小,执行成功之后通过指针返回的是实际解压缩后的数据的大小。所以压缩之后的数据在传输的时候需要将原始数据的大小和压缩后数据一起传输,否则对方在解压缩的时候将无法解压。

三、VS2013下文件压缩和解压测试

1、测试函数

接下来在VS2013下写了个测试函数压缩和解压一个文件:

int test(void)
{FILE *fileRead;FILE *fileOut;unsigned long bufRead[1024 * 1];unsigned long bufWrkMem[1024 * 16];unsigned long bufWrite[1024 * 1 + sizeof(bufRead) / 16 + 64 + 3];unsigned long ret;unsigned long readLen;unsigned long writeLen;unsigned long param[1500];  //存放压缩后的数据长度unsigned long paramCnt;printf("bufWrkMem sizeof = %d\n", sizeof(bufWrkMem));fileRead = fopen("testFile.txt", "rb");if (NULL == fileRead){printf("Opren Read File Fail!!!\n");}fileOut = fopen("testFileCompress.txt", "wb");if (lzo_init() != LZO_E_OK){printf("internal error - lzo_init() failed !!!\n");printf("(this usually indicates a compiler bug - try recompiling\nwithout optimizations, and enable '-DLZO_DEBUG' for diagnostics)\n");return 3;}paramCnt = 0;//压缩while ((readLen = fread(bufRead, 1, sizeof(bufRead), fileRead)) > 0){ret = lzo1x_1_compress(bufRead, readLen, bufWrite, &writeLen, bufWrkMem);if (ret == LZO_E_OK){fwrite(bufWrite, 1, writeLen, fileOut);param[paramCnt++] = writeLen;printf("compressed %lu bytes into %lu bytes\n", (unsigned long)readLen, (unsigned long)writeLen);}else{/* this should NEVER happen */printf("internal error - compression failed: %d\n", ret);return 2;}}param[paramCnt++] = sizeof(bufRead);fclose(fileRead);fclose(fileOut);fileRead = fopen("testFileCompress.txt", "rb");if (NULL == fileRead){printf("Opren Read File Fail!!!\n");}fileOut = fopen("testFileDecompress.txt", "wb");paramCnt = 0;readLen = param[paramCnt++];//解压while ((fread(bufRead, 1, readLen, fileRead)) > 0){ret = lzo1x_decompress(bufRead, readLen, bufWrite, &writeLen, NULL);if (ret == LZO_E_OK){fwrite(bufWrite, 1, writeLen, fileOut);printf("decompressed %lu bytes back into %lu bytes\n", (unsigned long)readLen, (unsigned long)writeLen);readLen = param[paramCnt++];}else{/* this should NEVER happen */printf("internal error - decompression failed: %d\n", ret);return 1;}}fclose(fileRead);fclose(fileOut);return 0;
}

测试函数里我用的lzo1x_decompress来解压数据,所以第四个参数“压缩前的大小”随便传,因此在程序中我只记录了每一个数据块压缩后的数据大小。压缩函数所需要的工作内存wrkmem我也没有用默认的,而是创建了一个64KB的数组给它调用。下面是程序的执行输出(太长了,只截取一段):

bufWrkMem sizeof = 65536
compressed 4096 bytes into 1238 bytes
compressed 4096 bytes into 795 bytes
compressed 4096 bytes into 1306 bytes
compressed 4096 bytes into 1348 bytes
compressed 4096 bytes into 1323 bytes
compressed 4096 bytes into 1296 bytes
compressed 4096 bytes into 1289 bytes
compressed 4096 bytes into 748 bytes
compressed 4096 bytes into 691 bytes
compressed 4096 bytes into 953 bytes/*************** 此处省略一大段 **************/compressed 4096 bytes into 1296 bytes
compressed 4096 bytes into 1334 bytes
compressed 4096 bytes into 1284 bytes
compressed 4096 bytes into 1347 bytes
compressed 4096 bytes into 1303 bytes
compressed 4096 bytes into 1357 bytes
compressed 4096 bytes into 1314 bytes
compressed 4096 bytes into 1310 bytes
compressed 4096 bytes into 1308 bytes
compressed 4096 bytes into 1336 bytes
compressed 320 bytes into 181 bytes
decompressed 1238 bytes back into 4096 bytes
decompressed 795 bytes back into 4096 bytes
decompressed 1306 bytes back into 4096 bytes
decompressed 1348 bytes back into 4096 bytes
decompressed 1323 bytes back into 4096 bytes
decompressed 1296 bytes back into 4096 bytes
decompressed 1289 bytes back into 4096 bytes
decompressed 748 bytes back into 4096 bytes
decompressed 691 bytes back into 4096 bytes
decompressed 953 bytes back into 4096 bytes/*************** 此处省略一大段 **************/decompressed 1296 bytes back into 4096 bytes
decompressed 1334 bytes back into 4096 bytes
decompressed 1284 bytes back into 4096 bytes
decompressed 1347 bytes back into 4096 bytes
decompressed 1303 bytes back into 4096 bytes
decompressed 1357 bytes back into 4096 bytes
decompressed 1314 bytes back into 4096 bytes
decompressed 1310 bytes back into 4096 bytes
decompressed 1308 bytes back into 4096 bytes
decompressed 1336 bytes back into 4096 bytes
decompressed 181 bytes back into 320 bytes

下面是原始文件、压缩后的文件和解压后的文件:

2、数据比较工具Beyond Compare

可以看到从5021KB压缩到了1612KB。解压后的文件“testFileDecompress.txt”和原始文件“testFile.txt”的大小是一样的,当然不能说大小一样就认为解压成功了,还要真实去比较一下数据是不是真的一致。这里推荐一个数据比较的工具“Beyond Compare”

这个工具并不大,非常好用,平常公司同事之间互相同步代码也会用到这个工具,网上搜一下就能下载到。

安装好工具之后,选中要比较的两个文件右键出现一个比较选项,点击出现一个界面,选择“十六进制比较”,然后选择“打开视图”。

两个文件有数据不同的话工具会自动把不同的部分标成红色的,没看到红色的地方的话说明两个文件内容完全一样。从上图可以得知miniLZO的压缩和解压并没有问题。

3、实验分析

因为上面用的压缩方法是将原始文件切成很多份4KB的数据块为一个单位来进行压缩的,如果每一份的大小再调大些的话,压缩后的文件会更小的。既然用miniLZO,那肯定是看中它的“轻量型”了,一般都用在单片机这种RAM小的平台上,所以用到这个库对数据进行压缩,在待压缩数据较大的情况下,肯定是要把待压缩数据切成一块块的进行压缩,压缩后的数据也需要一块块的存储,上述压缩后的文件就是这样存的,解压再根据已有的每份的压缩大小去还原原始数据。

解压函数是必须要精准知道当前解压的数据是多大的,一次只能给一个压缩块给它解,给多个解不出来,试过了如果解压的这个块有2752字节,那么待解压数据大小(第二个形参)传成了2753也是解压报错的。所以压缩的时候还需要保存每个压缩块的数据大小。

按照上面的用法如果移植到单片机上的话,我们算算需要开辟一片多大的RAM空间。工作内存wrkmem要64KB、待压缩数据区4KB、解压数据区5.3KB,每个块的压缩长度记录数组5.9KB(当然这个会根据实际压缩数据的大小改变)。所以一共用了79.2KB的RAM空间,对于普通的单片机来说这个内存需求算挺大的了,考虑到待压缩数据缓存和解压数据缓存是可以根据实际情况再改小点的,只不过会损失掉一点压缩率,所以再综合考虑一下得要个70KB的空间吧,这样还是不太能接受。说白了就是工作内存wrkmem要的空间太大了,那么工作内存wrkmem可以给小一点吗?针对上面的文件压缩测试,我把wrkmem砍了一半的空间变成了32KB大小,发现程序运行没有问题。那再砍一半变成16KB大小,程序报错了,在如下函数挂了:

/*
*    minilzo.c的4395~4407行代码
*/LZOLIB_PUBLIC(lzo_hvoid_p, lzo_hmemset) (lzo_hvoid_p s, int cc, lzo_hsize_t len)
{
#if (LZO_HAVE_MM_HUGE_PTR) || !(HAVE_MEMSET)lzo_hbyte_p p = LZO_STATIC_CAST(lzo_hbyte_p, s);unsigned char c = LZO_ITRUNC(unsigned char, cc);if __lzo_likely(len > 0) do*p++ = c;while __lzo_likely(--len > 0);return s;
#elsereturn memset(s, cc, len);    //程序挂在了这里
#endif
}

程序在执行return memset(s, cc, len)的时候报错,看了下调用栈,找到了一个上一级的调用函数lzo_memset(wrkmem, 0, ((lzo_uint)1 << D_BITS) * sizeof(lzo_dict_t))。在这里D_BITS为宏定义14,sizeof(lzo_dict_t)的大小是2,即此处将wrkmem的内存前32KB清0,因为我们刚刚只给了16KB的空间,所以导致运行报错。突然又想了一下,为什么前面例子申请64KB空间,这里给我的一种感觉实际只用了32KB,而用多少空间和D_BITS这个宏有关,看了下源代码D_BITS这个宏貌似会影响算法的字典的大小,可以从6到19变化,而且数字越大压缩后的文件越小,相当于是个压缩等级吧。这么看的话wrkmem最小可以给到2^6*2=128字节的大小,这个内存对于大部分的单片机来说都可以接受了。

四、STM32F103ZET6平台下的测试

上面只是Windows下的测试,一切条件都很理想,咱得实际到单片机的平台去试试,是骡子是马拉出来溜溜。测试平台是STM32F103ZET6,CPU主频72MHz,测试代码如下:

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "minilzo.h"
#include <stdlib.h>void TIM4_Init(u16 per,u16 psc);#define TEST_BUF_SIZE  (1024 * 1)
#define WRKMEM_BUF_SIZE (1 << (11 + 1))#define BUF_UNIT_NUM  (TEST_BUF_SIZE/4)
#define WRKMEM_UNIT_NUM (WRKMEM_BUF_SIZE/4)u32 dwWrkmemBuf[WRKMEM_UNIT_NUM];
u32 dwRawDatBuf[BUF_UNIT_NUM];
u32 dwCompressBuf[BUF_UNIT_NUM + (sizeof(dwRawDatBuf) / 16 + 64 + 3) / 4];
u32 dwDecompressBuf[BUF_UNIT_NUM];int test(void)
{u32 dwCompressLen;u32  dwDecompressLen;u32 dwRet;u32 i,j;u32 dwRnd;//生成随机数for(i = 0; i < sizeof(dwRawDatBuf) / sizeof(dwRawDatBuf[0]); ){dwRnd = rand();for(j = 0; j < 10; j++){dwRawDatBuf[i + j] = dwRnd;i++;}}//开启定时器记时TIM4->CNT = 0;TIM_Cmd(TIM4,ENABLE); //压缩dwRet = lzo1x_1_compress((const lzo_bytep)dwRawDatBuf, sizeof(dwRawDatBuf), (lzo_bytep)dwCompressBuf, (lzo_uintp)&dwCompressLen, dwWrkmemBuf);TIM_Cmd(TIM4,DISABLE); if (dwRet == LZO_E_OK){printf("compressed %lu bytes into %lu bytes, time = %d us\r\n", (unsigned long)sizeof(dwRawDatBuf), (unsigned long)dwCompressLen, TIM4->CNT);}else{/* this should NEVER happen */printf("internal error - compression failed: %d\r\n", dwRet);return 2;}//开启定时器记时TIM4->CNT = 0;TIM_Cmd(TIM4,ENABLE); //解压dwRet = lzo1x_decompress((const lzo_bytep)dwCompressBuf, dwCompressLen, (lzo_bytep)dwDecompressBuf, (lzo_uintp)&dwDecompressLen, NULL);TIM_Cmd(TIM4,DISABLE); if (dwRet == LZO_E_OK){printf("decompressed %lu bytes back into %lu bytes, time = %d us\r\n", (unsigned long)dwCompressLen, (unsigned long)dwDecompressLen, TIM4->CNT);}else{/* this should NEVER happen */printf("internal error - decompression failed: %d\r\n", dwRet);return 1;}//检查原始数据和解压后的数据是否一致for(i = 0; i < sizeof(dwRawDatBuf) / sizeof(dwRawDatBuf[0]); i++){if(dwRawDatBuf[i] != dwDecompressBuf[i]){printf("Decompress Fail !!!\r\n");}}return 0;
}int main()
{SysTick_Init(72);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组 分2组LED_Init();USART1_Init(57600);printf("开始测试\r\n");TIM4_Init(60000,72-1);  while(1){test();led2=!led2;}}/*******************************************************************************
* 函 数 名         : TIM4_Init
* 函数功能         : TIM4初始化函数
* 输    入         : per:重装载值psc:分频系数
* 输    出         : 无
*******************************************************************************/
void TIM4_Init(u16 per,u16 psc)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//使能TIM4时钟TIM_TimeBaseInitStructure.TIM_Period=per;   //自动装载值TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //开启定时器中断TIM_ClearITPendingBit(TIM4,TIM_IT_Update);NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//定时器中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;     //子优先级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;          //IRQ通道使能NVIC_Init(&NVIC_InitStructure);    TIM_Cmd(TIM4,DISABLE); //使能定时器
}/*******************************************************************************
* 函 数 名         : TIM4_IRQHandler
* 函数功能         : TIM4中断函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void TIM4_IRQHandler(void)
{printf("goto IRQ\r\n");if(TIM_GetITStatus(TIM4,TIM_IT_Update)){led2=!led2;}TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
}

上述测试程序把miniLZO的D_BITS设置为了11,所以这里设置了一个4KB大小的dwWrkmemBuf作为工作内存。开启了一个定时器,1微秒计数一次,用来记录压缩和解压的时间。其他用到的内存情况如下(map文件信息里的):

dwWrkmemBuf                              0x20000020   Data        4096  main.o(.bss)
dwRawDatBuf                              0x20001020   Data        1024  main.o(.bss)
dwCompressBuf                            0x20001420   Data        1152  main.o(.bss)
dwDecompressBuf                          0x200018a0   Data        1024  main.o(.bss)

整个工程编译后占的RAM空间也才8KB左右,完全可以被单片机接受。

下面看下打印输出:

开始测试
goto IRQ
compressed 1024 bytes into 279 bytes, time = 711 us
decompressed 279 bytes back into 1024 bytes, time = 83 us
compressed 1024 bytes into 271 bytes, time = 711 us
decompressed 271 bytes back into 1024 bytes, time = 83 us
compressed 1024 bytes into 279 bytes, time = 712 us
decompressed 279 bytes back into 1024 bytes, time = 83 us
compressed 1024 bytes into 274 bytes, time = 710 us
decompressed 274 bytes back into 1024 bytes, time = 83 us
compressed 1024 bytes into 275 bytes, time = 711 us
decompressed 275 bytes back into 1024 bytes, time = 83 us
compressed 1024 bytes into 277 bytes, time = 711 us
decompressed 277 bytes back into 1024 bytes, time = 83 us
compressed 1024 bytes into 278 bytes, time = 712 us

可以看到,对于上面的例子,原始数据是每10个数据单位重复一次,1KB的数据压缩平均花了711微秒,再解压出来花了83微秒,这个速度感觉还行,压缩到了275字节还不错。接下来试了下原始数据dwRawDatBuf全部填充0,然后去做压缩和解压,性能如下:

开始测试
goto IRQ
compressed 1024 bytes into 32 bytes, time = 405 us
decompressed 32 bytes back into 1024 bytes, time = 51 us
compressed 1024 bytes into 32 bytes, time = 405 us
decompressed 32 bytes back into 1024 bytes, time = 51 us
compressed 1024 bytes into 32 bytes, time = 405 us
decompressed 32 bytes back into 1024 bytes, time = 51 us
compressed 1024 bytes into 32 bytes, time = 406 us
decompressed 32 bytes back into 1024 bytes, time = 50 us
compressed 1024 bytes into 32 bytes, time = 406 us
decompressed 32 bytes back into 1024 bytes, time = 50 us
compressed 1024 bytes into 32 bytes, time = 406 us
decompressed 32 bytes back into 1024 bytes, time = 51 us
compressed 1024 bytes into 32 bytes, time = 405 us
decompressed 32 bytes back into 1024 bytes, time = 51 us

从上面数据可以知道数据规律性越强,压缩解压速度越快,压缩能力越强。接下来再试一下往dwRawDatBuf填充完全的随机数:

开始测试
goto IRQ
compressed 1024 bytes into 1032 bytes, time = 648 us
decompressed 1032 bytes back into 1024 bytes, time = 46 us
compressed 1024 bytes into 1032 bytes, time = 648 us
decompressed 1032 bytes back into 1024 bytes, time = 46 us
compressed 1024 bytes into 1032 bytes, time = 648 us
decompressed 1032 bytes back into 1024 bytes, time = 46 us
compressed 1024 bytes into 1032 bytes, time = 648 us
decompressed 1032 bytes back into 1024 bytes, time = 46 us
compressed 1024 bytes into 1032 bytes, time = 648 us
decompressed 1032 bytes back into 1024 bytes, time = 46 us

可以看出来,全是随机数时,因为数据难以压缩,压缩后的数据反而更大了,但是有一个特点,压缩和解压速度仍然很快。

五、总结

本文分析了miniLZO库的移植和使用,分析了压缩和解压函数的调用方式及参数的注意点,通过在VS下进行实验发现了通过更改源代码里面D_BITS这个宏定义可以调整算法所需要的工作内存的大小,从而能够更灵活的适应单片机平台的需求。接着推荐了一个非常好用的数据对比工具Beyond Compare然后又将miniLZO库移植到STM32单片机上进行了实际的测试,从几个方面验证了miniLZO的压缩性能。最后总结一下miniLZO的优点和缺点:

优点:

(1)miniLZO库在压缩时拥有较好的时间性能,通常情况下都会有很快压缩和解压速度。

(2)miniLZO可以调整工作缓存(wrkmem)的大小,从而适应不同的嵌入式平台。

(3)miniLZO库代码量不多,很适合单片机这类ROM空间小的平台使用。

缺点:

(1)数据需要分块压缩,而且在解压时必须精确知道压缩块是多大的,不能实现数据流的连续压缩。举个例子,对于一个比较大的文件而言,我们需要人为把文件切成很多份来进行压缩,从压缩算法的角度上来说,相当于丢掉了各部分之间的关联性信息,表现为每次都要重新构建新的字典,那么压缩率必定不如连续数据流的压缩。我想着每次压缩可以不必严格一块一块数据分出来压缩,而是前一块数据压缩完后可以不用将前一次的字典信息等缓存清除,然后下一块数据可以继续利用上一次数据的字典信息等,这样就能再提高压缩率了,这个功能可以作为一个选项被用户选择。

总的来说,miniLZO还是很棒的。本文所述如有不对的地方,欢迎批评指正。

六、参考资料

文章:

《基于STM32的无损压缩算法miniLZO移植》 基于STM32的无损压缩算法miniLZO移植,压缩率很高,20KB随机数压缩到638字节,耗时275us - 腾讯云开发者社区-腾讯云

《miniLZO的基本使用》 openKylin论坛 -

《LZO和MiniLZO编码介绍》 LZO和MiniLZO编码介绍 | 学步园

《LZO使用和介绍》 LZO 使用和介绍 - Boblim - 博客园

《LZO实现代码参考》 LZO压缩算法(C语言)

资料:

《minilzo-2.10.tar.gz》

《STM32F1_miniLZO测试工程》

链接:https://pan.baidu.com/s/1saC4-pXI59uD3ssIMSyIrg  提取码:3m19

无损压缩算法专题——miniLZO相关推荐

  1. 无损压缩算法专题——RLE算法实现

    一.前言 本文是基于我的另一篇博客<无损压缩算法专题--无损压缩算法介绍>的基础上来实现的,RLE算法最简单的理解就是用(重复数,数据值)这样一个标记来代替待压缩数据中的连续重复的数据,以 ...

  2. 数仓无损压缩算法:gzip算法

    摘要:一种无损的压缩数据格式,是一个在类Unix上的一种文件解压缩软件. 本文分享自华为云社区<GaussDB(DWS) gzip算法简介>,作者:hw0086. [算法原理] gzip是 ...

  3. 常用无损压缩算法原理简析

    无损压缩算法原理 压缩一般分为两个步骤,建模和编码.一个完美的模型可以描述数据流是如何产生的,相当于一个python类里面的generator.只需要这个generator就可以产生所有数据,从而大大 ...

  4. python图片压缩原理_LZ77无损压缩算法原理详解(结合图片和简单代码)

    LZ77算法是无损压缩算法,由以色列人Abraham Lempel发表于1977年.LZ77是典型的基于字典的压缩算法,现在很多压缩技术都是基于LZ77.鉴于其在数据压缩领域的地位,本文将结合图片和源 ...

  5. 任意数据无损压缩算法

    任意二进制状态数据都能无损压缩,其压缩模型由以下组成:25*25矩阵.障碍格.计数路径.被压缩数据.洗牌.计数器.简化数据等等,建模见(图1),  其中屏蔽掉120个元素(取名:障碍格,在图3中用2表 ...

  6. 视频压缩算法有哪些php,常用的无损压缩算法有哪些

    常用的无损压缩算法有:1.LZ77算法,该算法是很多其他无损压缩算法的基础:2.LZR算法,是旨在提升LZ77的一个算法:3.LZSS算法,该算法目标是成为LZ77的一个线性时间替换算法:4.DEFL ...

  7. 基于STM32的无损压缩算法miniLZO移植,压缩率很高,20KB随机数压缩到638字节,耗时275us

    说明: 1.miniLZO是采用C编写的无损压缩库. 2.提供了快速压缩和超快速解压缩能力. 3.比较耗内存,需要64KB内存用于压缩,对于H7这种大内存的,非常合适.或者有外置SRAM/SDRAM的 ...

  8. 数据压缩算法—2无损压缩算法

    几个常见的编码算法 (一) 字典算法   字典算法是最为简单的压缩算法之一.它是把文本中出现频率比较多的单词或词汇组合做成一个对应的字典列表,并用特殊代码来表示这个单词或词汇.例如:   有字典列表: ...

  9. 无损压缩算法历史——熵编码是最早出现的,后来才有Lzx这些压缩算法

    Lossless Entropy type Unary Arithmetic Asymmetric Numeral Systems Golomb Huffman  Adaptive Canonical ...

最新文章

  1. Spring 容器的启动过程
  2. 连续时间傅里叶变换的性质(简介及推导)
  3. 继承SectionIndexer,实现联系人侧边栏
  4. 富友电子商务系统的四大优势助网商轻松赚钱
  5. 计算机组装与维护补考论文,计算机组装与维护期末论文
  6. nginx图片过滤处理模块http_image_filter_module
  7. Java 冒泡排序的实现
  8. Leetcode-SingleNumberII
  9. 中药和西药的历史渊源,到底谁才是科学好药
  10. UI设计干货|从此做数据可视化页面不枯燥!
  11. golang 目录分隔符号_Golang 从0到1之任务提醒(一)
  12. 计算机维修英语情景对话大全,快速英语情景对话大全 日常生活对话 7 排除电脑故障...
  13. 查看 Linux 中文件打开情况
  14. 电商项目---完成内容管理cms系统
  15. gg修改器怎么能让服务器检测不到,gg修改器怎么绕过检测 | 手游网游页游攻略大全...
  16. java robot识别验证码,robotframework处理登录验证码
  17. PHP面试技巧——什么是职场暗语?
  18. 债券收益率预测模型_利率预测模型系列之一:简单的N-S模型运用
  19. 有什么能测试安卓硬件的软件吗,手机硬件检测工具有哪些 总有一款适合你
  20. 一种绘制有向图的方法<TSE93> - 2. 最优层级分配

热门文章

  1. VB. NET MDI窗体设计
  2. docker安装mysql5.7(仅供测试使用)
  3. 同花顺指标公式巴菲力监控主力副图源码
  4. P2051 [AHOI2009]中国象棋
  5. css之display
  6. 部署检验Linux数据库
  7. 2.3 软件构架(第二章 自动驾驶的软硬件构架)
  8. 好公司管理,差公司监控
  9. JAVA基础知识学习
  10. 第一台计算机是怎么样的