Snap for 11386347 from 292246687c925257dd63d23a62d3efb618584150 to 24D1-release Change-Id: Idb1c40b935724eeef4164f5b64686453a5b92e40
diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..71e0936 --- /dev/null +++ b/.cargo/config.toml
@@ -0,0 +1,4 @@ +[alias] + +# The list of features should be the same as the one under `[package.metadata.docs.rs]` +nightly_docs = "doc --no-deps -F nightly_docs,derive,extern_crate_alloc,extern_crate_std,zeroable_maybe_uninit,zeroable_atomics,min_const_generics,wasm_simd,must_cast"
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 499b132..3a8d028 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@ { "git": { - "sha1": "b19f8abfe34d6ab660e748086874c01e0212b738" + "sha1": "1e1a22e5eac57e5b473e0c427dc187008228ad46" }, "path_in_vcs": "" } \ No newline at end of file
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0807319..0fb1358 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml
@@ -46,8 +46,8 @@ runs-on: ubuntu-latest strategy: matrix: - # note: the mips targets are here so that we have big-endian coverage (both 32bit and 64bit) - target: [i686-unknown-linux-gnu, mips-unknown-linux-gnu, mips64-unknown-linux-gnuabi64] + # we once had mips runners for Big-endian coverage but those got demoted to tier 3. + target: [i686-unknown-linux-gnu] steps: - uses: hecrj/setup-rust-action@v1 with:
diff --git a/Android.bp b/Android.bp index 47ab7d8..7943b6d 100644 --- a/Android.bp +++ b/Android.bp
@@ -6,7 +6,7 @@ host_supported: true, crate_name: "array_tests", cargo_env_compat: true, - cargo_pkg_version: "1.13.1", + cargo_pkg_version: "1.14.1", srcs: ["tests/array_tests.rs"], test_suites: ["general-tests"], auto_gen_config: true, @@ -27,7 +27,7 @@ host_supported: true, crate_name: "cast_slice_tests", cargo_env_compat: true, - cargo_pkg_version: "1.13.1", + cargo_pkg_version: "1.14.1", srcs: ["tests/cast_slice_tests.rs"], test_suites: ["general-tests"], auto_gen_config: true, @@ -48,7 +48,7 @@ host_supported: true, crate_name: "checked_tests", cargo_env_compat: true, - cargo_pkg_version: "1.13.1", + cargo_pkg_version: "1.14.1", srcs: ["tests/checked_tests.rs"], test_suites: ["general-tests"], auto_gen_config: true, @@ -69,7 +69,7 @@ host_supported: true, crate_name: "doc_tests", cargo_env_compat: true, - cargo_pkg_version: "1.13.1", + cargo_pkg_version: "1.14.1", srcs: ["tests/doc_tests.rs"], test_suites: ["general-tests"], auto_gen_config: true, @@ -90,7 +90,7 @@ host_supported: true, crate_name: "offset_of_tests", cargo_env_compat: true, - cargo_pkg_version: "1.13.1", + cargo_pkg_version: "1.14.1", srcs: ["tests/offset_of_tests.rs"], test_suites: ["general-tests"], auto_gen_config: true, @@ -111,7 +111,7 @@ host_supported: true, crate_name: "std_tests", cargo_env_compat: true, - cargo_pkg_version: "1.13.1", + cargo_pkg_version: "1.14.1", srcs: ["tests/std_tests.rs"], test_suites: ["general-tests"], auto_gen_config: true, @@ -132,7 +132,7 @@ host_supported: true, crate_name: "transparent", cargo_env_compat: true, - cargo_pkg_version: "1.13.1", + cargo_pkg_version: "1.14.1", srcs: ["tests/transparent.rs"], test_suites: ["general-tests"], auto_gen_config: true, @@ -153,7 +153,7 @@ host_supported: true, crate_name: "bytemuck", cargo_env_compat: true, - cargo_pkg_version: "1.13.1", + cargo_pkg_version: "1.14.1", srcs: ["src/lib.rs"], edition: "2018", features: [
diff --git a/Cargo.toml b/Cargo.toml index b7d55e8..826afad 100644 --- a/Cargo.toml +++ b/Cargo.toml
@@ -12,7 +12,7 @@ [package] edition = "2018" name = "bytemuck" -version = "1.13.1" +version = "1.14.1" authors = ["Lokathor <zefria@gmail.com>"] exclude = ["/pedantic.bat"] description = "A crate for mucking around with piles of bytes." @@ -31,6 +31,7 @@ [package.metadata.docs.rs] features = [ + "nightly_docs", "derive", "extern_crate_alloc", "extern_crate_std", @@ -38,6 +39,7 @@ "zeroable_atomics", "min_const_generics", "wasm_simd", + "must_cast", ] [package.metadata.playground] @@ -49,6 +51,7 @@ "zeroable_atomics", "min_const_generics", "wasm_simd", + "must_cast", ] [dependencies.bytemuck_derive] @@ -57,10 +60,13 @@ [features] aarch64_simd = [] +align_offset = [] derive = ["bytemuck_derive"] extern_crate_alloc = [] extern_crate_std = ["extern_crate_alloc"] min_const_generics = [] +must_cast = [] +nightly_docs = [] nightly_portable_simd = [] nightly_stdsimd = [] unsound_ptr_pod_impl = []
diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 9d6be11..fdd9e06 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig
@@ -1,7 +1,7 @@ [package] name = "bytemuck" description = "A crate for mucking around with piles of bytes." -version = "1.13.1" +version = "1.14.1" authors = ["Lokathor <zefria@gmail.com>"] repository = "https://github.com/Lokathor/bytemuck" readme = "README.md" @@ -18,23 +18,39 @@ extern_crate_std = ["extern_crate_alloc"] zeroable_maybe_uninit = [] zeroable_atomics = [] -min_const_generics = [] -wasm_simd = [] # Until >= 1.54.0 is MSRV this is an off-by-default feature. -aarch64_simd = [] # Until >= 1.59.0 is MSRV this is an off-by-default feature. -# Do not use if you can avoid it, because this is unsound. +# All MSRV notes below are GUIDELINES and future versions may require even more +# MSRV on any feature. + +# MSRV 1.36: Use `align_offset` method instead of casting to `usize` to check +# alignment of pointers, this *may* improve codegen in some cases (but it has +# never been formally benchmarked!) +align_offset = [] + +min_const_generics = [] # MSRV 1.51: support arrays via min_const_generics + +wasm_simd = [] # MSRV 1.54.0: support wasm simd types +aarch64_simd = [] # MSRV 1.59.0: support aarch64 simd types + +must_cast = [] # MSRV 1.57.0: support the `must` module. + +# Do not use if you can avoid it, because this is **unsound**!!!! unsound_ptr_pod_impl = [] # NOT SEMVER SUPPORTED! TEMPORARY ONLY! nightly_portable_simd = [] nightly_stdsimd = [] +# Improved documentation using the nightly toolchain +nightly_docs = [] + [dependencies] bytemuck_derive = { version = "1.4", path = "derive", optional = true } [package.metadata.docs.rs] # Note(Lokathor): Don't use all-features or it would use `unsound_ptr_pod_impl` too. features = [ + "nightly_docs", "derive", "extern_crate_alloc", "extern_crate_std", @@ -42,6 +58,7 @@ "zeroable_atomics", "min_const_generics", "wasm_simd", + "must_cast", ] [package.metadata.playground] @@ -54,4 +71,5 @@ "zeroable_atomics", "min_const_generics", "wasm_simd", + "must_cast", ]
diff --git a/METADATA b/METADATA index bd36741..15a6d60 100644 --- a/METADATA +++ b/METADATA
@@ -1,20 +1,20 @@ +# This project was upgraded with external_updater. +# Usage: tools/external_updater/updater.sh update external/rust/crates/bytemuck +# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md + name: "bytemuck" description: "A crate for mucking around with piles of bytes." third_party { - url { - type: HOMEPAGE - value: "https://crates.io/crates/bytemuck" - } - url { - type: ARCHIVE - value: "https://static.crates.io/crates/bytemuck/bytemuck-1.13.1.crate" - } - version: "1.13.1" - # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same. license_type: NOTICE last_upgrade_date { - year: 2023 - month: 7 - day: 10 + year: 2024 + month: 1 + day: 31 + } + homepage: "https://crates.io/crates/bytemuck" + identifier { + type: "Archive" + value: "https://static.crates.io/crates/bytemuck/bytemuck-1.14.1.crate" + version: "1.14.1" } }
diff --git a/changelog.md b/changelog.md index 0dff0c2..e890764 100644 --- a/changelog.md +++ b/changelog.md
@@ -1,5 +1,23 @@ # `bytemuck` changelog +## 1.14.1 + +* docs clarifications. + +## 1.14 + +* `write_zeroes` and `fill_zeroes` functions: Writes (to one) or fills (a slice) + zero bytes to all bytes covered by the provided reference. If your type has + padding, this will even zero out the padding bytes. +* `align_offset` feature: causes pointer alignment checks to use the + `align_offset` pointer method rather than as-casting the pointer to `usize`. + This *may* improve codegen, if the compiler would have otherwise thought that + the pointer address escaped. No formal benchmarks have been done either way. +* `must_cast` feature: Adds `must_*` family of functions. These functions will + fail to compile if the cast requested can't be statically known to succeed. + The error messages can be kinda bad when this happens, but eliminating the + possibility of a runtime error might be worth it to you. + ## 1.13.1 * Remove the requirement for the *source* data type to be `AnyBitPattern` on
diff --git a/src/allocation.rs b/src/allocation.rs index 0ab6625..cda5e60 100644 --- a/src/allocation.rs +++ b/src/allocation.rs
@@ -19,6 +19,7 @@ vec, vec::Vec, }; +use core::ops::{Deref, DerefMut}; /// As [`try_cast_box`](try_cast_box), but unwraps for you. #[inline] @@ -685,4 +686,118 @@ } } } -impl<I: ?Sized, T: TransparentWrapper<I>> TransparentWrapperAlloc<I> for T {} + +impl<I: ?Sized, T: ?Sized + TransparentWrapper<I>> TransparentWrapperAlloc<I> + for T +{ +} + +/// As `Box<[u8]>`, but remembers the original alignment. +pub struct BoxBytes { + // SAFETY: `ptr` is owned, was allocated with `layout`, and points to + // `layout.size()` initialized bytes. + ptr: NonNull<u8>, + layout: Layout, +} + +impl Deref for BoxBytes { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + // SAFETY: See type invariant. + unsafe { + core::slice::from_raw_parts(self.ptr.as_ptr(), self.layout.size()) + } + } +} + +impl DerefMut for BoxBytes { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: See type invariant. + unsafe { + core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.layout.size()) + } + } +} + +impl Drop for BoxBytes { + fn drop(&mut self) { + // SAFETY: See type invariant. + unsafe { alloc::alloc::dealloc(self.ptr.as_ptr(), self.layout) }; + } +} + +impl<T: NoUninit> From<Box<T>> for BoxBytes { + fn from(value: Box<T>) -> Self { + let layout = Layout::new::<T>(); + let ptr = Box::into_raw(value) as *mut u8; + // SAFETY: Box::into_raw() returns a non-null pointer. + let ptr = unsafe { NonNull::new_unchecked(ptr) }; + BoxBytes { ptr, layout } + } +} + +/// Re-interprets `Box<T>` as `BoxBytes`. +#[inline] +pub fn box_bytes_of<T: NoUninit>(input: Box<T>) -> BoxBytes { + input.into() +} + +/// Re-interprets `BoxBytes` as `Box<T>`. +/// +/// ## Panics +/// +/// This is [`try_from_box_bytes`] but will panic on error and the input will be +/// dropped. +#[inline] +pub fn from_box_bytes<T: AnyBitPattern>(input: BoxBytes) -> Box<T> { + try_from_box_bytes(input).map_err(|(error, _)| error).unwrap() +} + +/// Re-interprets `BoxBytes` as `Box<T>`. +/// +/// ## Panics +/// +/// * If the input isn't aligned for the new type +/// * If the input's length isn’t exactly the size of the new type +#[inline] +pub fn try_from_box_bytes<T: AnyBitPattern>( + input: BoxBytes, +) -> Result<Box<T>, (PodCastError, BoxBytes)> { + let layout = Layout::new::<T>(); + if input.layout.align() != layout.align() { + return Err((PodCastError::AlignmentMismatch, input)); + } else if input.layout.size() != layout.size() { + return Err((PodCastError::SizeMismatch, input)); + } else { + let (ptr, _) = input.into_raw_parts(); + // SAFETY: See type invariant. + Ok(unsafe { Box::from_raw(ptr.as_ptr() as *mut T) }) + } +} + +impl BoxBytes { + /// Constructs a `BoxBytes` from its raw parts. + /// + /// # Safety + /// + /// The pointer is owned, has been allocated with the provided layout, and + /// points to `layout.size()` initialized bytes. + pub unsafe fn from_raw_parts(ptr: NonNull<u8>, layout: Layout) -> Self { + BoxBytes { ptr, layout } + } + + /// Deconstructs a `BoxBytes` into its raw parts. + /// + /// The pointer is owned, has been allocated with the provided layout, and + /// points to `layout.size()` initialized bytes. + pub fn into_raw_parts(self) -> (NonNull<u8>, Layout) { + let me = ManuallyDrop::new(self); + (me.ptr, me.layout) + } + + /// Returns the original layout. + pub fn layout(&self) -> Layout { + self.layout + } +}
diff --git a/src/anybitpattern.rs b/src/anybitpattern.rs index 9332d61..1792eb2 100644 --- a/src/anybitpattern.rs +++ b/src/anybitpattern.rs
@@ -56,5 +56,6 @@ unsafe impl<T: Pod> AnyBitPattern for T {} #[cfg(feature = "zeroable_maybe_uninit")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "zeroable_maybe_uninit")))] unsafe impl<T> AnyBitPattern for core::mem::MaybeUninit<T> where T: AnyBitPattern {}
diff --git a/src/checked.rs b/src/checked.rs index 2d97340..1105528 100644 --- a/src/checked.rs +++ b/src/checked.rs
@@ -170,6 +170,7 @@ } } +// Rust 1.70.0 documents that NonZero[int] has the same layout as [int]. macro_rules! impl_checked_for_nonzero { ($($nonzero:ty: $primitive:ty),* $(,)?) => { $( @@ -178,14 +179,7 @@ #[inline] fn is_valid_bit_pattern(bits: &Self::Bits) -> bool { - // Note(zachs18): The size and alignment check are almost certainly - // not necessary, but Rust currently doesn't explicitly document that - // NonZero[int] has the same layout as [int], so we check it to be safe. - // In a const to reduce debug-profile overhead. - const LAYOUT_SAME: bool = - core::mem::size_of::<$nonzero>() == core::mem::size_of::<$primitive>() - && core::mem::align_of::<$nonzero>() == core::mem::align_of::<$primitive>(); - LAYOUT_SAME && *bits != 0 + *bits != 0 } } )* @@ -230,6 +224,7 @@ } } #[cfg(feature = "extern_crate_std")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "extern_crate_std")))] impl std::error::Error for CheckedCastError {} impl From<crate::PodCastError> for CheckedCastError { @@ -249,7 +244,7 @@ pub fn try_from_bytes<T: CheckedBitPattern>( s: &[u8], ) -> Result<&T, CheckedCastError> { - let pod = unsafe { internal::try_from_bytes(s) }?; + let pod = crate::try_from_bytes(s)?; if <T as CheckedBitPattern>::is_valid_bit_pattern(pod) { Ok(unsafe { &*(pod as *const <T as CheckedBitPattern>::Bits as *const T) }) @@ -287,7 +282,7 @@ pub fn try_pod_read_unaligned<T: CheckedBitPattern>( bytes: &[u8], ) -> Result<T, CheckedCastError> { - let pod = unsafe { internal::try_pod_read_unaligned(bytes) }?; + let pod = crate::try_pod_read_unaligned(bytes)?; if <T as CheckedBitPattern>::is_valid_bit_pattern(&pod) { Ok(unsafe { transmute!(pod) }) @@ -311,7 +306,7 @@ pub fn try_cast<A: NoUninit, B: CheckedBitPattern>( a: A, ) -> Result<B, CheckedCastError> { - let pod = unsafe { internal::try_cast(a) }?; + let pod = crate::try_cast(a)?; if <B as CheckedBitPattern>::is_valid_bit_pattern(&pod) { Ok(unsafe { transmute!(pod) }) @@ -331,7 +326,7 @@ pub fn try_cast_ref<A: NoUninit, B: CheckedBitPattern>( a: &A, ) -> Result<&B, CheckedCastError> { - let pod = unsafe { internal::try_cast_ref(a) }?; + let pod = crate::try_cast_ref(a)?; if <B as CheckedBitPattern>::is_valid_bit_pattern(pod) { Ok(unsafe { &*(pod as *const <B as CheckedBitPattern>::Bits as *const B) }) @@ -380,7 +375,7 @@ pub fn try_cast_slice<A: NoUninit, B: CheckedBitPattern>( a: &[A], ) -> Result<&[B], CheckedCastError> { - let pod = unsafe { internal::try_cast_slice(a) }?; + let pod = crate::try_cast_slice(a)?; if pod.iter().all(|pod| <B as CheckedBitPattern>::is_valid_bit_pattern(pod)) { Ok(unsafe { @@ -455,7 +450,7 @@ /// /// ## Panics /// -/// * This is like [`try_cast`](try_cast), but will panic on a size mismatch. +/// * This is like [`try_cast`], but will panic on a size mismatch. #[inline] pub fn cast<A: NoUninit, B: CheckedBitPattern>(a: A) -> B { match try_cast(a) {
diff --git a/src/internal.rs b/src/internal.rs index 6068d0e..9f36279 100644 --- a/src/internal.rs +++ b/src/internal.rs
@@ -130,6 +130,26 @@ } } +/// Checks if `ptr` is aligned to an `align` memory boundary. +/// +/// ## Panics +/// * If `align` is not a power of two. This includes when `align` is zero. +#[inline] +pub(crate) fn is_aligned_to(ptr: *const (), align: usize) -> bool { + #[cfg(feature = "align_offset")] + { + // This is in a way better than `ptr as usize % align == 0`, + // because casting a pointer to an integer has the side effect that it + // exposes the pointer's provenance, which may theoretically inhibit + // some compiler optimizations. + ptr.align_offset(align) == 0 + } + #[cfg(not(feature = "align_offset"))] + { + ((ptr as usize) % align) == 0 + } +} + /// Re-interprets `&[u8]` as `&T`. /// /// ## Failure @@ -142,7 +162,7 @@ ) -> Result<&T, PodCastError> { if s.len() != size_of::<T>() { Err(PodCastError::SizeMismatch) - } else if (s.as_ptr() as usize) % align_of::<T>() != 0 { + } else if !is_aligned_to(s.as_ptr() as *const (), align_of::<T>()) { Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) } else { Ok(unsafe { &*(s.as_ptr() as *const T) }) @@ -161,7 +181,7 @@ ) -> Result<&mut T, PodCastError> { if s.len() != size_of::<T>() { Err(PodCastError::SizeMismatch) - } else if (s.as_ptr() as usize) % align_of::<T>() != 0 { + } else if !is_aligned_to(s.as_ptr() as *const (), align_of::<T>()) { Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) } else { Ok(unsafe { &mut *(s.as_mut_ptr() as *mut T) }) @@ -284,7 +304,7 @@ // Note(Lokathor): everything with `align_of` and `size_of` will optimize away // after monomorphization. if align_of::<B>() > align_of::<A>() - && (a as *const A as usize) % align_of::<B>() != 0 + && !is_aligned_to(a as *const A as *const (), align_of::<B>()) { Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) } else if size_of::<B>() == size_of::<A>() { @@ -304,7 +324,7 @@ // Note(Lokathor): everything with `align_of` and `size_of` will optimize away // after monomorphization. if align_of::<B>() > align_of::<A>() - && (a as *mut A as usize) % align_of::<B>() != 0 + && !is_aligned_to(a as *const A as *const (), align_of::<B>()) { Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) } else if size_of::<B>() == size_of::<A>() { @@ -336,7 +356,7 @@ // Note(Lokathor): everything with `align_of` and `size_of` will optimize away // after monomorphization. if align_of::<B>() > align_of::<A>() - && (a.as_ptr() as usize) % align_of::<B>() != 0 + && !is_aligned_to(a.as_ptr() as *const (), align_of::<B>()) { Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) } else if size_of::<B>() == size_of::<A>() { @@ -362,7 +382,7 @@ // Note(Lokathor): everything with `align_of` and `size_of` will optimize away // after monomorphization. if align_of::<B>() > align_of::<A>() - && (a.as_mut_ptr() as usize) % align_of::<B>() != 0 + && !is_aligned_to(a.as_ptr() as *const (), align_of::<B>()) { Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) } else if size_of::<B>() == size_of::<A>() {
diff --git a/src/lib.rs b/src/lib.rs index d1bbca4..aecc9c6 100644 --- a/src/lib.rs +++ b/src/lib.rs
@@ -2,6 +2,7 @@ #![warn(missing_docs)] #![allow(clippy::match_like_matches_macro)] #![allow(clippy::uninlined_format_args)] +#![cfg_attr(feature = "nightly_docs", feature(doc_cfg))] #![cfg_attr(feature = "nightly_portable_simd", feature(portable_simd))] #![cfg_attr(feature = "nightly_stdsimd", feature(stdsimd))] @@ -18,28 +19,64 @@ //! * `&[T]` uses [`cast_slice`] //! * `&mut [T]` uses [`cast_slice_mut`] //! -//! Some casts will never fail (eg: `cast::<u32, f32>` always works), other -//! casts might fail (eg: `cast_ref::<[u8; 4], u32>` will fail if the reference -//! isn't already aligned to 4). Each casting function has a "try" version which -//! will return a `Result`, and the "normal" version which will simply panic on -//! invalid input. +//! Depending on the function, the [`NoUninit`] and/or [`AnyBitPattern`] traits +//! are used to maintain memory safety. +//! +//! **Historical Note:** When the crate first started the [`Pod`] trait was used +//! instead, and so you may hear people refer to that, but it has the strongest +//! requirements and people eventually wanted the more fine-grained system, so +//! here we are. All types that impl `Pod` have a blanket impl to also support +//! `NoUninit` and `AnyBitPattern`. The traits unfortunately do not have a +//! perfectly clean hierarchy for semver reasons. +//! +//! ## Failures +//! +//! Some casts will never fail, and other casts might fail. +//! +//! * `cast::<u32, f32>` always works (and [`f32::from_bits`]). +//! * `cast_ref::<[u8; 4], u32>` might fail if the specific array reference +//! given at runtime doesn't have alignment 4. +//! +//! In addition to the "normal" forms of each function, which will panic on +//! invalid input, there's also `try_` versions which will return a `Result`. +//! +//! If you would like to statically ensure that a cast will work at runtime you +//! can use the `must_cast` crate feature and the `must_` casting functions. A +//! "must cast" that can't be statically known to be valid will cause a +//! compilation error (and sometimes a very hard to read compilation error). //! //! ## Using Your Own Types //! -//! All the functions here are guarded by the [`Pod`] trait, which is a +//! All the functions listed above are guarded by the [`Pod`] trait, which is a //! sub-trait of the [`Zeroable`] trait. //! -//! If you're very sure that your type is eligible, you can implement those -//! traits for your type and then they'll have full casting support. However, -//! these traits are `unsafe`, and you should carefully read the requirements -//! before adding the them to your own types. +//! If you enable the crate's `derive` feature then these traits can be derived +//! on your own types. The derive macros will perform the necessary checks on +//! your type declaration, and trigger an error if your type does not qualify. //! -//! ## Features +//! The derive macros might not cover all edge cases, and sometimes they will +//! error when actually everything is fine. As a last resort you can impl these +//! traits manually. However, these traits are `unsafe`, and you should +//! carefully read the requirements before using a manual implementation. //! -//! * This crate is core only by default, but if you're using Rust 1.36 or later -//! you can enable the `extern_crate_alloc` cargo feature for some additional -//! methods related to `Box` and `Vec`. Note that the `docs.rs` documentation -//! is always built with `extern_crate_alloc` cargo feature enabled. +//! ## Cargo Features +//! +//! The crate supports Rust 1.34 when no features are enabled, and so there's +//! cargo features for thing that you might consider "obvious". +//! +//! The cargo features **do not** promise any particular MSRV, and they may +//! increase their MSRV in new versions. +//! +//! * `derive`: Provide derive macros for the various traits. +//! * `extern_crate_alloc`: Provide utilities for `alloc` related types such as +//! Box and Vec. +//! * `zeroable_maybe_uninit` and `zeroable_atomics`: Provide more [`Zeroable`] +//! impls. +//! * `wasm_simd` and `aarch64_simd`: Support more SIMD types. +//! * `min_const_generics`: Provides appropriate impls for arrays of all lengths +//! instead of just for a select list of array lengths. +//! * `must_cast`: Provides the `must_` functions, which will compile error if +//! the requested cast can't be statically verified. #[cfg(all(target_arch = "aarch64", feature = "aarch64_simd"))] use core::arch::aarch64; @@ -78,10 +115,13 @@ /// with relevant cargo features enabled. #[allow(unused)] macro_rules! impl_unsafe_marker_for_simd { - (unsafe impl $trait:ident for $platform:ident :: {}) => {}; - (unsafe impl $trait:ident for $platform:ident :: { $first_type:ident $(, $types:ident)* $(,)? }) => { + ($(#[cfg($cfg_predicate:meta)])? unsafe impl $trait:ident for $platform:ident :: {}) => {}; + ($(#[cfg($cfg_predicate:meta)])? unsafe impl $trait:ident for $platform:ident :: { $first_type:ident $(, $types:ident)* $(,)? }) => { + $( #[cfg($cfg_predicate)] )? + $( #[cfg_attr(feature = "nightly_docs", doc(cfg($cfg_predicate)))] )? unsafe impl $trait for $platform::$first_type {} - impl_unsafe_marker_for_simd!(unsafe impl $trait for $platform::{ $( $types ),* }); + $( #[cfg($cfg_predicate)] )? // To prevent recursion errors if nothing is going to be expanded anyway. + impl_unsafe_marker_for_simd!($( #[cfg($cfg_predicate)] )? unsafe impl $trait for $platform::{ $( $types ),* }); }; } @@ -91,6 +131,7 @@ #[cfg(feature = "extern_crate_alloc")] extern crate alloc; #[cfg(feature = "extern_crate_alloc")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "extern_crate_alloc")))] pub mod allocation; #[cfg(feature = "extern_crate_alloc")] pub use allocation::*; @@ -113,6 +154,12 @@ mod pod_in_option; pub use pod_in_option::*; +#[cfg(feature = "must_cast")] +mod must; +#[cfg(feature = "must_cast")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "must_cast")))] +pub use must::*; + mod no_uninit; pub use no_uninit::*; @@ -120,12 +167,14 @@ pub use contiguous::*; mod offset_of; -pub use offset_of::*; +// ^ no import, the module only has a macro_rules, which are cursed and don't +// follow normal import/export rules. mod transparent; pub use transparent::*; #[cfg(feature = "derive")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "derive")))] pub use bytemuck_derive::{ AnyBitPattern, ByteEq, ByteHash, CheckedBitPattern, Contiguous, NoUninit, Pod, TransparentWrapper, Zeroable, @@ -160,6 +209,7 @@ } } #[cfg(feature = "extern_crate_std")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "extern_crate_std")))] impl std::error::Error for PodCastError {} /// Re-interprets `&T` as `&[u8]`. @@ -184,7 +234,7 @@ /// /// ## Panics /// -/// This is [`try_from_bytes`] but will panic on error. +/// This is like [`try_from_bytes`] but will panic on error. #[inline] pub fn from_bytes<T: AnyBitPattern>(s: &[u8]) -> &T { unsafe { internal::from_bytes(s) } @@ -194,7 +244,7 @@ /// /// ## Panics /// -/// This is [`try_from_bytes_mut`] but will panic on error. +/// This is like [`try_from_bytes_mut`] but will panic on error. #[inline] pub fn from_bytes_mut<T: NoUninit + AnyBitPattern>(s: &mut [u8]) -> &mut T { unsafe { internal::from_bytes_mut(s) } @@ -202,6 +252,9 @@ /// Reads from the bytes as if they were a `T`. /// +/// Unlike [`from_bytes`], the slice doesn't need to respect alignment of `T`, only sizes +/// must match. +/// /// ## Failure /// * If the `bytes` length is not equal to `size_of::<T>()`. #[inline] @@ -213,6 +266,9 @@ /// Reads the slice into a `T` value. /// +/// Unlike [`from_bytes`], the slice doesn't need to respect alignment of `T`, only sizes +/// must match. +/// /// ## Panics /// * This is like `try_pod_read_unaligned` but will panic on failure. #[inline] @@ -248,7 +304,7 @@ /// /// ## Panics /// -/// * This is like [`try_cast`](try_cast), but will panic on a size mismatch. +/// * This is like [`try_cast`], but will panic on a size mismatch. #[inline] pub fn cast<A: NoUninit, B: AnyBitPattern>(a: A) -> B { unsafe { internal::cast(a) } @@ -301,7 +357,8 @@ unsafe { internal::cast_slice_mut(a) } } -/// As `align_to`, but safe because of the [`Pod`] bound. +/// As [`align_to`](https://doc.rust-lang.org/std/primitive.slice.html#method.align_to), +/// but safe because of the [`Pod`] bound. #[inline] pub fn pod_align_to<T: NoUninit, U: AnyBitPattern>( vals: &[T], @@ -309,7 +366,8 @@ unsafe { vals.align_to::<U>() } } -/// As `align_to_mut`, but safe because of the [`Pod`] bound. +/// As [`align_to_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.align_to_mut), +/// but safe because of the [`Pod`] bound. #[inline] pub fn pod_align_to_mut< T: NoUninit + AnyBitPattern, @@ -398,3 +456,47 @@ ) -> Result<&mut [B], PodCastError> { unsafe { internal::try_cast_slice_mut(a) } } + +/// Fill all bytes of `target` with zeroes (see [`Zeroable`]). +/// +/// This is similar to `*target = Zeroable::zeroed()`, but guarantees that any +/// padding bytes in `target` are zeroed as well. +/// +/// See also [`fill_zeroes`], if you have a slice rather than a single value. +#[inline] +pub fn write_zeroes<T: Zeroable>(target: &mut T) { + struct EnsureZeroWrite<T>(*mut T); + impl<T> Drop for EnsureZeroWrite<T> { + #[inline(always)] + fn drop(&mut self) { + unsafe { + core::ptr::write_bytes(self.0, 0u8, 1); + } + } + } + unsafe { + let guard = EnsureZeroWrite(target); + core::ptr::drop_in_place(guard.0); + drop(guard); + } +} + +/// Fill all bytes of `slice` with zeroes (see [`Zeroable`]). +/// +/// This is similar to `slice.fill(Zeroable::zeroed())`, but guarantees that any +/// padding bytes in `slice` are zeroed as well. +/// +/// See also [`write_zeroes`], which zeroes all bytes of a single value rather +/// than a slice. +#[inline] +pub fn fill_zeroes<T: Zeroable>(slice: &mut [T]) { + if core::mem::needs_drop::<T>() { + // If `T` needs to be dropped then we have to do this one item at a time, in + // case one of the intermediate drops does a panic. + slice.iter_mut().for_each(write_zeroes); + } else { + // Otherwise we can be really fast and just fill everthing with zeros. + let len = core::mem::size_of_val::<[T]>(slice); + unsafe { core::ptr::write_bytes(slice.as_mut_ptr() as *mut u8, 0u8, len) } + } +}
diff --git a/src/must.rs b/src/must.rs new file mode 100644 index 0000000..b3f1701 --- /dev/null +++ b/src/must.rs
@@ -0,0 +1,203 @@ +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::let_unit_value)] +#![allow(clippy::let_underscore_untyped)] +#![allow(clippy::ptr_as_ptr)] + +use crate::{AnyBitPattern, NoUninit}; +use core::mem::{align_of, size_of}; + +struct Cast<A, B>((A, B)); +impl<A, B> Cast<A, B> { + const ASSERT_ALIGN_GREATER_THAN_EQUAL: () = + assert!(align_of::<A>() >= align_of::<B>()); + const ASSERT_SIZE_EQUAL: () = assert!(size_of::<A>() == size_of::<B>()); + const ASSERT_SIZE_MULTIPLE_OF: () = assert!( + (size_of::<A>() == 0) == (size_of::<B>() == 0) + && (size_of::<A>() % size_of::<B>() == 0) + ); +} + +// Workaround for https://github.com/rust-lang/miri/issues/2423. +// Miri currently doesn't see post-monomorphization errors until runtime, +// so `compile_fail` tests relying on post-monomorphization errors don't +// actually fail. Instead use `should_panic` under miri as a workaround. +#[cfg(miri)] +macro_rules! post_mono_compile_fail_doctest { + () => { + "```should_panic" + }; +} +#[cfg(not(miri))] +macro_rules! post_mono_compile_fail_doctest { + () => { + "```compile_fail,E0080" + }; +} + +/// Cast `A` into `B` if infalliable, or fail to compile. +/// +/// Note that for this particular type of cast, alignment isn't a factor. The +/// input value is semantically copied into the function and then returned to a +/// new memory location which will have whatever the required alignment of the +/// output type is. +/// +/// ## Failure +/// +/// * If the types don't have the same size this fails to compile. +/// +/// ## Examples +/// ``` +/// // compiles: +/// let bytes: [u8; 2] = bytemuck::must_cast(12_u16); +/// ``` +#[doc = post_mono_compile_fail_doctest!()] +/// // fails to compile (size mismatch): +/// let bytes : [u8; 3] = bytemuck::must_cast(12_u16); +/// ``` +#[inline] +pub fn must_cast<A: NoUninit, B: AnyBitPattern>(a: A) -> B { + let _ = Cast::<A, B>::ASSERT_SIZE_EQUAL; + unsafe { transmute!(a) } +} + +/// Convert `&A` into `&B` if infalliable, or fail to compile. +/// +/// ## Failure +/// +/// * If the target type has a greater alignment requirement. +/// * If the source type and target type aren't the same size. +/// +/// ## Examples +/// ``` +/// // compiles: +/// let bytes: &[u8; 2] = bytemuck::must_cast_ref(&12_u16); +/// ``` +#[doc = post_mono_compile_fail_doctest!()] +/// // fails to compile (size mismatch): +/// let bytes : &[u8; 3] = bytemuck::must_cast_ref(&12_u16); +/// ``` +#[doc = post_mono_compile_fail_doctest!()] +/// // fails to compile (alignment requirements increased): +/// let bytes : &u16 = bytemuck::must_cast_ref(&[1u8, 2u8]); +/// ``` +#[inline] +pub fn must_cast_ref<A: NoUninit, B: AnyBitPattern>(a: &A) -> &B { + let _ = Cast::<A, B>::ASSERT_SIZE_EQUAL; + let _ = Cast::<A, B>::ASSERT_ALIGN_GREATER_THAN_EQUAL; + unsafe { &*(a as *const A as *const B) } +} + +/// Convert a `&mut A` into `&mut B` if infalliable, or fail to compile. +/// +/// As [`must_cast_ref`], but `mut`. +/// +/// ## Examples +/// ``` +/// let mut i = 12_u16; +/// // compiles: +/// let bytes: &mut [u8; 2] = bytemuck::must_cast_mut(&mut i); +/// ``` +#[doc = post_mono_compile_fail_doctest!()] +/// # let mut bytes: &mut [u8; 2] = &mut [1, 2]; +/// // fails to compile (alignment requirements increased): +/// let i : &mut u16 = bytemuck::must_cast_mut(bytes); +/// ``` +#[doc = post_mono_compile_fail_doctest!()] +/// # let mut i = 12_u16; +/// // fails to compile (size mismatch): +/// let bytes : &mut [u8; 3] = bytemuck::must_cast_mut(&mut i); +/// ``` +#[inline] +pub fn must_cast_mut< + A: NoUninit + AnyBitPattern, + B: NoUninit + AnyBitPattern, +>( + a: &mut A, +) -> &mut B { + let _ = Cast::<A, B>::ASSERT_SIZE_EQUAL; + let _ = Cast::<A, B>::ASSERT_ALIGN_GREATER_THAN_EQUAL; + unsafe { &mut *(a as *mut A as *mut B) } +} + +/// Convert `&[A]` into `&[B]` (possibly with a change in length) if +/// infalliable, or fail to compile. +/// +/// * `input.as_ptr() as usize == output.as_ptr() as usize` +/// * `input.len() * size_of::<A>() == output.len() * size_of::<B>()` +/// +/// ## Failure +/// +/// * If the target type has a greater alignment requirement. +/// * If the target element type doesn't evenly fit into the the current element +/// type (eg: 3 `u16` values is 1.5 `u32` values, so that's a failure). +/// * Similarly, you can't convert between a [ZST](https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts) +/// and a non-ZST. +/// +/// ## Examples +/// ``` +/// let indicies: &[u16] = &[1, 2, 3]; +/// // compiles: +/// let bytes: &[u8] = bytemuck::must_cast_slice(indicies); +/// ``` +#[doc = post_mono_compile_fail_doctest!()] +/// # let bytes : &[u8] = &[1, 0, 2, 0, 3, 0]; +/// // fails to compile (bytes.len() might not be a multiple of 2): +/// let byte_pairs : &[[u8; 2]] = bytemuck::must_cast_slice(bytes); +/// ``` +#[doc = post_mono_compile_fail_doctest!()] +/// # let byte_pairs : &[[u8; 2]] = &[[1, 0], [2, 0], [3, 0]]; +/// // fails to compile (alignment requirements increased): +/// let indicies : &[u16] = bytemuck::must_cast_slice(byte_pairs); +/// ``` +#[inline] +pub fn must_cast_slice<A: NoUninit, B: AnyBitPattern>(a: &[A]) -> &[B] { + let _ = Cast::<A, B>::ASSERT_SIZE_MULTIPLE_OF; + let _ = Cast::<A, B>::ASSERT_ALIGN_GREATER_THAN_EQUAL; + let new_len = if size_of::<A>() == size_of::<B>() { + a.len() + } else { + a.len() * (size_of::<A>() / size_of::<B>()) + }; + unsafe { core::slice::from_raw_parts(a.as_ptr() as *const B, new_len) } +} + +/// Convert `&mut [A]` into `&mut [B]` (possibly with a change in length) if +/// infalliable, or fail to compile. +/// +/// As [`must_cast_slice`], but `&mut`. +/// +/// ## Examples +/// ``` +/// let mut indicies = [1, 2, 3]; +/// let indicies: &mut [u16] = &mut indicies; +/// // compiles: +/// let bytes: &mut [u8] = bytemuck::must_cast_slice_mut(indicies); +/// ``` +#[doc = post_mono_compile_fail_doctest!()] +/// # let mut bytes = [1, 0, 2, 0, 3, 0]; +/// # let bytes : &mut [u8] = &mut bytes[..]; +/// // fails to compile (bytes.len() might not be a multiple of 2): +/// let byte_pairs : &mut [[u8; 2]] = bytemuck::must_cast_slice_mut(bytes); +/// ``` +#[doc = post_mono_compile_fail_doctest!()] +/// # let mut byte_pairs = [[1, 0], [2, 0], [3, 0]]; +/// # let byte_pairs : &mut [[u8; 2]] = &mut byte_pairs[..]; +/// // fails to compile (alignment requirements increased): +/// let indicies : &mut [u16] = bytemuck::must_cast_slice_mut(byte_pairs); +/// ``` +#[inline] +pub fn must_cast_slice_mut< + A: NoUninit + AnyBitPattern, + B: NoUninit + AnyBitPattern, +>( + a: &mut [A], +) -> &mut [B] { + let _ = Cast::<A, B>::ASSERT_SIZE_MULTIPLE_OF; + let _ = Cast::<A, B>::ASSERT_ALIGN_GREATER_THAN_EQUAL; + let new_len = if size_of::<A>() == size_of::<B>() { + a.len() + } else { + a.len() * (size_of::<A>() / size_of::<B>()) + }; + unsafe { core::slice::from_raw_parts_mut(a.as_mut_ptr() as *mut B, new_len) } +}
diff --git a/src/pod.rs b/src/pod.rs index 2ea7f4b..58b43f8 100644 --- a/src/pod.rs +++ b/src/pod.rs
@@ -54,19 +54,32 @@ unsafe impl<T: Pod> Pod for Wrapping<T> {} #[cfg(feature = "unsound_ptr_pod_impl")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "unsound_ptr_pod_impl")) +)] unsafe impl<T: 'static> Pod for *mut T {} #[cfg(feature = "unsound_ptr_pod_impl")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "unsound_ptr_pod_impl")) +)] unsafe impl<T: 'static> Pod for *const T {} #[cfg(feature = "unsound_ptr_pod_impl")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "unsound_ptr_pod_impl")) +)] unsafe impl<T: 'static> PodInOption for NonNull<T> {} -unsafe impl<T: Pod> Pod for PhantomData<T> {} +unsafe impl<T: ?Sized + 'static> Pod for PhantomData<T> {} unsafe impl Pod for PhantomPinned {} unsafe impl<T: Pod> Pod for ManuallyDrop<T> {} // Note(Lokathor): MaybeUninit can NEVER be Pod. #[cfg(feature = "min_const_generics")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "min_const_generics")))] unsafe impl<T, const N: usize> Pod for [T; N] where T: Pod {} #[cfg(not(feature = "min_const_generics"))] @@ -76,54 +89,58 @@ 512, 1024, 2048, 4096 ); -#[cfg(all(target_arch = "wasm32", feature = "wasm_simd"))] impl_unsafe_marker_for_simd!( - unsafe impl Pod for wasm32::{v128} + #[cfg(all(target_arch = "wasm32", feature = "wasm_simd"))] + unsafe impl Pod for wasm32::{v128} ); -#[cfg(all(target_arch = "aarch64", feature = "aarch64_simd"))] impl_unsafe_marker_for_simd!( - unsafe impl Pod for aarch64::{ - float32x2_t, float32x2x2_t, float32x2x3_t, float32x2x4_t, float32x4_t, - float32x4x2_t, float32x4x3_t, float32x4x4_t, float64x1_t, float64x1x2_t, - float64x1x3_t, float64x1x4_t, float64x2_t, float64x2x2_t, float64x2x3_t, - float64x2x4_t, int16x4_t, int16x4x2_t, int16x4x3_t, int16x4x4_t, int16x8_t, - int16x8x2_t, int16x8x3_t, int16x8x4_t, int32x2_t, int32x2x2_t, int32x2x3_t, - int32x2x4_t, int32x4_t, int32x4x2_t, int32x4x3_t, int32x4x4_t, int64x1_t, - int64x1x2_t, int64x1x3_t, int64x1x4_t, int64x2_t, int64x2x2_t, int64x2x3_t, - int64x2x4_t, int8x16_t, int8x16x2_t, int8x16x3_t, int8x16x4_t, int8x8_t, - int8x8x2_t, int8x8x3_t, int8x8x4_t, poly16x4_t, poly16x4x2_t, poly16x4x3_t, - poly16x4x4_t, poly16x8_t, poly16x8x2_t, poly16x8x3_t, poly16x8x4_t, - poly64x1_t, poly64x1x2_t, poly64x1x3_t, poly64x1x4_t, poly64x2_t, - poly64x2x2_t, poly64x2x3_t, poly64x2x4_t, poly8x16_t, poly8x16x2_t, - poly8x16x3_t, poly8x16x4_t, poly8x8_t, poly8x8x2_t, poly8x8x3_t, poly8x8x4_t, - uint16x4_t, uint16x4x2_t, uint16x4x3_t, uint16x4x4_t, uint16x8_t, - uint16x8x2_t, uint16x8x3_t, uint16x8x4_t, uint32x2_t, uint32x2x2_t, - uint32x2x3_t, uint32x2x4_t, uint32x4_t, uint32x4x2_t, uint32x4x3_t, - uint32x4x4_t, uint64x1_t, uint64x1x2_t, uint64x1x3_t, uint64x1x4_t, - uint64x2_t, uint64x2x2_t, uint64x2x3_t, uint64x2x4_t, uint8x16_t, - uint8x16x2_t, uint8x16x3_t, uint8x16x4_t, uint8x8_t, uint8x8x2_t, - uint8x8x3_t, uint8x8x4_t, - } + #[cfg(all(target_arch = "aarch64", feature = "aarch64_simd"))] + unsafe impl Pod for aarch64::{ + float32x2_t, float32x2x2_t, float32x2x3_t, float32x2x4_t, float32x4_t, + float32x4x2_t, float32x4x3_t, float32x4x4_t, float64x1_t, float64x1x2_t, + float64x1x3_t, float64x1x4_t, float64x2_t, float64x2x2_t, float64x2x3_t, + float64x2x4_t, int16x4_t, int16x4x2_t, int16x4x3_t, int16x4x4_t, int16x8_t, + int16x8x2_t, int16x8x3_t, int16x8x4_t, int32x2_t, int32x2x2_t, int32x2x3_t, + int32x2x4_t, int32x4_t, int32x4x2_t, int32x4x3_t, int32x4x4_t, int64x1_t, + int64x1x2_t, int64x1x3_t, int64x1x4_t, int64x2_t, int64x2x2_t, int64x2x3_t, + int64x2x4_t, int8x16_t, int8x16x2_t, int8x16x3_t, int8x16x4_t, int8x8_t, + int8x8x2_t, int8x8x3_t, int8x8x4_t, poly16x4_t, poly16x4x2_t, poly16x4x3_t, + poly16x4x4_t, poly16x8_t, poly16x8x2_t, poly16x8x3_t, poly16x8x4_t, + poly64x1_t, poly64x1x2_t, poly64x1x3_t, poly64x1x4_t, poly64x2_t, + poly64x2x2_t, poly64x2x3_t, poly64x2x4_t, poly8x16_t, poly8x16x2_t, + poly8x16x3_t, poly8x16x4_t, poly8x8_t, poly8x8x2_t, poly8x8x3_t, poly8x8x4_t, + uint16x4_t, uint16x4x2_t, uint16x4x3_t, uint16x4x4_t, uint16x8_t, + uint16x8x2_t, uint16x8x3_t, uint16x8x4_t, uint32x2_t, uint32x2x2_t, + uint32x2x3_t, uint32x2x4_t, uint32x4_t, uint32x4x2_t, uint32x4x3_t, + uint32x4x4_t, uint64x1_t, uint64x1x2_t, uint64x1x3_t, uint64x1x4_t, + uint64x2_t, uint64x2x2_t, uint64x2x3_t, uint64x2x4_t, uint8x16_t, + uint8x16x2_t, uint8x16x3_t, uint8x16x4_t, uint8x8_t, uint8x8x2_t, + uint8x8x3_t, uint8x8x4_t, + } ); -#[cfg(target_arch = "x86")] impl_unsafe_marker_for_simd!( - unsafe impl Pod for x86::{ - __m128i, __m128, __m128d, - __m256i, __m256, __m256d, - } + #[cfg(target_arch = "x86")] + unsafe impl Pod for x86::{ + __m128i, __m128, __m128d, + __m256i, __m256, __m256d, + } ); -#[cfg(target_arch = "x86_64")] impl_unsafe_marker_for_simd!( - unsafe impl Pod for x86_64::{ - __m128i, __m128, __m128d, - __m256i, __m256, __m256d, - } + #[cfg(target_arch = "x86_64")] + unsafe impl Pod for x86_64::{ + __m128i, __m128, __m128d, + __m256i, __m256, __m256d, + } ); #[cfg(feature = "nightly_portable_simd")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "nightly_portable_simd")) +)] unsafe impl<T, const N: usize> Pod for core::simd::Simd<T, N> where T: core::simd::SimdElement + Pod, @@ -131,18 +148,18 @@ { } -#[cfg(all(target_arch = "x86", feature = "nightly_stdsimd"))] impl_unsafe_marker_for_simd!( - unsafe impl Pod for x86::{ - __m128bh, __m256bh, __m512, - __m512bh, __m512d, __m512i, - } + #[cfg(all(target_arch = "x86", feature = "nightly_stdsimd"))] + unsafe impl Pod for x86::{ + __m128bh, __m256bh, __m512, + __m512bh, __m512d, __m512i, + } ); -#[cfg(all(target_arch = "x86_64", feature = "nightly_stdsimd"))] impl_unsafe_marker_for_simd!( - unsafe impl Pod for x86_64::{ - __m128bh, __m256bh, __m512, - __m512bh, __m512d, __m512i, - } + #[cfg(all(target_arch = "x86_64", feature = "nightly_stdsimd"))] + unsafe impl Pod for x86_64::{ + __m128bh, __m256bh, __m512, + __m512bh, __m512d, __m512i, + } );
diff --git a/src/zeroable.rs b/src/zeroable.rs index 5f19b98..5a3272a 100644 --- a/src/zeroable.rs +++ b/src/zeroable.rs
@@ -19,8 +19,8 @@ /// /// * `MaybeUninit<T>` was not available in 1.34.0, but is available under the /// `zeroable_maybe_uninit` feature flag. -/// * `Atomic*` types require Rust 1.60.0 or later to work on certain platforms, but is available -/// under the `zeroable_atomics` feature flag. +/// * `Atomic*` types require Rust 1.60.0 or later to work on certain platforms, +/// but is available under the `zeroable_atomics` feature flag. /// * `[T; N]` for arbitrary `N` requires the `min_const_generics` feature flag. pub unsafe trait Zeroable: Sized { /// Calls [`zeroed`](core::mem::zeroed). @@ -71,6 +71,7 @@ unsafe impl<T: Zeroable> Zeroable for core::cell::Cell<T> {} #[cfg(feature = "zeroable_atomics")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "zeroable_atomics")))] mod atomic_impls { use super::Zeroable; @@ -106,6 +107,10 @@ } #[cfg(feature = "zeroable_maybe_uninit")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "zeroable_maybe_uninit")) +)] unsafe impl<T> Zeroable for core::mem::MaybeUninit<T> {} unsafe impl<A: Zeroable> Zeroable for (A,) {} @@ -154,6 +159,7 @@ } #[cfg(feature = "min_const_generics")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "min_const_generics")))] unsafe impl<T, const N: usize> Zeroable for [T; N] where T: Zeroable {} #[cfg(not(feature = "min_const_generics"))] @@ -163,47 +169,47 @@ 512, 1024, 2048, 4096 ); -#[cfg(all(target_arch = "wasm32", feature = "wasm_simd"))] impl_unsafe_marker_for_simd!( - unsafe impl Zeroable for wasm32::{v128} + #[cfg(all(target_arch = "wasm32", feature = "wasm_simd"))] + unsafe impl Zeroable for wasm32::{v128} ); -#[cfg(all(target_arch = "aarch64", feature = "aarch64_simd"))] impl_unsafe_marker_for_simd!( - unsafe impl Zeroable for aarch64::{ - float32x2_t, float32x2x2_t, float32x2x3_t, float32x2x4_t, float32x4_t, - float32x4x2_t, float32x4x3_t, float32x4x4_t, float64x1_t, float64x1x2_t, - float64x1x3_t, float64x1x4_t, float64x2_t, float64x2x2_t, float64x2x3_t, - float64x2x4_t, int16x4_t, int16x4x2_t, int16x4x3_t, int16x4x4_t, int16x8_t, - int16x8x2_t, int16x8x3_t, int16x8x4_t, int32x2_t, int32x2x2_t, int32x2x3_t, - int32x2x4_t, int32x4_t, int32x4x2_t, int32x4x3_t, int32x4x4_t, int64x1_t, - int64x1x2_t, int64x1x3_t, int64x1x4_t, int64x2_t, int64x2x2_t, int64x2x3_t, - int64x2x4_t, int8x16_t, int8x16x2_t, int8x16x3_t, int8x16x4_t, int8x8_t, - int8x8x2_t, int8x8x3_t, int8x8x4_t, poly16x4_t, poly16x4x2_t, poly16x4x3_t, - poly16x4x4_t, poly16x8_t, poly16x8x2_t, poly16x8x3_t, poly16x8x4_t, - poly64x1_t, poly64x1x2_t, poly64x1x3_t, poly64x1x4_t, poly64x2_t, - poly64x2x2_t, poly64x2x3_t, poly64x2x4_t, poly8x16_t, poly8x16x2_t, - poly8x16x3_t, poly8x16x4_t, poly8x8_t, poly8x8x2_t, poly8x8x3_t, poly8x8x4_t, - uint16x4_t, uint16x4x2_t, uint16x4x3_t, uint16x4x4_t, uint16x8_t, - uint16x8x2_t, uint16x8x3_t, uint16x8x4_t, uint32x2_t, uint32x2x2_t, - uint32x2x3_t, uint32x2x4_t, uint32x4_t, uint32x4x2_t, uint32x4x3_t, - uint32x4x4_t, uint64x1_t, uint64x1x2_t, uint64x1x3_t, uint64x1x4_t, - uint64x2_t, uint64x2x2_t, uint64x2x3_t, uint64x2x4_t, uint8x16_t, - uint8x16x2_t, uint8x16x3_t, uint8x16x4_t, uint8x8_t, uint8x8x2_t, - uint8x8x3_t, uint8x8x4_t, - } + #[cfg(all(target_arch = "aarch64", feature = "aarch64_simd"))] + unsafe impl Zeroable for aarch64::{ + float32x2_t, float32x2x2_t, float32x2x3_t, float32x2x4_t, float32x4_t, + float32x4x2_t, float32x4x3_t, float32x4x4_t, float64x1_t, float64x1x2_t, + float64x1x3_t, float64x1x4_t, float64x2_t, float64x2x2_t, float64x2x3_t, + float64x2x4_t, int16x4_t, int16x4x2_t, int16x4x3_t, int16x4x4_t, int16x8_t, + int16x8x2_t, int16x8x3_t, int16x8x4_t, int32x2_t, int32x2x2_t, int32x2x3_t, + int32x2x4_t, int32x4_t, int32x4x2_t, int32x4x3_t, int32x4x4_t, int64x1_t, + int64x1x2_t, int64x1x3_t, int64x1x4_t, int64x2_t, int64x2x2_t, int64x2x3_t, + int64x2x4_t, int8x16_t, int8x16x2_t, int8x16x3_t, int8x16x4_t, int8x8_t, + int8x8x2_t, int8x8x3_t, int8x8x4_t, poly16x4_t, poly16x4x2_t, poly16x4x3_t, + poly16x4x4_t, poly16x8_t, poly16x8x2_t, poly16x8x3_t, poly16x8x4_t, + poly64x1_t, poly64x1x2_t, poly64x1x3_t, poly64x1x4_t, poly64x2_t, + poly64x2x2_t, poly64x2x3_t, poly64x2x4_t, poly8x16_t, poly8x16x2_t, + poly8x16x3_t, poly8x16x4_t, poly8x8_t, poly8x8x2_t, poly8x8x3_t, poly8x8x4_t, + uint16x4_t, uint16x4x2_t, uint16x4x3_t, uint16x4x4_t, uint16x8_t, + uint16x8x2_t, uint16x8x3_t, uint16x8x4_t, uint32x2_t, uint32x2x2_t, + uint32x2x3_t, uint32x2x4_t, uint32x4_t, uint32x4x2_t, uint32x4x3_t, + uint32x4x4_t, uint64x1_t, uint64x1x2_t, uint64x1x3_t, uint64x1x4_t, + uint64x2_t, uint64x2x2_t, uint64x2x3_t, uint64x2x4_t, uint8x16_t, + uint8x16x2_t, uint8x16x3_t, uint8x16x4_t, uint8x8_t, uint8x8x2_t, + uint8x8x3_t, uint8x8x4_t, + } ); -#[cfg(target_arch = "x86")] impl_unsafe_marker_for_simd!( - unsafe impl Zeroable for x86::{ - __m128i, __m128, __m128d, - __m256i, __m256, __m256d, - } + #[cfg(target_arch = "x86")] + unsafe impl Zeroable for x86::{ + __m128i, __m128, __m128d, + __m256i, __m256, __m256d, + } ); -#[cfg(target_arch = "x86_64")] impl_unsafe_marker_for_simd!( + #[cfg(target_arch = "x86_64")] unsafe impl Zeroable for x86_64::{ __m128i, __m128, __m128d, __m256i, __m256, __m256d, @@ -211,6 +217,10 @@ ); #[cfg(feature = "nightly_portable_simd")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "nightly_portable_simd")) +)] unsafe impl<T, const N: usize> Zeroable for core::simd::Simd<T, N> where T: core::simd::SimdElement + Zeroable, @@ -218,18 +228,18 @@ { } -#[cfg(all(target_arch = "x86", feature = "nightly_stdsimd"))] impl_unsafe_marker_for_simd!( - unsafe impl Zeroable for x86::{ - __m128bh, __m256bh, __m512, - __m512bh, __m512d, __m512i, - } + #[cfg(all(target_arch = "x86", feature = "nightly_stdsimd"))] + unsafe impl Zeroable for x86::{ + __m128bh, __m256bh, __m512, + __m512bh, __m512d, __m512i, + } ); -#[cfg(all(target_arch = "x86_64", feature = "nightly_stdsimd"))] impl_unsafe_marker_for_simd!( - unsafe impl Zeroable for x86_64::{ - __m128bh, __m256bh, __m512, - __m512bh, __m512d, __m512i, - } + #[cfg(all(target_arch = "x86_64", feature = "nightly_stdsimd"))] + unsafe impl Zeroable for x86_64::{ + __m128bh, __m256bh, __m512, + __m512bh, __m512d, __m512i, + } );
diff --git a/src/zeroable_in_option.rs b/src/zeroable_in_option.rs index 1ee2c7b..13737f4 100644 --- a/src/zeroable_in_option.rs +++ b/src/zeroable_in_option.rs
@@ -31,4 +31,5 @@ unsafe impl<T: ?Sized> ZeroableInOption for &'_ mut T {} #[cfg(feature = "extern_crate_alloc")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "extern_crate_alloc")))] unsafe impl<T: ?Sized> ZeroableInOption for alloc::boxed::Box<T> {}
diff --git a/tests/cast_slice_tests.rs b/tests/cast_slice_tests.rs index 2838d46..0390f5c 100644 --- a/tests/cast_slice_tests.rs +++ b/tests/cast_slice_tests.rs
@@ -1,3 +1,6 @@ +#![allow(clippy::unnecessary_cast)] +#![allow(clippy::manual_slice_size_calculation)] + use core::mem::size_of; use bytemuck::*;
diff --git a/tests/checked_tests.rs b/tests/checked_tests.rs index 19e6348..4b14909 100644 --- a/tests/checked_tests.rs +++ b/tests/checked_tests.rs
@@ -1,3 +1,6 @@ +#![allow(clippy::unnecessary_cast)] +#![allow(clippy::manual_slice_size_calculation)] + use core::{ mem::size_of, num::{NonZeroU32, NonZeroU8},
diff --git a/tests/std_tests.rs b/tests/std_tests.rs index 79adcb5..947d8f2 100644 --- a/tests/std_tests.rs +++ b/tests/std_tests.rs
@@ -3,6 +3,7 @@ //! depend on that can go here. use bytemuck::*; +use core::num::NonZeroU8; #[test] fn test_transparent_vtabled() { @@ -44,3 +45,62 @@ unsafe impl Zeroable for Empty {} let _: Box<Empty> = try_zeroed_box().unwrap(); } + +#[test] +#[cfg(feature = "extern_crate_alloc")] +fn test_try_from_box_bytes() { + // Different layout: target alignment is greater than source alignment. + assert_eq!( + try_from_box_bytes::<u32>(Box::new([0u8; 4]).into()).map_err(|(x, _)| x), + Err(PodCastError::AlignmentMismatch) + ); + + // Different layout: target alignment is less than source alignment. + assert_eq!( + try_from_box_bytes::<u32>(Box::new(0u64).into()).map_err(|(x, _)| x), + Err(PodCastError::AlignmentMismatch) + ); + + // Different layout: target size is greater than source size. + assert_eq!( + try_from_box_bytes::<[u32; 2]>(Box::new(0u32).into()).map_err(|(x, _)| x), + Err(PodCastError::SizeMismatch) + ); + + // Different layout: target size is less than source size. + assert_eq!( + try_from_box_bytes::<u32>(Box::new([0u32; 2]).into()).map_err(|(x, _)| x), + Err(PodCastError::SizeMismatch) + ); + + // Round trip: alignment is equal to size. + assert_eq!(*from_box_bytes::<u32>(Box::new(1000u32).into()), 1000u32); + + // Round trip: alignment is divider of size. + assert_eq!(&*from_box_bytes::<[u8; 5]>(Box::new(*b"hello").into()), b"hello"); + + // It's ok for T to have uninitialized bytes. + #[cfg(feature = "derive")] + { + #[derive(Debug, Copy, Clone, PartialEq, Eq, AnyBitPattern)] + struct Foo(u8, u16); + assert_eq!( + *from_box_bytes::<Foo>(Box::new([0xc5c5u16; 2]).into()), + Foo(0xc5u8, 0xc5c5u16) + ); + } +} + +#[test] +#[cfg(feature = "extern_crate_alloc")] +fn test_box_bytes_of() { + assert_eq!(&*box_bytes_of(Box::new(*b"hello")), b"hello"); + + #[cfg(target_endian = "big")] + assert_eq!(&*box_bytes_of(Box::new(0x12345678)), b"\x12\x34\x56\x78"); + #[cfg(target_endian = "little")] + assert_eq!(&*box_bytes_of(Box::new(0x12345678)), b"\x78\x56\x34\x12"); + + // It's ok for T to have invalid bit patterns. + assert_eq!(&*box_bytes_of(Box::new(NonZeroU8::new(0xc5))), b"\xc5"); +}