foreign 0.4.0

Conversion between foreign and Rust types
Documentation
use crate::foreign::*;

impl<T: ?Sized> FreeForeign for Box<T>
where
    T: FreeForeign,
{
    type Foreign = <T as FreeForeign>::Foreign;

    unsafe fn free_foreign(x: *mut Self::Foreign) {
        T::free_foreign(x)
    }
}

impl<T: ?Sized> CloneToForeign for Box<T>
where
    T: CloneToForeign,
{
    fn clone_to_foreign(&self) -> OwnedPointer<Self> {
        self.as_ref().clone_to_foreign().into()
    }
}

impl<T> FromForeign for Box<T>
where
    T: FromForeign,
{
    unsafe fn cloned_from_foreign(p: *const Self::Foreign) -> Self {
        Box::new(T::cloned_from_foreign(p))
    }
}

impl<T: ?Sized> BorrowForeign for Box<T>
where
    T: BorrowForeign,
{
    type Storage<'a>
        = <T as BorrowForeign>::Storage<'a>
    where
        Self: 'a;

    fn borrow_foreign(&self) -> BorrowedPointer<Self, Self::Storage<'_>> {
        self.as_ref().borrow_foreign().into()
    }
}

/// Storage that owns a [`Box`] as a raw pointer.
///
/// The result of [`IntoForeign`] needs to keep its storage alive and
/// drop it when the result goes out of scope, but the storage cannot
/// be a `Box`: in the stacked borrows model, the box is retagged whenever
/// the [`BorrowedMutPointer`] holding it moves, and that invalidates the
/// pointer derived from it.  A raw pointer is not retagged, so keep
/// the box in that form and put it back together on drop.  Essentially,
/// this is emulating tree borrows just for the storage of
/// `Box::into_foreign`.
pub struct BoxStorage<T: ?Sized>(*mut T);

impl<T: ?Sized> Drop for BoxStorage<T> {
    fn drop(&mut self) {
        // SAFETY: the pointer came from Box::into_raw() and, since a
        // BoxStorage cannot be copied or cloned, is reconstituted once
        unsafe { drop(Box::from_raw(self.0)) }
    }
}

impl<T: ?Sized + FreeForeign> FreeForeign for BoxStorage<T> {
    type Foreign = <T as FreeForeign>::Foreign;

    unsafe fn free_foreign(p: *mut Self::Foreign) {
        T::free_foreign(p)
    }
}

impl<T: ?Sized + CloneToForeign> CloneToForeign for BoxStorage<T> {
    fn clone_to_foreign(&self) -> OwnedPointer<Self> {
        // SAFETY: BoxStorage owns the box for as long as it is alive
        let p = unsafe { (*self.0).clone_to_foreign() };
        OwnedPointer::from(p)
    }
}

impl<T: ?Sized> IntoForeign for Box<T>
where
    T: BorrowForeignMut,
{
    type Storage = BoxStorage<T>;

    fn into_foreign(self) -> BorrowedMutPointer<Self, Self::Storage> {
        let raw = Box::into_raw(self);
        // SAFETY: raw is valid and uniquely owned here.  Deriving the pointer
        // after into_raw() matters: into_raw() takes the box by value and
        // would retag it, invalidating a pointer taken beforehand.
        unsafe {
            let p = (*raw).borrow_foreign_mut().as_mut_ptr();
            BorrowedMutPointer::new(p, BoxStorage(raw))
        }
    }
}

impl<T: ?Sized> BorrowForeignMut for Box<T>
where
    T: BorrowForeignMut,
{
    type Storage<'a>
        = <T as BorrowForeignMut>::Storage<'a>
    where
        Self: 'a;

    fn borrow_foreign_mut(&mut self) -> BorrowedMutPointer<Self, Self::Storage<'_>> {
        self.as_mut().borrow_foreign_mut().into()
    }
}

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

    use std::ffi::{c_char, c_void};

    #[test]
    fn test_box_cloned_from_foreign() {
        // A box can be produced if the inner type has the capability.
        let s = Box::new("Hello, world!".to_string());
        let cstr = c_str!("Hello, world!");
        let cloned = unsafe { Box::<String>::cloned_from_foreign(cstr.as_ptr()) };
        assert_eq!(s, cloned);
    }

    #[test]
    fn test_box_clone_to_foreign() {
        // A box can be turned into a foreign object if the inner type has the capability.
        let s = Box::new(128u32);
        let cloned = s.clone_to_foreign();
        assert_eq!(*s, unsafe { *(cloned.as_ptr()) });
    }

    #[test]
    fn test_box_into_foreign() {
        // A box can be consumed if the inner type can be borrowed.
        let s = Box::new([128i32, 256i32]);
        let consumed = s.into_foreign();
        assert_eq!(unsafe { *consumed.as_ptr() }, 128);
        assert_eq!(unsafe { *consumed.as_ptr().offset(1) }, 256);
    }

    #[test]
    fn test_box_into_foreign_clone_to_foreign() {
        // BoxStorage still owns the value, so the consumed pointer can be
        // cloned into a fresh C datum.
        let s = Box::new(128u32);
        let consumed = s.into_foreign();
        let cloned = consumed.clone_to_foreign();
        assert_ne!(cloned.as_ptr(), consumed.as_ptr());
        unsafe { assert_eq!(*cloned.as_ptr(), 128) };
    }

    #[test]
    fn test_box_into_foreign_write() {
        // As for Vec, but this is the case that needs BoxStorage: a Box held
        // in the storage is retagged when the BorrowedMutPointer moves, and
        // that invalidates the pointer being written through.
        let b = Box::new(123u16);
        let mut consumed = b.into_foreign();
        unsafe {
            *consumed.as_mut_ptr() = 456;
            assert_eq!(*consumed.as_ptr(), 456);
        }
        let cloned = consumed.clone_to_foreign();
        unsafe { assert_eq!(*cloned.as_ptr(), 456) };
    }

    #[test]
    fn test_box_borrow() {
        // Contents of a Box can be borrowed.
        let s = Box::new(c_str!("Hello, world!"));
        let borrowed = s.borrow_foreign();
        let cloned = unsafe { Box::<String>::cloned_from_foreign(borrowed.as_ptr()) };
        assert_eq!(s.to_str().unwrap(), *cloned);
    }

    #[test]
    fn test_box_borrow_clone_to_foreign() {
        // As for Vec: a Box's borrow forwards the inner type's storage.
        let b: Box<u16> = Box::new(456);
        let p = b.borrow_foreign().clone_to_foreign_ptr();
        unsafe {
            assert_eq!(*p, 456);
            libc::free(p.cast::<c_void>());
        }
    }

    #[test]
    fn test_box_borrow_mut() {
        // Contents of a Box can be borrowed.
        let mut s = Box::new(123u16);
        let mut borrowed = s.borrow_foreign_mut();
        unsafe { *borrowed.as_mut_ptr() = 456 };
        assert_eq!(*s, 456);
    }

    #[test]
    fn test_box_unsized() {
        let original = c_str!("Hello, world!");
        let boxed = original.to_owned().into_bytes_with_nul().into_boxed_slice();
        let borrowed = boxed.borrow_foreign();
        unsafe {
            let len = libc::strlen(borrowed.as_ptr().cast::<c_char>());
            assert_eq!(len, original.to_bytes().len());
            assert_eq!(
                libc::memcmp(
                    borrowed.as_ptr().cast::<c_void>(),
                    original.as_ptr().cast::<c_void>(),
                    len + 1
                ),
                0
            );
        }
    }
}