arti_rpc_client_core/ffi/
util.rs

1//! Helpers for working with FFI.
2
3use std::mem::MaybeUninit;
4
5/// Helper for output parameters represented as `*mut T`.
6///
7/// This is for an API which, from a C POV, returns an output via a parameter of type
8/// `Foo *foo_out` .  When an `OutPtr` is constructed, `foo_out` is necessarily non-null;
9///
10/// If `foo_out` is not NULL, then `*foo_out` is always initialized when an `OutPtr`
11/// is constructed, so that even if the FFI code panics, the inner pointer will be initialized to
12/// _something_.
13pub(super) struct OutVal<'a, T>(&'a mut T);
14
15/// Alias for an `OutVal` representing a `*mut *mut T`.
16pub(super) type OutPtr<'a, T> = OutVal<'a, *mut T>;
17
18impl<'a, T> OutVal<'a, T> {
19    /// Construct `Option<Self>` from a possibly NULL pointer; initialize `*ptr` to `initial_value` if possible.
20    ///
21    /// # Safety
22    ///
23    /// The outer pointer, if set, must be valid, and must not alias any other pointers.
24    ///
25    /// See also the requirements on `pointer::as_mut()`.
26    ///
27    /// # No panics!
28    ///
29    /// This method can be invoked in cases where panicking is not allowed (such as
30    /// in a FFI method, outside of `handle_errors()` or `catch_panic()`.)
31    //
32    // (I have tested this using the `no-panic` crate.  But `no-panic` is not suitable
33    // for use in production, since it breaks when run in debug mode.)
34    pub(super) unsafe fn from_opt_ptr(ptr: *mut T, initial_value: T) -> Option<Self> {
35        if ptr.is_null() {
36            None
37        } else {
38            // TODO: Use `.as_mut_uninit` once it is stable.
39            //
40            // SAFETY: See documentation for [`<*mut *mut T>::as_uninit_mut`]
41            // at https://doc.rust-lang.org/std/primitive.pointer.html#method.as_uninit_mut :
42            // This is the same code.
43            let ptr: &mut MaybeUninit<T> = unsafe { &mut *(ptr as *mut MaybeUninit<T>) };
44            let ptr: &mut T = ptr.write(initial_value);
45            Some(OutVal(ptr))
46        }
47    }
48
49    /// Consume this `OutVal` and the provided value, writing the value into the outval.
50    pub(super) fn write_value(self, value: T) {
51        // Note that all the unsafety happened when we constructed a &mut from the pointer.
52        //
53        // Note also that this method consumes `self`.  That's because we want to avoid multiple
54        // writes to the same OutVal: If we did that, we would sometimes have to free a previous
55        // value.
56        *self.0 = value;
57    }
58}
59
60impl<'a, T> OutVal<'a, *mut T> {
61    /// Consume this `OutPtr` and the provided value, writing the value into the outptr.
62    pub(super) fn write_value_boxed(self, value: T) {
63        self.write_value(Box::into_raw(Box::new(value)));
64    }
65}
66
67/// Implement OptOutPtrExt and OptOutValExt.
68///
69/// This is a separate module so we can seal these traits.
70mod out_ptr_ext {
71    use super::OutVal;
72
73    /// Trait to prevent implementation of OptOutPtrExt outside this module.
74    trait Sealed {}
75
76    /// Extension trait on `Option<OutPtr<T>>`
77    #[allow(private_bounds)]
78    pub(in crate::ffi) trait OptOutPtrExt<T>: Sealed {
79        /// Consume this `Option<OutPtr<T>>` and the provided value.
80        ///
81        /// If this is Some, write the value into the outptr.
82        ///
83        /// Otherwise, discard the value.
84        fn write_boxed_value_if_ptr_set(self, value: T);
85    }
86    /// Extension trait on `Option<OutVal<T>>`
87    #[allow(private_bounds)]
88    pub(in crate::ffi) trait OptOutValExt<T>: Sealed {
89        /// Consume this `Option<OutVal<T>>` and the provided value.
90        ///
91        /// If this is Some, write the value into the outptr.
92        ///
93        /// Otherwise, discard the value.
94        fn write_value_if_ptr_set(self, value: T);
95    }
96    impl<'a, T> Sealed for Option<OutVal<'a, T>> {}
97    impl<'a, T> OptOutPtrExt<T> for Option<OutVal<'a, *mut T>> {
98        fn write_boxed_value_if_ptr_set(self, value: T) {
99            if let Some(outptr) = self {
100                outptr.write_value_boxed(value);
101            }
102        }
103    }
104    impl<'a, T> OptOutValExt<T> for Option<OutVal<'a, T>> {
105        fn write_value_if_ptr_set(self, value: T) {
106            if let Some(outptr) = self {
107                outptr.write_value(value);
108            }
109        }
110    }
111}
112pub(super) use out_ptr_ext::{OptOutPtrExt, OptOutValExt};
113
114/// Helper for output parameters represented as `*mut ArtiRpcRawSocket`.
115///
116/// Implements the case where these parameters take ownership the associated socket.
117#[derive(derive_more::From)]
118pub(super) struct OutSocketOwned<'a>(OutVal<'a, ArtiRpcRawSocket>);
119
120#[cfg(not(windows))]
121use std::os::fd::IntoRawFd as IntoRawSocketTrait;
122#[cfg(windows)]
123use std::os::windows::io::IntoRawSocket as IntoRawSocketTrait;
124
125impl<'a> OutSocketOwned<'a> {
126    /// Take ownership of the provided socket,
127    /// consume it,
128    /// and store its associated `ArtiRpcRawSocket` into `self`.
129    pub(super) fn write_socket<T: IntoRawSocketTrait>(self, socket: T) {
130        #[cfg(windows)]
131        let sock = socket.into_raw_socket();
132
133        #[cfg(not(windows))]
134        let sock = socket.into_raw_fd();
135
136        self.0.write_value(ArtiRpcRawSocket(sock));
137    }
138}
139
140/// Implement the body of an FFI function.
141///
142/// This macro handles the calling convention of an FFI function.
143/// Proper use of this macro will ensure that the FFI function behaves as documented,
144/// as regards pointer handling, ownership, lifetimes, and error handling.
145/// It also catches panics, making sure that we don't unwind into the FFI caller.
146/// I.e. it ensures that correct callers will not experience UB.
147///
148/// This variant is for functions that
149/// don't pass back an `ArtiRpcError` via an out parameter.
150/// See [`ffi_body_with_err!`] for that.
151///
152/// This macro is meant to be invoked as follows:
153///
154/// ```ignore
155///     ffi_body_raw!(
156///         {
157///             [CONVERSIONS]
158///         } in {
159///             [BODY]
160///         } on invalid {
161///             [VALUE_ON_BAD_INPUT]
162///         }
163///     )
164/// ```
165///
166/// For example:
167///
168/// ```ignore
169/// pub extern "C" fn arti_rpc_cook_meal(
170///     recipe: *const Recipe,
171///     special_ingredients: *const Ingredients,
172///     n_guests: usize,
173///     dietary_constraints: *const c_char,
174///     food_out: *mut *mut DeliciousMeal,
175/// ) -> usize {
176///     ffi_body_raw!(
177///         { // [CONVERSIONS]
178///             let recipe: Option<&Recipe> [in_ptr_opt];
179///             let ingredients: Option<&Ingredients> [in_ptr_opt];
180///             let dietary_constraints: Option<&str> [in_str_opt];
181///             let food_out: OutPtr<DeliciousMeal> [out_ptr_opt];
182///         } in {
183///             // [BODY]
184///             let Some(recipe) = recipe else { return 0 };
185///             let delicious_meal = prepare_meal(
186///                 recipe, ingredients, dietary_constraints, n_guests
187///             );
188///             food_out.write_value_if_nonnull(delicious_meal);
189///             n_guests
190///         } on invalid {
191///             // [VALUE_ON_BAD_INPUT]
192///             0
193///         }
194///     )
195/// }
196/// ```
197///
198/// The first part (`CONVERSIONS`) defines a set of conversions to be done on the function inputs.
199/// These are documented below.
200/// Each conversion performs an unsafe operation,
201/// making certain assumptions about an input variable,
202/// in order to produce an output of the specified type.
203/// Conversions can reject input values.
204/// If they do, the function will return;
205/// see discussion of `[VALUE_ON_BAD_INPUT]`
206///
207/// Pointer parameters to the outer function *must not be ignored*.
208/// Every raw pointer parameter must be processed by this macro.
209/// (For raw pointer arguments that are not,
210/// no guarantees are made by the macro,
211/// and the overall function will probably be unsound.
212/// There is no checking that every pointer parameter is properly used,
213/// other than Rust's usual detection of unused variables.)
214///
215/// The second part (`BODY`) is the body of the function.
216/// The body is *outside* `unsafe`, and
217/// it should generally be possible to write this body without using unsafe code.
218/// The result of this block is the returned value of the function.
219///
220/// The third part (`VALUE_ON_BAD_INPUT`) is an expression to be returned
221/// as the result of the function if any input pointer has a rejected value.
222/// You may omit the entire `on invalid { ... }` part of the macro's input
223/// when all of the conversions are infallible.
224/// (This is checked statically.)
225///
226/// ## Supported conversions
227///
228/// All conversions take the following format:
229///
230/// `let NAME : TYPE [METHOD] ;`
231///
232/// The `NAME` must match one of the inputs to the function.
233///
234/// The `TYPE` must match the actual type that the input will be converted to.
235/// (These types are generally easy to use ergonomically from safe rust.)
236///
237/// The `METHOD` is an identifier explaining how the input is to be converted.
238///
239/// The following methods are recognized:
240///
241/// | method               | input type      | converted to       | can reject input? |
242/// |----------------------|-----------------|--------------------|-------------------|
243/// | `in_ptr_opt`         | `*const T`      | `Option<&T>`       | N                 |
244/// | `in_str_opt`         | `*const c_char` | `Option<&str>`     | Y                 |
245/// | `in_ptr_consume_opt` | `*mut T`        | `Option<Box<T>>`   | N                 |
246/// | `out_ptr_opt`        | `*mut *mut T`   | `Option<OutPtr<T>>`| N                 |
247/// | `out_val_opt`        | `*mut T`        | `Option<OutVal<T>>`| N                 |
248/// | `out_socket_owned_opt` | *mut ArtiRpcRawSocket` | `Option<OutSocketOwned>`| N   |
249/// | `in_mut_ptr_opt`     | (NO!)           | (Do not add!)      | (NO!)             |
250///
251/// > (Note: Other conversion methods are logically possible, but have not been added yet,
252/// > since they would not yet be used in this crate.)
253/// >
254/// > (Note: There is **deliberately** no in_mut_ptr_opt, or anything similar:
255/// > while we're okay with returning objects via `OutPtr`/`OutVal`,
256/// > we do not want to expose APIs that take a non-sharable object via `&mut T``,
257/// > since other languages have no way to enforce Rust's requirement
258/// > that no other `&mut` references exits.)
259///
260/// ## Safety
261///
262/// The `in_ptr_opt` method
263/// has the safety requirements of
264/// [`<*const T>::as_ref`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref).
265/// Informally, this means:
266/// * If the pointer is not null, it must point
267///   to a valid aligned dereferenceable instance of `T`.
268/// * The underlying `T` must not be freed or modified for so long as the function is running.
269///
270/// The `in_str_opt` method, when its input is non-NULL,
271/// has the safety requirements of [`CStr::from_ptr`](std::ffi::CStr::from_ptr).
272/// Informally, this means:
273///  * If the pointer is not null, it must point to a nul-terminated string.
274///  * The string must not be freed or modified for so long as the function is running.
275///
276/// Additionally, the `[in_str_opt]` method
277/// will detect invalid any string that is not UTF-8.
278///
279/// The `in_ptr_consume_opt` method, when its input is non-NULL,
280/// has the safety requirements of [`Box::from_raw`].
281/// Informally, this is satisfied when:
282///  * If the pointer is not null, it should be
283///    the result of an earlier a call to `Box<T>::into_raw`.
284///    (Note that using either `out_ptr_*` method
285///    will output pointers that can later be consumed in this way.)
286///
287/// The `out_val_opt` method
288/// has the safety requirements of
289/// [`<*mut T>::as_uninit_mut`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_uninit_mut).
290/// Informally, this means:
291///   * If the pointer (call it "out") is non-NULL, then `*out` must point to aligned
292///     "dereferenceable" (q.v.) memory holding a possibly uninitialized "T".
293///
294/// The `out_ptr_opt` method
295/// has the safety requirements of
296/// [`<*mut *mut T>::as_uninit_mut`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_uninit_mut).
297/// Informally, this means:
298///   * If the pointer (call it "out") is non-NULL, then `*out` must point to aligned
299///     "dereferenceable" (q.v.) memory holding a possibly uninitialized "*mut T".
300///
301/// (Note that immediately upon conversion, if `out` is non-NULL,
302/// `*out` is set to NULL.  See documentation for `OptPtr`.)
303///
304/// The return value of `BODY` becomes the return value of the C FFI function.
305/// It is the macro user's responsibility to ensure
306/// that it conforms to the published API.
307/// For example, if the return value is a raw pointer,
308/// the macro user must ensure it's suitably dereferenceable,
309/// that its lifetime is documented,
310/// and only null when the API says that's allowed.
311//
312// Design notes:
313// - I am keeping the conversions separate from the body below, since we don't want to catch
314//   InvalidInput from the body.
315// - The "on invalid" value must be specified explicitly if it can happen,
316//   since in general we should force the caller to think about it.
317//   Getting a 0 or -1 wrong here can have nasty results.
318// - The conversion syntax deliberately includes the type of the converted argument,
319//   on the theory that it makes the functions more readable.
320// - The conversion code deliberately shadows the original parameter with the
321//   converted parameter.
322macro_rules! ffi_body_raw {
323    {
324        {
325            $(
326                let $name:ident : $type:ty [$how:ident]
327            );*
328            $(;)?
329        } in {
330            $($body:tt)+
331        } on invalid {
332            $err:expr
333        }
334    } => {
335        crate::ffi::err::abort_on_panic(|| {
336            // run conversions and check for invalid input exceptions.
337            crate::ffi::util::ffi_initialize!{
338                {
339                    $( let $name : $type [$how]; )*
340                } else with _ignore_err : crate::ffi::err::InvalidInput {
341                    #[allow(clippy::unused_unit)]
342                    return $err;
343                }
344            };
345
346            $($body)+
347
348            },
349        )
350    };
351
352    {
353        {
354            $(
355                let $name:ident : $type:ty [$how:ident]
356            );*
357            $(;)?
358        } in {
359            $($body:tt)+
360        }
361    } => {
362        crate::ffi::err::abort_on_panic(|| {
363            // run conversions and check for invalid input exceptions.
364            crate::ffi::util::ffi_initialize!{
365                {
366                    $( let $name : $type [$how]; )*
367                } else with impossible_error : void::Void {
368                    void::unreachable(impossible_error);
369                }
370            };
371
372            $($body)+
373
374            },
375        )
376    };
377
378}
379pub(super) use ffi_body_raw;
380
381/// Implement the body of an FFI function that returns an ArtiRpcStatus.
382///
383/// This macro is meant to be invoked as follows:
384/// ```text
385/// ffi_body_with_err!(
386///         {
387///             [CONVERSIONS]
388///             err [ERRNAME] : OutPtr<ArtiRpcError>;
389///         } in {
390///             [BODY]
391///         }
392/// })```
393///
394/// For example:
395///
396/// ```ignore
397/// pub extern "C" fn arti_rpc_wombat_feed(
398///     wombat: *const Wombat,
399///     wombat_chow: *const Meal,
400///     error_out: *mut *mut ArtiRpcError
401/// ) -> ArtiRpcStatus {
402///     ffi_body_with_err!(
403///         {
404///             let wombat: Option<&Wombat> [in_ptr_opt];
405///             let wombat_chow: Option<&Meal> [in_ptr_opt];
406///             err error_out: Option<OutPtr<ArtiRpcError>>;
407///         } in {
408///             let wombat = wombat.ok_or(InvalidInput::NullPointer)?
409///             let wombat_chow = wombat_chow.ok_or(InvalidInput::NullPointer)?
410///             wombat.please_enjoy(wombat_chow)?;
411///         }
412///     )
413/// }
414/// ```
415///
416/// The resulting function has the same kinds
417/// of conversions as would [`ffi_body_raw!`].
418///
419/// The differences are:
420///   * Instead of returning a value, the body can only give errors with `?`.
421///   * The function must return ArtiRpcStatus.
422///   * Any errors that occur during the conversions or the body
423///     are converted into an ArtiRpcError,
424///     and given to the user via `error_out` if it is non-NULL.
425///     A corresponding ArtiRpcStatus is returned.
426///
427/// ## Safety
428///
429/// The safety requirements are the same as for `ffi_body_raw`, except that:
430///
431/// The safety requirements for the `err` conversion
432/// are the same as those for `out_ptr_opt` (q.v.).
433///
434/// `ffi_body_with_err` then additionally ensures conformance of
435/// the return value with the API's error handling rules.
436macro_rules! ffi_body_with_err {
437    {
438        {
439            $(
440                let $name:ident : $type:ty [$how:ident];
441            )*
442            err $err_out:ident : $err_type:ty $(;)?
443        } in {
444            $($body:tt)+
445        }
446    } => {{
447        use void::ResultVoidExt as _;
448        let $err_out: $err_type =
449            unsafe { crate::ffi::util::arg_conversion::out_ptr_opt($err_out) }
450            .void_unwrap();
451
452        crate::ffi::err::handle_errors($err_out,
453            || {
454                crate::ffi::util::ffi_initialize!{
455                    {
456                        $( let $name : $type [$how]; )*
457                    } else with err: crate::ffi::err::ArtiRpcError {
458                        return Err(crate::ffi::err::ArtiRpcError::from(err));
459                    }
460                };
461
462                let () = { $($body)+ };
463
464                Ok(())
465            }
466        )
467    }}
468}
469pub(super) use ffi_body_with_err;
470
471/// Implement a set of conversions, trying each one.
472///
473/// (It's important that this cannot exit early,
474/// since some conversions have side effects: notably, the ones that create an OutPtr
475/// can initialize that pointer to NULL, and we want to do that unconditionally.
476///
477/// If any conversion fails, run `return ($on_invalid)(error)`
478/// _after_ every conversion has succeeded or failed.
479///
480/// The syntax is:
481///
482/// ```ignore
483/// ffi_initialize!{
484///    { [CONVERSIONS] }
485///    else with [ERR_IDENT] { [ERR_BODY] }
486/// }
487/// ```
488///
489/// The `[CONVERSIONS]` have the same syntax and behavior as in [`ffi_body_raw!`].
490/// After every conversion has been tried, if one or more of them failed,
491/// then the `[ERR_BODY]` code is run,
492/// with `[ERR_IDENT]` bound to an instance of `InvalidInput`.
493macro_rules! ffi_initialize {
494    {
495        {
496            $( let $name:ident : $type:ty [$how:ident] ; )*
497        } else with $err_id:ident: $err_type:ty {
498            $($on_invalid:tt)*
499        }
500    } => {
501        // General approach
502        //
503        // First, we process each `$name` into `Result<$type>`, without doing any early exits.
504        // This ensures that we process every `$name`, even if some of the processing fails.
505        //
506        // Then we convert each `Result<X>` into just `X`
507        // (with an IEFE that returns a `Result<(X,...)>` - one `Result` with a big tuple.
508        // We rebinding the `$name`'s to the values from the tuple.
509        #[allow(unused_parens)]
510        let ($($name,)*) : ($($type,)*) = {
511            $(
512                let $name : Result<$type, _>
513                   = unsafe { crate::ffi::util::arg_conversion::$how($name) };
514            )*
515
516            #[allow(clippy::needless_question_mark)]
517            // Note that the question marks here exit from _this_ closure.
518            match (|| -> Result<_,$err_type> {
519                Ok(($($name?,)*))
520            })() {
521                Ok(v) => v,
522                Err($err_id) => {
523                    $($on_invalid)*
524                }
525            }
526        };
527    };
528}
529
530/// Functions to implement argument conversion.
531///
532/// Each of these functions corresponds to a conversion mode used in `ffi_initialize!`.
533///
534/// Every function has all of these properties:
535///
536/// - It returns  `Err(InvalidInput)` if the conversion fails,
537///   and `Ok($ty)` if the conversion succeeds.
538///   (Infallible conversions always return `Ok`.)
539///
540/// Nothing outside of the `ffi_initialize!` macro should actually invoke these functions!
541#[allow(clippy::unnecessary_wraps)]
542pub(super) mod arg_conversion {
543    use super::{OutPtr, OutSocketOwned, OutVal};
544    use crate::ffi::{err::InvalidInput, ArtiRpcRawSocket};
545    use std::ffi::{c_char, CStr};
546    use void::Void;
547
548    /// Try to convert a const pointer to an optional reference.
549    ///
550    /// A null pointer is allowed, and converted to `None`.
551    ///
552    /// # Safety
553    ///
554    /// As for [`<*const T>::as_ref`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref).
555    pub(in crate::ffi) unsafe fn in_ptr_opt<'a, T>(input: *const T) -> Result<Option<&'a T>, Void> {
556        Ok(unsafe { input.as_ref() })
557    }
558
559    /// Try to convert a `const char *` to a `&str`.
560    ///
561    /// A null pointer is allowed, and converted to `None`.
562    /// Non-UTF-8 inputs will give an error.
563    ///
564    /// # Safety
565    ///
566    /// As for [`CStr::from_ptr`](std::ffi::CStr::from_ptr).
567    pub(in crate::ffi) unsafe fn in_str_opt<'a>(
568        input: *const c_char,
569    ) -> Result<Option<&'a str>, InvalidInput> {
570        if input.is_null() {
571            return Ok(None);
572        }
573
574        // Safety: We require that the safety properties of CStr::from_ptr hold.
575        unsafe { CStr::from_ptr(input) }
576            .to_str()
577            .map(Some)
578            .map_err(|_| InvalidInput::BadUtf8)
579    }
580
581    /// Try to convert a mutable pointer to a `Option<Box<T>>`.
582    ///
583    /// A null pointer is allowed, and converted to `None`.
584    ///
585    /// # Safety
586    ///
587    /// As for  [`Box::from_raw`].
588    pub(in crate::ffi) unsafe fn in_ptr_consume_opt<T>(
589        input: *mut T,
590    ) -> Result<Option<Box<T>>, Void> {
591        Ok(if input.is_null() {
592            None
593        } else {
594            Some(unsafe { Box::from_raw(input) })
595        })
596    }
597
598    /// Try to convert a mutable pointer-to-pointer into an `Option<OutPtr<T>>`.
599    ///
600    /// A null pointer is allowed, and converted into None.
601    ///
602    /// Whatever the target of the original pointer (`input: *mut *mut T`), if `input` is non-null.
603    /// then `*input` is initialized to NULL.
604    ///
605    /// It is safe for `*input` to be uninitialized.
606    ///
607    /// # Safety
608    ///
609    /// As for
610    /// [`<*mut *mut T>::as_uninit_mut`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_uninit_mut).
611    pub(in crate::ffi) unsafe fn out_ptr_opt<'a, T>(
612        input: *mut *mut T,
613    ) -> Result<Option<OutPtr<'a, T>>, Void> {
614        Ok(unsafe { crate::ffi::util::OutPtr::from_opt_ptr(input, std::ptr::null_mut()) })
615    }
616
617    /// Try to convert a mutable pointer-to-value into an `Option<OutVal<T>>`.
618    ///
619    /// A null pointer is allowed, and converted into None.
620    ///
621    /// Whatever the target of the original pointer (`input: *mut T`), if `input` is non-null.
622    /// then `*input` is initialized to T::default().
623    ///
624    /// It is safe for `*input` to be uninitialized.
625    ///
626    /// # Safety
627    ///
628    /// As for
629    /// [`<*mut T>::as_uninit_mut`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_uninit_mut).
630    pub(in crate::ffi) unsafe fn out_val_opt<'a, T>(
631        input: *mut T,
632    ) -> Result<Option<OutVal<'a, T>>, Void>
633    where
634        T: Default,
635    {
636        Ok(unsafe { crate::ffi::util::OutVal::from_opt_ptr(input, T::default()) })
637    }
638
639    /// Try to convert a mutable pointer-to-socket into an `Option<OutSocketOwned>`.
640    ///
641    /// A null pointer is allowed, and converted into None.
642    ///
643    /// Whatever the target of the original pointer, if `input` is non-null,
644    /// then `*input` is initialized to -1 or `INVALID_SOCKET`.
645    ///
646    /// It is safe for `*input` to be uninitialized.
647    ///
648    /// # Safety
649    ///
650    /// As for
651    /// [`<* mut ArtiRpcRawSocket>::as_uninit_mut`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_uninit_mut).
652    ///
653    /// Additionally, if the resulting `ArtiRpcRawSocket` is used to send an fd/`SOCKET` to the
654    /// application, then the application becomes the owner of that socket, and is responsible for
655    /// making sure it is eventually closed.
656    pub(in crate::ffi) unsafe fn out_socket_owned_opt<'a>(
657        input: *mut ArtiRpcRawSocket,
658    ) -> Result<Option<OutSocketOwned<'a>>, Void> {
659        let optval: Option<OutVal<'a, ArtiRpcRawSocket>> =
660            unsafe { crate::ffi::util::OutVal::from_opt_ptr(input, ArtiRpcRawSocket::default()) };
661
662        Ok(optval.map(OutSocketOwned::from))
663    }
664}
665
666pub(super) use ffi_initialize;
667
668use super::ArtiRpcRawSocket;
669
670#[cfg(test)]
671mod test {
672    // @@ begin test lint list maintained by maint/add_warning @@
673    #![allow(clippy::bool_assert_comparison)]
674    #![allow(clippy::clone_on_copy)]
675    #![allow(clippy::dbg_macro)]
676    #![allow(clippy::mixed_attributes_style)]
677    #![allow(clippy::print_stderr)]
678    #![allow(clippy::print_stdout)]
679    #![allow(clippy::single_char_pattern)]
680    #![allow(clippy::unwrap_used)]
681    #![allow(clippy::unchecked_duration_subtraction)]
682    #![allow(clippy::useless_vec)]
683    #![allow(clippy::needless_pass_by_value)]
684    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
685
686    use super::*;
687
688    unsafe fn outptr_user(ptr: *mut *mut i8, set_to_val: Option<i8>) {
689        let ptr = unsafe { OutPtr::from_opt_ptr(ptr, std::ptr::null_mut()) };
690
691        if let Some(v) = set_to_val {
692            ptr.write_boxed_value_if_ptr_set(v);
693        }
694    }
695
696    #[test]
697    fn outptr() {
698        let mut ptr_to_int: *mut i8 = 7 as _; // This is a junk dangling pointer.  It will get overwritten.
699
700        // Case 1: Don't set to anything.
701        unsafe { outptr_user(&mut ptr_to_int as _, None) };
702        assert!(ptr_to_int.is_null());
703
704        // Cases 2, 3: Provide a null pointer for the output pointer.
705        ptr_to_int = 7 as _; // make it junk again.
706        unsafe { outptr_user(std::ptr::null_mut(), None) };
707        assert_eq!(ptr_to_int, 7 as _); // we didn't pass this in, so it wasn't set.
708        unsafe { outptr_user(std::ptr::null_mut(), Some(5)) };
709        assert_eq!(ptr_to_int, 7 as _); // we didn't pass this in, so it wasn't set.
710
711        // Case 4: Actually set something.
712        unsafe { outptr_user(&mut ptr_to_int as _, Some(123)) };
713        assert!(!ptr_to_int.is_null());
714        let boxed = unsafe { Box::from_raw(ptr_to_int) };
715        assert_eq!(*boxed, 123);
716    }
717}