diff options
Diffstat (limited to 'rust/qemu-api/src/offset_of.rs')
-rw-r--r-- | rust/qemu-api/src/offset_of.rs | 161 |
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); + } +} |