嵌入式压缩库推荐之一:minz

130阅读 0评论2025-07-16 wlbdd
分类:嵌入式

转:

一. 前言

    在嵌入式项目中,有时候需要进行压缩解压,此时希望能在代码中简单的方式就能嵌入压缩解压算法库,并且关注代码大小,RAM需求,执行速度和使用方便性等。这里就推荐一个知名的开源压缩库miniz

Miniz 是一个仅单源文件就实现无损、高性能的数据压缩库。遵循 zlibRFC 1950)和 DeflateRFC 1951)压缩数据格式规范标准。支持 zlib 库中{BANNED}最佳常用的导出函数,且是完全独立的实现,因此不受 zlib 许可条款的约束。Miniz 还包含用于写入 .PNG 格式图像文件以及读取/写入/追加 .ZIP 格式存档的简单易用的函数。Miniz 的压缩速度经过优化,可与 zlib 相媲美,并且它还具有专门的实时压缩器函数,可与 fastlz/minilzo 相媲美。

Miniz具备以下特点

lMIT 许可证

l C 语言编写,c./h文件库。已在 GCCClang 和 Visual Studio 中测试。

l通过宏定义轻松调整和裁剪

l可直接替代 zlib {BANNED}最佳常用的 API(已在多个使用 zlib 的开源项目中测试,例如 libpng 和 libzip)。

l填补了 zlib 与几个流行的实时压缩器在单线程性能与压缩比之间的差距。例如,在级别 时,miniz.c 的压缩比比 minilzo 高约 5% - 9%,但速度慢约 35%。在级别 2 - 9 时,miniz.c 的设计旨在在压缩比和速度方面优于 zlib性能对比见:

l不是基于块的压缩器:miniz.c 完全支持使用协程式实现的流式处理。如果只有单个字节,也可以逐字节调用 zlib 风格的 API 函数。

l易于使用。底层压缩器(tdefl)和解压缩器(tinfl)具有简单的状态结构,可根据需要通过简单的 memcpy 进行保存/恢复。底层编解码器 API 完全不使用堆。

l整个inflater (包括可选的 zlib 头解析和 Adler-32 校验)在一个单独的协程函数中实现,该函数单独存在于一个小型(约 550 行)的源文件中:miniz_tinfl.c

l一套相当完整的(但完全可选的).ZIP 存档操作和提取 API。存档功能旨在解决嵌入式、移动或游戏开发环境中常见的问题。(存档 API 有意设计得足够强大,只需添加一些额外的高级逻辑即可编写整个存档器。)

l不支持加密归档。

l目前没有什么文档熟悉基本的zlib API则容易入手可以参考代码前的注释,以及examples程序

二. 源码

Release下下载的文件源码已经组合成miniz.c/miniz.h


miniz.c/miniz.h,添加到自己的项目即可。


Git直接clone的代码

git clone

如下


执行./amalgamate.sh可以使用命令生成miniz.cminiz.h


即将各种文件放在一起。类似于sqlite的做法。生成的位于amalgamation下,和release下载的就是一样的了。

三. 嵌入式平台适配

miniz.h

如果有标准库的堆管理直接使用

没有就需要替换以下接口

 
	

点击(此处)折叠或打开

  1. #define MZ_MALLOC(x) malloc(x)
  2. #define MZ_FREE(x) free(x)
  3. #define MZ_REALLOC(p, x) realloc(p, x)

{BANNED}最佳好是专门实现miniz_port.c/h去实现相关需要配置的结果。

如果只需要基于buffer的压缩解压只需要适配这几个接口即可。

如果还需要进行文件的解压压缩操作则还需要实现对应的接口。

四. 测试

复制miniz.cmniz.h到自己的项目。

添加miniz_test.c内容如下

