foreign 0.3.3

Conversion between foreign and Rust types
Documentation
use std::alloc::Layout;
use std::ffi::{c_char, c_void, CStr, CString};
use std::mem::ManuallyDrop;
use std::ptr;

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

impl FreeForeign for str {
    type Foreign = c_char;

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

impl CloneToForeign for str {
    /// Return a NUL-terminated copy of `self`, allocated with `malloc`.
    ///
    /// # Panics
    ///
    /// Panics if `self` contains a NUL byte, because C code would see a
    /// string truncated at that byte.  This matches the behaviour of
    /// [`into_foreign`](IntoForeign::into_foreign).
    fn clone_to_foreign(&self) -> OwnedPointer<Self> {
        assert!(
            !self.as_bytes().contains(&0),
            "cannot convert a string with an interior NUL byte to a C string"
        );

        let layout = Layout::array::<c_char>(self.len() + 1)
            .expect("string is too large to convert to a C string");
        // SAFETY: self.as_ptr() is guaranteed to point to self.len() bytes;
        // the destination is freshly allocated
        unsafe {
            let p = alloc::<Self::Foreign>(layout);
            ptr::copy_nonoverlapping(self.as_ptr().cast::<c_char>(), p, self.len());
            *p.add(self.len()) = 0;
            OwnedPointer::new(p)
        }
    }
}

impl FreeForeign for CStr {
    type Foreign = c_char;

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

impl CloneToForeign for CStr {
    fn clone_to_foreign(&self) -> OwnedPointer<Self> {
        let slice = self.to_bytes_with_nul();
        let layout = Layout::array::<c_char>(slice.len())
            .expect("string is too large to convert to a C string");
        // SAFETY: self.as_ptr() is guaranteed to point to self.len() bytes;
        // the destination is freshly allocated
        unsafe {
            let p = alloc::<Self::Foreign>(layout);
            ptr::copy_nonoverlapping(self.as_ptr().cast::<c_char>(), p, slice.len());
            OwnedPointer::new(p)
        }
    }
}

impl<'a> BorrowForeign<'a> for CStr {
    type Storage = &'a CStr;

    fn borrow_foreign(&self) -> BorrowedPointer<Self, &CStr> {
        // SAFETY: a CStr is a stable pointer
        unsafe { BorrowedPointer::new(self.as_ptr(), self) }
    }
}

impl FreeForeign for String {
    type Foreign = c_char;

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

impl CloneToForeign for String {
    /// Return a NUL-terminated copy of `self`, allocated with `malloc`.
    ///
    /// # Panics
    ///
    /// Panics if `self` contains a NUL byte, as for [`str`].
    fn clone_to_foreign(&self) -> OwnedPointer<Self> {
        self.as_str().clone_to_foreign().into()
    }
}

impl FromForeign for String {
    /// Copy the contents of the C string at `p` into a `String`.
    ///
    /// Byte sequences that are not valid UTF-8
    /// are replaced with U+FFFD REPLACEMENT CHARACTER, as in
    /// [`String::from_utf8_lossy`].  (This may change to a
    /// panic in the future).
    unsafe fn cloned_from_foreign(p: *const c_char) -> Self {
        let cstr = CStr::from_ptr(p);
        String::from_utf8_lossy(cstr.to_bytes()).into_owned()
    }
}

impl IntoForeign for String {
    type Storage = Vec<c_char>;

    /// Convert `self` into a NUL-terminated C string, reusing its
    /// allocation instead of copying.
    ///
    /// # Panics
    ///
    /// Panics if `self` contains a NUL byte, because the result would be
    /// truncated when read from C.
    fn into_foreign(self) -> BorrowedMutPointer<Self, Vec<c_char>> {
        CString::new(self).unwrap().into_foreign().into()
    }
}

impl IntoForeign for CString {
    type Storage = Vec<c_char>;

