diff mbox series

[13/14] rust: introduce alternative to offset_of!

Message ID 20240701145853.1394967-14-pbonzini@redhat.com (mailing list archive)
State New, archived
Headers show
Series rust: example of bindings code for Rust in QEMU | expand

Commit Message

Paolo Bonzini July 1, 2024, 2:58 p.m. UTC
Allow working with Rust versions prior to 1.77.  The code was
taken from Rust's Discourse platform and is used with permission of
the author.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 qemu/Cargo.toml                 |  3 +
 qemu/build.rs                   |  5 ++
 qemu/src/hw/core/device_impl.rs |  4 +-
 qemu/src/lib.rs                 |  4 ++
 qemu/src/qom/object_impl.rs     | 13 +++--
 qemu/src/util/mod.rs            |  1 +
 qemu/src/util/offset_of.rs      | 99 +++++++++++++++++++++++++++++++++
 qemu/tests/main.rs              | 11 +++-
 9 files changed, 137 insertions(+), 10 deletions(-)
 create mode 100644 qemu/build.rs
 create mode 100644 qemu/src/util/offset_of.rs
diff mbox series

Patch

diff --git a/qemu/Cargo.toml b/qemu/Cargo.toml
index a07a449..93808a5 100644
--- a/qemu/Cargo.toml
+++ b/qemu/Cargo.toml
@@ -12,3 +12,6 @@  cstr = { version = "=0.2.10" }
 
 [dev-dependencies]
 matches = ">=0"
+
+[build-dependencies]
+version_check = { version = "~0.9" }
diff --git a/qemu/build.rs b/qemu/build.rs
new file mode 100644
index 0000000..34f7b49
--- /dev/null
+++ b/qemu/build.rs
@@ -0,0 +1,5 @@ 
+fn main() {
+    if let Some(true) = version_check::is_min_version("1.77.0") {
+        println!("cargo:rustc-cfg=has_offset_of");
+    }
+}
diff --git a/qemu/src/hw/core/device_impl.rs b/qemu/src/hw/core/device_impl.rs
index 80b0e5e..b1d2f04 100644
--- a/qemu/src/hw/core/device_impl.rs
+++ b/qemu/src/hw/core/device_impl.rs
@@ -111,7 +111,7 @@  macro_rules! qdev_prop {
             $kind,
             $name,
             (<$crate::conf_type!($type) as ConstDefault>::DEFAULT).$field,
-            <$type as $crate::DeviceTypeImpl>::CONF_OFFSET + std::mem::offset_of!($crate::conf_type!($type), $field)
+            <$type as $crate::DeviceTypeImpl>::CONF_OFFSET + $crate::offset_of!($crate::conf_type!($type), $field)
         )
     };
 }
