qemu-devel
[Top][All Lists]
Advanced

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

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


From: marcandre . lureau
Subject: [RFC v3 12/32] rust: provide a common crate for QEMU
Date: Tue, 7 Sep 2021 16:19:23 +0400

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::*;
+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>;
+
+impl Error {
+    fn message(&self) -> String {
+        use Error::*;
+        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.
+//! 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;
+}
+
+#[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.
+#[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()
+            },
+        }
+    }};
+}
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]