diff options
Diffstat (limited to 'vendor/bytemuck/src/offset_of.rs')
-rw-r--r-- | vendor/bytemuck/src/offset_of.rs | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/vendor/bytemuck/src/offset_of.rs b/vendor/bytemuck/src/offset_of.rs new file mode 100644 index 0000000..7e8aedf --- /dev/null +++ b/vendor/bytemuck/src/offset_of.rs @@ -0,0 +1,135 @@ +#![forbid(unsafe_code)] + +/// Find the offset in bytes of the given `$field` of `$Type`. Requires an +/// already initialized `$instance` value to work with. +/// +/// This is similar to the macro from [`memoffset`](https://docs.rs/memoffset), +/// however it uses no `unsafe` code. +/// +/// This macro has a 3-argument and 2-argument version. +/// * In the 3-arg version you specify an instance of the type, the type itself, +/// and the field name. +/// * In the 2-arg version the macro will call the [`default`](Default::default) +/// method to make a temporary instance of the type for you. +/// +/// The output of this macro is the byte offset of the field (as a `usize`). The +/// calculations of the macro are fixed across the entire program, but if the +/// type used is `repr(Rust)` then they're *not* fixed across compilations or +/// compilers. +/// +/// ## Examples +/// +/// ### 3-arg Usage +/// +/// ```rust +/// # use bytemuck::offset_of; +/// // enums can't derive default, and for this example we don't pick one +/// enum MyExampleEnum { +/// A, +/// B, +/// C, +/// } +/// +/// // so now our struct here doesn't have Default +/// #[repr(C)] +/// struct MyNotDefaultType { +/// pub counter: i32, +/// pub some_field: MyExampleEnum, +/// } +/// +/// // but we provide an instance of the type and it's all good. +/// let val = MyNotDefaultType { counter: 5, some_field: MyExampleEnum::A }; +/// assert_eq!(offset_of!(val, MyNotDefaultType, some_field), 4); +/// ``` +/// +/// ### 2-arg Usage +/// +/// ```rust +/// # use bytemuck::offset_of; +/// #[derive(Default)] +/// #[repr(C)] +/// struct Vertex { +/// pub loc: [f32; 3], +/// pub color: [f32; 3], +/// } +/// // if the type impls Default the macro can make its own default instance. +/// assert_eq!(offset_of!(Vertex, loc), 0); +/// assert_eq!(offset_of!(Vertex, color), 12); +/// ``` +/// +/// # Usage with `#[repr(packed)]` structs +/// +/// Attempting to compute the offset of a `#[repr(packed)]` struct with +/// `bytemuck::offset_of!` requires an `unsafe` block. We hope to relax this in +/// the future, but currently it is required to work around a soundness hole in +/// Rust (See [rust-lang/rust#27060]). +/// +/// [rust-lang/rust#27060]: https://github.com/rust-lang/rust/issues/27060 +/// +/// <p style="background:rgba(255,181,77,0.16);padding:0.75em;"> +/// <strong>Warning:</strong> This is only true for versions of bytemuck > +/// 1.4.0. Previous versions of +/// <code style="background:rgba(41,24,0,0.1);">bytemuck::offset_of!</code> +/// will only emit a warning when used on the field of a packed struct in safe +/// code, which can lead to unsoundness. +/// </p> +/// +/// For example, the following will fail to compile: +/// +/// ```compile_fail +/// #[repr(C, packed)] +/// #[derive(Default)] +/// struct Example { +/// field: u32, +/// } +/// // Doesn't compile: +/// let _offset = bytemuck::offset_of!(Example, field); +/// ``` +/// +/// While the error message this generates will mention the +/// `safe_packed_borrows` lint, the macro will still fail to compile even if +/// that lint is `#[allow]`ed: +/// +/// ```compile_fail +/// # #[repr(C, packed)] #[derive(Default)] struct Example { field: u32 } +/// // Still doesn't compile: +/// #[allow(safe_packed_borrows)] +/// { +/// let _offset = bytemuck::offset_of!(Example, field); +/// } +/// ``` +/// +/// This *can* be worked around by using `unsafe`, but it is only sound to do so +/// if you can guarantee that taking a reference to the field is sound. +/// +/// In practice, this means it only works for fields of align(1) types, or if +/// you know the field's offset in advance (defeating the point of `offset_of`) +/// and can prove that the struct's alignment and the field's offset are enough +/// to prove the field's alignment. +/// +/// Once the `raw_ref` macros are available, a future version of this crate will +/// use them to lift the limitations of packed structs. For the duration of the +/// `1.x` version of this crate that will be behind an on-by-default cargo +/// feature (to maintain minimum rust version support). +#[macro_export] +macro_rules! offset_of { + ($instance:expr, $Type:path, $field:tt) => {{ + #[forbid(safe_packed_borrows)] + { + // 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 + } + }}; + ($Type:path, $field:tt) => {{ + $crate::offset_of!(<$Type as Default>::default(), $Type, $field) + }}; +} |