    fn into_foreign(self) -> BorrowedMutPointer<Self, Vec<c_char>> {
        let bytes = self.into_bytes_with_nul();

        // Change u8 to c_char.
        let mut bytes = ManuallyDrop::new(bytes);
        let (ptr, length, capacity) = (bytes.as_mut_ptr(), bytes.len(), bytes.capacity());
        // SAFETY: c_char has the same size and alignment as u8, therefore
        // Vec<u8> and Vec<c_char>'s allocations have the same layout; the
        // original `Vec` is wrapped in a ManuallyDrop to avoid double-free
        // of the storage.
        let bytes: Vec<c_char> = unsafe { Vec::from_raw_parts(ptr.cast(), length, capacity) };
        bytes.into_foreign().into()
    }
}

impl FreeForeign for CString {
    type Foreign = c_char;

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

impl CloneToForeign for CString {
    fn clone_to_foreign(&self) -> OwnedPointer<Self> {
        self.as_c_str().clone_to_foreign().into()
    }
}

impl FromForeign for CString {
    unsafe fn cloned_from_foreign(p: *const c_char) -> Self {
        CStr::from_ptr(p).to_owned()
    }
}

#[allow(clippy::undocumented_unsafe_blocks)]
#[cfg(test)]
mod tests {
    use std::ffi::{c_char, c_void, CStr, CString};

    use crate::c_str::c_str;
    use crate::foreign::*;

    #[test]
    fn test_cloned_from_foreign_string() {
        let s = "Hello, world!".to_string();
        let cstr = c_str!("Hello, world!");
        let cloned = unsafe { String::cloned_from_foreign(cstr.as_ptr()) };
        assert_eq!(s, cloned);
    }

    #[test]
    fn test_cloned_from_foreign_cstring() {
        let s = CString::new("Hello, world!").unwrap();
        let cloned = s.clone_to_foreign();
        let copy = unsafe { CString::cloned_from_foreign(cloned.as_ptr()) };
        assert_ne!(copy.as_ptr(), cloned.as_ptr());
        assert_ne!(copy.as_ptr(), s.as_ptr());
        assert_eq!(copy, s);
    }

    #[test]
    fn test_from_foreign_string() {
        let s = "Hello, world!".to_string();
        let cloned = s.clone_to_foreign_ptr();
        let copy = unsafe { String::from_foreign(cloned) };
        assert_eq!(s, copy);
    }

    #[test]
    fn test_owned_pointer_into() {
        let s = "Hello, world!";
        let cloned: OwnedPointer<String> = s.clone_to_foreign().into();
        let copy = cloned.into_native();
        assert_eq!(s, copy);
    }

    #[test]
    fn test_owned_pointer_into_native() {
        let s = "Hello, world!".to_string();
        let cloned = s.clone_to_foreign();
        let copy = cloned.into_native();
        assert_eq!(s, copy);
    }

    #[test]
    fn test_ptr_into_native() {
        let s = "Hello, world!".to_string();
        let cloned = s.clone_to_foreign_ptr();
        let copy: String = unsafe { cloned.into_native() };
        assert_eq!(s, copy);

        // This is why type bounds are needed... they aren't for
        // OwnedPointer::into_native
        let cloned = s.clone_to_foreign_ptr();
        let copy: c_char = unsafe { cloned.into_native() };
        assert_eq!(s.as_bytes()[0], copy as u8);
    }

    #[test]
    #[should_panic(expected = "interior NUL")]
    fn test_clone_to_foreign_str_interior_nul() {
        let _ = "Hello\0world!".clone_to_foreign();
    }

    #[test]
    #[should_panic(expected = "interior NUL")]
    fn test_clone_to_foreign_string_interior_nul() {
        let _ = "Hello\0world!".to_string().clone_to_foreign();
    }

