aboutsummaryrefslogtreecommitdiff
path: root/rust/qemu-api/src/offset_of.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/qemu-api/src/offset_of.rs')
-rw-r--r--rust/qemu-api/src/offset_of.rs161
1 files changed, 161 insertions, 0 deletions
diff --git a/rust/qemu-api/src/offset_of.rs b/rust/qemu-api/src/offset_of.rs
new file mode 100644
index 0000000000..075e98f986
--- /dev/null
+++ b/rust/qemu-api/src/offset_of.rs
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: MIT
+
+/// This macro provides the same functionality as `core::mem::offset_of`,
+/// except that only one level of field access is supported. The declaration
+/// of the struct must be wrapped with `with_offsets! { }`.
+///
+/// It is needed because `offset_of!` was only stabilized in Rust 1.77.
+#[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 {
+ // This method to generate field offset constants comes from:
+ //
+ // 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
+ (
+ $(#[$struct_meta:meta])*
+ $struct_vis:vis
+ struct $StructName:ident {
+ $(
+ $(#[$field_meta:meta])*
+ $field_vis:vis
+ $field_name:ident : $field_ty:ty
+ ),*
+ $(,)?
+ }
+ ) => (
+ #[cfg(not(has_offset_of))]
+ const _: () = {
+ struct StructOffsetsHelper<T>(std::marker::PhantomData<T>);
+ const END_OF_PREV_FIELD: usize = 0;
+
+ // populate StructOffsetsHelper<T> with associated consts,
+ // one for each field
+ $crate::with_offsets! {
+ @struct $StructName
+ @names [ $($field_name)* ]
+ @tys [ $($field_ty ,)*]
+ }
+
+ // now turn StructOffsetsHelper<T>'s consts into a single struct,
+ // applying field visibility. This provides better error messages
+ // than if offset_of! used StructOffsetsHelper::<T> directly.
+ pub
+ struct StructOffsets {
+ $(
+ $field_vis
+ $field_name: usize,
+ )*
+ }
+ impl $StructName {
+ pub
+ const OFFSET_TO__: StructOffsets = StructOffsets {
+ $(
+ $field_name: StructOffsetsHelper::<$StructName>::$field_name,
+ )*
+ };
+ }
+ };
+ );
+
+ (
+ @struct $StructName:ident
+ @names []
+ @tys []
+ ) => ();
+
+ (
+ @struct $StructName:ident
+ @names [$field_name:ident $($other_names:tt)*]
+ @tys [$field_ty:ty , $($other_tys:tt)*]
+ ) => (
+ #[allow(non_local_definitions)]
+ #[allow(clippy::modulo_one)]
+ impl StructOffsetsHelper<$StructName> {
+ #[allow(nonstandard_style)]
+ const $field_name: usize = {
+ const ALIGN: usize = std::mem::align_of::<$field_ty>();
+ const TRAIL: usize = END_OF_PREV_FIELD % ALIGN;
+ END_OF_PREV_FIELD + (if TRAIL == 0 { 0usize } else { ALIGN - TRAIL })
+ };
+ }
+ const _: () = {
+ const END_OF_PREV_FIELD: usize =
+ StructOffsetsHelper::<$StructName>::$field_name +
+ std::mem::size_of::<$field_ty>()
+ ;
+ $crate::with_offsets! {
+ @struct $StructName
+ @names [$($other_names)*]
+ @tys [$($other_tys)*]
+ }
+ };
+ );
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::offset_of;
+
+ #[repr(C)]
+ struct Foo {
+ a: u16,
+ b: u32,
+ c: u64,
+ d: u16,
+ }
+
+ #[repr(C)]
+ struct Bar {
+ pub a: u16,
+ pub b: u64,
+ c: Foo,
+ d: u64,
+ }
+
+ crate::with_offsets! {
+ #[repr(C)]
+ struct Bar {
+ pub a: u16,
+ pub b: u64,
+ c: Foo,
+ d: u64,
+ }
+ }
+
+ #[repr(C)]
+ pub struct Baz {
+ b: u32,
+ a: u8,
+ }
+ crate::with_offsets! {
+ #[repr(C)]
+ pub struct Baz {
+ b: u32,
+ a: u8,
+ }
+ }
+
+ #[test]
+ fn test_offset_of() {
+ const OFFSET_TO_C: usize = offset_of!(Bar, c);
+
+ assert_eq!(offset_of!(Bar, a), 0);
+ assert_eq!(offset_of!(Bar, b), 8);
+ assert_eq!(OFFSET_TO_C, 16);
+ assert_eq!(offset_of!(Bar, d), 40);
+
+ assert_eq!(offset_of!(Baz, b), 0);
+ assert_eq!(offset_of!(Baz, a), 4);
+ }
+}