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}