rust-brotli:Brotli compressor and decompressor written in rust that optionally avoids the stdlib

Brotli compressor and decompressor written in rust that optionally avoids the stdlib

分支60Tags71
文件最后提交记录最后更新时间
2 年前
6 个月前
7 年前
2 年前
7 年前
17 天前
3 年前
2 年前
7 年前
17 天前
1 年前
1 年前
1 个月前
1 个月前
2 年前

rust-brotli

crates.io Build Status

8.0.3 版本新特性

修复:避免在 Broccoli FFI 边界与 BroCatLi 交互时出现的 panic 问题 修复:CompressMulti 工作线程在出错时会正常退出

8.0.2 版本新特性

修复 ffi API 中的内存泄漏问题

8.0.1 版本新特性

提升 ffi 构建的兼容性

8.0.0 版本新特性

7.0.0 版本新特性

  • 修复短写入(short writes)错误
  • 允许部分 API 使用 quality=10,并将默认值设为 9.5

6.0.0 版本新特性

  • 移除未使用的 SIMD 相关 use 语句
    • 隐藏部分警告 - 这些是待办事项(TODOs),将在单独的 PR 中修复
    • 不在 MSRV 构建中包含 SIMD - 将 nightly 版本与 MSRV 结合没有实际意义

5.0.0 版本新特性

  • FFI 默认不再启用,以避免当多个依赖 crate 包含不同版本的 brotli 时可能出现的 ODR(One Definition Rule)问题。

4.0.0 版本新特性

固定依赖于 rust-brotli-decompressor,该版本可通过 ffi-api 标志禁用 ffi。 这有助于避免与其他 brotli 库发生符号冲突。

3.5 版本新特性

更新 SIMD 支持。 改进 CI 集成。 清理部分 clippy 警告。

3.4 版本新特性

当 Brotli 解压缩器的 reader 和 writer 处理末尾带有额外位的 brotli 流时,行为更加完善。 目前已对 stdsimd 等可选特性进行测试或暂时禁用。

3.2 版本新特性

  • Reader 和 Writer 类均支持 into_inner 转换

3.0 版本新特性

  • 完全兼容的 FFI,可直接替代 https://github.com/google/brotli 二进制文件
    • 完全支持自定义分配器
  • 多线程压缩,多个线程可协同处理单个文件
  • 可连接模式,以支持 https://github.com/google/brotli/issues/628 中提出的功能需求
    • 如果第一个文件使用 -apendable 参数,第二个文件使用 -catable 参数,则可通过二进制工具 catbrotli 实现此功能
  • 验证模式,可对文件进行双重检查,确保其能使用相同设置进行解压缩;此模式对基准测试或模糊测试非常有用
  • 幻数(Magic Number):brotli 文件可包含一个有用的头部,其中包含几个幻数字节、可连接性信息以及用于预分配内存的最终输出大小

2.5 版本新特性

  • 在 2.5 版本中,回调函数还会向下传递一个分配器,用于创建新的 StaticCommands、PDF 以及 256 位浮点向量。
  • 在 2.4 版本中,带有压缩中间表示的回调函数现在每次传递一个完整的元块。此外,这些项是可变的,以便进行进一步优化(如果需要)

2.3 版本新特性

  • 现在,Flush 会生成输出,而不是调用流上的 finish。这使您能够使用写入器抽象来获取即时输出,而不必借助 CompressStream 内部抽象

项目要求

将 C 语言版 brotli 压缩器直接移植到 Rust,且不依赖标准库

不依赖 Rust 标准库:该库非常适合在 Rust 内核等环境中进行解压缩。

这有助于在采用相同算法、数据结构和优化的情况下,对 C 和 Rust 进行直接比较。

压缩使用方法

Rust brotli 当前支持 0 - 11 级压缩 在 0-9 级压缩级别下,它们应与 brotli C 压缩引擎的结果位对位相同 推荐的 lg_window_size 介于 20 和 22 之间

使用 io::Read 抽象

let mut input = brotli::CompressorReader::new(&mut io::stdin(), 4096 /* buffer size */,
                                              quality as u32, lg_window_size as u32);

