2 个版本

0.1.1 2020年11月26日
0.1.0 2020年11月26日

#1596解析器实现

Download history 141/week @ 2024-03-15 201/week @ 2024-03-22 233/week @ 2024-03-29 142/week @ 2024-04-05 138/week @ 2024-04-12 335/week @ 2024-04-19 337/week @ 2024-04-26 195/week @ 2024-05-03 297/week @ 2024-05-10 381/week @ 2024-05-17 167/week @ 2024-05-24 325/week @ 2024-05-31 202/week @ 2024-06-07 153/week @ 2024-06-14 252/week @ 2024-06-21 104/week @ 2024-06-28

每月 787 次下载
用于 11 包(4 个直接使用)

MIT/Apache

53KB
982

printf-compat

Crates.io Docs.rs

printf 在 Rust 中重实现

这是使用不稳定(即 需要 Nightly 编译器)的 c_variadic 功能在 Rust 中对 printf 的完整重实现。

  • 许多 C 提供了一种提供自定义日志回调的方式。使用这个包,你可以提供一个纯 Rust 选项,并对其进行任何操作。将其记录到控制台,存储在字符串中,或进行其他任何操作。
  • 如果你正在编写针对微控制器的 Rust 首选程序并且需要与 C 库进行接口,你可能没有 libc 并且需要自己重新实现它。如果它使用 printf,则可以使用此包轻松添加自己的输出。 core::fmt 太大?没问题!编写自己的格式化代码,或使用类似 ufmtdefmt 的最小化格式化库。不需要 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