@@ -126,7 +126,7 @@  macro_rules! qdev_define_type {
             @extends $super $(,$supers)*, $crate::Object);
 
         unsafe impl $crate::DeviceTypeImpl for $struct {
-            const CONF_OFFSET: usize = std::mem::offset_of!($struct, conf);
+            const CONF_OFFSET: usize = $crate::offset_of!($struct, conf);
 
             fn properties() -> *const $crate::Property {
                 static mut PROPERTIES: &'static [$crate::Property] = &[$($props),+];
diff --git a/qemu/src/lib.rs b/qemu/src/lib.rs
index 3f0491c..2d43a25 100644
--- a/qemu/src/lib.rs
+++ b/qemu/src/lib.rs
@@ -31,3 +31,7 @@  pub use util::foreign::IntoNative;
 pub use util::foreign::OwnedPointer;
 pub use util::zeroed::Zeroed;
 pub type Result<T> = std::result::Result<T, Error>;
+
+// with_offsets is exported directly from util::offset_of
+#[cfg(has_offset_of)]
+pub use std::mem::offset_of;
diff --git a/qemu/src/qom/object_impl.rs b/qemu/src/qom/object_impl.rs
index 61546b6..b1768b9 100644
--- a/qemu/src/qom/object_impl.rs
+++ b/qemu/src/qom/object_impl.rs
@@ -95,11 +95,14 @@  unsafe fn rust_type_register<T: TypeImpl + ObjectImpl>() {
 #[macro_export]
 macro_rules! qom_define_type {
     ($name:expr, $struct:ident, $conf_ty:ty, $state_ty:ty; @extends $super:ty $(,$supers:ty)*) => {
-        struct $struct {
-            // self.base dropped by call to superclass instance_finalize
-            base: std::mem::ManuallyDrop<$super>,
-            conf: $conf_ty,
-            state: $state_ty,
+        $crate::with_offsets! {
+            #[repr(C)]
+            struct $struct {
+                // self.base dropped by call to superclass instance_finalize
+                base: std::mem::ManuallyDrop<$super>,
+                conf: $conf_ty,
+                state: $state_ty,
+            }
         }
 
         // Define IsA markers for the struct itself and all the superclasses
diff --git a/qemu/src/util/mod.rs b/qemu/src/util/mod.rs
index 9c081b6..e4df7c9 100644
--- a/qemu/src/util/mod.rs
+++ b/qemu/src/util/mod.rs
@@ -1,3 +1,4 @@ 
 pub mod error;
 pub mod foreign;
+pub mod offset_of;
 pub mod zeroed;
diff --git a/qemu/src/util/offset_of.rs b/qemu/src/util/offset_of.rs
new file mode 100644
index 0000000..4ce5188
--- /dev/null
+++ b/qemu/src/util/offset_of.rs
@@ -0,0 +1,99 @@ 
+#[cfg(not(has_offset_of))]
+#[macro_export]
+macro_rules! offset_of {
+    ($Container:ty, $field:ident) => {
+        <$Container>::offset_to.$field
+    };
+}
+
+/// A wrapper for struct declarations, that allows using `offset_of!` in
+/// versions of Rust prior to 1.77
+#[macro_export]
+macro_rules! with_offsets {
+    // source: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=10a22a9b8393abd7b541d8fc844bc0df
+    // used under MIT license with permission of Yandros aka Daniel Henry-Mantilla
+    (
+        #[repr(C)]
+        $(#[$struct_meta:meta])*
+        $struct_vis:vis
+        struct $StructName:ident {
+            $(
+                $(#[$field_meta:meta])*
+                $field_vis:vis
+                $field_name:ident : $field_ty:ty
+            ),*
+            $(,)?
+        }
+    ) => (
+        #[repr(C)]
+        $(#[$struct_meta])*
+        $struct_vis
+        struct $StructName {
+            $(
+                $(#[$field_meta])*
+                $field_vis
+                $field_name : $field_ty ,
+            )*
+        }
+
+        #[cfg(not(has_offset_of))]
+        #[allow(nonstandard_style)]
+        const _: () = {
+            pub
+            struct StructOffsets {
+                $(
+                    $field_vis
+                    $field_name: usize,
+                )*
+            }
+            struct Helper;
+            impl $StructName {
+                pub
+                const offset_to: StructOffsets = StructOffsets {
+                    $(
+                        $field_name: Helper::$field_name,
+                    )*
+                };
+            }
+            const END_OF_PREV_FIELD: usize = 0;
+            $crate::with_offsets! {
+                @names [ $($field_name)* ]
+                @tys [ $($field_ty ,)*]
+            }
+        };
+    );
+
+    (
+        @names []
+        @tys []
+    ) => ();
+
+    (
+        @names [$field_name:ident $($other_names:tt)*]
+        @tys [$field_ty:ty , $($other_tys:tt)*]
+    ) => (
+        impl Helper {
+            const $field_name: usize = {
+                let align =
+                    std::mem::align_of::<$field_ty>()
+                ;
+                let trail =
+                    END_OF_PREV_FIELD % align
+                ;
+                0   + END_OF_PREV_FIELD
+                    + (align - trail)
+                        * [1, 0][(trail == 0) as usize]
+            };
+        }
+        const _: () = {
+            const END_OF_PREV_FIELD: usize =
+                Helper::$field_name +
+                std::mem::size_of::<$field_ty>()
+            ;
+            $crate::with_offsets! {
+                @names [$($other_names)*]
+                @tys [$($other_tys)*]
+            }
+        };
+    );
+}
diff --git a/qemu/tests/main.rs b/qemu/tests/main.rs
index 601e92b..854c626 100644
--- a/qemu/tests/main.rs
+++ b/qemu/tests/main.rs
@@ -14,11 +14,16 @@  use qemu::DeviceState;
 
 use qemu::Result;
 
+use qemu::with_offsets;
+
 use std::cell::RefCell;
 
-#[derive(Default, ConstDefault)]
-struct TestConf {
-    foo: bool,
+with_offsets! {
+    #[repr(C)]
+    #[derive(Default, ConstDefault)]
+    struct TestConf {
+        foo: bool,
+    }
 }
 
 #[derive(Default)]