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