diff --git a/src/lib.rs b/src/lib.rs index 7cfb450..a90199d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,11 +38,17 @@ //! is always built with `extern_crate_alloc` cargo feature enabled. #[cfg(target_arch = "x86")] -pub(crate) use core::arch::x86; +use core::arch::x86; #[cfg(target_arch = "x86_64")] -pub(crate) use core::arch::x86_64; +use core::arch::x86_64; // -pub(crate) use core::{marker::*, mem::*, num::*, ptr::*}; +use core::{marker::*, mem::*, num::*, ptr::*}; + +// Used from macros to ensure we aren't using some locally defined name and +// actually are referencing libcore. This also would allow pre-2018 edition +// crates to use our macros, but I'm not sure how important that is. +#[doc(hidden)] +pub use ::core as __core; macro_rules! impl_unsafe_marker_for_array { ( $marker:ident , $( $n:expr ),* ) => { @@ -66,14 +72,33 @@ pub use pod::*; mod contiguous; pub use contiguous::*; +mod offset_of; +pub use offset_of::*; + mod transparent; pub use transparent::*; -// Used from macros to ensure we aren't using some locally defined name and -// actually are referencing libcore. This also would allow pre-2018 edition -// crates to use our macros, but I'm not sure how important that is. -#[doc(hidden)] -pub use ::core as __core; +/* + +Note(Lokathor): We've switched all of the `unwrap` to `match` because there is +apparently a bug: https://github.com/rust-lang/rust/issues/68667 +and it doesn't seem to show up in simple godbolt examples but has been reported +as having an impact when there's a cast mixed in with other more complicated +code around it. Rustc/LLVM ends up missing that the `Err` can't ever happen for +particular type combinations, and then it doesn't fully eliminated the panic +possibility code branch. + +*/ + +/// Immediately panics. +#[cold] +#[inline(never)] +fn something_went_wrong(src: &str, err: PodCastError) -> ! { + // Note(Lokathor): Keeping the panic here makes the panic _formatting_ go + // here too, which helps assembly readability and also helps keep down + // the inline pressure. + panic!("{src}>{err:?}", src = src, err = err) +} /// Re-interprets `&T` as `&[u8]`. /// @@ -81,7 +106,10 @@ pub use ::core as __core; /// empty slice might not match the pointer value of the input reference. #[inline] pub fn bytes_of(t: &T) -> &[u8] { - try_cast_slice::(core::slice::from_ref(t)).unwrap_or(&[]) + match try_cast_slice::(core::slice::from_ref(t)) { + Ok(s) => s, + Err(_) => unreachable!(), + } } /// Re-interprets `&mut T` as `&mut [u8]`. @@ -90,27 +118,36 @@ pub fn bytes_of(t: &T) -> &[u8] { /// empty slice might not match the pointer value of the input reference. #[inline] pub fn bytes_of_mut(t: &mut T) -> &mut [u8] { - try_cast_slice_mut::(core::slice::from_mut(t)).unwrap_or(&mut []) + match try_cast_slice_mut::(core::slice::from_mut(t)) { + Ok(s) => s, + Err(_) => unreachable!(), + } } /// Re-interprets `&[u8]` as `&T`. /// /// ## Panics /// -/// This is [`try_from_bytes`] with an unwrap. +/// This is [`try_from_bytes`] but will panic on error. #[inline] pub fn from_bytes(s: &[u8]) -> &T { - try_from_bytes(s).unwrap() + match try_from_bytes(s) { + Ok(t) => t, + Err(e) => something_went_wrong("from_bytes", e), + } } /// Re-interprets `&mut [u8]` as `&mut T`. /// /// ## Panics /// -/// This is [`try_from_bytes_mut`] with an unwrap. +/// This is [`try_from_bytes_mut`] but will panic on error. #[inline] pub fn from_bytes_mut(s: &mut [u8]) -> &mut T { - try_from_bytes_mut(s).unwrap() + match try_from_bytes_mut(s) { + Ok(t) => t, + Err(e) => something_went_wrong("from_bytes_mut", e), + } } /// Re-interprets `&[u8]` as `&T`. @@ -172,50 +209,89 @@ pub enum PodCastError { /// /// ## Panics /// -/// This is [`try_cast`] with an unwrap. +/// This is [`try_cast`] but will panic on error. #[inline] pub fn cast(a: A) -> B { - try_cast(a).unwrap() + if size_of::() == size_of::() { + // Plz mr compiler, just notice that we can't ever hit Err in this case. + match try_cast(a) { + Ok(b) => b, + Err(_) => unreachable!(), + } + } else { + match try_cast(a) { + Ok(b) => b, + Err(e) => something_went_wrong("cast", e), + } + } } /// Cast `&mut T` into `&mut U`. /// /// ## Panics /// -/// This is [`try_cast_mut`] with an unwrap. +/// This is [`try_cast_mut`] but will panic on error. #[inline] pub fn cast_mut(a: &mut A) -> &mut B { - try_cast_mut(a).unwrap() + if size_of::() == size_of::() && align_of::() >= align_of::() { + // Plz mr compiler, just notice that we can't ever hit Err in this case. + match try_cast_mut(a) { + Ok(b) => b, + Err(_) => unreachable!(), + } + } else { + match try_cast_mut(a) { + Ok(b) => b, + Err(e) => something_went_wrong("cast_mut", e), + } + } } /// Cast `&T` into `&U`. /// /// ## Panics /// -/// This is [`try_cast_ref`] with an unwrap. +/// This is [`try_cast_ref`] but will panic on error. #[inline] pub fn cast_ref(a: &A) -> &B { - try_cast_ref(a).unwrap() + if size_of::() == size_of::() && align_of::() >= align_of::() { + // Plz mr compiler, just notice that we can't ever hit Err in this case. + match try_cast_ref(a) { + Ok(b) => b, + Err(_) => unreachable!(), + } + } else { + match try_cast_ref(a) { + Ok(b) => b, + Err(e) => something_went_wrong("cast_ref", e), + } + } } /// Cast `&[T]` into `&[U]`. /// /// ## Panics /// -/// This is [`try_cast_slice`] with an unwrap. +/// This is [`try_cast_slice`] but will panic on error. #[inline] pub fn cast_slice(a: &[A]) -> &[B] { - try_cast_slice(a).unwrap() + match try_cast_slice(a) { + Ok(b) => b, + Err(e) => something_went_wrong("cast_slice", e), + } } /// Cast `&mut [T]` into `&mut [U]`. /// /// ## Panics /// -/// This is [`try_cast_slice_mut`] with an unwrap. +/// This is [`try_cast_slice_mut`] but will panic on error. #[inline] pub fn cast_slice_mut(a: &mut [A]) -> &mut [B] { - try_cast_slice_mut(a).unwrap() + match try_cast_slice_mut(a) { + Ok(b) => b, + Err(e) => something_went_wrong("cast_slice_mut", e), + } } /// As `align_to`, but safe because of the [`Pod`] bound. @@ -355,104 +431,3 @@ pub fn try_cast_slice_mut( Err(PodCastError::OutputSliceWouldHaveSlop) } } - -/// Find the offset in bytes of the given `$field` of `$Type`, using `$instance` -/// as an already-initialized reference value. -/// -/// 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 get an already-initialized -/// instance of `$Type` rather than trying to find a way access the fields of an -/// uninitialized one without hitting soundness problems. -/// -/// 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 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 - }}; -} diff --git a/src/offset_of.rs b/src/offset_of.rs new file mode 100644 index 0000000..fa85727 --- /dev/null +++ b/src/offset_of.rs @@ -0,0 +1,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 + }}; +}