跳转至

miniz - 简易zlib压缩

作者:沈园园

一、概述

FastLZ 和 miniz 都是流行的压缩库,但它们在设计目标、压缩算法、性能特点等方面有显著区别,适用于不同的应用场景。

FastLZ 是一款高效且小巧的开源压缩库,主要用于实现基于 LZ77 算法的字节对齐数据压缩。

LZ77(Lempel-Ziv 1977)是数据压缩领域的一种基础方法,其核心思想是通过查找源数据中的重复模式来减少存储空间。

适用于压缩文本/段落序列、原始像素数据序列或具有大量重复的任何其他数据块。但是不打算用于图像、视频和其他通常已经以最佳压缩形式存在的格式的数据。

FastLZ 的重点是非常快速的压缩和解压缩。

miniz是一个单一文件的小型压缩库,它是zlib的一个替代品,专门为需要轻量级压缩解决方案的项目设计,它被设计成可以轻松地集成到项目中,并且具有最小的依赖性。

miniz的主要特点包括:

  1. 支持zlib兼容的压缩和解压缩。
  2. 单个源文件实现,易于集成。
  3. 小的内存占用,适合资源受限的环境。
  4. 支持流式压缩和解压缩。

压缩比, miniz的压缩比要好于fastlz

总结:FastLZ 是 "速度优先"的轻量选择,miniz 是 "兼容zlib和压缩率优先" 的通用选择。

二、核心示例

1、核心示例是指:使用本库文件提供的核心API,开发的基础业务逻辑的演示代码;

2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;

3、更加完整和详细的demo,请参考 LuatOS仓库 中各个产品目录下的demo/miniz

-- 准备好数据
local bigdata = "123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw"
-- 压缩之, 压缩得到的数据是zlib兼容的,其他语言可通过zlib相关的库进行解压
local cdata = miniz.compress(bigdata) 
-- lua 的 字符串相当于有长度的char[],可存放包括0x00的一切数据
if cdata then
    -- 检查压缩前后的数据大小
    log.info("miniz", "before", #bigdata, "after", #cdata)
    log.info("miniz", "cdata as hex", cdata:toHex())

    -- 解压, 得到原文
    local udata = miniz.uncompress(cdata)
    log.info("miniz", "udata", udata)
end

三、常量详解

核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;

每个常量对应的常量取值仅做日志打印时查询使用,不要将这个常量取值用做具体的业务逻辑判断,因为LuatOS内核固件可能会变更每个常量对应的常量取值;

如果用做具体的业务逻辑判断,一旦常量取值发生改变,业务逻辑就会出错;

3.1 miniz.WRITE_ZLIB_HEADER

常量含义:压缩参数,是否写入zlib头部数据,compress函数的默认值
数据类型:number
常量取值:0x01000
示例代码:miniz.compress("123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw", miniz.WRITE_ZLIB_HEADER)

3.2 miniz.COMPUTE_ADLER32

常量含义:压缩/解压参数,是否计算/校验adler-32
数据类型:number
常量取值:0x02000
示例代码:miniz.compress("123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw", miniz.COMPUTE_ADLER32)

3.3 miniz.GREEDY_PARSING_FLAG

常量含义:压缩参数,是否快速greedy处理, 默认使用较慢的处理模式;
数据类型:number
常量取值:0x04000
示例代码:miniz.compress("123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw", miniz.GREEDY_PARSING_FLAG)

3.4 miniz.NONDETERMINISTIC_PARSING_FLAG

常量含义:压缩参数,是否快速初始化压缩器;
数据类型:number
常量取值:0x04000
示例代码:miniz.compress("123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw", miniz.NONDETERMINISTIC_PARSING_FLAG)

3.5 miniz.RLE_MATCHES

常量含义:压缩参数, 仅扫描RLE
数据类型:number
常量取值:0x10000
示例代码:miniz.compress("123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw", miniz.RLE_MATCHES)

3.6 miniz.FILTER_MATCHES

常量含义:压缩参数,过滤少于5次的字符;
数据类型:number
常量取值:0x20000
示例代码:miniz.compress("123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw", miniz.FILTER_MATCHES)

3.7 miniz.FORCE_ALL_STATIC_BLOCKS

常量含义:压缩参数,是否禁用优化过的Huffman表
数据类型:number
常量取值:0x40000
示例代码:miniz.compress("123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw", miniz.FORCE_ALL_STATIC_BLOCKS)

3.8 miniz.FORCE_ALL_RAW_BLOCKS

常量含义:压缩参数,是否只要raw块
数据类型:number
常量取值:0x80000
示例代码:miniz.compress("123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw", miniz.FORCE_ALL_RAW_BLOCKS)

3.9 miniz.PARSE_ZLIB_HEADER

常量含义:解压参数,是否处理zlib头部,uncompress函数的默认值
数据类型:number
常量取值:1
示例代码:{
            local cdata = miniz.compress("123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw")
            local udata = miniz.uncompress(cdata, miniz.PARSE_ZLIB_HEADER)
         }

3.10 miniz.HAS_MORE_INPUT

常量含义:解压参数,是否还有更多数据,仅流式解压可用,暂不支持;
数据类型:number
常量取值:2
示例代码:暂无;

3.11 miniz.USING_NON_WRAPPING_OUTPUT_BUF

常量含义:解压参数,解压区间是否够全部数据,仅流式解压可用,暂不支持;
数据类型:number
常量取值:4
示例代码:暂无;

3.12 miniz.COMPUTE_ADLER32

常量含义:解压参数,是否强制校验adler-32
数据类型:number
常量取值:8
示例代码:{
            local cdata = miniz.compress("123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw", miniz.COMPUTE_ADLER32)
            local udata = miniz.uncompress(cdata, miniz.COMPUTE_ADLER32)
         }

四、函数详解

4.1 miniz.compress(data, flags)

快速压缩,需要165KB的系统内存和32KB的LuaVM内存

参数

data

参数含义:待压缩的数据;
数据类型:string
取值范围:压缩后的数据不能大于32KB
        {
            在使用 miniz 库进行压缩前,无法精确预测压缩后的大小(因为压缩率取决于数据的冗余度、重复模式等特性)
            可根据数据类型做经验性预估(近似判断):
            文本类数据(如 JSONXML、日志):压缩率通常在 30%~70%,可假设压缩后大小约为原大小的 50%。若原数据 > 64KB32KB / 50%),则压缩后可能超过 32KB
            二进制数据(如图片、已压缩文件):压缩率低(甚至可能膨胀),若原数据 > 30KB,压缩后可能超过 32KB
            空数据或极小数据:压缩后可能因 miniz 的头部信息(约 20 字节)略大于原大小,但肯定 <32KB
        }
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:"123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw"

