1// Copyright (c) 2017 Gilad Naaman 2// 3// Permission is hereby granted, free of charge, to any person obtaining a copy 4// of this software and associated documentation files (the "Software"), to deal 5// in the Software without restriction, including without limitation the rights 6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7// copies of the Software, and to permit persons to whom the Software is 8// furnished to do so, subject to the following conditions: 9// 10// The above copyright notice and this permission notice shall be included in all 11// copies or substantial portions of the Software. 12// 13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19// SOFTWARE. 20 21/// Reexport for `local_inner_macros`; see 22/// <https://doc.rust-lang.org/edition-guide/rust-2018/macros/macro-changes.html#macros-using-local_inner_macros>. 23#[doc(hidden)] 24#[macro_export] 25macro_rules! _memoffset__compile_error { 26 ($($inner:tt)*) => { 27 compile_error! { $($inner)* } 28 } 29} 30 31/// Produces a range instance representing the sub-slice containing the specified member. 32/// 33/// This macro provides 2 forms of differing functionalities. 34/// 35/// The first form is identical to the appearance of the `offset_of!` macro. 36/// 37/// ```ignore 38/// span_of!(Struct, member) 39/// ``` 40/// 41/// The second form of `span_of!` returns a sub-slice which starts at one field, and ends at another. 42/// The general pattern of this form is: 43/// 44/// ```ignore 45/// // Exclusive 46/// span_of!(Struct, member_a .. member_b) 47/// // Inclusive 48/// span_of!(Struct, member_a ..= member_b) 49/// 50/// // Open-ended ranges 51/// span_of!(Struct, .. end) 52/// span_of!(Struct, start ..) 53/// ``` 54/// 55/// ### Note 56/// This macro uses recursion in order to resolve the range expressions, so there is a limit to 57/// the complexity of the expression. 58/// In order to raise the limit, the compiler's recursion limit should be lifted. 59/// 60/// ### Safety 61/// The inter-field form mentioned above assumes that the first field is positioned before the 62/// second. 63/// This is only guarenteed for `repr(C)` structs. 64/// Usage with `repr(Rust)` structs may yield unexpected results, like downward-going ranges, 65/// spans that include unexpected fields, empty spans, or spans that include *unexpected* padding bytes. 66/// 67/// ## Examples 68/// ``` 69/// use memoffset::span_of; 70/// 71/// #[repr(C)] 72/// struct Florp { 73/// a: u32 74/// } 75/// 76/// #[repr(C)] 77/// struct Blarg { 78/// x: [u32; 2], 79/// y: [u8; 56], 80/// z: Florp, 81/// egg: [[u8; 4]; 4] 82/// } 83/// 84/// fn main() { 85/// assert_eq!(0..84, span_of!(Blarg, ..)); 86/// assert_eq!(0..8, span_of!(Blarg, .. y)); 87/// assert_eq!(0..64, span_of!(Blarg, ..= y)); 88/// assert_eq!(0..8, span_of!(Blarg, x)); 89/// assert_eq!(8..84, span_of!(Blarg, y ..)); 90/// assert_eq!(0..8, span_of!(Blarg, x .. y)); 91/// assert_eq!(0..64, span_of!(Blarg, x ..= y)); 92/// } 93/// ``` 94#[macro_export(local_inner_macros)] 95macro_rules! span_of { 96 (@helper $root:ident, [] ..=) => { 97 _memoffset__compile_error!("Expected a range, found '..='") 98 }; 99 (@helper $root:ident, [] ..) => { 100 _memoffset__compile_error!("Expected a range, found '..'") 101 }; 102 // No explicit begin for range. 103 (@helper $root:ident, $parent:path, [] ..) => {{ 104 ($root as usize, 105 $root as usize + $crate::__priv::size_of_pointee($root)) 106 }}; 107 (@helper $root:ident, $parent:path, [] ..= $end:tt) => {{ 108 let end = raw_field!($root, $parent, $end); 109 ($root as usize, end as usize + $crate::__priv::size_of_pointee(end)) 110 }}; 111 (@helper $root:ident, $parent:path, [] .. $end:tt) => {{ 112 ($root as usize, raw_field!($root, $parent, $end) as usize) 113 }}; 114 // Explicit begin and end for range. 115 (@helper $root:ident, $parent:path, # $begin:tt [] ..= $end:tt) => {{ 116 let begin = raw_field!($root, $parent, $begin); 117 let end = raw_field!($root, $parent, $end); 118 (begin as usize, end as usize + $crate::__priv::size_of_pointee(end)) 119 }}; 120 (@helper $root:ident, $parent:path, # $begin:tt [] .. $end:tt) => {{ 121 (raw_field!($root, $parent, $begin) as usize, 122 raw_field!($root, $parent, $end) as usize) 123 }}; 124 // No explicit end for range. 125 (@helper $root:ident, $parent:path, # $begin:tt [] ..) => {{ 126 (raw_field!($root, $parent, $begin) as usize, 127 $root as usize + $crate::__priv::size_of_pointee($root)) 128 }}; 129 (@helper $root:ident, $parent:path, # $begin:tt [] ..=) => {{ 130 _memoffset__compile_error!( 131 "Found inclusive range to the end of a struct. Did you mean '..' instead of '..='?") 132 }}; 133 // Just one field. 134 (@helper $root:ident, $parent:path, # $field:tt []) => {{ 135 let field = raw_field!($root, $parent, $field); 136 (field as usize, field as usize + $crate::__priv::size_of_pointee(field)) 137 }}; 138 // Parsing. 139 (@helper $root:ident, $parent:path, $(# $begin:tt)+ [] $tt:tt $($rest:tt)*) => {{ 140 span_of!(@helper $root, $parent, $(#$begin)* #$tt [] $($rest)*) 141 }}; 142 (@helper $root:ident, $parent:path, [] $tt:tt $($rest:tt)*) => {{ 143 span_of!(@helper $root, $parent, #$tt [] $($rest)*) 144 }}; 145 146 // Entry point. 147 ($sty:path, $($exp:tt)+) => ({ 148 // Get a base pointer. 149 _memoffset__let_base_ptr!(root, $sty); 150 let base = root as usize; 151 let (begin, end) = span_of!(@helper root, $sty, [] $($exp)*); 152 begin-base..end-base 153 }); 154} 155 156#[cfg(test)] 157mod tests { 158 use core::mem; 159 160 #[test] 161 fn span_simple() { 162 #[repr(C)] 163 struct Foo { 164 a: u32, 165 b: [u8; 2], 166 c: i64, 167 } 168 169 assert_eq!(span_of!(Foo, a), 0..4); 170 assert_eq!(span_of!(Foo, b), 4..6); 171 assert_eq!(span_of!(Foo, c), 8..8 + 8); 172 } 173 174 #[test] 175 #[cfg_attr(miri, ignore)] // this creates unaligned references 176 fn span_simple_packed() { 177 #[repr(C, packed)] 178 struct Foo { 179 a: u32, 180 b: [u8; 2], 181 c: i64, 182 } 183 184 assert_eq!(span_of!(Foo, a), 0..4); 185 assert_eq!(span_of!(Foo, b), 4..6); 186 assert_eq!(span_of!(Foo, c), 6..6 + 8); 187 } 188 189 #[test] 190 fn span_forms() { 191 #[repr(C)] 192 struct Florp { 193 a: u32, 194 } 195 196 #[repr(C)] 197 struct Blarg { 198 x: u64, 199 y: [u8; 56], 200 z: Florp, 201 egg: [[u8; 4]; 5], 202 } 203 204 // Love me some brute force 205 assert_eq!(0..8, span_of!(Blarg, x)); 206 assert_eq!(64..68, span_of!(Blarg, z)); 207 assert_eq!(68..mem::size_of::<Blarg>(), span_of!(Blarg, egg)); 208 209 assert_eq!(8..64, span_of!(Blarg, y..z)); 210 assert_eq!(0..64, span_of!(Blarg, x..=y)); 211 } 212 213 #[test] 214 fn ig_test() { 215 #[repr(C)] 216 struct Member { 217 foo: u32, 218 } 219 220 #[repr(C)] 221 struct Test { 222 x: u64, 223 y: [u8; 56], 224 z: Member, 225 egg: [[u8; 4]; 4], 226 } 227 228 assert_eq!(span_of!(Test, ..x), 0..0); 229 assert_eq!(span_of!(Test, ..=x), 0..8); 230 assert_eq!(span_of!(Test, ..y), 0..8); 231 assert_eq!(span_of!(Test, ..=y), 0..64); 232 assert_eq!(span_of!(Test, ..z), 0..64); 233 assert_eq!(span_of!(Test, ..=z), 0..68); 234 assert_eq!(span_of!(Test, ..egg), 0..68); 235 assert_eq!(span_of!(Test, ..=egg), 0..84); 236 assert_eq!(span_of!(Test, ..), 0..mem::size_of::<Test>()); 237 assert_eq!( 238 span_of!(Test, x..), 239 offset_of!(Test, x)..mem::size_of::<Test>() 240 ); 241 assert_eq!( 242 span_of!(Test, y..), 243 offset_of!(Test, y)..mem::size_of::<Test>() 244 ); 245 246 assert_eq!( 247 span_of!(Test, z..), 248 offset_of!(Test, z)..mem::size_of::<Test>() 249 ); 250 assert_eq!( 251 span_of!(Test, egg..), 252 offset_of!(Test, egg)..mem::size_of::<Test>() 253 ); 254 assert_eq!( 255 span_of!(Test, x..y), 256 offset_of!(Test, x)..offset_of!(Test, y) 257 ); 258 assert_eq!( 259 span_of!(Test, x..=y), 260 offset_of!(Test, x)..offset_of!(Test, y) + mem::size_of::<[u8; 56]>() 261 ); 262 } 263} 264