[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
>
>
- [RFC v3 06/32] scripts/qapi: add a CABI module, (continued)
- [RFC v3 06/32] scripts/qapi: add a CABI module, marcandre . lureau, 2021/09/07
- [RFC v3 07/32] scripts/qapi: generate CABI dump for C types, marcandre . lureau, 2021/09/07
- [RFC v3 08/32] tests: build qapi-cabi (C ABI dump), marcandre . lureau, 2021/09/07
- [RFC v3 09/32] build-sys: add i686 cpu target, marcandre . lureau, 2021/09/07
- [RFC v3 10/32] build-sys: add --with-rust{-target} & basic build infrastructure, marcandre . lureau, 2021/09/07
- [RFC v3 11/32] build-sys: add a cargo-wrapper script, marcandre . lureau, 2021/09/07
- [RFC v3 12/32] rust: provide a common crate for QEMU, marcandre . lureau, 2021/09/07
- [RFC v3 13/32] rust: use vendored-sources, marcandre . lureau, 2021/09/07
- Re: [RFC v3 13/32] rust: use vendored-sources, Ian Jackson, 2021/09/08
- Re: [RFC v3 13/32] rust: use vendored-sources, Marc-André Lureau, 2021/09/08
- Re: [RFC v3 13/32] rust: use vendored-sources, Ian Jackson, 2021/09/08
- Re: [RFC v3 13/32] rust: use vendored-sources, Marc-André Lureau, 2021/09/08
- Re: [RFC v3 13/32] rust: use vendored-sources, Peter Maydell, 2021/09/08
- Re: [RFC v3 13/32] rust: use vendored-sources, Ian Jackson, 2021/09/08