foreign 0.4.0

Conversion between foreign and Rust types
Documentation
use std::alloc::Layout;
use std::ffi::c_void;

use crate::foreign::*;
use crate::r#impl::alloc;

impl<T> FreeForeign for [T]
where
    T: FixedAlloc,
{
    type Foreign = T::Foreign;

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

impl<T> CloneToForeign for [T]
where
    T: FixedAlloc,
{
    fn clone_to_foreign(&self) -> OwnedPointer<Self> {
        // Ensure 0 is not passed to malloc, which would result in
        // an OwnedPointer with a NULL pointer inside.
        let len = if self.is_empty() { 1 } else { self.len() };
        // Each element occupies T::FOREIGN_LEN values of Self::Foreign, not one.
        let len = len
            .checked_mul(T::FOREIGN_LEN)
            .expect("slice is too large to convert to a C array");
        let layout = Layout::array::<Self::Foreign>(len)
            .expect("slice is too large to convert to a C array");
        // SAFETY: self.as_ptr() is guaranteed to point to the same number of bytes
        // as the freshly allocated destination
        unsafe {
            let p = alloc::<Self::Foreign>(layout);
            T::clone_from_native_slice(p, self);
            OwnedPointer::new(p)
        }
    }
}

impl<T> BorrowForeign for [T]
where
    T: FixedAlloc + FreeForeign<Foreign = T> + BorrowForeign,
{
    type Storage<'a>
        = &'a Self
    where
        Self: 'a;

    fn borrow_foreign(&self) -> BorrowedPointer<Self, Self::Storage<'_>> {
        // SAFETY: data behind a shared reference cannot move
        // as long as the reference is alive
        unsafe { BorrowedPointer::new_borrowed(self, |raw| raw.cast::<T>()) }
    }
}

impl<T> BorrowForeignMut for [T]
where
    T: FixedAlloc + FreeForeign<Foreign = T> + BorrowForeignMut,
{
    type Storage<'a>
        = BorrowedStorage<'a, Self>
    where
        Self: 'a;

    fn borrow_foreign_mut(&mut self) -> BorrowedMutPointer<Self, Self::Storage<'_>> {
        // SAFETY: a slice pointer cast to its element type addresses the
        // first element, which is the C representation
        unsafe { BorrowedMutPointer::new_borrowed(self, |raw| raw.cast::<T>()) }
    }
}

impl<T, const N: usize> FreeForeign for [T; N]
where
    T: FixedAlloc,
{
    type Foreign = T::Foreign;

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

impl<T, const N: usize> CloneToForeign for [T; N]
where
    T: FixedAlloc,
{
    fn clone_to_foreign(&self) -> OwnedPointer<Self> {
        self[..].clone_to_foreign().into()
    }
}

impl<T, const N: usize> FromForeign for [T; N]
where
    T: FixedAllocFrom,
{
    unsafe fn cloned_from_foreign(src: *const Self::Foreign) -> Self {
        T::clone_array_from_foreign(src)
    }
}

impl<T, const N: usize> BorrowForeign for [T; N]
where
    T: FixedAlloc + FreeForeign<Foreign = T> + BorrowForeign,
{
    type Storage<'a>
        = &'a [T; N]
    where
        Self: 'a;

    fn borrow_foreign(&self) -> BorrowedPointer<Self, Self::Storage<'_>> {
        // SAFETY: data behind a shared reference cannot move
        // as long as the reference is alive
        unsafe { BorrowedPointer::new_borrowed(self, |raw| raw.cast::<T>()) }
    }
}

impl<T, const N: usize> BorrowForeignMut for [T; N]
where
    T: FixedAlloc + FreeForeign<Foreign = T> + BorrowForeignMut,
{
    type Storage<'a>
        = BorrowedStorage<'a, Self>
    where
        Self: 'a;

    fn borrow_foreign_mut(&mut self) -> BorrowedMutPointer<Self, Self::Storage<'_>> {
        // SAFETY: an array is laid out as its elements, so the cast
        // addresses the first one
        unsafe { BorrowedMutPointer::new_borrowed(self, |raw| raw.cast::<T>()) }
    }
}

// SAFETY: the inner type is also fixed-allocation, and the
// resulting type can thus be represented as a fixed
// allocation that is N times larger.
unsafe impl<T, const N: usize> FixedAlloc for [T; N]
where
    T: FixedAlloc,
{
    // Self::Foreign is the element's Foreign type, so an array of N of them
    // occupies N times whatever one element occupies.
    const FOREIGN_LEN: usize = N * T::FOREIGN_LEN;

    unsafe fn clone_into_foreign(dest: *mut Self::Foreign, src: &Self) {
        T::clone_from_native_slice(dest, &src[..]);
    }
}

