1 #![forbid(unsafe_code)]
2 
3 /// Find the offset in bytes of the given `$field` of `$Type`. Requires an
4 /// already initialized `$instance` value to work with.
5 ///
6 /// This is similar to the macro from [`memoffset`](https://docs.rs/memoffset),
7 /// however it uses no `unsafe` code.
8 ///
9 /// This macro has a 3-argument and 2-argument version.
10 /// * In the 3-arg version you specify an instance of the type, the type itself,
11 ///   and the field name.
12 /// * In the 2-arg version the macro will call the [`default`](Default::default)
13 ///   method to make a temporary instance of the type for you.
14 ///
15 /// The output of this macro is the byte offset of the field (as a `usize`). The
16 /// calculations of the macro are fixed across the entire program, but if the
17 /// type used is `repr(Rust)` then they're *not* fixed across compilations or
18 /// compilers.
19 ///
20 /// ## Examples
21 ///
22 /// ### 3-arg Usage
23 ///
24 /// ```rust
25 /// # use bytemuck::offset_of;
26 /// // enums can't derive default, and for this example we don't pick one
27 /// enum MyExampleEnum {
28 ///   A,
29 ///   B,
30 ///   C,
31 /// }
32 ///
33 /// // so now our struct here doesn't have Default
34 /// #[repr(C)]
35 /// struct MyNotDefaultType {
36 ///   pub counter: i32,
37 ///   pub some_field: MyExampleEnum,
38 /// }
39 ///
40 /// // but we provide an instance of the type and it's all good.
41 /// let val = MyNotDefaultType { counter: 5, some_field: MyExampleEnum::A };
42 /// assert_eq!(offset_of!(val, MyNotDefaultType, some_field), 4);
43 /// ```
44 ///
45 /// ### 2-arg Usage
46 ///
47 /// ```rust
48 /// # use bytemuck::offset_of;
49 /// #[derive(Default)]
50 /// #[repr(C)]
51 /// struct Vertex {
52 ///   pub loc: [f32; 3],
53 ///   pub color: [f32; 3],
54 /// }
55 /// // if the type impls Default the macro can make its own default instance.
56 /// assert_eq!(offset_of!(Vertex, loc), 0);
57 /// assert_eq!(offset_of!(Vertex, color), 12);
58 /// ```
59 ///
60 /// # Usage with `#[repr(packed)]` structs
61 ///
62 /// Attempting to compute the offset of a `#[repr(packed)]` struct with
63 /// `bytemuck::offset_of!` requires an `unsafe` block. We hope to relax this in
64 /// the future, but currently it is required to work around a soundness hole in
65 /// Rust (See [rust-lang/rust#27060]).
66 ///
67 /// [rust-lang/rust#27060]: https://github.com/rust-lang/rust/issues/27060
68 ///
69 /// <p style="background:rgba(255,181,77,0.16);padding:0.75em;">
70 /// <strong>Warning:</strong> This is only true for versions of bytemuck >
71 /// 1.4.0. Previous versions of
72 /// <code style="background:rgba(41,24,0,0.1);">bytemuck::offset_of!</code>
73 /// will only emit a warning when used on the field of a packed struct in safe
74 /// code, which can lead to unsoundness.
75 /// </p>
76 ///
77 /// For example, the following will fail to compile:
78 ///
79 /// ```compile_fail
80 /// #[repr(C, packed)]
81 /// #[derive(Default)]
82 /// struct Example {
83 ///   field: u32,
84 /// }
85 /// // Doesn't compile:
86 /// let _offset = bytemuck::offset_of!(Example, field);
87 /// ```
88 ///
89 /// While the error message this generates will mention the
90 /// `safe_packed_borrows` lint, the macro will still fail to compile even if
91 /// that lint is `#[allow]`ed:
92 ///
93 /// ```compile_fail
94 /// # #[repr(C, packed)] #[derive(Default)] struct Example { field: u32 }
95 /// // Still doesn't compile:
96 /// #[allow(safe_packed_borrows)]
97 /// {
98 ///   let _offset = bytemuck::offset_of!(Example, field);
99 /// }
100 /// ```
101 ///
102 /// This *can* be worked around by using `unsafe`, but it is only sound to do so
103 /// if you can guarantee that taking a reference to the field is sound.
104 ///
105 /// In practice, this means it only works for fields of align(1) types, or if
106 /// you know the field's offset in advance (defeating the point of `offset_of`)
107 /// and can prove that the struct's alignment and the field's offset are enough
108 /// to prove the field's alignment.
109 ///
110 /// Once the `raw_ref` macros are available, a future version of this crate will
111 /// use them to lift the limitations of packed structs. For the duration of the
112 /// `1.x` version of this crate that will be behind an on-by-default cargo
113 /// feature (to maintain minimum rust version support).
114 #[macro_export]
115 macro_rules! offset_of {
116   ($instance:expr, $Type:path, $field:tt) => {{
117     #[forbid(safe_packed_borrows)]
118     {
119       // This helps us guard against field access going through a Deref impl.
120       #[allow(clippy::unneeded_field_pattern)]
121       let $Type { $field: _, .. };
122       let reference: &$Type = &$instance;
123       let address = reference as *const _ as usize;
124       let field_pointer = &reference.$field as *const _ as usize;
125       // These asserts/unwraps are compiled away at release, and defend against
126       // the case where somehow a deref impl is still invoked.
127       let result = field_pointer.checked_sub(address).unwrap();
128       assert!(result <= $crate::__core::mem::size_of::<$Type>());
129       result
130     }
131   }};
132   ($Type:path, $field:tt) => {{
133     $crate::offset_of!(<$Type as Default>::default(), $Type, $field)
134   }};
135 }
136