• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /// Macro to create a local `base_ptr` raw pointer of the given type, avoiding UB as
22 /// much as is possible currently.
23 #[cfg(maybe_uninit)]
24 #[macro_export]
25 #[doc(hidden)]
26 macro_rules! _memoffset__let_base_ptr {
27     ($name:ident, $type:ty) => {
28         // No UB here, and the pointer does not dangle, either.
29         // But we have to make sure that `uninit` lives long enough,
30         // so it has to be in the same scope as `$name`. That's why
31         // `let_base_ptr` declares a variable (several, actually)
32         // instead of returning one.
33         let uninit = $crate::__priv::mem::MaybeUninit::<$type>::uninit();
34         let $name: *const $type = uninit.as_ptr();
35     };
36 }
37 #[cfg(not(maybe_uninit))]
38 #[macro_export]
39 #[doc(hidden)]
40 macro_rules! _memoffset__let_base_ptr {
41     ($name:ident, $type:ty) => {
42         // No UB right here, but we will later dereference this pointer to
43         // offset into a field, and that is UB because the pointer is dangling.
44         let $name = $crate::__priv::mem::align_of::<$type>() as *const $type;
45     };
46 }
47 
48 /// Macro to compute the distance between two pointers.
49 #[cfg(stable_const)]
50 #[macro_export]
51 #[doc(hidden)]
52 macro_rules! _memoffset_offset_from_unsafe {
53     ($field:expr, $base:expr) => {{
54         let field = $field; // evaluate $field outside the `unsafe` block
55         let base = $base; // evaluate $base outside the `unsafe` block
56         // Compute offset, with unstable `offset_from` for const-compatibility.
57         // (Requires the pointers to not dangle, but we already need that for `raw_field!` anyway.)
58         unsafe { (field as *const u8).offset_from(base as *const u8) as usize }
59     }};
60 }
61 #[cfg(not(stable_const))]
62 #[macro_export]
63 #[doc(hidden)]
64 macro_rules! _memoffset_offset_from_unsafe {
65     ($field:expr, $base:expr) => {
66         // Compute offset.
67         ($field as usize) - ($base as usize)
68     };
69 }
70 #[cfg(not(stable_offset_of))]
71 #[macro_export(local_inner_macros)]
72 #[doc(hidden)]
73 macro_rules! _memoffset__offset_of_impl {
74     ($parent:path, $field:tt) => {{
75         // Get a base pointer (non-dangling if rustc supports `MaybeUninit`).
76         _memoffset__let_base_ptr!(base_ptr, $parent);
77         // Get field pointer.
78         let field_ptr = raw_field!(base_ptr, $parent, $field);
79         // Compute offset.
80         _memoffset_offset_from_unsafe!(field_ptr, base_ptr)
81     }};
82 }
83 #[cfg(stable_offset_of)]
84 #[macro_export]
85 #[doc(hidden)]
86 macro_rules! _memoffset__offset_of_impl {
87     ($parent:path, $field:tt) => {{
88         $crate::__priv::mem::offset_of!($parent, $field)
89     }};
90 }
91 
92 /// Calculates the offset of the specified field from the start of the named struct.
93 ///
94 /// ## Examples
95 /// ```
96 /// use memoffset::offset_of;
97 ///
98 /// #[repr(C, packed)]
99 /// struct Foo {
100 ///     a: u32,
101 ///     b: u64,
102 ///     c: [u8; 5]
103 /// }
104 ///
105 /// assert_eq!(offset_of!(Foo, a), 0);
106 /// assert_eq!(offset_of!(Foo, b), 4);
107 /// ```
108 ///
109 /// ## Notes
110 /// Rust's ABI is unstable, and [type layout can be changed with each
111 /// compilation](https://doc.rust-lang.org/reference/type-layout.html).
112 ///
113 /// Using `offset_of!` with a `repr(Rust)` struct will return the correct offset of the
114 /// specified `field` for a particular compilation, but the exact value may change
115 /// based on the compiler version, concrete struct type, time of day, or rustc's mood.
116 ///
117 /// As a result, the value should not be retained and used between different compilations.
118 #[macro_export(local_inner_macros)]
119 macro_rules! offset_of {
120     ($parent:path, $field:tt) => {
121         // Macro implementation is delegated to another macro to have a
122         // single top-level macro to attach documentation to.
123         _memoffset__offset_of_impl!($parent, $field)
124     };
125 }
126 
127 #[cfg(tuple_ty)]
128 #[cfg(not(stable_offset_of))]
129 #[macro_export(local_inner_macros)]
130 #[doc(hidden)]
131 macro_rules! _memoffset__offset_of_tuple_impl {
132     ($parent:ty, $field:tt) => {{
133         // Get a base pointer (non-dangling if rustc supports `MaybeUninit`).
134         _memoffset__let_base_ptr!(base_ptr, $parent);
135         // Get field pointer.
136         let field_ptr = raw_field_tuple!(base_ptr, $parent, $field);
137         // Compute offset.
138         _memoffset_offset_from_unsafe!(field_ptr, base_ptr)
139     }};
140 }
141 
142 #[cfg(tuple_ty)]
143 #[cfg(stable_offset_of)]
144 #[macro_export(local_inner_macros)]
145 #[doc(hidden)]
146 macro_rules! _memoffset__offset_of_tuple_impl {
147     ($parent:ty, $field:tt) => {{
148         $crate::__priv::mem::offset_of!($parent, $field)
149     }};
150 }
151 
152 /// Calculates the offset of the specified field from the start of the tuple.
153 ///
154 /// ## Examples
155 /// ```
156 /// use memoffset::offset_of_tuple;
157 ///
158 /// assert!(offset_of_tuple!((u8, u32), 1) >= 0, "Tuples do not have a defined layout");
159 /// ```
160 #[cfg(tuple_ty)]
161 #[macro_export(local_inner_macros)]
162 macro_rules! offset_of_tuple {
163     ($parent:ty, $field:tt) => {{
164         // Macro implementation is delegated to another macro to have a
165         // single top-level macro to attach documentation to.
166         _memoffset__offset_of_tuple_impl!($parent, $field)
167     }};
168 }
169 
170 #[cfg(not(stable_offset_of))]
171 #[macro_export(local_inner_macros)]
172 #[doc(hidden)]
173 macro_rules! _memoffset__offset_of_union_impl {
174     ($parent:path, $field:tt) => {{
175         // Get a base pointer (non-dangling if rustc supports `MaybeUninit`).
176         _memoffset__let_base_ptr!(base_ptr, $parent);
177         // Get field pointer.
178         let field_ptr = raw_field_union!(base_ptr, $parent, $field);
179         // Compute offset.
180         _memoffset_offset_from_unsafe!(field_ptr, base_ptr)
181     }};
182 }
183 
184 #[cfg(stable_offset_of)]
185 #[macro_export(local_inner_macros)]
186 #[doc(hidden)]
187 macro_rules! _memoffset__offset_of_union_impl {
188     ($parent:path, $field:tt) => {{
189         $crate::__priv::mem::offset_of!($parent, $field)
190     }};
191 }
192 
193 /// Calculates the offset of the specified union member from the start of the union.
194 ///
195 /// ## Examples
196 /// ```
197 /// use memoffset::offset_of_union;
198 ///
199 /// #[repr(C, packed)]
200 /// union Foo {
201 ///     foo32: i32,
202 ///     foo64: i64,
203 /// }
204 ///
205 /// assert!(offset_of_union!(Foo, foo64) == 0);
206 /// ```
207 ///
208 /// ## Note
209 /// Due to `macro_rules!` limitations, this macro will accept structs with a single field as well as unions.
210 /// This is not a stable guarantee, and future versions of this crate might fail
211 /// on any use of this macro with a struct, without a semver bump.
212 #[macro_export(local_inner_macros)]
213 macro_rules! offset_of_union {
214     ($parent:path, $field:tt) => {{
215         // Macro implementation is delegated to another macro to have a
216         // single top-level macro to attach documentation to.
217         _memoffset__offset_of_union_impl!($parent, $field)
218     }};
219 }
220 
221 #[cfg(test)]
222 mod tests {
223     #![cfg_attr(allow_clippy, allow(clippy::identity_op))] // For `... + 0` constructs below.
224 
225     #[test]
offset_simple()226     fn offset_simple() {
227         #[repr(C)]
228         struct Foo {
229             a: u32,
230             b: [u8; 2],
231             c: i64,
232         }
233 
234         assert_eq!(offset_of!(Foo, a), 0);
235         assert_eq!(offset_of!(Foo, b), 4);
236         assert_eq!(offset_of!(Foo, c), 8);
237     }
238 
239     #[test]
offset_simple_packed()240     fn offset_simple_packed() {
241         #[repr(C, packed)]
242         struct Foo {
243             a: u32,
244             b: [u8; 2],
245             c: i64,
246         }
247 
248         assert_eq!(offset_of!(Foo, a), 0);
249         assert_eq!(offset_of!(Foo, b), 4);
250         assert_eq!(offset_of!(Foo, c), 6);
251     }
252 
253     #[test]
tuple_struct()254     fn tuple_struct() {
255         #[repr(C)]
256         struct Tup(i32, i32);
257 
258         assert_eq!(offset_of!(Tup, 0), 0);
259         assert_eq!(offset_of!(Tup, 1), 4);
260     }
261 
262     #[test]
offset_union()263     fn offset_union() {
264         // Since we're specifying repr(C), all fields are supposed to be at offset 0
265         #[repr(C)]
266         union Foo {
267             a: u32,
268             b: [u8; 2],
269             c: i64,
270         }
271 
272         assert_eq!(offset_of_union!(Foo, a), 0);
273         assert_eq!(offset_of_union!(Foo, b), 0);
274         assert_eq!(offset_of_union!(Foo, c), 0);
275     }
276 
277     #[test]
path()278     fn path() {
279         mod sub {
280             #[repr(C)]
281             pub struct Foo {
282                 pub x: u32,
283             }
284         }
285 
286         assert_eq!(offset_of!(sub::Foo, x), 0);
287     }
288 
289     #[test]
inside_generic_method()290     fn inside_generic_method() {
291         struct Pair<T, U>(T, U);
292 
293         fn foo<T, U>(_: Pair<T, U>) -> usize {
294             offset_of!(Pair<T, U>, 1)
295         }
296 
297         assert_eq!(foo(Pair(0, 0)), 4);
298     }
299 
300     #[cfg(tuple_ty)]
301     #[test]
test_tuple_offset()302     fn test_tuple_offset() {
303         let f = (0i32, 0.0f32, 0u8);
304         let f_ptr = &f as *const _;
305         let f1_ptr = &f.1 as *const _;
306 
307         assert_eq!(
308             f1_ptr as usize - f_ptr as usize,
309             offset_of_tuple!((i32, f32, u8), 1)
310         );
311     }
312 
313     #[test]
test_raw_field()314     fn test_raw_field() {
315         #[repr(C)]
316         struct Foo {
317             a: u32,
318             b: [u8; 2],
319             c: i64,
320         }
321 
322         let f: Foo = Foo {
323             a: 0,
324             b: [0, 0],
325             c: 0,
326         };
327         let f_ptr = &f as *const _;
328         assert_eq!(f_ptr as usize + 0, raw_field!(f_ptr, Foo, a) as usize);
329         assert_eq!(f_ptr as usize + 4, raw_field!(f_ptr, Foo, b) as usize);
330         assert_eq!(f_ptr as usize + 8, raw_field!(f_ptr, Foo, c) as usize);
331     }
332 
333     #[cfg(tuple_ty)]
334     #[test]
test_raw_field_tuple()335     fn test_raw_field_tuple() {
336         let t = (0u32, 0u8, false);
337         let t_ptr = &t as *const _;
338         let t_addr = t_ptr as usize;
339 
340         assert_eq!(
341             &t.0 as *const _ as usize - t_addr,
342             raw_field_tuple!(t_ptr, (u32, u8, bool), 0) as usize - t_addr
343         );
344         assert_eq!(
345             &t.1 as *const _ as usize - t_addr,
346             raw_field_tuple!(t_ptr, (u32, u8, bool), 1) as usize - t_addr
347         );
348         assert_eq!(
349             &t.2 as *const _ as usize - t_addr,
350             raw_field_tuple!(t_ptr, (u32, u8, bool), 2) as usize - t_addr
351         );
352     }
353 
354     #[test]
test_raw_field_union()355     fn test_raw_field_union() {
356         #[repr(C)]
357         union Foo {
358             a: u32,
359             b: [u8; 2],
360             c: i64,
361         }
362 
363         let f = Foo { a: 0 };
364         let f_ptr = &f as *const _;
365         assert_eq!(f_ptr as usize + 0, raw_field_union!(f_ptr, Foo, a) as usize);
366         assert_eq!(f_ptr as usize + 0, raw_field_union!(f_ptr, Foo, b) as usize);
367         assert_eq!(f_ptr as usize + 0, raw_field_union!(f_ptr, Foo, c) as usize);
368     }
369 
370     #[cfg(any(stable_offset_of, stable_const))]
371     #[test]
const_offset()372     fn const_offset() {
373         #[repr(C)]
374         struct Foo {
375             a: u32,
376             b: [u8; 2],
377             c: i64,
378         }
379 
380         assert_eq!([0; offset_of!(Foo, b)].len(), 4);
381     }
382 
383     #[cfg(stable_offset_of)]
384     #[test]
const_offset_interior_mutable()385     fn const_offset_interior_mutable() {
386         #[repr(C)]
387         struct Foo {
388             a: u32,
389             b: core::cell::Cell<u32>,
390         }
391 
392         assert_eq!([0; offset_of!(Foo, b)].len(), 4);
393     }
394 
395     #[cfg(any(stable_offset_of, stable_const))]
396     #[test]
const_fn_offset()397     fn const_fn_offset() {
398         const fn test_fn() -> usize {
399             #[repr(C)]
400             struct Foo {
401                 a: u32,
402                 b: [u8; 2],
403                 c: i64,
404             }
405 
406             offset_of!(Foo, b)
407         }
408 
409         assert_eq!([0; test_fn()].len(), 4);
410     }
411 }
412