#[allow(clippy::undocumented_unsafe_blocks)]
#[cfg(test)]
mod tests {
    use std::ffi::c_void;
    use std::ptr;

    use crate::foreign::*;
    use crate::r#impl::tests::PrimitiveWithLifetime;

    #[test]
    fn test_slice_convert() {
        let a = [123i8, 45i8, 67i8];
        let i = &a[0..=1];
        let p = i.clone_to_foreign();
        unsafe {
            assert_eq!(
                libc::memcmp(
                    i.as_ptr().cast::<c_void>(),
                    p.as_ptr().cast::<c_void>(),
                    i.len()
                ),
                0
            );
            assert_eq!(i, <[i8; 2]>::cloned_from_foreign(p.as_ptr()));
        }

        let p = i.clone_to_foreign();
        unsafe {
            assert_eq!(i, <[i8; 2]>::from_foreign(p.into_inner()));
        }
    }

    #[test]
    fn test_slice_borrow() {
        let i = [123i8, 45i8];
        let borrowed = i.borrow_foreign();
        unsafe {
            assert_eq!(
                libc::memcmp(
                    i.as_ptr().cast::<c_void>(),
                    borrowed.as_ptr().cast::<c_void>(),
                    i.len()
                ),
                0
            );

            let cloned = <[i8; 2]>::cloned_from_foreign(borrowed.as_ptr());
            assert_eq!(i, cloned);
        }
    }

    #[test]
    fn test_slice_borrow_mut() {
        let expected = [123u8, 45u8];
        let mut i = expected;
        let mut borrowed = i.borrow_foreign_mut();
        unsafe {
            assert_eq!(
                libc::memcmp(
                    expected.as_ptr().cast::<c_void>(),
                    borrowed.as_ptr().cast::<c_void>(),
                    expected.len()
                ),
                0
            );

            ptr::write(borrowed.as_mut_ptr().offset(1), 234);
            let cloned = <[u8; 2]>::cloned_from_foreign(borrowed.as_ptr());

            assert_eq!(i[1], 234);
            assert_eq!(i, cloned);
        }
    }

    #[test]
    fn test_empty_slice_convert() {
        // A zero-length allocation must still produce a non-NULL pointer:
        // NULL inside an OwnedPointer means "absent" to Option, and
        // aligned_alloc() is free to return it for a size of zero.
        let empty: [i8; 0] = [];
        let p = empty.clone_to_foreign();
        assert_ne!(p.as_ptr(), ptr::null());

        let v: Vec<i8> = Vec::new();
        let p = v.clone_to_foreign();
        assert_ne!(p.as_ptr(), ptr::null());

        let p = empty[..].clone_to_foreign();
        assert_ne!(p.as_ptr(), ptr::null());
    }

    #[test]
    fn test_nested_array_convert() {
        // [T; N]'s Foreign is the *element's* Foreign type, so a nested array
        // occupies more than one of them; FOREIGN_LEN is what says so.
        let a: [[u8; 3]; 4] = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]];
        let p = a.clone_to_foreign();
        unsafe {
            let flat = std::slice::from_raw_parts(p.as_ptr(), 12);
            assert_eq!(flat, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
            assert_eq!(<[[u8; 3]; 4]>::cloned_from_foreign(p.as_ptr()), a);
        }

        // ...and via a slice of arrays, which is how Vec gets there too.
        let v: Vec<[u8; 3]> = a.to_vec();
        let p = v.clone_to_foreign();
        unsafe {
            let flat = std::slice::from_raw_parts(p.as_ptr(), 12);
            assert_eq!(flat, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
        }
    }

    #[test]
    fn test_borrowed_element_clone_to_foreign() {
        // does not compile, PrimitiveWithLifetime is not FixedAllocFrom:
        // let _ = unsafe { <[PrimitiveWithLifetime; 2]>::cloned_from_foreign(p.as_ptr()) };
        let n = 7u8;
        let a = [
            PrimitiveWithLifetime::new(&n),
            PrimitiveWithLifetime::new(&n),
        ];
        let p = a.clone_to_foreign();
        unsafe {
            assert_eq!((*p.as_ptr()).0, 7);
            assert_eq!((*p.as_ptr().add(1)).0, 7);
        }
    }

    #[test]
    fn test_array_convert() {
        let i = [123i8, 45i8];
        let p = i.clone_to_foreign();
        unsafe {
            assert_eq!(
                libc::memcmp(
                    i.as_ptr().cast::<c_void>(),
                    p.as_ptr().cast::<c_void>(),
                    i.len()
                ),
                0
            );
            assert_eq!(i, <[i8; 2]>::cloned_from_foreign(p.as_ptr()));
        }

        let p = i.clone_to_foreign();
        unsafe {
            assert_eq!(i, <[i8; 2]>::from_foreign(p.into_inner()));
        }
    }
}