Provide sample implementations in util::foreign for strings and elementary integer types, and use them to test the code.
Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- qemu/Cargo.toml | 4 + qemu/src/util/foreign.rs | 456 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 474 insertions(+) diff --git a/qemu/Cargo.toml b/qemu/Cargo.toml index 18d0fa4..1100725 100644 --- a/qemu/Cargo.toml +++ b/qemu/Cargo.toml @@ -5,3 +5,7 @@ edition = "2021" [dependencies] const-default = { version = "~1", features = ["derive"] } +libc = "^0" + +[dev-dependencies] +matches = ">=0" diff --git a/qemu/src/util/foreign.rs b/qemu/src/util/foreign.rs index a591925..0b8b708 100644 --- a/qemu/src/util/foreign.rs +++ b/qemu/src/util/foreign.rs @@ -5,6 +5,7 @@ /// Similar to glib-rs but a bit simpler and possibly more /// idiomatic. use std::borrow::Cow; +use std::ffi::{c_char, c_void, CStr, CString}; use std::fmt; use std::fmt::Debug; use std::mem; @@ -22,6 +23,14 @@ pub trait CloneToForeign { /// # Safety /// /// `p` must be `NULL` or point to valid data. + /// + /// ``` + /// # use qemu::CloneToForeign; + /// let foreign = "Hello, world!".clone_to_foreign(); + /// unsafe { + /// String::free_foreign(foreign.into_inner()); + /// } + /// ``` unsafe fn free_foreign(p: *mut Self::Foreign); /// Convert a native Rust object to a foreign C struct, copying @@ -119,6 +128,17 @@ pub trait IntoNative<T> { /// /// `p` must point to valid data, or can be `NULL` if Self is an /// `Option` type. It becomes invalid after the function returns. + /// + /// ``` + /// # use qemu::{CloneToForeign, IntoNative}; + /// let s = "Hello, world!".to_string(); + /// let foreign = s.clone_to_foreign(); + /// let native: String = unsafe { + /// foreign.into_native() + /// // foreign is not leaked + /// }; + /// assert_eq!(s, native); + /// ``` unsafe fn into_native(self) -> T; } @@ -141,6 +161,15 @@ pub trait FromForeign: CloneToForeign + Sized { /// /// `p` must point to valid data, or can be `NULL` is `Self` is an /// `Option` type. + /// + /// ``` + /// # use qemu::FromForeign; + /// let p = c"Hello, world!".as_ptr(); + /// let s = unsafe { + /// String::cloned_from_foreign(p as *const std::ffi::c_char) + /// }; + /// assert_eq!(s, "Hello, world!"); + /// ``` unsafe fn cloned_from_foreign(p: *const Self::Foreign) -> Self; /// Convert a C datum to a native Rust object, taking ownership of @@ -152,6 +181,16 @@ pub trait FromForeign: CloneToForeign + Sized { /// /// `p` must point to valid data, or can be `NULL` is `Self` is an /// `Option` type. `p` becomes invalid after the function returns. + /// + /// ``` + /// # use qemu::{CloneToForeign, FromForeign}; + /// let s = "Hello, world!"; + /// let foreign = s.clone_to_foreign(); + /// unsafe { + /// assert_eq!(String::from_foreign(foreign.into_inner()), s); + /// } + /// // foreign is not leaked + /// ``` unsafe fn from_foreign(p: *mut Self::Foreign) -> Self { let result = Self::cloned_from_foreign(p); Self::free_foreign(p); @@ -176,6 +215,12 @@ impl<T: CloneToForeign + ?Sized> OwnedPointer<T> { /// Safely create an `OwnedPointer` from one that has the same /// freeing function. + /// ``` + /// # use qemu::{CloneToForeign, OwnedPointer}; + /// let s = "Hello, world!"; + /// let foreign_str = s.clone_to_foreign(); + /// let foreign_string = OwnedPointer::<String>::from(foreign_str); + /// # assert_eq!(foreign_string.into_native(), s); pub fn from<U>(x: OwnedPointer<U>) -> Self where U: CloneToForeign<Foreign = <T as CloneToForeign>::Foreign> + ?Sized, @@ -189,6 +234,12 @@ impl<T: CloneToForeign + ?Sized> OwnedPointer<T> { /// Safely convert an `OwnedPointer` into one that has the same /// freeing function. + /// ``` + /// # use qemu::{CloneToForeign, OwnedPointer}; + /// let s = "Hello, world!"; + /// let foreign_str = s.clone_to_foreign(); + /// let foreign_string: OwnedPointer<String> = foreign_str.into(); + /// # assert_eq!(foreign_string.into_native(), s); pub fn into<U>(self) -> OwnedPointer<U> where U: CloneToForeign<Foreign = <T as CloneToForeign>::Foreign>, @@ -198,6 +249,16 @@ impl<T: CloneToForeign + ?Sized> OwnedPointer<T> { /// Return the pointer that is stored in the `OwnedPointer`. The /// pointer is valid for as long as the `OwnedPointer` itself. + /// + /// ``` + /// # use qemu::CloneToForeign; + /// let s = "Hello, world!"; + /// let foreign = s.clone_to_foreign(); + /// let p = foreign.as_ptr(); + /// let len = unsafe { libc::strlen(p) }; + /// drop(foreign); + /// # assert_eq!(len, 13); + /// ``` pub fn as_ptr(&self) -> *const <T as CloneToForeign>::Foreign { self.ptr } @@ -208,6 +269,15 @@ impl<T: CloneToForeign + ?Sized> OwnedPointer<T> { /// Return the pointer that is stored in the `OwnedPointer`, /// consuming the `OwnedPointer` but not freeing the pointer. + /// + /// ``` + /// # use qemu::CloneToForeign; + /// let s = "Hello, world!"; + /// let p = s.clone_to_foreign().into_inner(); + /// let len = unsafe { libc::strlen(p) }; + /// // p needs to be freed manually + /// # assert_eq!(len, 13); + /// ``` pub fn into_inner(mut self) -> *mut <T as CloneToForeign>::Foreign { let result = mem::replace(&mut self.ptr, ptr::null_mut()); mem::forget(self); @@ -218,6 +288,17 @@ impl<T: CloneToForeign + ?Sized> OwnedPointer<T> { impl<T: FromForeign + ?Sized> OwnedPointer<T> { /// Convert a C datum to a native Rust object, taking ownership of /// the pointer or Rust object (same as `from_glib_full` in `glib-rs`) + /// + /// ``` + /// # use qemu::{CloneToForeign, IntoNative}; + /// let s = "Hello, world!".to_string(); + /// let foreign = s.clone_to_foreign(); + /// let native: String = unsafe { + /// foreign.into_native() + /// // foreign is not leaked + /// }; + /// assert_eq!(s, native); + /// ``` pub fn into_native(self) -> T { // SAFETY: the pointer was passed to the unsafe constructor OwnedPointer::new unsafe { T::from_foreign(self.into_inner()) } @@ -245,3 +326,378 @@ impl<T: CloneToForeign + ?Sized> Drop for OwnedPointer<T> { unsafe { T::free_foreign(p) } } } + +impl CloneToForeign for str { + type Foreign = c_char; + + unsafe fn free_foreign(ptr: *mut c_char) { + libc::free(ptr as *mut c_void); + } + + fn clone_to_foreign(&self) -> OwnedPointer<Self> { + // SAFETY: self.as_ptr() is guaranteed to point to self.len() bytes; + // the destination is freshly allocated + unsafe { + let p = libc::malloc(self.len() + 1) as *mut c_char; + ptr::copy_nonoverlapping(self.as_ptr() as *const c_char, p, self.len()); + *p.add(self.len()) = 0; + OwnedPointer::new(p) + } + } +} + +impl CloneToForeign for CStr { + type Foreign = c_char; + + unsafe fn free_foreign(ptr: *mut c_char) { + libc::free(ptr as *mut c_void); + } + + fn clone_to_foreign(&self) -> OwnedPointer<Self> { + // SAFETY: self.as_ptr() is guaranteed to point to self.len() bytes; + // the destination is freshly allocated + unsafe { + let slice = self.to_bytes_with_nul(); + let p = libc::malloc(slice.len()) as *mut c_char; + ptr::copy_nonoverlapping(self.as_ptr() as *const c_char, p, slice.len()); + OwnedPointer::new(p) + } + } +} + +impl CloneToForeign for String { + type Foreign = c_char; + + unsafe fn free_foreign(ptr: *mut c_char) { + libc::free(ptr as *mut c_void); + } + + fn clone_to_foreign(&self) -> OwnedPointer<Self> { + self.as_str().clone_to_foreign().into() + } +} + +impl FromForeign for String { + 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 CloneToForeign for CString { + type Foreign = c_char; + + unsafe fn free_foreign(ptr: *mut c_char) { + libc::free(ptr as *mut c_void); + } + + 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() + } +} + +impl FromForeign for Cow<'_, str> +{ + unsafe fn cloned_from_foreign(p: *const c_char) -> Self { + let cstr = CStr::from_ptr(p); + cstr.to_string_lossy() + } +} + +macro_rules! foreign_copy_type { + ($rust_type:ty, $foreign_type:ty) => { + impl CloneToForeign for $rust_type { + type Foreign = $foreign_type; + + unsafe fn free_foreign(ptr: *mut Self::Foreign) { + libc::free(ptr as *mut c_void); + } + + fn clone_to_foreign(&self) -> OwnedPointer<Self> { + // Safety: we are copying into a freshly-allocated block + unsafe { + let p = libc::malloc(mem::size_of::<Self>()) as *mut Self::Foreign; + *p = *self as Self::Foreign; + OwnedPointer::new(p) + } + } + } + + impl FromForeign for $rust_type { + unsafe fn cloned_from_foreign(p: *const Self::Foreign) -> Self { + *p + } + } + + impl CloneToForeign for [$rust_type] { + type Foreign = $foreign_type; + + unsafe fn free_foreign(ptr: *mut Self::Foreign) { + libc::free(ptr as *mut c_void); + } + + fn clone_to_foreign(&self) -> OwnedPointer<Self> { + // SAFETY: self.as_ptr() is guaranteed to point to the same number of bytes + // as the freshly allocated destination + unsafe { + let size = mem::size_of::<Self::Foreign>(); + let p = libc::malloc(self.len() * size) as *mut Self::Foreign; + ptr::copy_nonoverlapping(self.as_ptr() as *const Self::Foreign, p, self.len()); + OwnedPointer::new(p) + } + } + } + }; +} +foreign_copy_type!(i8, i8); +foreign_copy_type!(u8, u8); +foreign_copy_type!(i16, i16); +foreign_copy_type!(u16, u16); +foreign_copy_type!(i32, i32); +foreign_copy_type!(u32, u32); +foreign_copy_type!(i64, i64); +foreign_copy_type!(u64, u64); +foreign_copy_type!(isize, libc::ptrdiff_t); +foreign_copy_type!(usize, libc::size_t); +foreign_copy_type!(f32, f32); +foreign_copy_type!(f64, f64); + +#[cfg(test)] +mod tests { + #![allow(clippy::shadow_unrelated)] + + use super::*; + use matches::assert_matches; + use std::ffi::c_void; + + #[test] + fn test_foreign_int_convert() { + let i = 123i8; + let p = i.clone_to_foreign(); + unsafe { + assert_eq!(i, *p.as_ptr()); + assert_eq!(i, i8::cloned_from_foreign(p.as_ptr())); + } + assert_eq!(i, p.into_native()); + + let p = i.clone_to_foreign(); + unsafe { + assert_eq!(i, i8::from_foreign(p.into_inner())); + } + } + + #[test] + fn test_cloned_from_foreign_string_cow() { + let s = "Hello, world!".to_string(); + let cstr = c"Hello, world!"; + let cloned = unsafe { Cow::cloned_from_foreign(cstr.as_ptr()) }; + assert_eq!(s, cloned); + } + + #[test] + fn test_cloned_from_foreign_string() { + let s = "Hello, world!".to_string(); + let cstr = c"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_default() { + let s: String = Default::default(); + let foreign: OwnedPointer<String> = Default::default(); + let native = foreign.into_native(); + assert_eq!(s, native); + } + + #[test] + fn test_owned_pointer_into() { + let s = "Hello, world!".to_string(); + 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] + fn test_clone_to_foreign_str() { + let s = "Hello, world!"; + let p = c"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() as *const c_void, + p as *const c_void, + len + 1 + ), + 0 + ); + } + } + + #[test] + fn test_clone_to_foreign_cstr() { + let s: &CStr = c"Hello, world!"; + let cloned = s.clone_to_foreign(); + unsafe { + let len = libc::strlen(cloned.as_ptr()); + assert_eq!(len, s.count_bytes()); + assert_eq!( + libc::memcmp( + cloned.as_ptr() as *const c_void, + s.as_ptr() as *const c_void, + len + 1 + ), + 0 + ); + } + } + + #[test] + fn test_clone_to_foreign_string_cow() { + let p = c"Hello, world!".as_ptr(); + for s in vec![ + Into::<Cow<str>>::into("Hello, world!"), + Into::<Cow<str>>::into("Hello, world!".to_string())] { + 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() as *const c_void, + p as *const 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() as *const c_char); + assert_eq!(len, s.len() - 1); + assert_eq!( + libc::memcmp( + cloned.as_ptr() as *const c_void, + s.as_ptr() as *const 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 { + assert_ne!(s.as_ptr(), cloned.as_ptr()); + assert_eq!( + libc::strcmp( + cloned.as_ptr() as *const c_char, + s.as_ptr() as *const c_char, + ), + 0 + ); + } + } + + #[test] + fn test_clone_to_foreign_string() { + let s = "Hello, world!".to_string(); + let cstr = c"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() as *const c_void, + cstr.as_ptr() as *const c_void, + len + 1 + ), + 0 + ); + } + } + + #[test] + fn test_option() { + // An Option can be used to produce or convert NULL pointers + let s = Some("Hello, world!".to_string()); + let cstr = c"Hello, world!"; + unsafe { + assert_eq!(Option::<String>::cloned_from_foreign(cstr.as_ptr()), s); + assert_matches!(Option::<String>::cloned_from_foreign(ptr::null()), None); + assert_matches!(Option::<String>::from_foreign(ptr::null_mut()), None); + } + } + + #[test] + fn test_box() { + // A box can be produced if the inner type has the capability. + let s = Box::new("Hello, world!".to_string()); + let cstr = c"Hello, world!"; + let cloned = unsafe { Box::<String>::cloned_from_foreign(cstr.as_ptr()) }; + assert_eq!(s, cloned); + + let s = Some(Box::new("Hello, world!".to_string())); + let cloned = unsafe { Option::<Box<String>>::cloned_from_foreign(cstr.as_ptr()) }; + assert_eq!(s, cloned); + } +} -- 2.45.2