iceoryx2_ffi/api/
sample_mut.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, IntoCInt, UserHeaderFfi, IOX2_OK,
18};
19
20use iceoryx2::prelude::*;
21use iceoryx2::sample_mut_uninit::SampleMutUninit;
22use iceoryx2_bb_elementary::static_assert::*;
23use iceoryx2_ffi_macros::iceoryx2_ffi;
24
25use core::ffi::{c_int, c_void};
26use core::mem::ManuallyDrop;
27
28use super::UninitPayloadFfi;
29
30// BEGIN types definition
31
32pub(super) union SampleMutUninitUnion {
33    ipc: ManuallyDrop<SampleMutUninit<ipc::Service, UninitPayloadFfi, UserHeaderFfi>>,
34    local: ManuallyDrop<SampleMutUninit<local::Service, UninitPayloadFfi, UserHeaderFfi>>,
35}
36
37impl SampleMutUninitUnion {
38    pub(super) fn new_ipc(
39        sample: SampleMutUninit<ipc::Service, UninitPayloadFfi, UserHeaderFfi>,
40    ) -> Self {
41        Self {
42            ipc: ManuallyDrop::new(sample),
43        }
44    }
45    pub(super) fn new_local(
46        sample: SampleMutUninit<local::Service, UninitPayloadFfi, UserHeaderFfi>,
47    ) -> Self {
48        Self {
49            local: ManuallyDrop::new(sample),
50        }
51    }
52}
53
54#[repr(C)]
55#[repr(align(8))] // alignment of Option<SampleMutUninitUnion>
56pub struct iox2_sample_mut_storage_t {
57    internal: [u8; 56], // magic number obtained with size_of::<Option<SampleMutUninitUnion>>()
58}
59
60#[repr(C)]
61#[iceoryx2_ffi(SampleMutUninitUnion)]
62pub struct iox2_sample_mut_t {
63    service_type: iox2_service_type_e,
64    value: iox2_sample_mut_storage_t,
65    deleter: fn(*mut iox2_sample_mut_t),
66}
67
68impl iox2_sample_mut_t {
69    pub(super) fn init(
70        &mut self,
71        service_type: iox2_service_type_e,
72        value: SampleMutUninitUnion,
73        deleter: fn(*mut iox2_sample_mut_t),
74    ) {
75        self.service_type = service_type;
76        self.value.init(value);
77        self.deleter = deleter;
78    }
79}
80
81pub struct iox2_sample_mut_h_t;
82/// The owning handle for `iox2_sample_mut_t`. Passing the handle to an function transfers the ownership.
83pub type iox2_sample_mut_h = *mut iox2_sample_mut_h_t;
84/// The non-owning handle for `iox2_sample_mut_t`. Passing the handle to an function does not transfers the ownership.
85pub type iox2_sample_mut_h_ref = *const iox2_sample_mut_h;
86
87impl AssertNonNullHandle for iox2_sample_mut_h {
88    fn assert_non_null(self) {
89        debug_assert!(!self.is_null());
90    }
91}
92
93impl AssertNonNullHandle for iox2_sample_mut_h_ref {
94    fn assert_non_null(self) {
95        debug_assert!(!self.is_null());
96        unsafe {
97            debug_assert!(!(*self).is_null());
98        }
99    }
100}
101
102impl HandleToType for iox2_sample_mut_h {
103    type Target = *mut iox2_sample_mut_t;
104
105    fn as_type(self) -> Self::Target {
106        self as *mut _ as _
107    }
108}
109
110impl HandleToType for iox2_sample_mut_h_ref {
111    type Target = *mut iox2_sample_mut_t;
112
113    fn as_type(self) -> Self::Target {
114        unsafe { *self as *mut _ as _ }
115    }
116}
117
118// END type definition
119
120// BEGIN C API
121
122/// cbindgen:ignore
123/// Internal API - do not use
124/// # Safety
125///
126/// * `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.
127/// * `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.
128/// * `dest_handle_ptr` must not be `null`
129#[doc(hidden)]
130#[no_mangle]
131pub unsafe extern "C" fn iox2_sample_mut_move(
132    source_struct_ptr: *mut iox2_sample_mut_t,
133    dest_struct_ptr: *mut iox2_sample_mut_t,
134    dest_handle_ptr: *mut iox2_sample_mut_h,
135) {
136    debug_assert!(!source_struct_ptr.is_null());
137    debug_assert!(!dest_struct_ptr.is_null());
138    debug_assert!(!dest_handle_ptr.is_null());
139
140    let source = &mut *source_struct_ptr;
141    let dest = &mut *dest_struct_ptr;
142
143    dest.service_type = source.service_type;
144    dest.value.init(
145        source
146            .value
147            .as_option_mut()
148            .take()
149            .expect("Source must have a valid sample"),
150    );
151    dest.deleter = source.deleter;
152
153    *dest_handle_ptr = (*dest_struct_ptr).as_handle();
154}
155
156/// Acquires the samples user header.
157///
158/// # Safety
159///
160/// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan())
161/// * `header_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer.
162#[no_mangle]
163pub unsafe extern "C" fn iox2_sample_mut_user_header(
164    handle: iox2_sample_mut_h_ref,
165    header_ptr: *mut *const c_void,
166) {
167    handle.assert_non_null();
168    debug_assert!(!header_ptr.is_null());
169
170    let sample = &mut *handle.as_type();
171
172    let header = match sample.service_type {
173        iox2_service_type_e::IPC => sample.value.as_mut().ipc.user_header(),
174        iox2_service_type_e::LOCAL => sample.value.as_mut().local.user_header(),
175    };
176
177    *header_ptr = (header as *const UserHeaderFfi).cast();
178}
179
180/// Acquires the samples header.
181///
182/// # Safety
183///
184/// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan())
185/// * `header_struct_ptr` - Must be either a NULL pointer or a pointer to a valid
186///     [`iox2_publish_subscribe_header_t`]. If it is a NULL pointer, the storage will be allocated on the heap.
187/// * `header_handle_ptr` valid pointer to a [`iox2_publish_subscribe_header_h`].
188#[no_mangle]
189pub unsafe extern "C" fn iox2_sample_mut_header(
190    handle: iox2_sample_mut_h_ref,
191    header_struct_ptr: *mut iox2_publish_subscribe_header_t,
192    header_handle_ptr: *mut iox2_publish_subscribe_header_h,
193) {
194    handle.assert_non_null();
195    debug_assert!(!header_handle_ptr.is_null());
196
197    fn no_op(_: *mut iox2_publish_subscribe_header_t) {}
198    let mut deleter: fn(*mut iox2_publish_subscribe_header_t) = no_op;
199    let mut storage_ptr = header_struct_ptr;
200    if header_struct_ptr.is_null() {
201        deleter = iox2_publish_subscribe_header_t::dealloc;
202        storage_ptr = iox2_publish_subscribe_header_t::alloc();
203    }
204    debug_assert!(!storage_ptr.is_null());
205
206    let sample = &mut *handle.as_type();
207
208    let header = *match sample.service_type {
209        iox2_service_type_e::IPC => sample.value.as_mut().ipc.header(),
210        iox2_service_type_e::LOCAL => sample.value.as_mut().local.header(),
211    };
212
213    (*storage_ptr).init(header, deleter);
214    *header_handle_ptr = (*storage_ptr).as_handle();
215}
216
217/// Acquires the samples mutable user header.
218///
219/// # Safety
220///
221/// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan())
222/// * `header_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer.
223#[no_mangle]
224pub unsafe extern "C" fn iox2_sample_mut_user_header_mut(
225    handle: iox2_sample_mut_h_ref,
226    header_ptr: *mut *mut c_void,
227) {
228    handle.assert_non_null();
229    debug_assert!(!header_ptr.is_null());
230
231    let sample = &mut *handle.as_type();
232
233    let header = match sample.service_type {
234        iox2_service_type_e::IPC => sample.value.as_mut().ipc.user_header_mut(),
235        iox2_service_type_e::LOCAL => sample.value.as_mut().local.user_header_mut(),
236    };
237
238    *header_ptr = (header as *mut UserHeaderFfi).cast();
239}
240
241/// Acquires the samples mutable payload.
242///
243/// # Safety
244///
245/// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan())
246/// * `payload_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer.
247/// * `payload_len` (optional) either a null poitner or a valid pointer pointing to a [`c_size_t`].
248#[no_mangle]
249pub unsafe extern "C" fn iox2_sample_mut_payload_mut(
250    handle: iox2_sample_mut_h_ref,
251    payload_ptr: *mut *mut c_void,
252    payload_len: *mut c_size_t,
253) {
254    handle.assert_non_null();
255    debug_assert!(!payload_ptr.is_null());
256
257    let sample = &mut *handle.as_type();
258
259    let payload = match sample.service_type {
260        iox2_service_type_e::IPC => sample.value.as_mut().ipc.payload_mut(),
261        iox2_service_type_e::LOCAL => sample.value.as_mut().local.payload_mut(),
262    };
263
264    *payload_ptr = payload.as_mut_ptr().cast();
265    if !payload_len.is_null() {
266        *payload_len = payload.len();
267    }
268}
269
270/// Acquires the samples payload.
271///
272/// # Safety
273///
274/// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan())
275/// * `payload_ptr` a valid, non-null pointer pointing to a [`*const c_void`] pointer.
276/// * `payload_len` (optional) either a null poitner or a valid pointer pointing to a [`c_size_t`].
277#[no_mangle]
278pub unsafe extern "C" fn iox2_sample_mut_payload(
279    handle: iox2_sample_mut_h_ref,
280    payload_ptr: *mut *const c_void,
281    payload_len: *mut c_size_t,
282) {
283    handle.assert_non_null();
284    debug_assert!(!payload_ptr.is_null());
285
286    let sample = &mut *handle.as_type();
287
288    let payload = match sample.service_type {
289        iox2_service_type_e::IPC => sample.value.as_mut().ipc.payload(),
290        iox2_service_type_e::LOCAL => sample.value.as_mut().local.payload(),
291    };
292
293    *payload_ptr = payload.as_ptr().cast();
294    if !payload_len.is_null() {
295        *payload_len = payload.len();
296    }
297}
298
299/// Takes the ownership of the sample and sends it
300///
301/// # Safety
302///
303/// * `handle` obtained by [`iox2_publisher_loan()`](crate::iox2_publisher_loan())
304/// * `number_of_recipients`, can be null or must point to a valid [`c_size_t`] to store the number
305///                 of subscribers that received the sample
306#[no_mangle]
307pub unsafe extern "C" fn iox2_sample_mut_send(
308    sample_handle: iox2_sample_mut_h,
309    number_of_recipients: *mut c_size_t,
310) -> c_int {
311    debug_assert!(!sample_handle.is_null());
312
313    let sample_struct = &mut *sample_handle.as_type();
314    let service_type = sample_struct.service_type;
315
316    let sample = sample_struct
317        .value
318        .as_option_mut()
319        .take()
320        .unwrap_or_else(|| panic!("Trying to send an already sent sample!"));
321    (sample_struct.deleter)(sample_struct);
322
323    match service_type {
324        iox2_service_type_e::IPC => {
325            let sample = ManuallyDrop::into_inner(sample.ipc);
326            match sample.assume_init().send() {
327                Ok(v) => {
328                    if !number_of_recipients.is_null() {
329                        *number_of_recipients = v;
330                    }
331                }
332                Err(e) => {
333                    return e.into_c_int();
334                }
335            }
336        }
337        iox2_service_type_e::LOCAL => {
338            let sample = ManuallyDrop::into_inner(sample.local);
339            match sample.assume_init().send() {
340                Ok(v) => {
341                    if !number_of_recipients.is_null() {
342                        *number_of_recipients = v;
343                    }
344                }
345                Err(e) => {
346                    return e.into_c_int();
347                }
348            }
349        }
350    }
351
352    IOX2_OK
353}
354
355/// This function needs to be called to destroy the sample!
356///
357/// # Arguments
358///
359/// * `sample_handle` - A valid [`iox2_sample_mut_h`]
360///
361/// # Safety
362///
363/// * The `sample_handle` is invalid after the return of this function and leads to undefined behavior if used in another function call!
364/// * The corresponding [`iox2_sample_mut_t`] can be re-used with a call to
365///   [`iox2_subscriber_receive`](crate::iox2_subscriber_receive)!
366#[no_mangle]
367pub unsafe extern "C" fn iox2_sample_mut_drop(sample_handle: iox2_sample_mut_h) {
368    debug_assert!(!sample_handle.is_null());
369
370    let sample = &mut *sample_handle.as_type();
371
372    match sample.service_type {
373        iox2_service_type_e::IPC => {
374            ManuallyDrop::drop(&mut sample.value.as_mut().ipc);
375        }
376        iox2_service_type_e::LOCAL => {
377            ManuallyDrop::drop(&mut sample.value.as_mut().local);
378        }
379    }
380    (sample.deleter)(sample);
381}
382
383// END C API