[√]ffi zlib

简介: [√]ffi zlib

FFI 库,是 LuaJIT 中最重要的一个扩展库。它允许从纯 Lua 代码调用外部 C 函数,使用 C 数据结构。

zlib压缩数据的demo

// encode
const char* src = "test zlib";
uLong srcLen = strlen(src) + 1;
uLong dstLen = 255;
Bytef dst[255] = { 0 };
int res = compress(dst, &dstLen, (const Bytef*)src, srcLen);
CCLOG("compress data addr: %p", dst); // zlib数据是以78 9c开头
// decode
uLong decodeLen = 255;
char decode[255] = { 0 };
res = uncompress((Bytef*)decode, &decodeLen, dst, dstLen);
CCLOG("decode");

但是我看到ffi-zlib使用的是

int inflate(z_stream*, int flush);
int inflateEnd(z_stream*);
int inflateInit2_(z_stream*, int windowBits, const char* version, int stream_size);
int deflate(z_stream*, int flush);
int deflateEnd(z_stream* );
int deflateInit2_(z_stream*, int level, int method, int windowBits, int memLevel,int strategy, const char *version, int stream_size);

deflate/inflate提供一套基于stream流的操作接口,需要使用者管理较多的状态,但可以实现过程中暂停/恢复操作;

compress/uncompress提供一套基于block块的操作接口,对deflate/inflate的封装,需要用户确保足够内存,一次调用完成操作.

uncompress将内存中数据进行解压,与compress函数一起使用,实现过程中会调用inflate函数,而且需要对inflate函数中的流参数进行初始化。

inflate函数是uncompress实现的一部分,提现的是deflate压缩思想,但是不能直接使用,需要很多参数的配置。

一些概念

  • deflate(RFC1951):一种压缩算法,使用LZ77和哈弗曼进行编码;
  • zlib(RFC1950):一种格式,是对deflate进行了简单的封装
  • gzip(RFC1952):一种格式,也是对deflate进行的封装。

Deflate算法

Deflate 是一种用于数据压缩的算法,它广泛应用于各种领域和技术中。

Deflate 算法基于哈夫曼编码和 LZ77 算法,通过消除数据中的冗余和重复信息来实现高效的压缩。

Deflate 算法的主要步骤包括:

  1. 压缩:原始数据被分为多个小块,并使用 LZ77 算法找到每个块中的重复片段。这些重复片段由一个指针和长度表示。然后,通过哈夫曼编码将指针和长度进行编码,以减少其表示所需的位数。最终,所有编码的块和额外的压缩信息被组合成一个压缩的数据流。
  2. 解压缩:解压缩过程与压缩过程相反。首先,被压缩的数据流被解码为压缩块和其他压缩信息。然后,使用哈夫曼解码器解码指针和长度,恢复出重复片段。最后,根据得到的重复片段,使用 LZ77 解压算法重新构建原始数据。

Deflate 算法在很多应用中都有广泛的应用,包括网页传输中的 HTTP 响应压缩、文件压缩格式(如 ZIP)、PNG 图像文件的压缩等。

local function createStream(bufsize)
    -- Setup Stream
    -- z_stream是C层定义的结构体
    -- ffi.new 是用于创建 C 数据类型实例的函数
    local stream = ffi_new("z_stream") 
    -- Create input buffer var
    -- ?是占位符,代表数组的大小,+1是为了留出末尾的空字符
    local inbuf = ffi_new('char[?]', bufsize+1)
    --     ↓待压缩的数据    ↓待压缩数据的长度
    stream.next_in, stream.avail_in = inbuf, 0
    -- create the output buffer
    local outbuf = ffi_new('char[?]', bufsize)
    --     ↓压缩后的数据      ↓压缩后的长度
    stream.next_out, stream.avail_out = outbuf, 0
    return stream, inbuf, outbuf
