foreign 0.4.0

Conversion between foreign and Rust types
Documentation
use std::alloc::{handle_alloc_error, Layout};

mod r#box;
mod cow;
mod option;
mod primitive;
mod slice;
mod string;
mod vec;

pub(crate) fn alloc<T>(layout: Layout) -> *mut T {
    // SAFETY: nothing that can go wrong here, the memory
    // is allocated by the C library and returned straight
    // away
    let p = unsafe { libc::aligned_alloc(layout.align(), layout.size()).cast::<T>() };
    if p.is_null() {
        handle_alloc_error(layout);
    }
    p
}

#[allow(clippy::undocumented_unsafe_blocks)]
#[cfg(test)]
pub(crate) mod tests {
    use crate::foreign::*;

    use std::ffi::{c_char, c_void, CStr, CString};
    use std::marker::PhantomData;
    use std::mem;
    use std::ptr;

    // Note that neither `PointerWithLifetime` nor `PrimitiveWithLifetime`
    // implement `FromForeign`.  Doing so would let the caller choose the
    // lifetime (see test_pointer_dangling_but_unsafe below).

    pub struct PointerWithLifetime<'s>(*const c_char, PhantomData<&'s CStr>);

    impl FreeForeign for PointerWithLifetime<'_> {
        type Foreign = *const c_char;

        unsafe fn free_foreign(p: *mut Self::Foreign) {
            libc::free(p.cast::<c_void>());
        }
    }

    impl<'s> PointerWithLifetime<'s> {
        fn new(bp: &BorrowedPointer<CStr, &'s CStr>) -> Self {
            PointerWithLifetime(bp.as_ptr(), PhantomData)
        }

        fn as_ptr(&self) -> *const *const c_char {
            &self.0
        }
    }

    impl<'s> BorrowForeign for PointerWithLifetime<'s> {
        type Storage<'a>
            = &'a Self
        where
            Self: 'a;

        fn borrow_foreign(&self) -> BorrowedPointer<Self, Self::Storage<'_>> {
            // SAFETY: no cells in sight, therefore the pointer in the
            // shared reference cannot change as long as the reference is alive
            unsafe { BorrowedPointer::new_borrowed(self, |raw| ptr::addr_of!((*raw).0)) }
        }
    }

    impl<'s> BorrowForeignMut for PointerWithLifetime<'s> {
        type Storage<'a>
            = BorrowedStorage<'a, Self>
        where
            Self: 'a;

        fn borrow_foreign_mut(&mut self) -> BorrowedMutPointer<Self, Self::Storage<'_>> {
            // SAFETY: this type's Foreign is the pointer it holds, so
            // the C representation is the address of field 0.
            unsafe { BorrowedMutPointer::new_borrowed(self, |raw| ptr::addr_of_mut!((*raw).0)) }
        }
    }

    /// A primitive type that borrows, whose C representation is itself.
    /// Unlike [`PointerWithLifetime`], whose `Foreign` is a `*const c_char`,
    /// this satisfies the `FreeForeign<Foreign = T>` so it can be converted
    /// even in a slice, array or `Vec`.
    #[derive(Copy, Clone)]
    #[repr(transparent)]
    pub(crate) struct PrimitiveWithLifetime<'s>(pub(crate) u8, PhantomData<&'s u8>);

    impl<'s> PrimitiveWithLifetime<'s> {
        /// Ties `'s` to a real borrow.  Constructing one from a copied value
        /// would leave the lifetime unconstrained, inference would settle on
        /// `'static`, and any test using it would prove nothing.
        pub(crate) fn new(r: &'s u8) -> Self {
            PrimitiveWithLifetime(*r, PhantomData)
        }
    }

    impl<'s> FreeForeign for PrimitiveWithLifetime<'s> {
        type Foreign = PrimitiveWithLifetime<'s>;

        unsafe fn free_foreign(p: *mut Self::Foreign) {
            libc::free(p.cast::<c_void>());
        }
    }

    impl<'s> CloneToForeign for PrimitiveWithLifetime<'s> {
        fn clone_to_foreign(&self) -> OwnedPointer<Self> {
            // SAFETY: copying into a freshly allocated block
            unsafe {
                let p = libc::malloc(mem::size_of::<Self>()).cast::<Self::Foreign>();
                assert!(!p.is_null());
                *p = *self;
                OwnedPointer::new(p)
            }
        }
    }

    unsafe impl<'s> FixedAlloc for PrimitiveWithLifetime<'s> {
        unsafe fn clone_into_foreign(dest: *mut Self::Foreign, src: &Self) {
            ptr::write(dest, *src);
        }
    }

    impl<'s> BorrowForeignMut for PrimitiveWithLifetime<'s> {
        // Storage<'a>, not Storage<'s>: the storage records the *borrow*.
        // Naming 's here would leave 'a unconstrained, so the result would
        // not borrow self at all and two of them could exist at once.
        type Storage<'a>
            = BorrowedStorage<'a, Self>
        where
            Self: 'a;

        fn borrow_foreign_mut(&mut self) -> BorrowedMutPointer<Self, Self::Storage<'_>> {
            // SAFETY: the C representation of this type is itself,
            // since it is #[repr(transparent)]
            unsafe { BorrowedMutPointer::new_borrowed(self, |raw| raw) }
        }
    }

    #[test]
    fn test_borrow_struct() {
        let s = CString::new("hello world").unwrap();
        let mut st = PointerWithLifetime::new(&s.borrow_foreign());

        // does not compile:
        // drop(s);

        let b = st.borrow_foreign();
        let p = st.as_ptr();
        assert_eq!(b.as_ptr(), p);
        assert_eq!(st.borrow_foreign_mut().as_ptr(), p);

        // does not compile:
        // assert_eq!(b.as_ptr(), p);
    }

    #[test]
    fn test_borrow_box() {
        let s = CString::new("hello world").unwrap();
        let st = PointerWithLifetime::new(&s.borrow_foreign());
        let mut st = Box::new(st);

        // does not compile:
        // drop(s);

        let b = st.borrow_foreign();
        let p = st.as_ptr();
        assert_eq!(b.as_ptr(), p);
        assert_eq!(st.borrow_foreign_mut().as_ptr(), p);

        // does not compile:
        // assert_eq!(b.as_ptr(), p);
    }

    #[test]
    fn test_pointer_dangling_but_unsafe() {
        let _owned: OwnedPointer<PointerWithLifetime<'static>> = {
            let s = CString::new("hello").unwrap();
            unsafe {
                let cell =
                    libc::malloc(std::mem::size_of::<*const c_char>()).cast::<*const c_char>();
                *cell = PointerWithLifetime::new(&s.borrow_foreign()).0;
                OwnedPointer::new(cell)
            }

            // `s` is dropped here.  The pointer stored in `owned`
            // is dangling, but...
        };

        // ... it cannot be used here from safe code: this does not compile.
        // let s = owned.into_native();
    }

    #[test]
    fn test_borrow_struct_via_box() {
        let s = CString::new("hello world").unwrap();
        let st = PointerWithLifetime::new(&s.borrow_foreign());
        let st = Box::new(st);

        // does not compile:
        // drop(s);

        let p = st.as_ptr();
        assert_eq!(st.into_foreign().as_ptr(), p);
    }
}