2 个版本
0.1.1 | 2020年11月26日 |
---|---|
0.1.0 | 2020年11月26日 |
#1596 在 解析器实现
每月 787 次下载
用于 11 个 包(4 个直接使用)
53KB
982 行
printf-compat
printf
在 Rust 中重实现
这是使用不稳定(即 需要 Nightly 编译器)的 c_variadic
功能在 Rust 中对 printf
的完整重实现。
- 许多 C 库 提供了一种提供自定义日志回调的方式。使用这个包,你可以提供一个纯 Rust 选项,并对其进行任何操作。将其记录到控制台,存储在字符串中,或进行其他任何操作。
- 如果你正在编写针对微控制器的 Rust 首选程序并且需要与 C 库进行接口,你可能没有 libc 并且需要自己重新实现它。如果它使用
printf
,则可以使用此包轻松添加自己的输出。core::fmt
太大?没问题!编写自己的格式化代码,或使用类似ufmt
或defmt
的最小化格式化库。不需要printf
格式字符串提供的每个选项?没问题!只需不实现它即可。 - 同样,如果你使用
wasm32-unknown-unknown
而不是 emscripten(因为 wasm-bindgen 只与前者兼容),你就没有 libc。如果你想与 C 库进行接口,你必须自己完成所有操作。使用这个包,这变成printf
的数百行代码中的 5 行。
优点
⚒ 模块化
printf-compat 允许您选择如何输出消息。使用预先编写的适配器来使用 fmt::Write
(如 String
)或 io::Write
(如 io::stdout()
),或者自行实现。
🔬 小型
此包与 no_std
兼容(在您的 Cargo.toml 中使用 printf-compat = { version = "0.1", default-features = false }
)。主机械不需要使用 core::fmt
,并且它不会 panic。
🔒 安全(尽可能)
当然,printf
由于需要使用 va_list
,所以是完全不安全的。然而,除此之外,所有的实际字符串解析都是完全安全的 Rust 编写。没有缓冲区溢出攻击!
将写入用户提供的指针的 n
格式说明符,如果用户提供的字符串被传递给 printf
,则被认为是一个严重的安全漏洞。该包支持它;然而,默认情况下它不执行任何操作,您必须显式进行写入。
🧹 测试过
使用广泛的 测试套件 确保许多不同的可能性与 glibc 的 printf
相同。差异已记录在案。
入门
首先添加不稳定功能
#![feature(c_variadic)]
现在,添加您的函数签名
use cty::{c_char, c_int};
#[no_mangle]
unsafe extern "C" fn c_library_print(str: *const c_char, mut args: ...) -> c_int {
todo!()
}
如果您可以访问 std
,即不是嵌入式平台,您可以使用 std::os::raw
而不是 cty
。同时,考虑您正在做什么
- 如果您实现
printf
是因为您没有,您将想调用它printf
并添加#[no_mangle]
。 - 同样,如果您为 C 库创建自定义日志函数并且它期望调用全局定义的函数,请保留
#[no_mangle]
并将函数重命名为它期望的名称。 - 另一方面,如果您的 C 库期望您调用一个函数来注册回调(示例 1,示例 2),请删除
#[no_mangle]
。
现在,添加您的逻辑
use printf_compat::{format, output};
let mut s = String::new();
let bytes_written = format(str, args.as_va_list(), output::fmt_write(&mut s));
println!("{}", s);
bytes_written
当然,你可以用你喜欢的任何东西替换 output::fmt_write
——一些已经在 output
中为你准备好了。如果你想自己写,请遵循它们的函数签名:你需要提供一个函数给 format()
,这个函数接受一个 Argument
并返回写入的字节数(尽管如果你的 C 库不需要它,你不需要这么做)或者在出错时返回 -1。
许可证:MIT 或 Apache-2.0
依赖项
~1MB
~17K SLoC