那么你可以像读取任何其他 io::Read 类一样简单地读取输入

使用 io::Write 抽象

let mut writer = brotli::Compressor::new(&mut io::stdout(), 4096 /* buffer size */,
                                         quality as u32, lg_window_size as u32);

此外,还提供了使用 with_params 静态函数构建压缩器读取器或写入器的方法

例如:

let params = BrotliEncoderParams::default();
// modify params to fit the application needs
let mut writer = brotli::Compressor::with_params(&mut io::stdout(), 4096 /* buffer size */,
                                         params);

或供读者参考

let params = BrotliEncoderParams::default();
// modify params to fit the application needs
let mut writer = brotli::CompressorReader::with_params(&mut io::stdin(), 4096 /* buffer size */,
                                                       params);

使用流复制抽象

match brotli::BrotliCompress(&mut io::stdin(), &mut io::stdout(), &brotli_encoder_params) {
    Ok(_) => {},
    Err(e) => panic!("Error {:?}", e),
}

解压缩使用方法

使用 io::Read 抽象

let mut input = brotli::Decompressor::new(&mut io::stdin(), 4096 /* buffer size */);

那么你可以像读取任何其他 io::Read 类一样简单地读取输入

使用 io::Write 抽象

let mut writer = brotli::DecompressorWriter::new(&mut io::stdout(), 4096 /* buffer size */);

使用流复制抽象

match brotli::BrotliDecompress(&mut io::stdin(), &mut io::stdout()) {
    Ok(_) => {},
    Err(e) => panic!("Error {:?}", e),
}

手动内存管理

在不使用标准库的情况下使用 brotli 分为 3 个步骤

  1. 设置内存管理器
  2. 设置 BrotliState
  3. 在循环中调用 BrotliDecompressStream

详细说明

// at global scope declare a MemPool type -- in this case we'll choose the heap to
// avoid unsafe code, and avoid restrictions of the stack size

declare_stack_allocator_struct!(MemPool, heap);

// at local scope, make a heap allocated buffers to hold uint8's uint32's and huffman codes
let mut u8_buffer = define_allocator_memory_pool!(4096, u8, [0; 32 * 1024 * 1024], heap);
let mut u32_buffer = define_allocator_memory_pool!(4096, u32, [0; 1024 * 1024], heap);
let mut hc_buffer = define_allocator_memory_pool!(4096, HuffmanCode, [0; 4 * 1024 * 1024], heap);
let heap_u8_allocator = HeapPrealloc::<u8>::new_allocator(4096, &mut u8_buffer, bzero);
let heap_u32_allocator = HeapPrealloc::<u32>::new_allocator(4096, &mut u32_buffer, bzero);
let heap_hc_allocator = HeapPrealloc::<HuffmanCode>::new_allocator(4096, &mut hc_buffer, bzero);

// At this point no more syscalls are going to be needed since everything can come from the allocators.

// Feel free to activate SECCOMP jailing or other mechanisms to secure your application if you wish.

// Now it's possible to setup the decompressor state
let mut brotli_state = BrotliState::new(heap_u8_allocator, heap_u32_allocator, heap_hc_allocator);

// at this point the decompressor simply needs an input and output buffer and the ability to track
// the available data left in each buffer
loop {
    result = BrotliDecompressStream(&mut available_in, &mut input_offset, &input.slice(),
                                    &mut available_out, &mut output_offset, &mut output.slice_mut(),
                                    &mut written, &mut brotli_state);

    // just end the decompression if result is BrotliResult::ResultSuccess or BrotliResult::ResultFailure
}

此接口与 C 语言 brotli 解压缩器所使用的接口完全相同。

您也可以随意使用直接调用 Box 的自定义分配器。此示例展示了一种在初始分配后避免后续系统调用的机制。

使用 C 接口

rust-brotli 是官方 https://github.com/google/brotli C 实现的即插即用替代品。这意味着您可以在任何支持该库的地方使用它。要以这种方式构建 rust-brotli,请进入 c 子目录并在那里运行 make:

cd c && make

这将构建 c/target/release/libbrotli.so,并构建用于压缩和解压缩任何 brotli 文件的纯 C 命令行工具。

