iceoryx2_ffi/api/
sample.rs

1// Copyright (c) 2024 Contributors to the Eclipse Foundation
2//
3// See the NOTICE file(s) distributed with this work for additional
4// information regarding copyright ownership.
5//
6// This program and the accompanying materials are made available under the
7// terms of the Apache Software License 2.0 which is available at
8// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
9// which is available at https://opensource.org/licenses/MIT.
10//
11// SPDX-License-Identifier: Apache-2.0 OR MIT
12
13#![allow(non_camel_case_types)]
14
15use crate::api::{
16    c_size_t, iox2_publish_subscribe_header_h, iox2_publish_subscribe_header_t,
17    iox2_service_type_e, AssertNonNullHandle, HandleToType, PayloadFfi, UserHeaderFfi,
18};
19
20use iceoryx2::prelude::*;
21use iceoryx2::sample::Sample;
22use iceoryx2_bb_elementary::static_assert::*;
23use iceoryx2_ffi_macros::iceoryx2_ffi;
24
25use core::ffi::c_void;
26use core::mem::ManuallyDrop;
27
28// BEGIN types definition
29
30pub(super) union SampleUnion {
31    ipc: ManuallyDrop<Sample<ipc::Service, PayloadFfi, UserHeaderFfi>>,
32    local: ManuallyDrop<Sample<local::Service, PayloadFfi, UserHeaderFfi>>,
33}
34
35impl SampleUnion {
36    pub(super) fn new_ipc(sample: Sample<ipc::Service, PayloadFfi, UserHeaderFfi>) -> Self {
37        Self {
38            ipc: ManuallyDrop::new(sample),
39        }
40    }
41    pub(super) fn new_local(sample: Sample<local::Service, PayloadFfi, UserHeaderFfi>) -> Self {
42        Self {
43            local: ManuallyDrop::new(sample),
44        }
45    }
46}
47
48#[repr(C)]
49#[repr(align(8))] // alignment of Option<SampleUnion>
50pub struct iox2_sample_storage_t {
51    internal: [u8; 80], // magic number obtained with size_of::<Option<SampleUnion>>()
52}
53
54#[repr(C)]
55#[iceoryx2_ffi(SampleUnion)]
56pub struct iox2_sample_t {
57    service_type: iox2_service_type_e,
58    value: iox2_sample_storage_t,
59    deleter: fn(*mut iox2_sample_t),
60}
61
62impl iox2_sample_t {
63    pub(super) fn init(
64        &mut self,
65        service_type: iox2_service_type_e,
66        value: SampleUnion,
67        deleter: fn(*mut iox2_sample_t),
68    ) {
69        self.service_type = service_type;
70        self.value.init(value);
71        self.deleter = deleter;
72    }
73}
74
75pub struct iox2_sample_h_t;
76/// The owning handle for `iox2_sample_t`. Passing the handle to an function transfers the ownership.
77pub type iox2_sample_h = *mut iox2_sample_h_t;
78/// The non-owning handle for `iox2_sample_t`. Passing the handle to an function does not transfers the ownership.
79pub type iox2_sample_h_ref = *const iox2_sample_h;
80
81impl AssertNonNullHandle for iox2_sample_h {
82    fn assert_non_null(self) {
83        debug_assert!(!self.is_null());
84    }
85}
86
87impl AssertNonNullHandle for iox2_sample_h_ref {
88    fn assert_non_null(self) {
89        debug_assert!(!self.is_null());
90        unsafe {
91            debug_assert!(!(*self).is_null());
92        }
93    }
94}
95
96impl HandleToType for iox2_sample_h {
97    type Target = *mut iox2_sample_t;
98
99    fn as_type(self) -> Self::Target {
100        self as *mut _ as _
101    }
102}
103
104impl HandleToType for iox2_sample_h_ref {
105    type Target = *mut iox2_sample_t;
106
107    fn as_type(self) -> Self::Target {
108        unsafe { *self as *mut _ as _ }
109    }
110}
111
112// END type definition
113
114// BEGIN C API
115
116/// cbindgen:ignore
117/// Internal API - do not use
118/// # Safety
119///
120/// * `source_struct_ptr` must not be `null` and the struct it is pointing to must be initialized and valid, i.e. not moved or dropped.
121/// * `dest_struct_ptr` must not be `null` and the struct it is pointing to must not contain valid data, i.e. initialized. It can be moved or dropped, though.
122/// * `dest_handle_ptr` must not be `null`
123#[doc(hidden)]
124#[no_mangle]
125pub unsafe extern "C" fn iox2_sample_move(
126    source_struct_ptr: *mut iox2_sample_t,
127    dest_struct_ptr: *mut iox2_sample_t,
128    dest_handle_ptr: *mut iox2_sample_h,
129) {
130    debug_assert!(!source_struct_ptr.is_null());
131    debug_assert!(!dest_struct_ptr.is_null());
132    debug_assert!(!dest_handle_ptr.is_null());
133
134    let source = &mut *source_struct_ptr;
135    let dest = &mut *dest_struct_ptr;
136
137    dest.service_type = source.service_type;
138    dest.value.init(
139        source
140            .value
141            .as_option_mut()
142            .take()
143            .expect("Source must have a valid sample"),
144    );
145    dest.deleter = source.deleter;
146
147    *dest_handle_ptr = (*dest_struct_ptr).as_handle();
148}
149
150/// Acquires the samples header.
151///
152/// # Safety
153///
154/// * `handle` obtained by [`iox2_subscriber_receive()`](crate::iox2_subscriber_receive())
155/// * `header_struct_ptr` - Must be either a NULL pointer or a pointer to a valid
156///     [`iox2_publish_subscribe_header_t`]. If it is a NULL pointer, the storage will be allocated on the heap.
157/// * `header_handle_ptr` valid pointer to a [`iox2_publish_subscribe_header_h`].
158#[no_mangle]
159pub unsafe extern "C" fn iox2_sample_header(
160    handle: iox2_sample_h_ref,
161    header_struct_ptr: *mut iox2_publish_subscribe_header_t,
162    header_handle_ptr: *mut iox2_publish_subscribe_header_h,
163) {
164    handle.assert_non_null();
165    debug_assert!(!header_handle_ptr.is_null());
166
167    fn no_op(_: *mut iox2_publish_subscribe_header_t) {}
168    let mut deleter: fn(*mut iox2_publish_subscribe_header_t) = no_op;
169    let mut storage_ptr = header_struct_ptr;
170    if header_struct_ptr.is_null() {
171        deleter = iox2_publish_subscribe_header_t::dealloc;
172        storage_ptr = iox2_publish_subscribe_header_t::alloc();
173    }
174    debug_assert!(!storage_ptr.is_null());
175
176    let sample = &mut *handle.as_type();
177
178    let header = *match sample.service_type {
179        iox2_service_type_e::IPC => sample.value.as_mut().ipc.header(),
180        iox2_service_type_e::LOCAL => sample.value.as_mut().local.header(),
181    };
182
183    (*storage_ptr).init(header, deleter);
184    *header_handle_ptr = (*storage_ptr).as_handle();
185}
186
187/// Acquires the samples user header.
188///
189/// # Safety
190///
191/// * `handle` obtained by [`iox2_subscriber_receive()`](crate::iox2_subscriber_receive())
192/// * `header_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer.
193#[no_mangle]
194pub unsafe extern "C" fn iox2_sample_user_header(
195    handle: iox2_sample_h_ref,
196    header_ptr: *mut *const c_void,
197) {
198    handle.assert_non_null();
199    debug_assert!(!header_ptr.is_null());
200
201    let sample = &mut *handle.as_type();
202
203    let header = match sample.service_type {
204        iox2_service_type_e::IPC => sample.value.as_mut().ipc.user_header(),
205        iox2_service_type_e::LOCAL => sample.value.as_mut().local.user_header(),
206    };
207
208    *header_ptr = (header as *const UserHeaderFfi).cast();
209}
210
211/// Acquires the samples payload.
212///
213/// # Safety
214///
215/// * `handle` obtained by [`iox2_subscriber_receive()`](crate::iox2_subscriber_receive())
216/// * `payload_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer.
217/// * `payload_len` (optional) either a null poitner or a valid pointer pointing to a [`c_size_t`].
218#[no_mangle]
219pub unsafe extern "C" fn iox2_sample_payload(
220    handle: iox2_sample_h_ref,
221    payload_ptr: *mut *const c_void,
222    payload_len: *mut c_size_t,
223) {
224    handle.assert_non_null();
225    debug_assert!(!payload_ptr.is_null());
226
227    let sample = &mut *handle.as_type();
228
229    let payload = match sample.service_type {
230        iox2_service_type_e::IPC => sample.value.as_mut().ipc.payload(),
231        iox2_service_type_e::LOCAL => sample.value.as_mut().local.payload(),
232    };
233
234    *payload_ptr = payload.as_ptr().cast();
235    if !payload_len.is_null() {
236        *payload_len = payload.len();
237    }
238}
239
240/// This function needs to be called to destroy the sample!
241///
242/// # Arguments
243///
244/// * `sample_handle` - A valid [`iox2_sample_h`]
245///
246/// # Safety
247///
248/// * The `sample_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call!
249/// * The corresponding [`iox2_sample_t`] can be re-used with a call to
250///   [`iox2_subscriber_receive`](crate::iox2_subscriber_receive)!
251#[no_mangle]
252pub unsafe extern "C" fn iox2_sample_drop(sample_handle: iox2_sample_h) {
253    debug_assert!(!sample_handle.is_null());
254
255    let sample = &mut *sample_handle.as_type();
256
257    match sample.service_type {
258        iox2_service_type_e::IPC => {
259            ManuallyDrop::drop(&mut sample.value.as_mut().ipc);
260        }
261        iox2_service_type_e::LOCAL => {
262            ManuallyDrop::drop(&mut sample.value.as_mut().local);
263        }
264    }
265    (sample.deleter)(sample);
266}
267
268// END C API