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