flags

参数含义:压缩参数
数据类型:number
取值范围:参考第三章常量详解;
是否必选:非必须传入此参数,默认是 miniz.WRITE_ZLIB_HEADER , 即写入zlib头部
注意事项:暂无;
参数示例:miniz.WRITE_ZLIB_HEADER

返回值 local cdata = miniz.compress(data, flags)

cdata

含义说明:若压缩成功,返回数据字符串, 否则返回nil
数据类型:string或者nil
取值范围:小于等于32KB
注意事项:暂无;
返回示例:可能会含有不可见字符,通过cdata:toHex()格式打印;

示例

local bigdata = io.readFile("/luadb/test.txt") or 
"123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw 123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw"
local cdata = miniz.compress(bigdata)
if cdata then
    log.info("miniz", "before", #bigdata, "after", #cdata) 
    --before 129 after 102 (对应例子中129字节数据)
    --before 14525 after 9554 (从文件中读取14525字节数据)
    --before 30180 after 19832 (从文件中读取30180字节数据)
    log.info("miniz", "cdata as hex", cdata:toHex()) 
    --[[
    78010540D10D8430085DE556D0FAA1E934187D3E6A23564258FF32CDA5C1742CEC37DAFE881FDAC11869951027DE6D6459F941D4BC121A2617D1434EF
    5FC4D7369301D0BFB8DB63FE2877630465A25C489771B59567E1035AF8486C945F490533DFF817D31E5 204]]
end

4.2 miniz.uncompress(data, flags)

快速解压,需要32KB的LuaVM内存

参数

data

参数含义:待解压的数据
数据类型:string
取值范围:长度小于32KB的任意字符串数据
是否必选:必须传入此参数;
注意事项:暂无;
参数示例:{
            --可以先把数据压缩,然后解压压缩后的数据看是否相同;
            local bigdata = "123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw 123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw"
            local cdata = miniz.compress(bigdata)
            local udata = miniz.uncompress(cdata)
         }

flags

参数含义:解压缩参数
数据类型:number
取值范围:参考第三章常量详解;
是否必选:非必须传入此参数,默认是 miniz.PARSE_ZLIB_HEADER , 即写入zlib头部
注意事项:暂无;
参数示例:miniz.PARSE_ZLIB_HEADER

返回值 local udata = miniz.uncompress(data, flags)

udata

含义说明:若解压成功,返回数据字符串, 否则返回nil
数据类型:string或者nil
取值范围:无特别限制;
注意事项:暂无;
返回示例:"123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw"

示例

local bigdata = "123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw 123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw"
local cdata = miniz.compress(bigdata)
if cdata then
    log.info("miniz", "before", #bigdata, "after", #cdata) --before 129 after 102
    log.info("miniz", "cdata as hex", cdata:toHex()) 
    --[[
    78010540D10D8430085DE556D0FAA1E934187D3E6A23564258FF32CDA5C1742CEC37DAFE881FDAC11869951027DE6D6459F941D4BC121A2617D1434EF5FC4D7369301D0B
    FB8DB63FE2877630465A25C489771B59567E1035AF8486C945F490533DFF817D31E5 204]]

    local udata = miniz.uncompress(cdata)
    log.info("miniz", "udata", udata) 
    --123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw 123jfoiq4hlkfjbnasdilfhuqwo;hfashfp9qw38hrfaios;hfiuoaghfluaeisw
end

五、产品支持说明

支持LuatOS开发的所有产品都支持miniz核心库。