#pointers #data #packing #fits #boxing #value #issue

yanked stowaway

一个将数据打包到指针中的库,如果合适,否则将其装箱

2.3.1 2020年9月26日
2.3.0 2020年8月23日
2.0.0 2020年4月18日
1.1.2 2020年2月11日
0.1.1 2020年1月19日

#9 in #boxing


stowaway-derive 中使用

MPL-2.0 许可证

40KB
448

stowaway

由于持续存在的安全性问题,此包被认为是永久不安全的,直到未来通知。请勿在项目中使用它。

Stowaway 是一个将数据打包到指针中的库,如果合适,否则将其装箱

关于测试的说明

此库的doctests包括非默认功能,确保在测试时使用 --all-features


lib.rs:

Stowaway 是一个高效存储值的库。Stowaway 的主要用例是作为一个与需要通过指针传递不可见数据的库交互的有用方式,例如期望 void*std::RawWakerboost::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