qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [RFC v3 12/32] rust: provide a common crate for QEMU


From: Alistair Francis
Subject: Re: [RFC v3 12/32] rust: provide a common crate for QEMU
Date: Fri, 10 Sep 2021 11:18:55 +1000

On Tue, Sep 7, 2021 at 10:41 PM <marcandre.lureau@redhat.com> wrote:
>
> From: Marc-André Lureau <marcandre.lureau@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.lureau@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
>
>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]