On Tue, Sep 7, 2021 at 10:41 PM <marcandre.lur...@redhat.com> wrote: > > From: Marc-André Lureau <marcandre.lur...@redhat.com> > > This crates provides common bindings and facilities for QEMU C API > shared by various projects. > > Most importantly, it defines the conversion traits used to convert from > C to Rust types. Those traits are largely adapted from glib-rs, since > those have proved to be very flexible, and should guide us to bind > further QEMU types such as QOM. If glib-rs becomes a dependency, we > should consider adopting glib translate traits. For QAPI, we need a > smaller subset. > > Cargo.lock is checked-in, as QEMU produces end-of-chain binaries, and it > is desirable to track the exact set of packages that are involved in > managed builds. > > Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> > --- > Cargo.lock | 63 +++++ > Cargo.toml | 4 +- > rust/common/Cargo.toml | 11 + > rust/common/src/error.rs | 113 ++++++++ > rust/common/src/ffi.rs | 93 +++++++ > rust/common/src/lib.rs | 21 ++ > rust/common/src/qemu.rs | 101 ++++++++ > rust/common/src/qmp.rs | 0 > rust/common/src/translate.rs | 482 +++++++++++++++++++++++++++++++++++ > 9 files changed, 887 insertions(+), 1 deletion(-) > create mode 100644 Cargo.lock > create mode 100644 rust/common/Cargo.toml > create mode 100644 rust/common/src/error.rs > create mode 100644 rust/common/src/ffi.rs > create mode 100644 rust/common/src/lib.rs > create mode 100644 rust/common/src/qemu.rs > create mode 100644 rust/common/src/qmp.rs > create mode 100644 rust/common/src/translate.rs > > diff --git a/Cargo.lock b/Cargo.lock > new file mode 100644 > index 0000000000..8dc2dd9da7 > --- /dev/null > +++ b/Cargo.lock > @@ -0,0 +1,63 @@ > +# This file is automatically @generated by Cargo. > +# It is not intended for manual editing. > +version = 3 > + > +[[package]] > +name = "autocfg" > +version = "1.0.1" > +source = "registry+https://github.com/rust-lang/crates.io-index" > +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" > + > +[[package]] > +name = "bitflags" > +version = "1.2.1" > +source = "registry+https://github.com/rust-lang/crates.io-index" > +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" > + > +[[package]] > +name = "cc" > +version = "1.0.70" > +source = "registry+https://github.com/rust-lang/crates.io-index" > +checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" > + > +[[package]] > +name = "cfg-if" > +version = "1.0.0" > +source = "registry+https://github.com/rust-lang/crates.io-index" > +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" > + > +[[package]] > +name = "common" > +version = "0.1.0" > +dependencies = [ > + "libc", > + "nix", > +] > + > +[[package]] > +name = "libc" > +version = "0.2.101" > +source = "registry+https://github.com/rust-lang/crates.io-index" > +checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" > + > +[[package]] > +name = "memoffset" > +version = "0.6.4" > +source = "registry+https://github.com/rust-lang/crates.io-index" > +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" > +dependencies = [ > + "autocfg", > +] > + > +[[package]] > +name = "nix" > +version = "0.20.1" > +source = "registry+https://github.com/rust-lang/crates.io-index" > +checksum = "df8e5e343312e7fbeb2a52139114e9e702991ef9c2aea6817ff2440b35647d56" > +dependencies = [ > + "bitflags", > + "cc", > + "cfg-if", > + "libc", > + "memoffset", > +] > diff --git a/Cargo.toml b/Cargo.toml > index c4b464ff15..14131eed3c 100644 > --- a/Cargo.toml > +++ b/Cargo.toml > @@ -1,2 +1,4 @@ > [workspace] > -members = [] > +members = [ > + "rust/common", > +] > diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml > new file mode 100644 > index 0000000000..6c240447f3 > --- /dev/null > +++ b/rust/common/Cargo.toml > @@ -0,0 +1,11 @@ > +[package] > +name = "common" > +version = "0.1.0" > +edition = "2018" > +publish = false > + > +[dependencies] > +libc = "0.2.92" > + > +[target."cfg(unix)".dependencies] > +nix = "0.20.0" > diff --git a/rust/common/src/error.rs b/rust/common/src/error.rs > new file mode 100644 > index 0000000000..f166ac42ea > --- /dev/null > +++ b/rust/common/src/error.rs > @@ -0,0 +1,113 @@ > +use std::{self, ffi::CString, fmt, io, ptr}; > + > +use crate::translate::*;
It's not uncommon to ban wildcard imports that aren't preludes as it can make it confusing to read. Does QEMU have a stance on that type of thing? > +use crate::{ffi, qemu}; > + > +/// Common error type for QEMU and related projects. > +#[derive(Debug)] > +pub enum Error { > + /// A generic error with file and line location. > + FailedAt(String, &'static str, u32), > + /// An IO error. > + Io(io::Error), > + #[cfg(unix)] > + /// A nix error. > + Nix(nix::Error), > +} > + > +/// Alias for a `Result` with the error type for QEMU. > +pub type Result<T> = std::result::Result<T, Error>; I think this is very confusing. Rust developers expect `Result` to be the one from `std::result`, it would be better to call this `QEMUResult` > + > +impl Error { > + fn message(&self) -> String { > + use Error::*; Do we need this here? Why not put it at the top of the file? > + match self { > + FailedAt(msg, _, _) => msg.into(), > + Io(io) => format!("IO error: {}", io), > + #[cfg(unix)] > + Nix(nix) => format!("Nix error: {}", nix), > + } > + } > + > + fn location(&self) -> Option<(&'static str, u32)> { > + use Error::*; > + match self { > + FailedAt(_, file, line) => Some((file, *line)), > + Io(_) => None, > + #[cfg(unix)] > + Nix(_) => None, > + } > + } > +} > + > +impl fmt::Display for Error { > + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { > + use Error::*; > + match self { > + FailedAt(msg, file, line) => write!(f, "{} ({}:{})", msg, file, > line), > + _ => write!(f, "{}", self.message()), > + } > + } > +} > + > +impl From<io::Error> for Error { > + fn from(val: io::Error) -> Self { > + Error::Io(val) > + } > +} > + > +#[cfg(unix)] > +impl From<nix::Error> for Error { > + fn from(val: nix::Error) -> Self { > + Error::Nix(val) > + } > +} > + > +impl QemuPtrDefault for Error { > + type QemuType = *mut ffi::Error; > +} > + > +impl<'a> ToQemuPtr<'a, *mut ffi::Error> for Error { > + type Storage = qemu::CError; > + > + fn to_qemu_none(&'a self) -> Stash<'a, *mut ffi::Error, Self> { > + let err = self.to_qemu_full(); > + > + Stash(err, unsafe { from_qemu_full(err) }) > + } > + > + fn to_qemu_full(&self) -> *mut ffi::Error { > + let cmsg = > + CString::new(self.message()).expect("ToQemuPtr<Error>: > unexpected '\0' character"); > + let mut csrc = CString::new("").unwrap(); > + let (src, line) = self.location().map_or((ptr::null(), 0_i32), |loc| > { > + csrc = CString::new(loc.0).expect("ToQemuPtr<Error>:: unexpected > '\0' character"); > + (csrc.as_ptr() as *const libc::c_char, loc.1 as i32) > + }); > + let func = ptr::null(); > + > + let mut err: *mut ffi::Error = ptr::null_mut(); > + unsafe { > + ffi::error_setg_internal( > + &mut err as *mut *mut _, > + src, > + line, > + func, > + cmsg.as_ptr() as *const libc::c_char, > + ); > + err > + } > + } > +} > + > +/// Convenience macro to build a [`Error::FailedAt`] error. > +/// > +/// Returns a `Result::Err` with the file:line location. > +/// (the error can then be converted to a QEMU `ffi::Error`) > +#[allow(unused_macros)] > +#[macro_export] > +macro_rules! err { > + ($msg:expr) => { > + Err(Error::FailedAt($msg.into(), file!(), line!())) > + }; > +} > diff --git a/rust/common/src/ffi.rs b/rust/common/src/ffi.rs > new file mode 100644 > index 0000000000..82818d503a > --- /dev/null > +++ b/rust/common/src/ffi.rs > @@ -0,0 +1,93 @@ > +//! Bindings to the raw low-level C API commonly provided by QEMU projects. > +//! > +//! Manual bindings to C API availabe when linking QEMU projects. s/availabe/available/g > +//! It includes minimal glib allocation functions too, since it's the default > +//! allocator used by QEMU, and we don't depend on glib-rs crate yet). > +//! > +//! Higher-level Rust-friendly bindings are provided by different modules. > + > +use libc::{c_char, c_void, size_t}; > + > +extern "C" { > + pub fn g_malloc0(n_bytes: size_t) -> *mut c_void; > + pub fn g_free(ptr: *mut c_void); > + pub fn g_strndup(str: *const c_char, n: size_t) -> *mut c_char; > +} We can get there from the glib/glib_sys crate: https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib_sys/fn.g_malloc0.html If we only plan on using these 3 I think this approach is fine, but something to keep in mind if we use more glib functions. > + > +#[repr(C)] > +pub struct QObject(c_void); > + > +impl ::std::fmt::Debug for QObject { > + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { > + f.debug_struct(&format!("QObject @ {:?}", self as *const _)) > + .finish() > + } > +} > + > +#[repr(C)] > +pub struct QNull(c_void); > + > +impl ::std::fmt::Debug for QNull { > + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { > + f.debug_struct(&format!("QNull @ {:?}", self as *const _)) > + .finish() > + } > +} > + > +#[repr(C)] > +pub struct Error(c_void); > + > +impl ::std::fmt::Debug for Error { > + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { > + f.debug_struct(&format!("Error @ {:?}", self as *const _)) > + .finish() > + } > +} > + > +extern "C" { > + pub fn error_setg_internal( > + errp: *mut *mut Error, > + src: *const c_char, > + line: i32, > + func: *const c_char, > + fmt: *const c_char, > + ... > + ); > + pub fn error_get_pretty(err: *const Error) -> *const c_char; > + pub fn error_free(err: *mut Error); > +} > + > +/// Wrap a QMP hanlder. handler > +#[macro_export] > +macro_rules! qmp { > + // the basic return value variant > + ($e:expr, $errp:ident, $errval:expr) => {{ > + assert!(!$errp.is_null()); > + unsafe { > + *$errp = std::ptr::null_mut(); > + } > + > + match $e { > + Ok(val) => val, > + Err(err) => unsafe { > + *$errp = err.to_qemu_full(); > + $errval > + }, > + } > + }}; > + // the ptr return value variant > + ($e:expr, $errp:ident) => {{ > + assert!(!$errp.is_null()); > + unsafe { > + *$errp = std::ptr::null_mut(); > + } > + > + match $e { > + Ok(val) => val.to_qemu_full().into(), > + Err(err) => unsafe { > + *$errp = err.to_qemu_full(); > + std::ptr::null_mut() > + }, > + } > + }}; > +} It would be a good idea to document why this code is safe Alistair > diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs > new file mode 100644 > index 0000000000..4de826bc2e > --- /dev/null > +++ b/rust/common/src/lib.rs > @@ -0,0 +1,21 @@ > +//! Common code for QEMU > +//! > +//! This crates provides common bindings and facilities for QEMU C API > shared by > +//! various projects. Most importantly, it defines the conversion traits > used to > +//! convert from C to Rust types. Those traits are largely adapted from > glib-rs, > +//! since those have prooven to be very flexible, and should guide us to bind > +//! further QEMU types such as QOM. If glib-rs becomes a dependency, we > should > +//! consider adopting glib translate traits. For QAPI, we need a smaller > subset. > + > +pub use libc; > + > +mod error; > +pub use error::*; > + > +mod qemu; > +pub use qemu::*; > + > +mod translate; > +pub use translate::*; > + > +pub mod ffi; > diff --git a/rust/common/src/qemu.rs b/rust/common/src/qemu.rs > new file mode 100644 > index 0000000000..dd01c6d92d > --- /dev/null > +++ b/rust/common/src/qemu.rs > @@ -0,0 +1,101 @@ > +use std::{ffi::CStr, ptr, str}; > + > +use crate::{ffi, translate}; > +use translate::{FromQemuPtrFull, FromQemuPtrNone, QemuPtrDefault, Stash, > ToQemuPtr}; > + > +/// A type representing an owned C QEMU Error. > +pub struct CError(ptr::NonNull<ffi::Error>); > + > +impl translate::FromQemuPtrFull<*mut ffi::Error> for CError { > + unsafe fn from_qemu_full(ptr: *mut ffi::Error) -> Self { > + assert!(!ptr.is_null()); > + Self(ptr::NonNull::new_unchecked(ptr)) > + } > +} > + > +impl CError { > + pub fn pretty(&self) -> &str { > + unsafe { > + let pretty = ffi::error_get_pretty(self.0.as_ptr()); > + let bytes = CStr::from_ptr(pretty).to_bytes(); > + str::from_utf8(bytes) > + .unwrap_or_else(|err| > str::from_utf8(&bytes[..err.valid_up_to()]).unwrap()) > + } > + } > +} > + > +impl Drop for CError { > + fn drop(&mut self) { > + unsafe { ffi::error_free(self.0.as_ptr()) } > + } > +} > + > +/// QObject (JSON object) > +#[derive(Clone, Debug)] > +pub struct QObject; > + > +impl QemuPtrDefault for QObject { > + type QemuType = *mut ffi::QObject; > +} > + > +impl FromQemuPtrFull<*mut ffi::QObject> for QObject { > + #[inline] > + unsafe fn from_qemu_full(_ffi: *mut ffi::QObject) -> Self { > + unimplemented!() > + } > +} > + > +impl FromQemuPtrNone<*const ffi::QObject> for QObject { > + #[inline] > + unsafe fn from_qemu_none(_ffi: *const ffi::QObject) -> Self { > + unimplemented!() > + } > +} > + > +impl<'a> ToQemuPtr<'a, *mut ffi::QObject> for QObject { > + type Storage = (); > + > + #[inline] > + fn to_qemu_none(&self) -> Stash<'a, *mut ffi::QObject, QObject> { > + unimplemented!() > + } > + #[inline] > + fn to_qemu_full(&self) -> *mut ffi::QObject { > + unimplemented!() > + } > +} > + > +/// QNull (JSON null) > +#[derive(Clone, Debug)] > +pub struct QNull; > + > +impl QemuPtrDefault for QNull { > + type QemuType = *mut ffi::QNull; > +} > + > +impl FromQemuPtrFull<*mut ffi::QObject> for QNull { > + #[inline] > + unsafe fn from_qemu_full(_ffi: *mut ffi::QObject) -> Self { > + unimplemented!() > + } > +} > + > +impl FromQemuPtrNone<*const ffi::QObject> for QNull { > + #[inline] > + unsafe fn from_qemu_none(_ffi: *const ffi::QObject) -> Self { > + unimplemented!() > + } > +} > + > +impl<'a> ToQemuPtr<'a, *mut ffi::QNull> for QNull { > + type Storage = (); > + > + #[inline] > + fn to_qemu_none(&self) -> Stash<'a, *mut ffi::QNull, QNull> { > + unimplemented!() > + } > + #[inline] > + fn to_qemu_full(&self) -> *mut ffi::QNull { > + unimplemented!() > + } > +} > diff --git a/rust/common/src/qmp.rs b/rust/common/src/qmp.rs > new file mode 100644 > index 0000000000..e69de29bb2 > diff --git a/rust/common/src/translate.rs b/rust/common/src/translate.rs > new file mode 100644 > index 0000000000..315e14fa25 > --- /dev/null > +++ b/rust/common/src/translate.rs > @@ -0,0 +1,482 @@ > +// largely adapted from glib-rs > +// we don't depend on glib-rs as this brings a lot more code that we may not > need > +// and also because there are issues with the conversion traits for our > ffi::*mut. > +use libc::{c_char, size_t}; > +use std::ffi::{CStr, CString}; > +use std::ptr; > + > +use crate::ffi; > + > +/// A pointer. > +pub trait Ptr: Copy + 'static { > + fn is_null(&self) -> bool; > + fn from<X>(ptr: *mut X) -> Self; > + fn to<X>(self) -> *mut X; > +} > + > +impl<T: 'static> Ptr for *const T { > + #[inline] > + fn is_null(&self) -> bool { > + (*self).is_null() > + } > + > + #[inline] > + fn from<X>(ptr: *mut X) -> *const T { > + ptr as *const T > + } > + > + #[inline] > + fn to<X>(self) -> *mut X { > + self as *mut X > + } > +} > + > +impl<T: 'static> Ptr for *mut T { > + #[inline] > + fn is_null(&self) -> bool { > + (*self).is_null() > + } > + > + #[inline] > + fn from<X>(ptr: *mut X) -> *mut T { > + ptr as *mut T > + } > + > + #[inline] > + fn to<X>(self) -> *mut X { > + self as *mut X > + } > +} > + > +/// Macro to declare a `NewPtr` struct. > +/// > +/// A macro to declare a newtype for pointers, to workaround that *T are not > +/// defined in our binding crates, and allow foreign traits implementations. > +/// (this is used by qapi-gen bindings) > +#[allow(unused_macros)] > +#[macro_export] > +#[doc(hidden)] > +macro_rules! new_ptr { > + () => { > + #[derive(Copy, Clone)] > + pub struct NewPtr<P: Ptr>(pub P); > + > + impl<P: Ptr> Ptr for NewPtr<P> { > + #[inline] > + fn is_null(&self) -> bool { > + self.0.is_null() > + } > + > + #[inline] > + fn from<X>(ptr: *mut X) -> Self { > + NewPtr(P::from(ptr)) > + } > + > + #[inline] > + fn to<X>(self) -> *mut X { > + self.0.to() > + } > + } > + }; > +} > + > +/// Provides the default pointer type to be used in some container > conversions. > +/// > +/// It's `*mut c_char` for `String`, `*mut ffi::GuestInfo` for `GuestInfo`... > +pub trait QemuPtrDefault { > + type QemuType: Ptr; > +} > + > +impl QemuPtrDefault for String { > + type QemuType = *mut c_char; > +} > + > +/// A Stash contains the temporary storage and a pointer into it. > +/// > +/// The pointer is valid for the lifetime of the `Stash`. As the lifetime of > the > +/// `Stash` returned from `to_qemu_none` is at least the enclosing statement, > +/// you can avoid explicitly binding the stash in most cases and just take > the > +/// pointer out of it: > +/// > +/// ```ignore > +/// pub fn set_device_name(&self, name: &str) { > +/// unsafe { > +/// ffi::qemu_device_set_name(self.pointer, > name.to_qemu_none().0) > +/// } > +/// } > +/// ``` > +pub struct Stash<'a, P: Copy, T: ?Sized + ToQemuPtr<'a, P>>( > + pub P, > + pub <T as ToQemuPtr<'a, P>>::Storage, > +); > + > +/// Translate to a pointer. > +pub trait ToQemuPtr<'a, P: Copy> { > + type Storage; > + > + /// The pointer in the `Stash` is only valid for the lifetime of the > `Stash`. > + fn to_qemu_none(&'a self) -> Stash<'a, P, Self>; > + > + /// Transfer the ownership to the ffi. > + fn to_qemu_full(&self) -> P { > + unimplemented!(); > + } > +} > + > +impl<'a, P: Ptr, T: ToQemuPtr<'a, P>> ToQemuPtr<'a, P> for Option<T> { > + type Storage = Option<<T as ToQemuPtr<'a, P>>::Storage>; > + > + #[inline] > + fn to_qemu_none(&'a self) -> Stash<'a, P, Option<T>> { > + self.as_ref() > + .map_or(Stash(Ptr::from::<()>(ptr::null_mut()), None), |s| { > + let s = s.to_qemu_none(); > + Stash(s.0, Some(s.1)) > + }) > + } > + > + #[inline] > + fn to_qemu_full(&self) -> P { > + self.as_ref() > + .map_or(Ptr::from::<()>(ptr::null_mut()), > ToQemuPtr::to_qemu_full) > + } > +} > + > +impl<'a, P: Ptr, T: ToQemuPtr<'a, P>> ToQemuPtr<'a, P> for Box<T> { > + type Storage = <T as ToQemuPtr<'a, P>>::Storage; > + > + #[inline] > + fn to_qemu_none(&'a self) -> Stash<'a, P, Box<T>> { > + let s = self.as_ref().to_qemu_none(); > + Stash(s.0, s.1) > + } > + > + #[inline] > + fn to_qemu_full(&self) -> P { > + ToQemuPtr::to_qemu_full(self.as_ref()) > + } > +} > + > +impl<'a> ToQemuPtr<'a, *mut c_char> for String { > + type Storage = CString; > + > + #[inline] > + fn to_qemu_none(&self) -> Stash<'a, *mut c_char, String> { > + let tmp = CString::new(&self[..]) > + .expect("String::ToQemuPtr<*mut c_char>: unexpected '\0' > character"); > + Stash(tmp.as_ptr() as *mut c_char, tmp) > + } > + > + #[inline] > + fn to_qemu_full(&self) -> *mut c_char { > + unsafe { ffi::g_strndup(self.as_ptr() as *const c_char, self.len() > as size_t) } > + } > +} > + > +/// Translate from a pointer type, without taking ownership. > +pub trait FromQemuPtrNone<P: Ptr>: Sized { > + /// # Safety > + /// > + /// `ptr` must be a valid pointer. It is not referenced after the call. > + unsafe fn from_qemu_none(ptr: P) -> Self; > +} > + > +/// Translate from a pointer type, taking ownership. > +pub trait FromQemuPtrFull<P: Ptr>: Sized { > + /// # Safety > + /// > + /// `ptr` must be a valid pointer. Ownership is transferred. > + unsafe fn from_qemu_full(ptr: P) -> Self; > +} > + > +/// See [`FromQemuPtrNone`](trait.FromQemuPtrNone.html). > +#[inline] > +#[allow(clippy::missing_safety_doc)] > +pub unsafe fn from_qemu_none<P: Ptr, T: FromQemuPtrNone<P>>(ptr: P) -> T { > + FromQemuPtrNone::from_qemu_none(ptr) > +} > + > +/// See [`FromQemuPtrFull`](trait.FromQemuPtrFull.html). > +#[inline] > +#[allow(clippy::missing_safety_doc)] > +pub unsafe fn from_qemu_full<P: Ptr, T: FromQemuPtrFull<P>>(ptr: P) -> T { > + FromQemuPtrFull::from_qemu_full(ptr) > +} > + > +impl<P: Ptr, T: FromQemuPtrNone<P>> FromQemuPtrNone<P> for Option<T> { > + #[inline] > + unsafe fn from_qemu_none(ptr: P) -> Option<T> { > + if ptr.is_null() { > + None > + } else { > + Some(from_qemu_none(ptr)) > + } > + } > +} > + > +impl<P: Ptr, T: FromQemuPtrFull<P>> FromQemuPtrFull<P> for Option<T> { > + #[inline] > + unsafe fn from_qemu_full(ptr: P) -> Option<T> { > + if ptr.is_null() { > + None > + } else { > + Some(from_qemu_full(ptr)) > + } > + } > +} > + > +impl FromQemuPtrNone<*const c_char> for String { > + #[inline] > + unsafe fn from_qemu_none(ptr: *const c_char) -> Self { > + assert!(!ptr.is_null()); > + String::from_utf8_lossy(CStr::from_ptr(ptr).to_bytes()).into_owned() > + } > +} > + > +impl FromQemuPtrFull<*mut c_char> for String { > + #[inline] > + unsafe fn from_qemu_full(ptr: *mut c_char) -> Self { > + let res = from_qemu_none(ptr as *const _); > + ffi::g_free(ptr as *mut _); > + res > + } > +} > + > +#[doc(hidden)] > +#[allow(unused_macros)] > +#[macro_export] > +macro_rules! vec_ffi_wrapper { > + ($ffi:ident) => { > + #[allow(non_camel_case_types)] > + pub struct $ffi(*mut qapi_ffi::$ffi); > + > + impl Drop for $ffi { > + fn drop(&mut self) { > + let mut list = self.0; > + unsafe { > + while !list.is_null() { > + let next = (*list).next; > + Box::from_raw(list); > + list = next; > + } > + } > + } > + } > + > + impl From<NewPtr<*mut qapi_ffi::$ffi>> for *mut qapi_ffi::$ffi { > + fn from(p: NewPtr<*mut qapi_ffi::$ffi>) -> Self { > + p.0 > + } > + } > + }; > +} > + > +#[doc(hidden)] > +#[allow(unused_macros)] > +#[macro_export] > +macro_rules! impl_vec_scalars_to_qemu { > + ($rs:ty, $ffi:ident) => { > + impl<'a> ToQemuPtr<'a, NewPtr<*mut qapi_ffi::$ffi>> for Vec<$rs> { > + type Storage = $ffi; > + > + #[inline] > + fn to_qemu_none(&self) -> Stash<NewPtr<*mut qapi_ffi::$ffi>, > Self> { > + let mut list: *mut qapi_ffi::$ffi = std::ptr::null_mut(); > + for value in self.iter().rev() { > + let b = Box::new(qapi_ffi::$ffi { > + next: list, > + value: *value, > + }); > + list = Box::into_raw(b); > + } > + Stash(NewPtr(list), $ffi(list)) > + } > + > + #[inline] > + fn to_qemu_full(&self) -> NewPtr<*mut qapi_ffi::$ffi> { > + let mut list: *mut qapi_ffi::$ffi = std::ptr::null_mut(); > + unsafe { > + for value in self.iter().rev() { > + let l = > ffi::g_malloc0(std::mem::size_of::<qapi_ffi::$ffi>()) > + as *mut qapi_ffi::$ffi; > + (*l).next = list; > + (*l).value = *value; > + list = l; > + } > + } > + NewPtr(list) > + } > + } > + }; > +} > + > +#[doc(hidden)] > +#[allow(unused_macros)] > +#[macro_export] > +macro_rules! impl_vec_scalars_from_qemu { > + ($rs:ty, $ffi:ident, $free_ffi:ident) => { > + impl FromQemuPtrFull<NewPtr<*mut qapi_ffi::$ffi>> for Vec<$rs> { > + #[inline] > + unsafe fn from_qemu_full(ffi: NewPtr<*mut qapi_ffi::$ffi>) -> > Self { > + let ret = from_qemu_none(NewPtr(ffi.0 as *const _)); > + qapi_ffi::$free_ffi(ffi.0); > + ret > + } > + } > + > + impl FromQemuPtrNone<NewPtr<*const qapi_ffi::$ffi>> for Vec<$rs> { > + #[inline] > + unsafe fn from_qemu_none(ffi: NewPtr<*const qapi_ffi::$ffi>) -> > Self { > + let mut ret = vec![]; > + let mut it = ffi.0; > + while !it.is_null() { > + let e = &*it; > + ret.push(e.value); > + it = e.next; > + } > + ret > + } > + } > + }; > +} > + > +#[doc(hidden)] > +#[allow(unused_macros)] > +#[macro_export] > +macro_rules! impl_vec_to_qemu { > + ($rs:ty, $ffi:ident) => { > + // impl doesn't use only types from inside the current crate > + // impl QemuPtrDefault for Vec<$rs> { > + // type QemuType = NewPtr<*mut qapi_ffi::$ffi>; > + // } > + > + impl<'a> ToQemuPtr<'a, NewPtr<*mut qapi_ffi::$ffi>> for Vec<$rs> { > + type Storage = ($ffi, Vec<Stash<'a, <$rs as > QemuPtrDefault>::QemuType, $rs>>); > + > + #[inline] > + fn to_qemu_none(&self) -> Stash<NewPtr<*mut qapi_ffi::$ffi>, > Self> { > + let stash_vec: Vec<_> = > self.iter().rev().map(ToQemuPtr::to_qemu_none).collect(); > + let mut list: *mut qapi_ffi::$ffi = std::ptr::null_mut(); > + for stash in &stash_vec { > + let b = Box::new(qapi_ffi::$ffi { > + next: list, > + value: Ptr::to(stash.0), > + }); > + list = Box::into_raw(b); > + } > + Stash(NewPtr(list), ($ffi(list), stash_vec)) > + } > + > + #[inline] > + fn to_qemu_full(&self) -> NewPtr<*mut qapi_ffi::$ffi> { > + let v: Vec<_> = > self.iter().rev().map(ToQemuPtr::to_qemu_full).collect(); > + let mut list: *mut qapi_ffi::$ffi = std::ptr::null_mut(); > + unsafe { > + for val in v { > + let l = > ffi::g_malloc0(std::mem::size_of::<qapi_ffi::$ffi>()) > + as *mut qapi_ffi::$ffi; > + (*l).next = list; > + (*l).value = val; > + list = l; > + } > + } > + NewPtr(list) > + } > + } > + }; > +} > + > +#[doc(hidden)] > +#[allow(unused_macros)] > +#[macro_export] > +macro_rules! impl_vec_from_qemu { > + ($rs:ty, $ffi:ident, $free_ffi:ident) => { > + impl FromQemuPtrFull<NewPtr<*mut qapi_ffi::$ffi>> for Vec<$rs> { > + #[inline] > + unsafe fn from_qemu_full(ffi: NewPtr<*mut qapi_ffi::$ffi>) -> > Self { > + let ret = from_qemu_none(NewPtr(ffi.0 as *const _)); > + qapi_ffi::$free_ffi(ffi.0); > + ret > + } > + } > + > + impl FromQemuPtrNone<NewPtr<*const qapi_ffi::$ffi>> for Vec<$rs> { > + #[inline] > + unsafe fn from_qemu_none(ffi: NewPtr<*const qapi_ffi::$ffi>) -> > Self { > + let mut ret = vec![]; > + let mut it = ffi.0; > + while !it.is_null() { > + let e = &*it; > + ret.push(from_qemu_none(e.value as *const _)); > + it = e.next; > + } > + ret > + } > + } > + }; > +} > + > +/// A macro to help the implementation of `Vec<T>` translations. > +#[allow(unused_macros)] > +#[macro_export] > +macro_rules! vec_type { > + (Vec<$rs:ty>, $ffi:ident, $free_ffi:ident, 0) => { > + vec_ffi_wrapper!($ffi); > + impl_vec_from_qemu!($rs, $ffi, $free_ffi); > + impl_vec_to_qemu!($rs, $ffi); > + }; > + (Vec<$rs:ty>, $ffi:ident, $free_ffi:ident, 1) => { > + vec_ffi_wrapper!($ffi); > + impl_vec_scalars_from_qemu!($rs, $ffi, $free_ffi); > + impl_vec_scalars_to_qemu!($rs, $ffi); > + }; > +} > + > +/// A macro to implement [`ToQemuPtr`] as boxed scalars > +#[allow(unused_macros)] > +#[macro_export] > +macro_rules! impl_to_qemu_scalar_boxed { > + ($ty:ty) => { > + impl<'a> ToQemuPtr<'a, *mut $ty> for $ty { > + type Storage = Box<$ty>; > + > + fn to_qemu_none(&'a self) -> Stash<'a, *mut $ty, Self> { > + let mut box_ = Box::new(*self); > + Stash(&mut *box_, box_) > + } > + > + fn to_qemu_full(&self) -> *mut $ty { > + unsafe { > + let ptr = ffi::g_malloc0(std::mem::size_of::<$ty>()) as > *mut _; > + *ptr = *self; > + ptr > + } > + } > + } > + }; > +} > + > +/// A macro to implement [`FromQemuPtrNone`] for scalar pointers. > +#[allow(unused_macros)] > +#[macro_export] > +macro_rules! impl_from_qemu_none_scalar { > + ($ty:ty) => { > + impl FromQemuPtrNone<*const $ty> for $ty { > + unsafe fn from_qemu_none(ptr: *const $ty) -> Self { > + *ptr > + } > + } > + }; > +} > + > +macro_rules! impl_scalar_boxed { > + ($($t:ident)*) => ( > + $( > + impl_to_qemu_scalar_boxed!($t); > + impl_from_qemu_none_scalar!($t); > + )* > + ) > +} > + > +// the only built-in used so far, feel free to add more as needed > +impl_scalar_boxed!(bool i64 f64); > -- > 2.33.0.113.g6c40894d24 > >