1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
#![forbid(unsafe_code)] /// Find the offset in bytes of the given `$field` of `$Type`, using `$instance` /// as an already-initialized value to work with. /// /// This is similar to the macro from `memoffset`, however it's fully well /// defined even in current versions of Rust (and uses no unsafe code). /// /// It does by using the `$instance` argument to have an already-initialized /// instance of `$Type` rather than trying to find a way access the fields of an /// uninitialized one without hitting soundness problems. The value passed to /// the macro is referenced but not moved. /// /// This means the API is more limited, but it's also sound even in rather /// extreme cases, like some of the examples. /// /// ## Caveats /// /// 1. The offset is in bytes, and so you will likely have to cast your base /// pointers to `*const u8`/`*mut u8` before getting field addresses. /// /// 2. The offset values of repr(Rust) types are not stable, and may change /// wildly between releases of the compiler. Use repr(C) if you can. /// /// 3. The value of the `$instance` parameter has no bearing on the output of /// this macro. It is just used to avoid soundness problems. The only /// requirement is that it be initialized. In particular, the value returned /// is not a field pointer, or anything like that. /// /// ## Examples /// /// ### Use with zeroable types /// A common requirement in GPU apis is to specify the layout of vertices. These /// will generally be [`Zeroable`] (if not [`Pod`]), and are a good fit for /// `offset_of!`. /// ``` /// # use bytemuck::{Zeroable, offset_of}; /// #[repr(C)] /// struct Vertex { /// pos: [f32; 2], /// uv: [u16; 2], /// color: [u8; 4], /// } /// unsafe impl Zeroable for Vertex {} /// /// let pos = offset_of!(Zeroable::zeroed(), Vertex, pos); /// let uv = offset_of!(Zeroable::zeroed(), Vertex, uv); /// let color = offset_of!(Zeroable::zeroed(), Vertex, color); /// /// assert_eq!(pos, 0); /// assert_eq!(uv, 8); /// assert_eq!(color, 12); /// ``` /// /// ### Use with other types /// /// More esoteric uses are possible too, including with types generally not safe /// to otherwise use with bytemuck. `Strings`, `Vec`s, etc. /// /// ``` /// #[derive(Default)] /// struct Foo { /// a: u8, /// b: &'static str, /// c: i32, /// } /// /// let a_offset = bytemuck::offset_of!(Default::default(), Foo, a); /// let b_offset = bytemuck::offset_of!(Default::default(), Foo, b); /// let c_offset = bytemuck::offset_of!(Default::default(), Foo, c); /// /// assert_ne!(a_offset, b_offset); /// assert_ne!(b_offset, c_offset); /// // We can't check against hardcoded values for a repr(Rust) type, /// // but prove to ourself this way. /// /// let foo = Foo::default(); /// // Note: offsets are in bytes. /// let as_bytes = &foo as *const _ as *const u8; /// /// // we're using wrapping_offset here becasue it's not worth /// // the unsafe block, but it would be valid to use `add` instead, /// // as it cannot overflow. /// assert_eq!(&foo.a as *const _ as usize, as_bytes.wrapping_add(a_offset) as usize); /// assert_eq!(&foo.b as *const _ as usize, as_bytes.wrapping_add(b_offset) as usize); /// assert_eq!(&foo.c as *const _ as usize, as_bytes.wrapping_add(c_offset) as usize); /// ``` #[macro_export] macro_rules! offset_of { ($instance:expr, $Type:path, $field:tt) => {{ // This helps us guard against field access going through a Deref impl. #[allow(clippy::unneeded_field_pattern)] let $Type { $field: _, .. }; let reference: &$Type = &$instance; let address = reference as *const _ as usize; let field_pointer = &reference.$field as *const _ as usize; // These asserts/unwraps are compiled away at release, and defend against // the case where somehow a deref impl is still invoked. let result = field_pointer.checked_sub(address).unwrap(); assert!(result <= $crate::__core::mem::size_of::<$Type>()); result }}; }