end
local function initDeflate(stream, options)
    -- Setup deflate process
    local method     = zlib.Z_DEFLATED
    local level      = options.level      or zlib.Z_DEFAULT_COMPRESSION
    local memLevel   = options.memLevel   or 8
    local strategy   = options.strategy   or zlib.Z_DEFAULT_STRATEGY
    local windowBits = options.windowBits or (15 + 16) -- +16 sets gzip wrapper not zlib
    local version    = ffi_str(zlib.zlibVersion())
    -- 加载zlib库
    local zlib = ffi.load(ffi.os == "Windows" and "zlib1" or "z")
    -- 如果成功则返回 `Z_OK` 
    -- 如果没有足够的内存,则返回 `Z_MEM_ERROR` 
    -- 如果参数无效(例如无效的方法),则返回 `Z_STREAM_ERROR` 
    -- 如果 zlib 库版本 ( `zlib_version` ) 与调用方假定的版本 ( `ZLIB_VERSION` ) 不兼容,则返回 `Z_VERSION_ERROR` 。
    return zlib.deflateInit2_(
        stream, -- 压缩上下文
        level, -- 压缩等级
        method, -- 压缩方法
        windowBits, -- 窗口比特数,此参数的值越大,压缩效果越好,但会牺牲内存使用量
        memLevel,  -- 压缩过程中对内存的限制
        strategy, -- 调整优化压缩算法的细节
        version, 
        ffi_sizeof(stream)
    )
end
local DEFAULT_CHUNK = 16384
function _M.deflateGzip(input, output, bufsize, options)
    local bufsize = bufsize or DEFAULT_CHUNK
    options = options or {}
    -- Takes 2 functions that provide plain input data and receives output data
    -- Returns gzip compressed string
    -- 创建数据流
    local stream, inbuf, outbuf = createStream(bufsize)
    -- 初始化deflate
    local init = initDeflate(stream, options)
    if init == Z_OK then
        -- 开始真正的压缩
        return deflate(input, output, bufsize, stream, inbuf, outbuf)
    else
        -- Init error 释放stream申请的内存
        zlib.deflateEnd(stream)
        return false, "INIT: "..zlib_err(init)
    end
end

之前的都是准备工作,真正的压缩逻辑在这里

local function deflate(input, output, bufsize, stream, inbuf, outbuf)
    local zlib_flate = zlib.deflate -- zlib的压缩函数
    local zlib_flateEnd = zlib.deflateEnd
    -- Deflate a stream
    local err = 0
    local mode = Z_NO_FLUSH
    repeat
        -- Read some input
        local data = input(bufsize)
        if data ~= nil then
            -- copy内存数据,将data复制到inbuf
            ffi_copy(inbuf, data)
            -- 更新stream的输入数据、源数据长度
            stream.next_in, stream.avail_in = inbuf, #data
        else
            -- EOF, try and finish up
            mode = Z_FINISH
            stream.avail_in = 0
        end
        -- While the output buffer is being filled completely just keep going
        repeat
            stream.next_out  = outbuf
            stream.avail_out = bufsize
            -- Process the stream
            -- mode:通常参数 `flush` 设置为 `Z_NO_FLUSH` ,决定在产生输出之前要积累多少数据,以最大化压缩。
            err = zlib_flate(stream, mode)
            -- Only possible *bad* return value here
            if err == Z_STREAM_ERR then
               -- Error, clean up and return
               zlib_flateEnd(stream)
               return false, "DEFLATE: "..zlib_err(err), stream
            end
            -- Write the data out:将数据输出到output
            local err = flushOutput(stream, bufsize, output, outbuf)
            if err then
               zlib_flateEnd(stream)
               return false, "DEFLATE: "..err
            end
        until stream.avail_out ~= 0
        -- In deflate mode all input must be used by this point
        if stream.avail_in ~= 0 then
            zlib_flateEnd(stream)
            return false, "DEFLATE: Input not used"
        end
    until err == Z_STREAM_END
    -- Stream finished, clean up and return
    zlib_flateEnd(stream)
    return true, zlib_err(err)