c/target/release 中的 libbrotli.so 应该能够替换任何其他 libbrotli.so 文件,同时具备使用安全 Rust 带来的所有优势(FFI 绑定部分除外)。

该代码还支持更广泛的选项,包括强制预测模式(例如 UTF8、有符号、MSB 或 LSB)以及将文字成本权重从 540 更改为其他值。

流拼接

Brotli 支持创建可拼接的流,这在需要独立压缩块但作为单个流解压缩的流式场景中非常有用。

简单拼接(快速)

对第一个文件使用 -bare -appendable,对后续文件使用 -bare -catable。这些文件可以通过简单的字节拼接(无需特殊工具)进行组合,并在末尾添加一个结束字节(0x03):

# Create the base file with header but no trailer (must specify window size)
brotli -c -bare -appendable -w22 input1.txt > base.br

# Create bare-catable streams (no header, no trailer, same window size!)
brotli -c -bare -catable -w22 input2.txt > part2.br
brotli -c -bare -catable -w22 input3.txt > part3.br

# Simple concatenation with finalization byte
# Note: printf '\x03' adds the required final byte
(cat base.br part2.br part3.br; printf '\x03') > combined.br

# Decompress normally
brotli -d combined.br -o output.txt

优势:

  • 即时拼接(无需处理)
  • 无需特殊工具
  • 裸流可按任意顺序追加

要求:

  • 所有文件必须使用相同的窗口大小(推荐 -w22
  • 第一个文件:-bare -appendable(包含头部,无尾部)
  • 后续文件:-bare -catable(无头部、无尾部、无字典引用)
  • 必须追加一个最终的 0x03 字节以完成流

高效拼接(大小优化)

使用带有 -catable-appendable 标志的 catbrotli 工具可获得更好的压缩效果,但会花费一定的处理时间:

# Create files for catbrotli tool
brotli -c -appendable input1.txt > appendable.br
brotli -c -catable input2.txt > catable1.br
brotli -c -catable input3.txt > catable2.br

# Concatenate using catbrotli tool
catbrotli appendable.br catable1.br catable2.br > combined.br

权衡取舍: catbrotli 生成的输出更小,但需要 CPU 时间来智能处理流。当大小比拼接速度更重要时,请使用此工具。

技术参考:流参数交互

流类型及其参数:

流类型 bare_stream byte_align appendable catable use_dictionary 描述
标准 false false false false true 带有头部和尾部的普通 brotli 流
首个(简单拼接) true true true false true 有头部,无尾部 - 用于简单的 cat 拼接
后续(简单拼接) true true true true false 无头部,无尾部,无字典引用 - 追加到首个流
可追加(catbrotli) false varies true false true catbrotli 工具使用
可拼接(catbrotli) false varies true true false catbrotli 工具使用

重要说明:

  • 参数依赖关系由库在 CLI 和 API 使用中自动应用
  • 库的 SanitizeParams 函数确保:
    • catable = true → 自动设置 appendable = trueuse_dictionary = false
    • bare_stream = true → 自动设置 byte_align = true
    • !appendable → 自动设置 byte_align = false
  • 使用 set_parameter() 时,依赖关系会立即应用
  • 直接设置字段时(例如 params.catable = true),依赖关系会在压缩初始化期间应用
  • 无需手动修正 - 库会处理所有参数依赖关系
  • 可拼接流的 use_dictionary = false 可防止引用块边界之前的字节
  • 简单拼接需要一个最终的 0x03 字节来完成流
  • 所有拼接的流必须使用相同的窗口大小

API 使用示例:

// First file: -bare -appendable equivalent
params.bare_stream = true;    // Sets bare_stream=true, byte_align=true (automatic)
params.appendable = true;     // Sets appendable=true

// Subsequent files: -bare -catable equivalent
params.bare_stream = true;    // Sets bare_stream=true, byte_align=true (automatic)
params.catable = true;        // Sets catable=true, appendable=true, use_dictionary=false (automatic)

// All parameter dependencies are handled automatically by the library.
// No manual fixups required - just set the primary flags you want.