点击(此处)折叠或打开

  1. #include
  2. #include
  3. #include "miniz.h"
  4. #include "miniz_port.h" // The string to compress.
  5. static const char *s_pStr = "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
  6.                             "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
  7.                             "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."\
  8.                             "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
  9.                             "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."\
  10.                             "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
  11.                             "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson.";
  12. int miniz_test(int argc, char *argv[]);
  13. int miniz_test(int argc, char *argv[])
  14. {
  15.     uint32_t step = 0;
  16.     int cmp_status;
  17.     uLong src_len = (uLong)strlen(s_pStr);
  18.     uLong cmp_len = compressBound(src_len);
  19.     uLong uncomp_len = src_len;
  20.     uint8_t *pCmp, *pUncomp;
  21.     uint32_t total_succeeded = 0;
  22.     (void)argc, (void)argv;
  23.     printf("miniz.c version: %s\n", MZ_VERSION);
  24.     do { // Allocate buffers to hold compressed and uncompressed data.
  25.         pCmp = (mz_uint8 *)miniz_port_malloc(0, (size_t)cmp_len);
  26.         pUncomp = (mz_uint8 *)miniz_port_malloc(0, (size_t)src_len);
  27.         if ((!pCmp) || (!pUncomp)) {
  28.             printf("Out of memory!\n"); // Compress the string.
  29.             return EXIT_FAILURE;
  30.         }
  31.         cmp_status = compress(pCmp, &cmp_len, (const unsigned char *)s_pStr, src_len);
  32.         if (cmp_status != Z_OK) {
  33.             printf("compress() failed!\n");
  34.             miniz_port_free(pCmp);
  35.             miniz_port_free(pUncomp);
  36.             return EXIT_FAILURE;
  37.         }
  38.         printf("Compressed from %u to %u bytes\n", (mz_uint32)src_len, (mz_uint32)cmp_len);
  39.         if (step) { // Purposely corrupt the compressed data if fuzzy testing (this is a very crude fuzzy test). uint3
  40.             2_t n = 1 + (rand() % 3);
  41.             while (n--) {
  42.                 uint32_t i = rand() % cmp_len;
  43.                 pCmp[i] ^= (rand() & 0xFF);
  44.             }
  45.         } // Decompress.
  46.         cmp_status = uncompress(pUncomp, &uncomp_len, pCmp, cmp_len);
  47.         total_succeeded += (cmp_status == Z_OK);
  48.         if (step) {
  49.             printf("Simple fuzzy test: step %u total_succeeded: %u\n", step, total_succeeded);
  50.         } else {
  51.             if (cmp_status != Z_OK) {
  52.                 printf("uncompress failed!\n");
  53.                 miniz_port_free(pCmp);
  54.                 miniz_port_free(pUncomp);
  55.                 return EXIT_FAILURE;
  56.             }
  57.             printf("Decompressed from %u to %u bytes\n", (mz_uint32)cmp_len, (mz_uint32)uncomp_len); // Ensure uncompress() returned the expected data.
  58.             if ((uncomp_len != src_len) || (memcmp(pUncomp, s_pStr, (size_t)src_len))) {
  59.                 printf("Decompression failed!\n");
  60.                 miniz_port_free(pCmp);
  61.                 miniz_port_free(pUncomp);
  62.                 return EXIT_FAILURE;
  63.             }
  64.         }
  65.         miniz_port_free(pCmp);
  66.         miniz_port_free(pUncomp);
  67.         step++; // Keep on fuzzy testing if there's a non-empty command line.
  68.     } while (argc >= 2);
  69.     printf("Success.\n");
  70.     return EXIT_SUCCESS;
  71. }


根据实际替换mallocfreeprintf等函数。我这里专门实现了miniz_port.c/h去做。

调用

miniz_test(0,0);

看到打印如下


五. 资源消耗

主要关注程序大小和RAM占用

我们通过malloc/free接口记录下{BANNED}最佳大堆使用量并打印出来,可以看到压缩大概需要320KB动态内存。

miniz_port.c如下

点击(此处)折叠或打开

  1. #include
  2. #include
  3. #include "miniz_port.h"
  4. static size_t s_max_used = 0;
  5. static size_t s_cur_used = 0;
  6. void *miniz_port_malloc(size_t size)
  7. {
  8.     void *p = malloc(size + 4); /* malloc默认配置是8字节对齐,所以预留的4字节强制类型转换不会有对齐问题 */
  9.     memset(p, 0, size);
  10.     *(uint32_t *)p = size; /* 记录分配的大小 用于调试使用*/
  11.     s_cur_used += size;
  12.     if (s_max_used < s_cur_used) {
  13.         s_max_used = s_cur_used;
  14.     }
  15.     printf("malloc:max:%u,cur:%u\r\n", s_max_used, s_cur_used);
  16.     return (uint8_t *)p + 4;
  17. }
  18. void miniz_port_free(void *ptr)
  19. {
  20.     free((uint8_t *)ptr - 4);
  21.     s_cur_used -= *(uint32_t *)((uint8_t *)ptr - 4);
  22.     printf("free:max:%u,cur:%u\r\n", s_max_used, s_cur_used);
  23. }
  24. void *miniz_port_realloc(void *ptr, size_t size)
  25. {
  26.     uint32_t len;
  27.     void *p = malloc(0, size + 4);
  28.     *(uint32_t *)p = size; /* 记录分配的大小 */
  29.     s_cur_used += size;
  30.     if (s_max_used < s_cur_used) {
  31.         s_max_used = s_cur_used;
  32.     }
  33.     len = *(uint32_t *)((uint8_t *)ptr - 4);
  34.     memcpy((uint8_t *)p + 4, ptr, len);
  35.     free((uint8_t *)ptr - 4);
  36.     s_cur_used -= len;
  37.     printf("realloc:max:%u,cur:%u\r\n", s_max_used, s_cur_used);
  38.     return (uint8_t *)p + 4;
  39. }



上述测试代码在某个嵌入式平台编译看到rom空间如下

rodata大概2KB


text大概16K


六. 推荐阅读

七. 总结

通过上述测试可以看到miniz ROM大概是需要20KB左右,RAM需要320KB左右,所以不太适合MCU平台,比较适合资源丰富点的SOC/MPU,或者有扩展RAMMCU平台。后面我们再继续推荐适合MCU平台使用的压缩算法库。

miniz的代码全部放在了一起,包括不同平台的适配也是通过宏去实现,都在miniz.c中,对于非linux和windows等嵌入式平台其实移植性不是很好,{BANNED}最佳好是对平台依赖接口,比如堆管理,时间接口,文件操作接口等单独剥离出来通过port文件移植实现{BANNED}最佳好。这样miniz.c里面不应该再出现和平台相关的代码和宏等,全部由port去适配。

上一篇:嵌入式压缩库推荐之一:minz
下一篇:嵌入式压缩库推荐之二:适合MCU资源消耗小且高效的压缩库minilzo