[][src]Crate stowaway

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 with Stowaway::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 a void* in a C API. Recover the value somewhere else.
  • Convert the pointer back into a Stowaway with Stowaway::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 a T, or use it as a container with Deref / AsRef / DerefMut / AsMut.

Simple example:

use stowaway::Stowaway;

fn demo_lifecycle<T: Clone + std::fmt::Debug + Eq>(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"
    );
}

Structs

Stowaway

A maybe-allocated container. This struct stores a single T value, either by boxing it or (if T is small enough) by packing it directly into the bytes of a raw pointer.

Functions

mut_ref_from_stowed

Get a mutable reference to a value that was previously stowed, either with stow or with Stowaway::into_raw. This function does not take ownership of the value in storage, but it does create a mutable reference to it, so you must take care to not separately take ownership of it somewhere else, or create any other shared or mutable references to it.

ref_from_stowed

Get a reference to a value that was previously stowed, either with stow or with Stowaway::into_raw. This function does not take ownership of the value in storage, but it does create a shared reference to it, so you must take care to not separately take ownership of it somewhere else, or create a mutable reference to it. It is safe to create multiple shared references with the same storage, though take care to respect Sync in threaded applications.

stow

Stow a value into a *mut (). This function will store the value's bytes directly into the pointer if it will fit; otherwise it will box the value and return the raw pointer. The value can be unstowed with a call to unstow, or converted into a Stowaway with Stowaway::from_raw

unstow

Restore a value that was previously stowed, either with stow or with Stowaway::into_raw. The storage pointer must be discarded after the call to this function, as this function takes back ownership of the inner T value.