1
//! Helpers for working with FFI.
2

            
3
use 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_.
13
pub(super) struct OutVal<'a, T>(&'a mut T);
14

            
15
/// Alias for an `OutVal` representing a `*mut *mut T`.
16
pub(super) type OutPtr<'a, T> = OutVal<'a, *mut T>;
17

            
18
impl<'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
8
    pub(super) unsafe fn from_opt_ptr(ptr: *mut T, initial_value: T) -> Option<Self> {
35
8
        if ptr.is_null() {
36
4
            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
4
            let ptr: &mut MaybeUninit<T> = unsafe { &mut *(ptr as *mut MaybeUninit<T>) };
44
4
            let ptr: &mut T = ptr.write(initial_value);
45
4
            Some(OutVal(ptr))
46
        }
47
8
    }
48

            
49
    /// Consume this `OutVal` and the provided value, writing the value into the outval.
50
2
    pub(super) fn write_value(self, value: T) {
51
2
        // Note that all the unsafety happened when we constructed a &mut from the pointer.
52
2
        //
53
2
        // Note also that this method consumes `self`.  That's because we want to avoid multiple
54
2
        // writes to the same OutVal: If we did that, we would sometimes have to free a previous
55
2
        // value.
56
2
        *self.0 = value;
57
2
    }
58
}
59

            
60
impl<'a, T> OutVal<'a, *mut T> {
61
    /// Consume this `OutPtr` and the provided value, writing the value into the outptr.
62
2
    pub(super) fn write_value_boxed(self, value: T) {
63
2
        self.write_value(Box::into_raw(Box::new(value)));
64
2
    }
65
}
66

            
67
/// Implement OptOutPtrExt and OptOutValExt.
68
///
69
/// This is a separate module so we can seal these traits.
70
mod 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
4
        fn write_boxed_value_if_ptr_set(self, value: T) {
99
4
            if let Some(outptr) = self {
100
2
                outptr.write_value_boxed(value);
101
2
            }
102
4
        }
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
}
112
pub(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)]
118
pub(super) struct OutSocketOwned<'a>(OutVal<'a, ArtiRpcRawSocket>);
119

            
120
#[cfg(not(windows))]
121
use std::os::fd::IntoRawFd as IntoRawSocketTrait;
122
#[cfg(windows)]
123
use std::os::windows::io::IntoRawSocket as IntoRawSocketTrait;
124

            
125
impl<'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.
322
macro_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
}
379
pub(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.
436
macro_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
}
469
pub(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`.
493
macro_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)]
542
pub(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

            
666
pub(super) use ffi_initialize;
667

            
668
use super::ArtiRpcRawSocket;
669

            
670
#[cfg(test)]
671
mod 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
}