xref: /openbmc/qemu/rust/qemu-api/src/offset_of.rs (revision 571bdc97)
1 // SPDX-License-Identifier: MIT
2 
3 /// This macro provides the same functionality as `core::mem::offset_of`,
4 /// except that only one level of field access is supported.  The declaration
5 /// of the struct must be wrapped with `with_offsets! { }`.
6 ///
7 /// It is needed because `offset_of!` was only stabilized in Rust 1.77.
8 #[cfg(not(has_offset_of))]
9 #[macro_export]
10 macro_rules! offset_of {
11     ($Container:ty, $field:ident) => {
12         <$Container>::OFFSET_TO__.$field
13     };
14 }
15 
16 /// A wrapper for struct declarations, that allows using `offset_of!` in
17 /// versions of Rust prior to 1.77
18 #[macro_export]
19 macro_rules! with_offsets {
20     // This method to generate field offset constants comes from:
21     //
22     //     https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=10a22a9b8393abd7b541d8fc844bc0df
23     //
24     // used under MIT license with permission of Yandros aka Daniel Henry-Mantilla
25     (
26         $(#[$struct_meta:meta])*
27         $struct_vis:vis
28         struct $StructName:ident {
29             $(
30                 $(#[$field_meta:meta])*
31                 $field_vis:vis
32                 $field_name:ident : $field_ty:ty
33             ),*
34             $(,)?
35         }
36     ) => (
37         #[cfg(not(has_offset_of))]
38         const _: () = {
39             struct StructOffsetsHelper<T>(std::marker::PhantomData<T>);
40             const END_OF_PREV_FIELD: usize = 0;
41 
42             // populate StructOffsetsHelper<T> with associated consts,
43             // one for each field
44             $crate::with_offsets! {
45                 @struct $StructName
46                 @names [ $($field_name)* ]
47                 @tys [ $($field_ty ,)*]
48             }
49 
50             // now turn StructOffsetsHelper<T>'s consts into a single struct,
51             // applying field visibility.  This provides better error messages
52             // than if offset_of! used StructOffsetsHelper::<T> directly.
53             pub
54             struct StructOffsets {
55                 $(
56                     $field_vis
57                     $field_name: usize,
58                 )*
59             }
60             impl $StructName {
61                 pub
62                 const OFFSET_TO__: StructOffsets = StructOffsets {
63                     $(
64                         $field_name: StructOffsetsHelper::<$StructName>::$field_name,
65                     )*
66                 };
67             }
68         };
69     );
70 
71     (
72         @struct $StructName:ident
73         @names []
74         @tys []
75     ) => ();
76 
77     (
78         @struct $StructName:ident
79         @names [$field_name:ident $($other_names:tt)*]
80         @tys [$field_ty:ty , $($other_tys:tt)*]
81     ) => (
82         #[allow(non_local_definitions)]
83         #[allow(clippy::modulo_one)]
84         impl StructOffsetsHelper<$StructName> {
85             #[allow(nonstandard_style)]
86             const $field_name: usize = {
87                 const ALIGN: usize = std::mem::align_of::<$field_ty>();
88                 const TRAIL: usize = END_OF_PREV_FIELD % ALIGN;
89                 END_OF_PREV_FIELD + (if TRAIL == 0 { 0usize } else { ALIGN - TRAIL })
90             };
91         }
92         const _: () = {
93             const END_OF_PREV_FIELD: usize =
94                 StructOffsetsHelper::<$StructName>::$field_name +
95                 std::mem::size_of::<$field_ty>()
96             ;
97             $crate::with_offsets! {
98                 @struct $StructName
99                 @names [$($other_names)*]
100                 @tys [$($other_tys)*]
101             }
102         };
103     );
104 }
105 
106 #[cfg(test)]
107 mod tests {
108     use crate::offset_of;
109 
110     #[repr(C)]
111     struct Foo {
112         a: u16,
113         b: u32,
114         c: u64,
115         d: u16,
116     }
117 
118     #[repr(C)]
119     struct Bar {
120         pub a: u16,
121         pub b: u64,
122         c: Foo,
123         d: u64,
124     }
125 
126     crate::with_offsets! {
127         #[repr(C)]
128         struct Bar {
129             pub a: u16,
130             pub b: u64,
131             c: Foo,
132             d: u64,
133         }
134     }
135 
136     #[repr(C)]
137     pub struct Baz {
138         b: u32,
139         a: u8,
140     }
141     crate::with_offsets! {
142         #[repr(C)]
143         pub struct Baz {
144             b: u32,
145             a: u8,
146         }
147     }
148 
149     #[test]
150     fn test_offset_of() {
151         const OFFSET_TO_C: usize = offset_of!(Bar, c);
152 
153         assert_eq!(offset_of!(Bar, a), 0);
154         assert_eq!(offset_of!(Bar, b), 8);
155         assert_eq!(OFFSET_TO_C, 16);
156         assert_eq!(offset_of!(Bar, d), 40);
157 
158         assert_eq!(offset_of!(Baz, b), 0);
159         assert_eq!(offset_of!(Baz, a), 4);
160     }
161 }
162