[√]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的逻辑里面不要操作数据源。

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

目录
相关文章
|
Windows
zlib、libzip、 libzippp 库编译(windows + cmake + vs2013)
"libzipp" 这库是基于 "libzip" 之上封装的,而 "libzip" 又是基于 "zlib"库封装的,所以要编译 "libzipp" 库就要先编译其他两个库。下载准备:"zlib-1.2.
3659 0
|
3月前
|
编译器 Linux C语言
CMake指定交叉编译指南:从编译器设置到验证 (CMake Cross-compilation Guide: From Compiler Setup to Verification)
CMake指定交叉编译指南:从编译器设置到验证 (CMake Cross-compilation Guide: From Compiler Setup to Verification)
980 1
|
3月前
|
开发工具 git
vs2017下gstreamer编译
vs2017下gstreamer编译
|
9月前
|
Linux
linux运行protoc出现错误 while loading shared libraries: libprotoc.so.19
linux运行protoc出现错误 while loading shared libraries: libprotoc.so.19
105 0
|
9月前
|
编译器
[√]cmake 编译选项
[√]cmake 编译选项
53 0
|
9月前
|
编译器 Android开发 iOS开发
[√]关于cmake的kits
[√]关于cmake的kits
94 0
|
9月前
|
Shell C++ Windows
[√]尝试使用cmake编译使用vld
[√]尝试使用cmake编译使用vld
86 0
|
11月前
|
开发工具 git
编译Gstreamer遇到的问题 autopoint: not found
编译Gstreamer遇到的问题 autopoint: not found
115 0
|
机器人 Linux 编译器
替代notepad++,notepad--介绍及插件cmake编译
替代notepad++,notepad--介绍及插件cmake编译
msys2编译ffmpeg:ERROR: libx264 not found
msys2编译ffmpeg:ERROR: libx264 not found
114 0