20 个版本
0.5.4 | 2023年10月7日 |
---|---|
0.5.0 | 2023年3月17日 |
0.3.4 | 2022年12月17日 |
0.3.3 | 2022年10月16日 |
0.1.2 | 2015年7月24日 |
#61 在 FFI 中
每月 56 次下载
72KB
1.5K SLoC
Sentinel
sentinel
是一个哨兵终止的切片库。
工作原理
Rust 切片
在 Rust 中,切片类型 &[T]
的定义基本上是这样的:(*const T, usize)
。这里的 usize 指示了在 *const T 中引用的 T 的数量。预先知道数组的尺寸具有许多优势,这里不再赘述。
然而,&[T]
类型存在两个主要问题
-
它不是(至少还不是)FFI 安全的。无法创建一个
extern "C" fn(s: &[u32])
函数,并期望从 C 代码中调用它时能够工作。 -
&[T]
的大小是两个 usize 的大小。
哨兵是什么?
哨兵是一个特殊值,用于确定数组的结束。例如,在 C 中,char * 类型可以是“以 null 结尾”的字符串的指针。这是一个哨兵终止的切片的例子。
CString:
char *ptr
|
'H' 'e' 'l' 'l' 'o' '\0'
^ sentinel, anything after this point may be invalid.
str:
*const u8, 5
|
'H' 'e' 'l' 'l' 'o'
^ no sentinel, we know the slice contains 5 elements.
这个 crate 在哨兵的定义上保持泛型。它使用 Sentinel
trait,它大致定义如下
trait Sentinel<T> {
fn is_sentinel(val: &T) -> bool;
}
它用于确定特定的 T 实例是否应被视为“哨兵”值。
SSlice
最后,与Sentinel
特质结合,这个包定义了SSlice<T>
类型。它是泛型类型T
的,存储元素的类型,具有很高的灵活性。
struct SSlice<T> {
_marker: PhantomData<T>,
}
请注意,这个类型实际上不包含任何数据。只能创建对这个类型的引用(即&SSlice<T>
或&mut SSlice<T>
),并且这些引用的大小是一个usize
。
FFI
SSlice<T>
类型是FFI安全的,这意味着你现在可以写出这个
// type CStr = sentinel::SSlice<u8>;
extern "C" {
/// # Safety
///
/// This will be `unsafe` because of `extern "C"`. But calling libc's `puts` with this
/// signature is always sound!
fn puts(s: &sentinel::CStr);
}
或者这个!
extern crate libc;
use sentinel::{cstr, CStr, SSlice};
fn print(s: &CStr) {
// SAFETY:
// `CStr` ensures that the string is null-terminated.
unsafe { libc::puts(s.as_ptr() as _) };
}
#[no_mangle]
extern "C" fn main(_ac: libc::c_int, argv: &SSlice<Option<&CStr>>) -> libc::c_int {
print(cstr!("Arguments:"));
for arg in argv.iter().unwrap_sentinels() {
print(arg);
}
0
}
功能
-
alloc
- 添加了对alloc
包的支持。这添加了SBox<T>
类型。 -
nightly
- 利用不稳定的extern_type
功能,确保不能在堆栈上创建SSlice<T>
的实例,使其成为!Sized
。此功能还启用了对新allocator_api
不稳定功能的支持。 -
libc
- 使用libc的strlen
和memchr
在哨兵终止的切片中查找空字符。 -
memchr
- 使用memchr
包在哨兵终止的切片中查找空字符。
alloc
和memchr
默认启用。
旧的sentinel
包
“sentinel”这个名字是由这个项目的先前维护者友好地赐予我的。
所有0.2版本之前的版本(在crates.io上)都包含该包的源代码。
依赖项
~110-260KB