#c2rust #libopus #transpiled #shape #unsafe #linker

bin+lib unopus

libopus 通过 c2rust 转译为 Rust

1 个不稳定版本

0.0.1 2024 年 6 月 14 日

#6 in #libopus

BSD-3-Clause

1.5MB
48K SLoC

unsafe-libopus

本库是使用 c2rust 将 C 语言编写的 libopus 1.3.1 转译为 unsafe Rust 的。

它被称为 "unsafe" libopus,因为它在很大程度上保持了与 C 代码相同的结构,并且充满了 "unsafe"。

这种转译仍然允许您摆脱 C 编译器工具链,并在纯 Rust 环境中使用该库,无需链接器黑客攻击和动态链接问题。

它也可能是 libopus 更 Rust 风格实现的起点,尽管我不确定这是否值得努力。

使用方法

您可以通过使用来自 此 PRopus crate 的分支来利用这个库。

[dependencies]
opus = { git = "https://github.com/DCNick3/opus-rs.git", branch = "unsafe-libopus", default-features = false, features = ["unsafe-libopus-backend"] }

也许,这个库将来会有安全的 API,但现在,它是一个(主要是)可以直接替换 audiopus_sys crate 的替代品。

转译技术

首先,使用以下命令编译了 libopus 1.3.1

CC=clang ./configure --disable-shared --disable-stack-protector --enable-extra-programs --disable-doc --disable-asm --disable-rtcd --disable-intrinsics --disable-dependency-tracking--disable-maintainer-mode --enable-hardening
CC=clang compiledb make -j

然后,使用生成的 compile_commands.json 文件,使用以下方式将 C 代码转译为 Rust:

c2rust transpile compile_commands.json -o . --overwrite-existing --reorganize-definitions --emit-modules --translate-const-macros --emit-build-files

然后将生成的代码手动重新组织,以删除所有结构重复并消除对 #[no_mangle] extern "C" 函数的使用:它们现在都链接到 Rust,无需导出。

其他重构包括

  • 将测试二进制文件与库分开,作为 "examples"
  • 通过在库中定义它们的变体来删除库中对 libc 函数的依赖(来自 unsafe-libyaml 的想法)
  • 通过用具体的Rust类型替换libc类型(例如 libc::c_int -> i32 等)来移除对libc crate的依赖。
  • 通过用自定义的VarArgs结构体替换C的varargs,因为Rust中的C变长参数还不稳定。这使得 opus_*_ctl 系列函数变成了宏。

性能

这个库在翻译时没有使用内联汇编、处理器内嵌函数和运行时CPU检测,所以它现在的速度不如原始代码快。具有这些功能的C版本在我的机器上比Rust版本快约20%。

正确性与安全性

这个库使用(大部分)C代码库中的原始测试进行测试。它们以Rust集成测试的形式存在于 tests 目录中。

opus的测试向量也用于在CI中测试库的解码器(见 src/bin/run_vectors.rs)。这几乎确保了解码器的正确性。这实际上帮助发现了 c2rust中的一个微妙错误

编码器的正确性是一个更加开放的问题,因为据我所知,还没有针对它的测试向量。在 tests/opus_encode 中进行了一些测试,但相当有限。

至于安全性,没有任何保证。代码是C代码的直接转换结果,因此几乎全部是unsafe代码(在Rust术语中)。

它似乎也根据miri有UB,但这些可能是假阳性。一个好的重构目标可能是使库通过miri检查。

依赖项

~0.2–1.2MB
~23K SLoC