ffi_convert/
conversions.rs

1use std::ffi::NulError;
2use std::str::Utf8Error;
3
4use thiserror::Error;
5
6macro_rules! impl_c_repr_of_for {
7    ($typ:ty) => {
8        impl CReprOf<$typ> for $typ {
9            fn c_repr_of(input: $typ) -> Result<$typ, CReprOfError> {
10                Ok(input)
11            }
12        }
13    };
14
15    ($from_typ:ty, $to_typ:ty) => {
16        impl CReprOf<$from_typ> for $to_typ {
17            fn c_repr_of(input: $from_typ) -> Result<$to_typ, CReprOfError> {
18                Ok(input as $to_typ)
19            }
20        }
21    };
22}
23
24/// implements a noop implementation of the CDrop trait for a given type.
25macro_rules! impl_c_drop_for {
26    ($typ:ty) => {
27        impl CDrop for $typ {
28            fn do_drop(&mut self) -> Result<(), CDropError> {
29                Ok(())
30            }
31        }
32    };
33}
34
35macro_rules! impl_as_rust_for {
36    ($typ:ty) => {
37        impl AsRust<$typ> for $typ {
38            fn as_rust(&self) -> Result<$typ, AsRustError> {
39                Ok(*self)
40            }
41        }
42    };
43
44    ($from_typ:ty, $to_typ:ty) => {
45        impl AsRust<$to_typ> for $from_typ {
46            fn as_rust(&self) -> Result<$to_typ, AsRustError> {
47                Ok(*self as $to_typ)
48            }
49        }
50    };
51}
52
53macro_rules! impl_rawpointerconverter_for {
54    ($typ:ty) => {
55        impl RawPointerConverter<$typ> for $typ {
56            fn into_raw_pointer(self) -> *const $typ {
57                convert_into_raw_pointer(self)
58            }
59            fn into_raw_pointer_mut(self) -> *mut $typ {
60                convert_into_raw_pointer_mut(self)
61            }
62            unsafe fn from_raw_pointer(
63                input: *const $typ,
64            ) -> Result<Self, UnexpectedNullPointerError> {
65                take_back_from_raw_pointer(input)
66            }
67            unsafe fn from_raw_pointer_mut(
68                input: *mut $typ,
69            ) -> Result<Self, UnexpectedNullPointerError> {
70                take_back_from_raw_pointer_mut(input)
71            }
72        }
73    };
74}
75
76#[derive(Error, Debug)]
77pub enum CReprOfError {
78    #[error("A string contains a nul bit")]
79    StringContainsNullBit(#[from] NulError),
80    #[error("An error occurred during conversion to C repr; {}", .0)]
81    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
82}
83
84/// Trait showing that the struct implementing it is a `repr(C)` compatible view of the parametrized
85/// type that can be created from an value of this type.
86pub trait CReprOf<T>: Sized + CDrop {
87    fn c_repr_of(input: T) -> Result<Self, CReprOfError>;
88}
89
90#[derive(Error, Debug)]
91pub enum CDropError {
92    #[error("unexpected null pointer")]
93    NullPointer(#[from] UnexpectedNullPointerError),
94    #[error("An error occurred while dropping C struct: {}", .0)]
95    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
96}
97
98/// Trait showing that the C-like struct implementing it can free up its part of memory that are not
99/// managed by Rust.
100pub trait CDrop {
101    fn do_drop(&mut self) -> Result<(), CDropError>;
102}
103
104#[derive(Error, Debug)]
105pub enum AsRustError {
106    #[error("unexpected null pointer")]
107    NullPointer(#[from] UnexpectedNullPointerError),
108
109    #[error("could not convert string as it is not UTF-8: {}", .0)]
110    Utf8Error(#[from] Utf8Error),
111    #[error("An error occurred during conversion to Rust: {}", .0)]
112    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
113}
114
115/// Trait showing that the struct implementing it is a `repr(C)` compatible view of the parametrized
116/// type and that an instance of the parametrized type can be created form this struct
117pub trait AsRust<T> {
118    fn as_rust(&self) -> Result<T, AsRustError>;
119}
120
121#[derive(Error, Debug)]
122#[error("Could not use raw pointer: unexpected null pointer")]
123pub struct UnexpectedNullPointerError;
124
125/// Trait representing the creation of a raw pointer from a struct and the recovery of said pointer.
126///
127/// The `from_raw_pointer` function should be used only on pointers obtained through the
128/// `into_raw_pointer` method (and is thus unsafe as we don't have any way to get insurance of that
129/// from the compiler).
130///
131/// The `from_raw_pointer` effectively takes back ownership of the pointer. If you didn't create the
132/// pointer yourself, please use the `as_ref` method on the raw pointer to borrow it
133pub trait RawPointerConverter<T>: Sized {
134    /// Creates a raw pointer from the value and leaks it, you should use [`Self::from_raw_pointer`]
135    /// or [`Self::drop_raw_pointer`] to free the value when you're done with it.
136    fn into_raw_pointer(self) -> *const T;
137    /// Creates a mutable raw pointer from the value and leaks it, you should use
138    /// [`Self::from_raw_pointer_mut`] or [`Self::drop_raw_pointer_mut`] to free the value when
139    /// you're done with it.
140    fn into_raw_pointer_mut(self) -> *mut T;
141    /// Takes back control of a raw pointer created by [`Self::into_raw_pointer`].
142    /// # Safety
143    /// This method is unsafe because passing it a pointer that was not created by
144    /// [`Self::into_raw_pointer`] can lead to memory problems. Also note that passing the same pointer
145    /// twice to this function will probably result in a double free
146    unsafe fn from_raw_pointer(input: *const T) -> Result<Self, UnexpectedNullPointerError>;
147    /// Takes back control of a raw pointer created by [`Self::into_raw_pointer_mut`].
148    /// # Safety
149    /// This method is unsafe because passing it a pointer that was not created by
150    /// [`Self::into_raw_pointer_mut`] can lead to memory problems. Also note that passing the same
151    /// pointer twice to this function will probably result in a double free
152    unsafe fn from_raw_pointer_mut(input: *mut T) -> Result<Self, UnexpectedNullPointerError>;
153
154    /// Takes back control of a raw pointer created by [`Self::into_raw_pointer`] and drop it.
155    /// # Safety
156    /// This method is unsafe for the same reasons as [`Self::from_raw_pointer`]
157    unsafe fn drop_raw_pointer(input: *const T) -> Result<(), UnexpectedNullPointerError> {
158        Self::from_raw_pointer(input).map(|_| ())
159    }
160
161    /// Takes back control of a raw pointer created by [`Self::into_raw_pointer_mut`] and drops it.
162    /// # Safety
163    /// This method is unsafe for the same reasons a [`Self::from_raw_pointer_mut`]
164    unsafe fn drop_raw_pointer_mut(input: *mut T) -> Result<(), UnexpectedNullPointerError> {
165        Self::from_raw_pointer_mut(input).map(|_| ())
166    }
167}
168
169#[doc(hidden)]
170pub fn convert_into_raw_pointer<T>(pointee: T) -> *const T {
171    Box::into_raw(Box::new(pointee)) as _
172}
173
174#[doc(hidden)]
175pub fn convert_into_raw_pointer_mut<T>(pointee: T) -> *mut T {
176    Box::into_raw(Box::new(pointee))
177}
178
179#[doc(hidden)]
180pub unsafe fn take_back_from_raw_pointer<T>(
181    input: *const T,
182) -> Result<T, UnexpectedNullPointerError> {
183    take_back_from_raw_pointer_mut(input as _)
184}
185
186#[doc(hidden)]
187pub unsafe fn take_back_from_raw_pointer_mut<T>(
188    input: *mut T,
189) -> Result<T, UnexpectedNullPointerError> {
190    if input.is_null() {
191        Err(UnexpectedNullPointerError)
192    } else {
193        Ok(*Box::from_raw(input))
194    }
195}
196
197/// Trait to create borrowed references to type T, from a raw pointer to a T. Note that this is
198/// implemented for all types.
199pub trait RawBorrow<T> {
200    /// Get a reference on the value behind the pointer or return an error if the pointer is `null`.
201    /// # Safety
202    /// As this is using `*const T::as_ref()` this is unsafe for exactly the same reasons.
203    unsafe fn raw_borrow<'a>(input: *const T) -> Result<&'a Self, UnexpectedNullPointerError>;
204}
205
206/// Trait to create mutable borrowed references to type T, from a raw pointer to a T. Note that this
207/// is implemented for all types.
208pub trait RawBorrowMut<T> {
209    /// Get a mutable reference on the value behind the pointer or return an error if the pointer is
210    /// `null`.
211    /// # Safety
212    /// As this is using `*mut T:as_ref()` this is unsafe for exactly the same reasons.
213    unsafe fn raw_borrow_mut<'a>(input: *mut T)
214        -> Result<&'a mut Self, UnexpectedNullPointerError>;
215}
216
217/// Trait that allows obtaining a borrowed reference to a type T from a raw pointer to T
218impl<T> RawBorrow<T> for T {
219    unsafe fn raw_borrow<'a>(input: *const T) -> Result<&'a Self, UnexpectedNullPointerError> {
220        input.as_ref().ok_or(UnexpectedNullPointerError)
221    }
222}
223
224/// Trait that allows obtaining a mutable borrowed reference to a type T from a raw pointer to T
225impl<T> RawBorrowMut<T> for T {
226    unsafe fn raw_borrow_mut<'a>(
227        input: *mut T,
228    ) -> Result<&'a mut Self, UnexpectedNullPointerError> {
229        input.as_mut().ok_or(UnexpectedNullPointerError)
230    }
231}
232
233impl RawPointerConverter<libc::c_void> for std::ffi::CString {
234    fn into_raw_pointer(self) -> *const libc::c_void {
235        self.into_raw() as _
236    }
237
238    fn into_raw_pointer_mut(self) -> *mut libc::c_void {
239        self.into_raw() as _
240    }
241
242    unsafe fn from_raw_pointer(
243        input: *const libc::c_void,
244    ) -> Result<Self, UnexpectedNullPointerError> {
245        Self::from_raw_pointer_mut(input as *mut libc::c_void)
246    }
247
248    unsafe fn from_raw_pointer_mut(
249        input: *mut libc::c_void,
250    ) -> Result<Self, UnexpectedNullPointerError> {
251        if input.is_null() {
252            Err(UnexpectedNullPointerError)
253        } else {
254            Ok(std::ffi::CString::from_raw(input as *mut libc::c_char))
255        }
256    }
257}
258
259impl RawPointerConverter<libc::c_char> for std::ffi::CString {
260    fn into_raw_pointer(self) -> *const libc::c_char {
261        self.into_raw() as _
262    }
263
264    fn into_raw_pointer_mut(self) -> *mut libc::c_char {
265        self.into_raw()
266    }
267
268    unsafe fn from_raw_pointer(
269        input: *const libc::c_char,
270    ) -> Result<Self, UnexpectedNullPointerError> {
271        Self::from_raw_pointer_mut(input as *mut libc::c_char)
272    }
273
274    unsafe fn from_raw_pointer_mut(
275        input: *mut libc::c_char,
276    ) -> Result<Self, UnexpectedNullPointerError> {
277        if input.is_null() {
278            Err(UnexpectedNullPointerError)
279        } else {
280            Ok(std::ffi::CString::from_raw(input as *mut libc::c_char))
281        }
282    }
283}
284
285impl RawBorrow<libc::c_char> for std::ffi::CStr {
286    unsafe fn raw_borrow<'a>(
287        input: *const libc::c_char,
288    ) -> Result<&'a Self, UnexpectedNullPointerError> {
289        if input.is_null() {
290            Err(UnexpectedNullPointerError)
291        } else {
292            Ok(Self::from_ptr(input))
293        }
294    }
295}
296
297impl_c_drop_for!(usize);
298impl_c_drop_for!(i8);
299impl_c_drop_for!(u8);
300impl_c_drop_for!(i16);
301impl_c_drop_for!(u16);
302impl_c_drop_for!(i32);
303impl_c_drop_for!(u32);
304impl_c_drop_for!(i64);
305impl_c_drop_for!(u64);
306impl_c_drop_for!(f32);
307impl_c_drop_for!(f64);
308impl_c_drop_for!(bool);
309impl_c_drop_for!(std::ffi::CString);
310
311impl_c_repr_of_for!(usize);
312impl_c_repr_of_for!(i8);
313impl_c_repr_of_for!(u8);
314impl_c_repr_of_for!(i16);
315impl_c_repr_of_for!(u16);
316impl_c_repr_of_for!(i32);
317impl_c_repr_of_for!(u32);
318impl_c_repr_of_for!(i64);
319impl_c_repr_of_for!(u64);
320impl_c_repr_of_for!(f32);
321impl_c_repr_of_for!(f64);
322impl_c_repr_of_for!(bool);
323
324impl_c_repr_of_for!(usize, i32);
325
326impl CReprOf<String> for std::ffi::CString {
327    fn c_repr_of(input: String) -> Result<Self, CReprOfError> {
328        Ok(std::ffi::CString::new(input)?)
329    }
330}
331
332impl_as_rust_for!(usize);
333impl_as_rust_for!(i8);
334impl_as_rust_for!(u8);
335impl_as_rust_for!(i16);
336impl_as_rust_for!(u16);
337impl_as_rust_for!(i32);
338impl_as_rust_for!(u32);
339impl_as_rust_for!(i64);
340impl_as_rust_for!(u64);
341impl_as_rust_for!(f32);
342impl_as_rust_for!(f64);
343impl_as_rust_for!(bool);
344
345impl_as_rust_for!(i32, usize);
346
347impl AsRust<String> for std::ffi::CStr {
348    fn as_rust(&self) -> Result<String, AsRustError> {
349        self.to_str().map(|s| s.to_owned()).map_err(|e| e.into())
350    }
351}
352
353impl_rawpointerconverter_for!(usize);
354impl_rawpointerconverter_for!(i16);
355impl_rawpointerconverter_for!(u16);
356impl_rawpointerconverter_for!(i32);
357impl_rawpointerconverter_for!(u32);
358impl_rawpointerconverter_for!(i64);
359impl_rawpointerconverter_for!(u64);
360impl_rawpointerconverter_for!(f32);
361impl_rawpointerconverter_for!(f64);
362impl_rawpointerconverter_for!(bool);
363
364impl<U, T: CReprOf<U>, const N: usize> CReprOf<[U; N]> for [T; N]
365where
366    [T; N]: CDrop,
367{
368    fn c_repr_of(input: [U; N]) -> Result<[T; N], CReprOfError> {
369        // TODO passing through a Vec here is a bit ugly, but as the conversion call may fail,
370        // TODO we don't want tobe in the case where we're in the middle of the conversion of the
371        // TODO array and we encounter an error, hence leaving the array partially uninitialised for
372        // TODO rust to try to cleanup. the try_map unstable method on array would be nice here
373        let result_vec: Result<Vec<T>, CReprOfError> =
374            input.into_iter().map(T::c_repr_of).collect();
375        let vec = result_vec?;
376
377        assert_eq!(vec.len(), N);
378
379        let mut result: [T; N] = unsafe { std::mem::zeroed() }; // we'll replace everything so "should" be good
380
381        for (i, t) in vec.into_iter().enumerate() {
382            result[i] = t;
383        }
384
385        Ok(result)
386    }
387}
388
389impl<T: CDrop, const N: usize> CDrop for [T; N] {
390    fn do_drop(&mut self) -> Result<(), CDropError> {
391        let result: Result<Vec<()>, CDropError> = self.iter_mut().map(T::do_drop).collect();
392        result?;
393        Ok(())
394    }
395}
396
397impl<U: AsRust<T>, T, const N: usize> AsRust<[T; N]> for [U; N] {
398    fn as_rust(&self) -> Result<[T; N], AsRustError> {
399        // TODO passing through a Vec here is a bit ugly, but as the conversion call may fail,
400        // TODO we don't want tobe in the case where we're in the middle of the conversion of the
401        // TODO array and we encounter an error, hence leaving the array partially uninitialised for
402        // TODO rust to try to cleanup. the try_map unstable method on array would be nice here
403        let result_vec: Result<Vec<T>, AsRustError> = self.iter().map(U::as_rust).collect();
404        let vec = result_vec?;
405
406        assert_eq!(vec.len(), N);
407
408        let mut result: [T; N] = unsafe { std::mem::zeroed() }; // we'll replace everything so "should" be good
409
410        for (i, t) in vec.into_iter().enumerate() {
411            result[i] = t;
412        }
413
414        Ok(result)
415    }
416}