#bindgen #c #api-bindings #fam #flexarray

无 std fambox

FamBox 数据结构,用于以舒适和安全的方式使用 C 的灵活数组成员

4 个版本

0.2.0 2024 年 6 月 2 日
0.1.2 2024 年 5 月 16 日
0.1.1 2024 年 5 月 15 日
0.1.0 2024 年 5 月 15 日

#81FFI

每月下载 29

MIT/Apache

62KB
1K SLoC

FamBox 数据结构,用于启用对具有 灵活数组成员 的 C 结构的舒适和安全使用。

FamBox 有几种口味

假设你有一个这个 C 结构

struct protocol_msg {
    uint8_t version;     /* Protocol version */
    uint8_t ty;          /* Protocol message type */
    uint16_t length;     /* Length in bytes including the flexible array member. */
    uint8_t elements[0]; /* msg data */
};

bindgen 将将其翻译为

#[repr(C)]
pub struct protocol_msg {
    #[doc = " Protocol version"]
    version: u8,
    #[doc = " Protocol message type"]
    ty: u8,
    #[doc = " Length in bytes including the flexible array member."]
    length: u16,
    #[doc = " msg data"]
    elements: __IncompleteArrayField<u8>,
}

可以使用 FamBoxOwned 来安全地构造此类的新实例。

use core::mem::size_of;
use fambox::FamBoxOwned;
// To be used safely, [`protocol_msg`] can't provide any safe mutable access to `length`.
pub use encapsulated_struct::protocol_msg;
mod encapsulated_struct {
    use core::mem::size_of;
    use fambox::FamHeader;

    pub struct InvalidLength;

    impl protocol_msg {
        pub fn new(version: u8, ty: u8, buffer_len: usize) -> Result<Self, InvalidLength> {
            Ok(Self {
                version,
                ty,
                length: (size_of::<Self>()
                    + buffer_len * size_of::<<Self as FamHeader>::Element>())
                .try_into()
                .or(Err(InvalidLength))?,
                elements: __IncompleteArrayField::new(),
            })
        }
        pub fn length(&self) -> u16 {
            self.length
        }
    }

    // Safety:
    // `protocol_msg` doesn't expose a mutable setter which would make the length inconsistent.
    unsafe impl FamHeader for protocol_msg {
        type Element = u8;

        fn fam_len(&self) -> usize {
            let bytes_in_fam = usize::from(self.length) - size_of::<Self>();
            bytes_in_fam / size_of::<Self::Element>()
        }
    }
}
let data_buffer = [0, 1, 2, 3, 4, 5];
let header = encapsulated_struct::protocol_msg::new(1, 2, data_buffer.len())?;
let header_and_fam = FamBoxOwned::from_fn(header, |i| data_buffer[i]);
let header = encapsulated_struct::protocol_msg::new(1, 2, data_buffer.len())?;
assert_eq!(header_and_fam.header(), &header);
assert_eq!(header_and_fam.fam(), data_buffer);
assert_eq!(
    usize::from(header_and_fam.header().length()),
    size_of::<protocol_msg>() + core::mem::size_of_val(&data_buffer)
);

或者一个 FamBoxShared/FamBoxMut 可以用于轻松访问和操作从 C 访问的 fam 包含的结构

extern "C" {
    fn original_header() -> protocol_msg;
    fn original_data() -> NonNull<u8>;
    fn original_data_len() -> usize;
    fn aliased_ptr_from_c() -> NonNull<protocol_msg>;
    fn alloc_in_c() -> NonNull<protocol_msg>;
    fn free_in_c(ptr: NonNull<protocol_msg>);
}

let header_and_fam = unsafe { FamBoxShared::from_raw(aliased_ptr_from_c()) };
assert_eq!(header_and_fam.as_parts(), unsafe {
    (&original_header(), unsafe { core::slice::from_raw_parts(original_data().as_ptr(), original_data_len()) })
});

let ptr = unsafe { alloc_in_c() };
let mut header_and_fam = unsafe { FamBoxMut::from_raw(ptr) };
assert_eq!(header_and_fam.as_parts(), unsafe {
    (&original_header(), unsafe { core::slice::from_raw_parts(original_data().as_ptr(), original_data_len()) })
});
header_and_fam.fam_mut()[2] = 10;
drop(header_and_fam);
unsafe { free_in_c(ptr) }

功能标志

  • serde: 为 SerializeDeserialize 实现 FamBoxOwned

许可证

根据您的选择,受以下任一许可证的许可

贡献

除非您明确声明,否则任何有意提交以包含在作品中的贡献,根据 Apache-2.0 许可证的定义,应双重许可如上所述,不附加任何额外条款或条件。

依赖项

~170KB