[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
- Re: [RFC v3 05/32] scripts/qapi: add QAPISchemaVisitor.visit_module_end, (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 <=
- [RFC v3 13/32] rust: use vendored-sources, marcandre . lureau, 2021/09/07