    #[test]
    fn test_clone_to_foreign_str() {
        let s = "Hello, world!";
        let p = c_str!("Hello, world!").as_ptr();
        let cloned = s.clone_to_foreign();
        unsafe {
            let len = libc::strlen(cloned.as_ptr());
            assert_eq!(len, s.len());
            assert_eq!(
                libc::memcmp(
                    cloned.as_ptr().cast::<c_void>(),
                    p.cast::<c_void>(),
                    len + 1
                ),
                0
            );
        }
    }

    #[test]
    fn test_into_foreign_cstring() {
        let s = c_str!("Hello, world!").to_owned();
        let p = c_str!("Hello, world!");
        let mut consumed = s.into_foreign();
        unsafe {
            let len = libc::strlen(consumed.as_ptr());
            assert_eq!(len, p.to_bytes().len());
            assert_eq!(
                libc::memcmp(
                    consumed.as_ptr().cast::<c_void>(),
                    p.as_ptr().cast::<c_void>(),
                    len + 1
                ),
                0
            );

            *consumed.as_mut_ptr().offset(5) = 0;
            assert_eq!(String::cloned_from_foreign(consumed.as_ptr()), "Hello");
        }
    }

    #[test]
    fn test_into_foreign_string() {
        let s = "Hello, world!".to_string();
        let p = c_str!("Hello, world!");
        let mut consumed = s.into_foreign();
        unsafe {
            let len = libc::strlen(consumed.as_ptr());
            assert_eq!(len, p.to_bytes().len());
            assert_eq!(
                libc::memcmp(
                    consumed.as_ptr().cast::<c_void>(),
                    p.as_ptr().cast::<c_void>(),
                    len + 1
                ),
                0
            );

            *consumed.as_mut_ptr().offset(5) = 0;
            assert_eq!(String::cloned_from_foreign(consumed.as_ptr()), "Hello");
        }
    }

    #[test]
    fn test_clone_to_foreign_cstr() {
        let s: &CStr = c_str!("Hello, world!");
        let cloned = s.clone_to_foreign();
        unsafe {
            let len = libc::strlen(cloned.as_ptr());
            assert_eq!(len, s.to_bytes().len());
            assert_eq!(
                libc::memcmp(
                    cloned.as_ptr().cast::<c_void>(),
                    s.as_ptr().cast::<c_void>(),
                    len + 1
                ),
                0
            );
        }
    }

    #[test]
    fn test_clone_to_foreign_bytes() {
        let s = b"Hello, world!\0";
        let cloned = s.clone_to_foreign();
        unsafe {
            let len = libc::strlen(cloned.as_ptr().cast::<c_char>());
            assert_eq!(len, s.len() - 1);
            assert_eq!(
                libc::memcmp(
                    cloned.as_ptr().cast::<c_void>(),
                    s.as_ptr().cast::<c_void>(),
                    len + 1
                ),
                0
            );
        }
    }

    #[test]
    fn test_clone_to_foreign_cstring() {
        let s = CString::new("Hello, world!").unwrap();
        let cloned = s.clone_to_foreign();
        unsafe {
            let len = libc::strlen(cloned.as_ptr());
            assert_eq!(len, s.to_bytes().len());
            assert_ne!(s.as_ptr(), cloned.as_ptr());
            assert_eq!(
                libc::memcmp(
                    cloned.as_ptr().cast::<c_void>(),
                    s.as_ptr().cast::<c_void>(),
                    len + 1
                ),
                0
            );
        }
    }

    #[test]
    fn test_clone_to_foreign_string() {
        let s = "Hello, world!".to_string();
        let cstr = c_str!("Hello, world!");
        let cloned = s.clone_to_foreign();
        unsafe {
            let len = libc::strlen(cloned.as_ptr());
            assert_eq!(len, s.len());
            assert_eq!(
                libc::memcmp(
                    cloned.as_ptr().cast::<c_void>(),
                    cstr.as_ptr().cast::<c_void>(),
                    len + 1
                ),
                0
            );
        }
    }
}