diff --git a/examples/c/publish_subscribe/src/publisher.c b/examples/c/publish_subscribe/src/publisher.c index d953eefe..5ce78926 100644 --- a/examples/c/publish_subscribe/src/publisher.c +++ b/examples/c/publish_subscribe/src/publisher.c @@ -16,8 +16,8 @@ #include int main(void) { - iox2_node_builder_mut_h node_builder_handle = iox2_node_builder_new(NULL); - iox2_node_mut_h node_handle = NULL; + iox2_node_builder_h node_builder_handle = iox2_node_builder_new(NULL); + iox2_node_h node_handle = NULL; int ret_val = iox2_node_builder_create(node_builder_handle, NULL, iox2_service_type_e_IPC, &node_handle); if (ret_val != IOX2_OK) { printf("Could not create node! Error code: %i", ret_val); diff --git a/iceoryx2-ffi/ffi/README.md b/iceoryx2-ffi/ffi/README.md index a0c58a85..5012b78d 100644 --- a/iceoryx2-ffi/ffi/README.md +++ b/iceoryx2-ffi/ffi/README.md @@ -2,8 +2,9 @@ - all constructs start with `iox2_` - `structs` end with a `_t` -- handles end with a `_h` and are a type definition to a `struct iox2_foo_h_t;` as `pub type iox2_foo_h = *mut iox2_foo_h_t` -- immutable pointer to the Rust type end with a `_ptr` and are a type definition to a `struct iox2_foo_ptr_t;` as `pub type iox2_foo_ptr = *mut iox2_foo_ptr_t` +- owning handles end with a `_h` and are a type definition to a `struct iox2_foo_h_t;` as `pub type iox2_foo_h = *mut iox2_foo_h_t` +- non-owning handles end with a `_ref_h` and are a type definition to a `struct iox2_foo_ref_h_t;` as `pub type iox2_foo_ref_h = *mut iox2_foo_ref_h_t` +- immutable pointer to the Rust type end with a `_ptr` and are a type definition to a `struct iox2_foo_ptr_t;` as `pub type iox2_foo_ptr = *const iox2_foo_ptr_t` - mutable pointer to the Rust type end with a `_mut_ptr` and are a type definition to a `struct iox2_foo_mut_ptr_t;` as `pub type iox2_foo_mut_ptr = *mut iox2_foo_mut_ptr_t` - `enums` ends with a `_e` @@ -12,7 +13,7 @@ The type erasure is usually done in two stages with `iox2_foo_storage_t` and `iox2_foo_t`. The `iox2_foo_storage_t` is the storage for the Rust type `Option` and must match the size and alignment of `Option`. -If the internal storage must hold multiple types, the size and alignment is respectively the max value of the types. +If the internal storage must hold multiple types, a union can be used. The struct is not supposed to be used standalone but always in combination with an `iox2_foo_t`. Assuming the size is 160 and the alignment is 8, then the storage is defined as following ```rs @@ -40,15 +41,18 @@ corresponding `iox2_foo_drop` shall be used to destruct the underlying Rust type If the Rust API takes the ownership of `Foo`, the C API will also take the ownership of the handle and `iox2_foo_drop` shall not be called. -When the handle is passed to a function, the ownership of the underlying data is moved to that specific function and the `*_h` handles +When the owning handle is passed to a function, the ownership of the underlying data is moved to that specific function and the `*_h` handles as well as all the `*_ptr` related to that handle are invalid. Accessing the handles or pointer afterwards lead to undefined behavior. -The only exception are the `iox2_cast_*` functions which can be used to get `_ptr` and `_mut_ptr` pointer the the Rust type. +The only exception are the `iox2_cast_*` functions which can be used to get `_ptr` and `_mut_ptr` pointer the the Rust type or a non-owning `_ref_h` handle to the C struct. The corresponding handle and pointer are defined like this ```rs pub struct iox2_foo_h_t; pub type iox2_foo_h = *mut iox2_foo_h_t; +pub struct iox2_foo_ref_h_t; +pub type iox2_foo_ref_h = *mut iox2_foo_ref_h_t; + pub struct iox2_foo_ptr_t; pub type iox2_foo_ptr = *const iox2_foo_ptr_t; diff --git a/iceoryx2-ffi/ffi/src/config.rs b/iceoryx2-ffi/ffi/src/config.rs index fc654fd2..ab1fa893 100644 --- a/iceoryx2-ffi/ffi/src/config.rs +++ b/iceoryx2-ffi/ffi/src/config.rs @@ -18,13 +18,13 @@ use core::ffi::c_void; // BEGIN type definition -pub type iox2_config_h = *const c_void; +pub type iox2_config_ptr = *const c_void; // END type definition // BEGIN C API #[no_mangle] -pub extern "C" fn iox2_config_global_config() -> iox2_config_h { +pub extern "C" fn iox2_config_global_config() -> iox2_config_ptr { Config::global_config() as *const _ as *const _ } diff --git a/iceoryx2-ffi/ffi/src/node.rs b/iceoryx2-ffi/ffi/src/node.rs index bedb8d73..e6e43db6 100644 --- a/iceoryx2-ffi/ffi/src/node.rs +++ b/iceoryx2-ffi/ffi/src/node.rs @@ -13,29 +13,20 @@ #![allow(non_camel_case_types)] use crate::{ - iox2_callback_progression_e, iox2_config_h, iox2_node_name_ptr, iox2_service_builder_mut_h, - iox2_service_builder_storage_t, iox2_service_name_mut_h, iox2_service_type_e, IntoCInt, - IOX2_OK, + iox2_callback_progression_e, iox2_config_ptr, iox2_node_name_ptr, iox2_service_builder_h, + iox2_service_builder_t, iox2_service_name_h, iox2_service_type_e, IntoCInt, IOX2_OK, }; use iceoryx2::node::{NodeListFailure, NodeView}; use iceoryx2::prelude::*; -use iceoryx2::service; -use iceoryx2_bb_elementary::math::max; use iceoryx2_bb_elementary::static_assert::*; use core::ffi::{c_int, c_void}; -use core::mem::{align_of, size_of, MaybeUninit}; +use core::mem::{align_of, size_of, ManuallyDrop, MaybeUninit}; use std::alloc::{alloc, dealloc, Layout}; // BEGIN type definition -#[repr(C)] -#[repr(align(8))] // magic number; the larger one of align_of::>() and align_of::>() -pub struct iox2_node_storage_internal_t { - internal: [u8; 8], // magic number; the larger one of size_of::>() and size_of::>() -} - #[repr(C)] #[derive(Copy, Clone)] pub enum iox2_node_list_failure_e { @@ -56,58 +47,103 @@ impl IntoCInt for NodeListFailure { } } +pub(crate) union NodeUnion { + ipc: ManuallyDrop>, + local: ManuallyDrop>, +} + +impl NodeUnion { + pub(crate) fn new_ipc(node: Node) -> Self { + Self { + ipc: ManuallyDrop::new(node), + } + } + pub(crate) fn new_local(node: Node) -> Self { + Self { + local: ManuallyDrop::new(node), + } + } +} + #[repr(C)] +#[repr(align(8))] // alignment of Option pub struct iox2_node_storage_t { - pub(crate) service_type: iox2_service_type_e, - pub(crate) internal: iox2_node_storage_internal_t, - pub(crate) deleter: fn(*mut iox2_node_storage_t), + internal: [u8; 16], // magic number obtained with size_of::>() } -/// The handle to use for the `iox2_node_*` functions which mutate the node -pub type iox2_node_mut_h = *mut iox2_node_storage_t; - impl iox2_node_storage_t { const fn assert_storage_layout() { - const MAX_NODE_ALIGNMENT: usize = max( - align_of::>(), - align_of::>(), - ); - const MAX_NODE_SIZE: usize = max( - size_of::>(), - size_of::>(), + static_assert_ge::< + { align_of::() }, + { align_of::>() }, + >(); + static_assert_ge::<{ size_of::() }, { size_of::>() }>( ); - static_assert_ge::<{ align_of::() }, { MAX_NODE_ALIGNMENT }>( - ); - static_assert_ge::<{ size_of::() }, { MAX_NODE_SIZE }>(); } - pub(crate) fn node_maybe_uninit( - &mut self, - ) -> &mut MaybeUninit> { + fn init(&mut self, node: NodeUnion) { iox2_node_storage_t::assert_storage_layout(); - unsafe { - &mut *(&mut self.internal as *mut iox2_node_storage_internal_t) - .cast::>>() - } + unsafe { &mut *(self as *mut Self).cast::>>() } + .write(Some(node)); } - pub(crate) unsafe fn node_assume_init( + unsafe fn as_option_mut(&mut self) -> &mut Option { + &mut *(self as *mut Self).cast::>() + } + + unsafe fn as_option_ref(&self) -> &Option { + &*(self as *const Self).cast::>() + } + + unsafe fn as_mut(&mut self) -> &mut NodeUnion { + self.as_option_mut().as_mut().unwrap() + } + + unsafe fn as_ref(&self) -> &NodeUnion { + self.as_option_ref().as_ref().unwrap() + } +} + +#[repr(C)] +pub struct iox2_node_t { + /// cbindgen:rename=internal1 + pub(crate) service_type: iox2_service_type_e, + /// cbindgen:rename=internal2 + pub(crate) node: iox2_node_storage_t, + pub(crate) deleter: fn(*mut iox2_node_t), +} + +impl iox2_node_t { + pub(crate) fn init( &mut self, - ) -> &mut Node { - self.node_maybe_uninit().assume_init_mut() + service_type: iox2_service_type_e, + node: NodeUnion, + deleter: fn(*mut iox2_node_t), + ) { + self.service_type = service_type; + self.node.init(node); + self.deleter = deleter; } - pub(crate) fn alloc() -> *mut iox2_node_storage_t { - unsafe { alloc(Layout::new::()) as *mut iox2_node_storage_t } + pub(crate) fn cast(node: iox2_node_h) -> *mut Self { + node as *mut _ as *mut Self } - pub(crate) fn dealloc(storage: *mut iox2_node_storage_t) { + + pub(crate) fn alloc() -> *mut iox2_node_t { + unsafe { alloc(Layout::new::()) as *mut iox2_node_t } + } + pub(crate) fn dealloc(storage: *mut iox2_node_t) { unsafe { - dealloc(storage as *mut _, Layout::new::()); + dealloc(storage as *mut _, Layout::new::()); } } } +pub struct iox2_node_h_t; +/// The handle for `iox2_node_builder_t`. Passing the handle to an function transfers the ownership. +pub type iox2_node_h = *mut iox2_node_h_t; + #[repr(C)] #[derive(Copy, Clone)] pub enum iox2_node_state_e { @@ -118,7 +154,8 @@ pub enum iox2_node_state_e { } // TODO: [#210] implement -pub type iox2_node_id_h = *const c_void; +pub struct iox2_node_id_ptr_t; +pub type iox2_node_id_ptr = *const iox2_node_id_ptr_t; /// An alias to a `void *` which can be used to pass arbitrary data to the callback pub type iox2_node_list_callback_context = *mut c_void; @@ -128,17 +165,17 @@ pub type iox2_node_list_callback_context = *mut c_void; /// # Arguments /// /// * [`iox2_node_state_e`] -/// * [`iox2_node_id_h`] +/// * [`iox2_node_id_ptr`] /// * [`iox2_node_name_ptr`](crate::iox2_node_name_ptr) -> `NULL` for `iox2_node_state_e::INACCESSIBLE` and `iox2_node_state_e::UNDEFINED` -/// * [`iox2_config_h`](crate::iox2_config_h) -> `NULL` for `iox2_node_state_e::INACCESSIBLE` and `iox2_node_state_e::UNDEFINED` +/// * [`iox2_config_ptr`](crate::iox2_config_ptr) -> `NULL` for `iox2_node_state_e::INACCESSIBLE` and `iox2_node_state_e::UNDEFINED` /// * [`iox2_node_list_callback_context`] -> provided by the user to [`iox2_node_list`] and can be `NULL` /// /// Returns a [`iox2_callback_progression_e`](crate::iox2_callback_progression_e) pub type iox2_node_list_callback = extern "C" fn( iox2_node_state_e, - iox2_node_id_h, + iox2_node_id_ptr, iox2_node_name_ptr, - iox2_config_h, + iox2_config_ptr, iox2_node_list_callback_context, ) -> iox2_callback_progression_e; @@ -150,51 +187,47 @@ pub type iox2_node_list_callback = extern "C" fn( /// /// # Safety /// -/// The `node_handle` must be valid and obtained by [`iox2_node_builder_create`](crate::iox2_node_builder_create)! +/// * The `node_handle` must be valid and obtained by [`iox2_node_builder_create`](crate::iox2_node_builder_create)! #[no_mangle] -pub unsafe extern "C" fn iox2_node_name(node_handle: iox2_node_mut_h) -> iox2_node_name_ptr { +pub unsafe extern "C" fn iox2_node_name(node_handle: iox2_node_h) -> iox2_node_name_ptr { debug_assert!(!node_handle.is_null()); - unsafe { - match (*node_handle).service_type { - iox2_service_type_e::IPC => (*node_handle) - .node_assume_init::() - .name() as *const _ as *const _, - iox2_service_type_e::LOCAL => (*node_handle) - .node_assume_init::() - .name() as *const _ as *const _, + let node_struct = &mut *iox2_node_t::cast(node_handle); + + match node_struct.service_type { + iox2_service_type_e::IPC => node_struct.node.as_ref().ipc.name() as *const _ as *const _, + iox2_service_type_e::LOCAL => { + node_struct.node.as_ref().local.name() as *const _ as *const _ } } } -/// Returns the immutable [`iox2_config_h`](crate::iox2_config_h) handle that the [`iox2_node_mut_h`] will use to create any iceoryx2 entity. +/// Returns the [`iox2_config_ptr`](crate::iox2_config_ptr), an immutable pointer to the config. /// /// # Safety /// -/// The `node_handle` must be valid and obtained by [`iox2_node_builder_create`](crate::iox2_node_builder_create)! +/// * The `node_handle` must be valid and obtained by [`iox2_node_builder_create`](crate::iox2_node_builder_create)! #[no_mangle] -pub unsafe extern "C" fn iox2_node_config(node_handle: iox2_node_mut_h) -> iox2_config_h { +pub unsafe extern "C" fn iox2_node_config(node_handle: iox2_node_h) -> iox2_config_ptr { debug_assert!(!node_handle.is_null()); - unsafe { - match (*node_handle).service_type { - iox2_service_type_e::IPC => (*node_handle) - .node_assume_init::() - .config() as *const _ as *const _, - iox2_service_type_e::LOCAL => (*node_handle) - .node_assume_init::() - .config() as *const _ as *const _, + let node_struct = &mut *iox2_node_t::cast(node_handle); + + match node_struct.service_type { + iox2_service_type_e::IPC => node_struct.node.as_ref().ipc.config() as *const _ as *const _, + iox2_service_type_e::LOCAL => { + node_struct.node.as_ref().local.config() as *const _ as *const _ } } } -/// Returns the immutable [`iox2_node_id_h`] handle of the [`iox2_node_mut_h`]. +/// Returns the [`iox2_node_id_ptr`](crate::iox2_node_id_ptr), an immutable pointer to the node id. /// /// # Safety /// -/// The `node_handle` must be valid and obtained by [`iox2_node_builder_create`](crate::iox2_node_builder_create)! +/// * The `node_handle` must be valid and obtained by [`iox2_node_builder_create`](crate::iox2_node_builder_create)! #[no_mangle] -pub unsafe extern "C" fn iox2_node_id(node_handle: iox2_node_mut_h) -> iox2_node_id_h { +pub unsafe extern "C" fn iox2_node_id(node_handle: iox2_node_h) -> iox2_node_id_ptr { debug_assert!(!node_handle.is_null()); todo!() // TODO: [#210] implement } @@ -264,13 +297,13 @@ fn iox2_node_list_impl( } } -/// Calls the callback repeatedly with an [`iox2_node_state_e`], [`iox2_node_id_h`], [´iox2_node_name_ptr´] and [`iox2_config_h`] for +/// Calls the callback repeatedly with an [`iox2_node_state_e`], [`iox2_node_id_ptr`], [´iox2_node_name_ptr´] and [`iox2_config_ptr`] for /// all [`Node`](iceoryx2::node::Node)s in the system under a given [`Config`](iceoryx2::config::Config). /// /// # Arguments /// /// * `service_type` - A [`iox2_service_type_e`] -/// * `config_handle` - A valid [`iox2_config_h`](crate::iox2_config_h) +/// * `config_ptr` - A valid [`iox2_config_ptr`](crate::iox2_config_ptr) /// * `callback` - A valid callback with [`iox2_node_list_callback`} signature /// * `callback_ctx` - An optional callback context [`iox2_node_list_callback_context`} to e.g. store information across callback iterations /// @@ -278,17 +311,17 @@ fn iox2_node_list_impl( /// /// # Safety /// -/// The `config_handle` must be valid and obtained by ether [`iox2_node_config`] or [`iox2_config_global_config`](crate::iox2_config_global_config)! +/// * The `config_ptr` must be valid and obtained by ether [`iox2_node_config`] or [`iox2_config_global_config`](crate::iox2_config_global_config)! #[no_mangle] pub unsafe extern "C" fn iox2_node_list( service_type: iox2_service_type_e, - config_handle: iox2_config_h, + config_ptr: iox2_config_ptr, callback: iox2_node_list_callback, callback_ctx: iox2_node_list_callback_context, ) -> c_int { - debug_assert!(!config_handle.is_null()); + debug_assert!(!config_ptr.is_null()); - let config = &*(config_handle as *const _); + let config = &*(config_ptr as *const _); let list_result = match service_type { iox2_service_type_e::IPC => Node::::list(config, |node_state| { @@ -307,18 +340,18 @@ pub unsafe extern "C" fn iox2_node_list( #[no_mangle] pub extern "C" fn iox2_service_name_new() {} -/// Instantiates a [`iox2_service_builder_mut_h`] for a service with the provided name. +/// Instantiates a [`iox2_service_builder_h`] for a service with the provided name. /// /// # Safety /// -/// The `node_handle` must be valid and obtained by [`iox2_node_builder_create`](crate::iox2_node_builder_create)! -/// The `service_name_handle` must be valid and obtained by [`iox2_service_name_new`]! +/// * The `node_handle` must be valid and obtained by [`iox2_node_builder_create`](crate::iox2_node_builder_create)! +/// * The `service_name_handle` must be valid and obtained by [`iox2_service_name_new`]! #[no_mangle] pub unsafe extern "C" fn iox2_node_service_builder( - node_handle: iox2_node_mut_h, - _service_builder_storage: *mut iox2_service_builder_storage_t, - service_name_handle: iox2_service_name_mut_h, -) -> iox2_service_builder_mut_h { + node_handle: iox2_node_h, + _service_builder: *mut iox2_service_builder_t, + service_name_handle: iox2_service_name_h, +) -> iox2_service_builder_h { debug_assert!(!node_handle.is_null()); debug_assert!(!service_name_handle.is_null()); todo!() // TODO: [#210] implement @@ -328,32 +361,27 @@ pub unsafe extern "C" fn iox2_node_service_builder( /// /// # Arguments /// -/// * `node_handle` - A valid [`iox2_node_mut_h`] +/// * `node_handle` - A valid [`iox2_node_h`] /// /// # Safety /// -/// The `node_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! -/// The corresponding [`iox2_node_storage_t`] can be re-used with a call to [`iox2_node_builder_create`](crate::iox2_node_builder_create)! +/// * The `node_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_node_t`] can be re-used with a call to [`iox2_node_builder_create`](crate::iox2_node_builder_create)! #[no_mangle] -pub unsafe extern "C" fn iox2_node_drop(node_handle: iox2_node_mut_h) { +pub unsafe extern "C" fn iox2_node_drop(node_handle: iox2_node_h) { debug_assert!(!node_handle.is_null()); - unsafe { - match (*node_handle).service_type { - iox2_service_type_e::IPC => { - std::ptr::drop_in_place( - (*node_handle).node_assume_init::() as *mut _ - ); - ((*node_handle).deleter)(node_handle); - } - iox2_service_type_e::LOCAL => { - std::ptr::drop_in_place( - (*node_handle).node_assume_init::() as *mut _, - ); - ((*node_handle).deleter)(node_handle); - } + let node_struct = &mut *iox2_node_t::cast(node_handle); + + match node_struct.service_type { + iox2_service_type_e::IPC => { + ManuallyDrop::drop(&mut node_struct.node.as_mut().ipc); + } + iox2_service_type_e::LOCAL => { + ManuallyDrop::drop(&mut node_struct.node.as_mut().local); } } + (node_struct.deleter)(node_struct); } // END C API @@ -369,27 +397,30 @@ mod test { const _STORAGE_LAYOUT_CHECK: () = iox2_node_storage_t::assert_storage_layout(); } - fn create_sut_node() -> iox2_node_mut_h { + fn create_sut_node() -> iox2_node_h { unsafe { let node_builder_handle = iox2_node_builder_new(std::ptr::null_mut()); - let mut node_name_handle_mut = std::ptr::null_mut(); + let mut node_name_handle = std::ptr::null_mut(); let node_name = "hypnotoad"; let ret_val = iox2_node_name_new( std::ptr::null_mut(), node_name.as_ptr() as *const _, node_name.len() as _, - &mut node_name_handle_mut, + &mut node_name_handle, ); assert_that!(ret_val, eq(IOX2_OK)); - iox2_node_builder_set_name(node_builder_handle, node_name_handle_mut); + iox2_node_builder_set_name( + iox2_cast_node_builder_ref_h(node_builder_handle), + node_name_handle, + ); - let mut node_handle: iox2_node_mut_h = std::ptr::null_mut(); + let mut node_handle: iox2_node_h = std::ptr::null_mut(); let ret_val = iox2_node_builder_create( node_builder_handle, std::ptr::null_mut(), iox2_service_type_e::IPC, - &mut node_handle as *mut iox2_node_mut_h, + &mut node_handle as *mut iox2_node_h, ); assert_that!(ret_val, eq(IOX2_OK)); @@ -413,9 +444,7 @@ mod test { fn basic_node_config_test() { unsafe { let node_handle = create_sut_node(); - let expected_config = (*node_handle) - .node_assume_init::() - .config(); + let expected_config = (*iox2_node_t::cast(node_handle)).node.as_ref().ipc.config(); let config = iox2_node_config(node_handle); @@ -429,9 +458,7 @@ mod test { fn basic_node_name_test() { unsafe { let node_handle = create_sut_node(); - let expected_node_name = (*node_handle) - .node_assume_init::() - .name(); + let expected_node_name = (*iox2_node_t::cast(node_handle)).node.as_ref().ipc.name(); assert_that!(expected_node_name.as_str(), eq("hypnotoad")); let node_name = iox2_node_name(node_handle); @@ -452,9 +479,9 @@ mod test { extern "C" fn node_list_callback( node_state: iox2_node_state_e, - _node_id: iox2_node_id_h, - _node_name: iox2_node_name_ptr, - _config: iox2_config_h, + _node_id_ptr: iox2_node_id_ptr, + _node_name_ptr: iox2_node_name_ptr, + _config_ptr: iox2_config_ptr, ctx: iox2_node_list_callback_context, ) -> iox2_callback_progression_e { let ctx = unsafe { &mut *(ctx as *mut NodeListCtx) }; diff --git a/iceoryx2-ffi/ffi/src/node_builder.rs b/iceoryx2-ffi/ffi/src/node_builder.rs index c8659abd..8313e7c1 100644 --- a/iceoryx2-ffi/ffi/src/node_builder.rs +++ b/iceoryx2-ffi/ffi/src/node_builder.rs @@ -13,8 +13,8 @@ #![allow(non_camel_case_types)] use crate::{ - iox2_node_mut_h, iox2_node_name_drop, iox2_node_name_h, iox2_node_name_t, iox2_node_storage_t, - iox2_service_type_e, IntoCInt, IOX2_OK, + iox2_node_h, iox2_node_name_drop, iox2_node_name_h, iox2_node_name_t, iox2_node_t, + iox2_service_type_e, IntoCInt, NodeUnion, IOX2_OK, }; use iceoryx2::node::NodeCreationFailure; @@ -46,59 +46,88 @@ impl IntoCInt for NodeCreationFailure { } #[repr(C)] -#[repr(align(8))] // alignment of NodeBuilder -pub struct iox2_node_builder_storage_internal_t { - internal: [u8; 18432], // magic number obtained with size_of::() -} - -#[repr(C)] +#[repr(align(8))] // alignment of Option pub struct iox2_node_builder_storage_t { - internal: iox2_node_builder_storage_internal_t, - deleter: fn(*mut iox2_node_builder_storage_t), + internal: [u8; 18432], // magic number obtained with size_of::>() } -/// The handle to use for the `iox2_node_builder_*` functions which mutate the node builder -pub type iox2_node_builder_mut_h = *mut iox2_node_builder_storage_t; - impl iox2_node_builder_storage_t { const fn assert_storage_layout() { static_assert_ge::< - { align_of::() }, - { align_of::() }, + { align_of::() }, + { align_of::>() }, >(); static_assert_ge::< - { size_of::() }, - { size_of::() }, + { size_of::() }, + { size_of::>() }, >(); } - fn node_builder_maybe_uninit(&mut self) -> &mut MaybeUninit { + fn init(&mut self, node_builder: NodeBuilder) { iox2_node_builder_storage_t::assert_storage_layout(); - unsafe { - &mut *(&mut self.internal as *mut iox2_node_builder_storage_internal_t) - .cast::>() - } + unsafe { &mut *(self as *mut Self).cast::>>() } + .write(Some(node_builder)); } - unsafe fn node_builder_assume_init(&mut self) -> &mut NodeBuilder { - self.node_builder_maybe_uninit().assume_init_mut() + + unsafe fn as_option_mut(&mut self) -> &mut Option { + &mut *(self as *mut Self).cast::>() } - fn alloc() -> *mut iox2_node_builder_storage_t { - unsafe { - alloc(Layout::new::()) as *mut iox2_node_builder_storage_t - } + unsafe fn _os_option_ref(&self) -> &Option { + &*(self as *const Self).cast::>() + } + + unsafe fn _as_mut(&mut self) -> &mut NodeBuilder { + self.as_option_mut().as_mut().unwrap() + } + + unsafe fn _as_ref(&self) -> &NodeBuilder { + self._os_option_ref().as_ref().unwrap() + } +} + +#[repr(C)] +pub struct iox2_node_builder_t { + /// cbindgen:rename=internal + node_builder: iox2_node_builder_storage_t, + deleter: fn(*mut iox2_node_builder_t), +} + +impl iox2_node_builder_t { + pub(crate) fn cast(node_builder: iox2_node_builder_h) -> *mut Self { + node_builder as *mut _ as *mut Self + } + pub(crate) fn cast_from_ref(node_builder: iox2_node_builder_ref_h) -> *mut Self { + node_builder as *mut _ as *mut Self + } + + pub(crate) fn take(&mut self) -> Option { + unsafe { self.node_builder.as_option_mut().take() } + } + + pub(crate) fn set(&mut self, node_builder: NodeBuilder) { + unsafe { *self.node_builder.as_option_mut() = Some(node_builder) } + } + + fn alloc() -> *mut iox2_node_builder_t { + unsafe { alloc(Layout::new::()) as *mut iox2_node_builder_t } } - fn dealloc(storage: *mut iox2_node_builder_storage_t) { + fn dealloc(storage: *mut iox2_node_builder_t) { unsafe { - dealloc( - storage as *mut _, - Layout::new::(), - ); + dealloc(storage as *mut _, Layout::new::()); } } } +pub struct iox2_node_builder_h_t; +/// The owning handle for `iox2_node_builder_t`. Passing the handle to an function transfers the ownership. +pub type iox2_node_builder_h = *mut iox2_node_builder_h_t; + +pub struct iox2_node_builder_ref_h_t; +/// The non-owning handle for `iox2_node_builder_t`. Passing the handle to an function does not transfers the ownership. +pub type iox2_node_builder_ref_h = *mut iox2_node_builder_ref_h_t; + // END type definition // BEGIN C API @@ -107,74 +136,93 @@ impl iox2_node_builder_storage_t { /// /// # Arguments /// -/// * `node_builder_storage` - Must be either a NULL pointer or a pointer to a valid [`iox2_node_builder_storage_t`]. If it is a NULL pointer, the storage will be allocated on the heap. +/// * `node_builder_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_node_builder_t`]. If it is a NULL pointer, the storage will be allocated on the heap. /// /// # Returns /// -/// A [`iox2_node_builder_mut_h`] handle to build the actual node. +/// A [`iox2_node_builder_h`] handle to build the actual node. /// /// # Safety /// -/// The same [`iox2_node_builder_storage_t`] cannot be used in subsequent calls to this function, unless [`iox2_node_builder_create`] was called before! +/// * The same [`iox2_node_builder_t`] cannot be used in subsequent calls to this function, unless [`iox2_node_builder_create`] was called before! #[no_mangle] pub unsafe extern "C" fn iox2_node_builder_new( - node_builder_storage: *mut iox2_node_builder_storage_t, -) -> iox2_node_builder_mut_h { - let mut handle = node_builder_storage; - fn no_op(_storage: *mut iox2_node_builder_storage_t) {} - let mut deleter: fn(*mut iox2_node_builder_storage_t) = no_op; - if handle.is_null() { - handle = iox2_node_builder_storage_t::alloc(); - deleter = iox2_node_builder_storage_t::dealloc; + node_builder_struct_ptr: *mut iox2_node_builder_t, +) -> iox2_node_builder_h { + let mut node_builder_struct_ptr = node_builder_struct_ptr; + fn no_op(_: *mut iox2_node_builder_t) {} + let mut deleter: fn(*mut iox2_node_builder_t) = no_op; + if node_builder_struct_ptr.is_null() { + node_builder_struct_ptr = iox2_node_builder_t::alloc(); + deleter = iox2_node_builder_t::dealloc; } - debug_assert!(!handle.is_null()); + debug_assert!(!node_builder_struct_ptr.is_null()); - unsafe { - (*handle).deleter = deleter; - } + (*node_builder_struct_ptr).deleter = deleter; + (*node_builder_struct_ptr) + .node_builder + .init(NodeBuilder::new()); - unsafe { - (*handle) - .node_builder_maybe_uninit() - .write(NodeBuilder::new()); - } + node_builder_struct_ptr as *mut _ as *mut _ +} - handle +/// This function casts an owning [`iox2_node_builder_h`] into a non-owning [`iox2_node_builder_ref_h`] +/// +/// # Arguments +/// +/// * `node_builder_handle` obtained by [`iox2_node_builder_new`] +/// +/// Returns a [`iox2_node_builder_ref_h`] +/// +/// # Safety +/// +/// * The `node_builder_handle` must be a valid handle. +/// * The `node_builder_handle` is still valid after the call to this function. +#[no_mangle] +pub unsafe extern "C" fn iox2_cast_node_builder_ref_h( + node_builder_handle: iox2_node_builder_h, +) -> iox2_node_builder_ref_h { + debug_assert!(!node_builder_handle.is_null()); + + node_builder_handle as *mut _ as _ } /// Sets the node name for the builder /// /// # Arguments /// -/// * `node_builder_handle` - Must be a valid [`iox2_node_builder_mut_h`] obtained by [`iox2_node_builder_new`]. +/// * `node_builder_handle` - Must be a valid [`iox2_node_builder_ref_h`] obtained by [`iox2_node_builder_new`] and casted by [`iox2_cast_node_builder_ref_h`]. /// * `node_name_handle` - Must be a valid [`iox2_node_name_h`] obtained by [`iox2_node_name_new`](crate::iox2_node_name_new). /// /// Returns IOX2_OK /// /// # Safety /// -/// `node_builder_handle` as well as `node_name_handle` must be valid handles +/// * `node_builder_handle` as well as `node_name_handle` must be valid handles #[no_mangle] pub unsafe extern "C" fn iox2_node_builder_set_name( - node_builder_handle: iox2_node_builder_mut_h, + node_builder_handle: iox2_node_builder_ref_h, node_name_handle: iox2_node_name_h, ) -> c_int { debug_assert!(!node_builder_handle.is_null()); debug_assert!(!node_name_handle.is_null()); - let node_name = unsafe { (*iox2_node_name_t::cast(node_name_handle)).take().unwrap() }; + let node_name_struct = &mut *iox2_node_name_t::cast(node_name_handle); + let node_name = node_name_struct.take().unwrap(); iox2_node_name_drop(node_name_handle); - let node_builder = std::mem::take(unsafe { (*node_builder_handle).node_builder_assume_init() }); + let node_builder_struct = &mut *iox2_node_builder_t::cast_from_ref(node_builder_handle); + + let node_builder = node_builder_struct.take().unwrap(); let node_builder = node_builder.name(node_name); - *(*node_builder_handle).node_builder_assume_init() = node_builder; + node_builder_struct.set(node_builder); IOX2_OK } #[no_mangle] pub extern "C" fn iox2_node_builder_set_config( - node_builder_handle: iox2_node_builder_mut_h, + node_builder_handle: iox2_node_builder_ref_h, ) -> c_int { debug_assert!(!node_builder_handle.is_null()); todo!() // TODO: [#210] implement @@ -182,56 +230,57 @@ pub extern "C" fn iox2_node_builder_set_config( // IOX2_OK } +// intentionally not public API +unsafe fn iox2_node_builder_drop(node_builder_handle: iox2_node_builder_h) { + debug_assert!(!node_builder_handle.is_null()); + + let node_builder_struct = &mut (*iox2_node_builder_t::cast(node_builder_handle)); + std::ptr::drop_in_place(node_builder_struct.node_builder.as_option_mut() as *mut _); + (node_builder_struct.deleter)(node_builder_struct); +} + /// Creates a node and consumes the builder /// /// # Arguments /// -/// * `node_builder_handle` - Must be a valid [`iox2_node_builder_mut_h`] obtained by [`iox2_node_builder_new`]. -/// * `node_storage` - Must be either a NULL pointer or a pointer to a valid [`iox2_node_storage_t`]. If it is a NULL pointer, the storage will be allocated on the heap. +/// * `node_builder_handle` - Must be a valid [`iox2_node_builder_h`] obtained by [`iox2_node_builder_new`]. +/// * `node_struct_ptr` - Must be either a NULL pointer or a pointer to a valid [`iox2_node_t`]. If it is a NULL pointer, the storage will be allocated on the heap. /// * `service_type` - The [`iox2_service_type_e`] for the node to be created. -/// * `node_handle_ptr` - An uninitialized or dangling [`iox2_node_mut_h`] handle which will be initialized by this function call. +/// * `node_handle_ptr` - An uninitialized or dangling [`iox2_node_h`] handle which will be initialized by this function call. /// /// Returns IOX2_OK on success, an [`iox2_node_creation_failure_e`] otherwise. /// /// # Safety /// -/// The `node_builder_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! -/// The corresponding [`iox2_node_builder_storage_t`] can be re-used with a call to [`iox2_node_builder_new`]! +/// * The `node_builder_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_node_builder_t`] can be re-used with a call to [`iox2_node_builder_new`]! #[no_mangle] pub unsafe extern "C" fn iox2_node_builder_create( - node_builder_handle: iox2_node_builder_mut_h, - node_storage: *mut iox2_node_storage_t, + node_builder_handle: iox2_node_builder_h, + node_struct_ptr: *mut iox2_node_t, service_type: iox2_service_type_e, - node_handle_ptr: *mut iox2_node_mut_h, + node_handle_ptr: *mut iox2_node_h, ) -> c_int { debug_assert!(!node_builder_handle.is_null()); debug_assert!(!node_handle_ptr.is_null()); - let node_builder = std::mem::take(unsafe { (*node_builder_handle).node_builder_assume_init() }); - unsafe { - std::ptr::drop_in_place((*node_builder_handle).node_builder_assume_init() as *mut _); - ((*node_builder_handle).deleter)(node_builder_handle); - } - - let mut node_handle = node_storage; - fn no_op(_storage: *mut iox2_node_storage_t) {} - let mut deleter: fn(*mut iox2_node_storage_t) = no_op; - if node_handle.is_null() { - node_handle = iox2_node_storage_t::alloc(); - deleter = iox2_node_storage_t::dealloc; - } - debug_assert!(!node_handle.is_null()); + let node_builder_struct = &mut *iox2_node_builder_t::cast(node_builder_handle); + let node_builder = node_builder_struct.take().unwrap(); + iox2_node_builder_drop(node_builder_handle); - unsafe { - (*node_handle).service_type = service_type; - (*node_handle).deleter = deleter; - *node_handle_ptr = node_handle; + let mut node_struct_ptr = node_struct_ptr; + fn no_op(_: *mut iox2_node_t) {} + let mut deleter: fn(*mut iox2_node_t) = no_op; + if node_struct_ptr.is_null() { + node_struct_ptr = iox2_node_t::alloc(); + deleter = iox2_node_t::dealloc; } + debug_assert!(!node_struct_ptr.is_null()); match service_type { iox2_service_type_e::IPC => match node_builder.create::() { Ok(node) => unsafe { - (*node_handle).node_maybe_uninit().write(node); + (*node_struct_ptr).init(service_type, NodeUnion::new_ipc(node), deleter); }, Err(error) => { return error.into_c_int(); @@ -239,7 +288,7 @@ pub unsafe extern "C" fn iox2_node_builder_create( }, iox2_service_type_e::LOCAL => match node_builder.create::() { Ok(node) => unsafe { - (*node_handle).node_maybe_uninit().write(node); + (*node_struct_ptr).init(service_type, NodeUnion::new_local(node), deleter); }, Err(error) => { return error.into_c_int(); @@ -247,6 +296,8 @@ pub unsafe extern "C" fn iox2_node_builder_create( }, } + *node_handle_ptr = node_struct_ptr as *mut _ as *mut _; + IOX2_OK } @@ -267,12 +318,12 @@ mod test { fn basic_node_builder_api_test() { unsafe { let node_builder_handle = iox2_node_builder_new(std::ptr::null_mut()); - let mut node_handle: iox2_node_mut_h = std::ptr::null_mut(); + let mut node_handle: iox2_node_h = std::ptr::null_mut(); let ret_val = iox2_node_builder_create( node_builder_handle, std::ptr::null_mut(), iox2_service_type_e::LOCAL, - &mut node_handle as *mut iox2_node_mut_h, + &mut node_handle as *mut iox2_node_h, ); assert_that!(ret_val, eq(IOX2_OK)); diff --git a/iceoryx2-ffi/ffi/src/node_name.rs b/iceoryx2-ffi/ffi/src/node_name.rs index f4face51..420daf53 100644 --- a/iceoryx2-ffi/ffi/src/node_name.rs +++ b/iceoryx2-ffi/ffi/src/node_name.rs @@ -49,12 +49,20 @@ impl iox2_node_name_storage_t { .write(Some(node_name)); } - unsafe fn assume_init_mut(&mut self) -> &mut Option { - (*(self as *mut Self).cast::>>()).assume_init_mut() + unsafe fn as_option_mut(&mut self) -> &mut Option { + &mut *(self as *mut Self).cast::>() } - unsafe fn assume_init_ref(&self) -> &Option { - (*(self as *const Self).cast::>>()).assume_init_ref() + unsafe fn as_option_ref(&self) -> &Option { + &*(self as *const Self).cast::>() + } + + unsafe fn _as_mut(&mut self) -> &mut NodeName { + self.as_option_mut().as_mut().unwrap() + } + + unsafe fn as_ref(&self) -> &NodeName { + self.as_option_ref().as_ref().unwrap() } } @@ -72,14 +80,11 @@ impl iox2_node_name_t { pub(crate) fn cast_node_name(node_name_ptr: iox2_node_name_ptr) -> *const NodeName { debug_assert!(!node_name_ptr.is_null()); - let maybe_node_name = - unsafe { (*(node_name_ptr as *const _ as *const Option)).as_ref() }; - debug_assert!(maybe_node_name.is_some()); - unsafe { maybe_node_name.unwrap_unchecked() as *const _ } + node_name_ptr as *const _ as *const _ } pub(crate) fn take(&mut self) -> Option { - unsafe { self.node_name.assume_init_mut().take() } + unsafe { self.node_name.as_option_mut().take() } } fn alloc() -> *mut iox2_node_name_t { @@ -121,8 +126,8 @@ pub type iox2_node_name_mut_ptr = *mut iox2_node_name_mut_ptr_t; /// /// # Safety /// -/// Terminates if `node_name_str` or `node_name_handle_ptr` is a NULL pointer! -/// It is undefined behavior to pass a `node_name_len` which is larger than the actual length of `node_name_str`! +/// * Terminates if `node_name_str` or `node_name_handle_ptr` is a NULL pointer! +/// * It is undefined behavior to pass a `node_name_len` which is larger than the actual length of `node_name_str`! #[no_mangle] pub unsafe extern "C" fn iox2_node_name_new( node_name_struct_ptr: *mut iox2_node_name_t, @@ -135,17 +140,17 @@ pub unsafe extern "C" fn iox2_node_name_new( *node_name_handle_ptr = std::ptr::null_mut(); - let mut handle = node_name_struct_ptr; - fn no_op(_storage: *mut iox2_node_name_t) {} + let mut node_name_struct_ptr = node_name_struct_ptr; + fn no_op(_: *mut iox2_node_name_t) {} let mut deleter: fn(*mut iox2_node_name_t) = no_op; - if handle.is_null() { - handle = iox2_node_name_t::alloc(); + if node_name_struct_ptr.is_null() { + node_name_struct_ptr = iox2_node_name_t::alloc(); deleter = iox2_node_name_t::dealloc; } - debug_assert!(!handle.is_null()); + debug_assert!(!node_name_struct_ptr.is_null()); unsafe { - (*handle).deleter = deleter; + (*node_name_struct_ptr).deleter = deleter; } let node_name = slice::from_raw_parts(node_name_str as *const _, node_name_len as usize); @@ -153,23 +158,23 @@ pub unsafe extern "C" fn iox2_node_name_new( let node_name = if let Ok(node_name) = str::from_utf8(node_name) { node_name } else { - deleter(handle); + deleter(node_name_struct_ptr); return iox2_semantic_string_error_e::INVALID_CONTENT as c_int; }; let node_name = match NodeName::new(node_name) { Ok(node_name) => node_name, Err(e) => { - deleter(handle); + deleter(node_name_struct_ptr); return e.into_c_int(); } }; unsafe { - (*handle).node_name.init(node_name); + (*node_name_struct_ptr).node_name.init(node_name); } - *node_name_handle_ptr = handle as *mut _ as *mut _; + *node_name_handle_ptr = node_name_struct_ptr as *mut _ as *mut _; IOX2_OK } @@ -184,8 +189,8 @@ pub unsafe extern "C" fn iox2_node_name_new( /// /// # Safety /// -/// The `node_name_handle` must be a valid handle. -/// The `node_name_handle` is still valid after the call to this function. +/// * The `node_name_handle` must be a valid handle. +/// * The `node_name_handle` is still valid after the call to this function. #[no_mangle] pub unsafe extern "C" fn iox2_cast_node_name_ptr( node_name_handle: iox2_node_name_h, @@ -194,7 +199,7 @@ pub unsafe extern "C" fn iox2_cast_node_name_ptr( (*iox2_node_name_t::cast(node_name_handle)) .node_name - .assume_init_ref() as *const _ as *const _ + .as_ref() as *const _ as *const _ } /// This function gives access to the node name as a C-style string @@ -208,7 +213,7 @@ pub unsafe extern "C" fn iox2_cast_node_name_ptr( /// /// # Safety /// -/// The `node_name_ptr` must be a valid pointer to a node name. +/// * The `node_name_ptr` must be a valid pointer to a node name. #[no_mangle] pub unsafe extern "C" fn iox2_node_name_as_c_str( node_name_ptr: iox2_node_name_ptr, @@ -237,15 +242,15 @@ pub unsafe extern "C" fn iox2_node_name_as_c_str( /// /// # Safety /// -/// The `node_name_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! -/// The corresponding [`iox2_node_name_t`] can be re-used with a call to [`iox2_node_name_new`]! +/// * The `node_name_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call! +/// * The corresponding [`iox2_node_name_t`] can be re-used with a call to [`iox2_node_name_new`]! #[no_mangle] pub unsafe extern "C" fn iox2_node_name_drop(node_name_handle: iox2_node_name_h) { debug_assert!(!node_name_handle.is_null()); let node_name_struct = &mut (*iox2_node_name_t::cast(node_name_handle)); - node_name_struct.node_name.assume_init_mut().take(); + std::ptr::drop_in_place(node_name_struct.node_name.as_option_mut() as *mut _); (node_name_struct.deleter)(node_name_struct); } diff --git a/iceoryx2-ffi/ffi/src/service_builder.rs b/iceoryx2-ffi/ffi/src/service_builder.rs index 4bb06324..6192a5b6 100644 --- a/iceoryx2-ffi/ffi/src/service_builder.rs +++ b/iceoryx2-ffi/ffi/src/service_builder.rs @@ -12,6 +12,6 @@ #![allow(non_camel_case_types)] -pub struct iox2_service_builder_storage_t {} // TODO: [#210] implement -pub type iox2_service_builder_mut_h = *mut (); // TODO: [#210] implement -pub type iox2_service_name_mut_h = *mut (); // TODO: [#210] implement +pub struct iox2_service_builder_t {} // TODO: [#210] implement +pub type iox2_service_builder_h = *mut (); // TODO: [#210] implement +pub type iox2_service_name_h = *mut (); // TODO: [#210] implement