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]
115macro_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