2.3.1 |
|
---|---|
2.3.0 |
|
2.0.0 |
|
1.1.2 |
|
0.1.1 |
|
#9 in #boxing
在 stowaway-derive 中使用
40KB
448 行
stowaway
由于持续存在的安全性问题,此包被认为是永久不安全的,直到未来通知。请勿在项目中使用它。
Stowaway 是一个将数据打包到指针中的库,如果合适,否则将其装箱
关于测试的说明
此库的doctests包括非默认功能,确保在测试时使用 --all-features
lib.rs
:
Stowaway 是一个高效存储值的库。Stowaway 的主要用例是作为一个与需要通过指针传递不可见数据的库交互的有用方式,例如期望 void*
、std::RawWaker
和 boost::context
的 C 库。
此库的核心功能是在 Stowaway
结构中打包值。如果 T
不大于指针(即,如果 sizeof(T) <= sizeof(*T))
),则将值直接打包到不可见指针值的字节中(具体来说,是一个 *mut ()
)。然后可以将此值作为类似 C 的接口的上下文指针传递,然后在另一端将其转换回 Stowaway
。
生命周期
Stowaway
值的基本生命周期如下
- 使用
Stowaway::new
创建Stowaway
。这将把值打包到指针的空间中,或者如果太大则装箱。 - 使用
Stowaway::into_raw
将该值转换为指针。 - 将该值存储在某个地方,例如在
RawWaker
中,或者在 C API 中作为void*
。在其他地方恢复该值。 - 使用
Stowaway::from_raw
将指针转换回Stowaway
。这会重新获得值的所有权,因此请确保只执行一次,并在之后丢弃指针。 - 将
Stowaway
转换回T
,或使用Deref
/AsRef
/DerefMut
/AsMut
作为容器。
该值必须具有 Stowable
标记特质;这个特质通知类型系统该类型不包含任何可能被打包到指针值中的未初始化字节(如果有的话,则无条件将其boxed)。
简单示例
use stowaway::{Stowaway, Stowable};
fn demo_lifecycle<T: Clone + std::fmt::Debug + Eq + Stowable>(value: T) {
let cloned = value.clone();
let stowed: Stowaway<T> = Stowaway::new(value);
let storage = Stowaway::into_raw(stowed);
// At this point, the storage pointer would be passed into a C API,
// and recovered somewhere else
let new_stowed: Stowaway<T> = unsafe { Stowaway::from_raw(storage) };
let unstowed: T = Stowaway::into_inner(new_stowed);
assert_eq!(unstowed, cloned);
}
// A small value, like a u16, is stored directly in the pointer. No
// allocations are performed in this example.
demo_lifecycle(137u16);
// A large value, like `Vec` (which internally is a data pointer and a
// pair of usize) cannot fit in a pointer, and will therefore be boxed
// when stored in `Stowaway`
demo_lifecycle(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
C语言API示例
在这个示例中,我们创建了一个假想的“类似C”的API。我们将在这个API中存储一系列函数指针和上下文数据指针,然后一次性调用它们。
use stowaway::Stowaway;
// Fake stdout
static mut stdout: String = String::new();
#[derive(Default)]
struct EventList {
events: Vec<(fn(*mut ()), *mut())>
}
impl EventList {
// our fake C API: add a function pointer and a *mut () to a list
// of callbacks to run.
fn add_event(&mut self, fptr: fn(*mut ()), ctx: *mut()) {
self.events.push((fptr, ctx));
}
// For each function pointer added with add_event, call the function
// with the context pointer.
fn run_all_events(self) {
self.events.into_iter().for_each(|(fptr, ctx)| {
fptr(ctx);
})
}
}
let mut event_list = EventList::default();
// Add some events to the list
// Event 1: print a simple number. u16 should easily fit in a pointer,
// so this won't allocate anything
fn print_u16(value: *mut()) {
unsafe {
let value: Stowaway<u16> = unsafe { Stowaway::from_raw(value) };
writeln!(&mut stdout, "A number: {}", *value).unwrap();
}
}
event_list.add_event(print_u16, Stowaway::into_raw(Stowaway::new(10u16)));
event_list.add_event(print_u16, Stowaway::into_raw(Stowaway::new(20u16)));
// Event 2: Print a large array (`[u64; 8]`). This won't definitely won't fit
// in a pointer, so stowaway will automatically allocate it for us
fn print_big_array(value: *mut()) {
unsafe {
let value: Stowaway<[u64; 8]> = unsafe { Stowaway::from_raw(value) };
writeln!(&mut stdout, "8 large numbers: {:?}", *value).unwrap();
}
}
let data: [u64; 8] = [1, 1, 2, 3, 5, 8, 13, 21];
event_list.add_event(print_big_array, Stowaway::into_raw(Stowaway::new(data)));
let data: [u64; 8] = [34, 55, 89, 144, 233, 377, 610, 987];
event_list.add_event(print_big_array, Stowaway::into_raw(Stowaway::new(data)));
// Execute all the events
event_list.run_all_events();
unsafe {
assert_eq!(stdout,
"A number: 10\n\
A number: 20\n\
8 large numbers: [1, 1, 2, 3, 5, 8, 13, 21]\n\
8 large numbers: [34, 55, 89, 144, 233, 377, 610, 987]\n"
);
}
依赖项
~220KB