2.3.1 |
|
---|---|
2.3.0 |
|
2.0.0 |
|
1.1.2 |
|
0.1.1 |
|
#7 in #boxing
22 downloads per month
Used in stowaway-derive
40KB
448 lines
stowaway
Due to ongoing soundness issues, this crate is considered permanently unsound until future notice. Do not use it in your projects.
Stowaway is a library for packing data into a pointer, if it fits, boxing it otherwise
Note about testing
The doctests for this library include non-default features, make sure to use --all-features
when testing
lib.rs
:
Stowaway is a library for efficiently storing values in a pointer. The
main use case for stowaway is as a helpful way to interoperate with
libraries that require opaque data to be passed via pointer, such as C
libraries expecting a void*
, std::RawWaker
, and boost::context
.
The central feature of this library is value packing in a Stowaway
struct. If T
is not larger than a pointer (that is, if
sizeof(T) <= sizeof(*T))
, then the value is packed directly into the
bytes of an opaque pointer value (specifically, a *mut ()
). This
value can then be passed as the context pointer for C-like interfaces,
and then converted back into a Stowaway
on the other end.
Lifecycle
The basic lifecycle of a Stowaway
value is as follows:
- Create a
Stowaway
withStowaway::new
. This will pack the value into the space of a pointer, or box it if it's too big. - Convert that value into a pointer with
Stowaway::into_raw
. - Store that value somewhere, such as in a
RawWaker
, or as avoid*
in a C API. Recover the value somewhere else. - Convert the pointer back into a
Stowaway
withStowaway::from_raw
. This takes back ownership of the value, so make sure to only do it once, and discard the pointer afterwards. - Covert the
Stowaway
back into aT
, or use it as a container withDeref
/AsRef
/DerefMut
/AsMut
.
The value must have the Stowable
marker trait; this trait informs
the type system that the type does not contain any uninitialized bytes
which might be packed into the pointer value (or, if it does, to box it
unconditionally).
Simple example:
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-like API Example
In this example, we create a fake "C like" API. We will store in this API a series of function pointers and context data pointers, which will then be called all at once.
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"
);
}
Dependencies
~235KB