end
local function flushOutput(stream, bufsize, output, outbuf)
    -- Calculate available output bytes
    local out_sz = bufsize - stream.avail_out
    local ss = 0 
    local ss2 = stream.total_in
    local ss3 = stream.total_out
    if out_sz == 0 then
        return
    end
    -- Read bytes from output buffer and pass to output function
    -- output是外边传如的function,ffi_str 将 C 字符串或字节数组转换为 Lua 字符串
    local ok, err = output(ffi_str(outbuf, out_sz))
    if not ok then
        return err
    end
end

测试例子

data: 12345671234567
len: 20
1F 8B 08 00 00 00 00 00 00 0B 03 00 00 00 00 00 00 00 00 00

c++的结果是len=18

image.png

原因是在input的逻辑里面不要操作数据源。

遇到的新问题,上传到腾讯云后,再下载后无法解压,提示数据错误,估计是下载导致的

目录
相关文章
|
人工智能 安全 大数据
华为开源操作系统openEuler安装与体验
华为开源操作系统openEuler安装与体验
1791 0
华为开源操作系统openEuler安装与体验
|
4月前
|
机器学习/深度学习 编解码 Python
Python图片上采样工具 - RealESRGANer
Real-ESRGAN基于深度学习实现图像超分辨率放大,有效改善传统PIL缩放的模糊问题。支持多种模型版本,推荐使用魔搭社区提供的预训练模型,适用于将小图高质量放大至大图,放大倍率越低效果越佳。
327 3
|
6月前
|
机器学习/深度学习 达摩院 PyTorch
GitHub 1.3k 一款能“填色回忆”的神器:DDColor 让老照片鲜活又逼真
DDColor 是阿里达摩院推出的图像自动着色模型,采用双解码器架构与 Colorfulness Loss 技术,实现黑白图到高保真彩色图的智能转换。支持 GPU/CPU 推理,兼容历史照片、动画、游戏截图等多场景,具备高效、真实、多样、易用等特点,广泛适用于影像修复、艺术创作等领域。
679 24
|
项目管理
软件项目经理需要具备这 11 个能力
软件项目经理需要具备这 11 个能力
849 1
|
11月前
|
开发框架 缓存 搜索推荐
PiliPala:开源项目真香,B站用户狂喜!这个开源APP竟能自定义主题+去广告?PiliPala隐藏功能大揭秘
嗨,大家好,我是小华同学。PiliPala 是一个基于 Flutter 开发的 BiliBili 第三方客户端,提供流畅、个性化的使用体验。核心功能包括视频浏览与推荐、用户互动、丰富的播放设置、多维度搜索和个性化主题等。相比官方客户端,PiliPala 功能更丰富、性能更优、界面更美观。
582 14
|
Ubuntu Linux 开发工具
ubuntu linux搭建lvgl
ubuntu linux搭建lvgl
789 6
|
人工智能 自然语言处理 物联网
智能体进化发展了一年,现在的RPA Agent迭代到什么程度了?
智能体技术在过去一年迅速发展,RPA Agent已成为连接多种应用系统的关键工具。实在智能推出的实在Agent 7.0,通过自然语言处理和屏幕识别技术,实现了从需求输入到任务执行的全流程自动化,大幅降低了智能体构建门槛。该平台不仅能在企业级应用中提供专业服务,还能满足个人用户的多样化需求,真正实现了端到端的自动化解决方案。
445 6
智能体进化发展了一年,现在的RPA Agent迭代到什么程度了?
|
算法 安全 搜索推荐
AES(Advanced Encryption Standard)是一种广泛使用的对称密钥加密算法,由美国国家标准技术研究所(NIST)制定。
AES(Advanced Encryption Standard)是一种广泛使用的对称密钥加密算法,由美国国家标准技术研究所(NIST)制定。
|
存储 Ubuntu Linux
fd一个简单快速的find命令替代方案
目录 fd特点 如何在Linux中安装fd CentOS安装 命令选项 如何在Linux中使用fd
968 0