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