blob: 5abd4916e026f49f0135a78b1db49bc4df7192c0 [file] [log] [blame]
//! Implements methods to write a vector type to a slice.
macro_rules! impl_slice_write_to_slice {
([$elem_ty:ident; $elem_count:expr]: $id:ident | $test_tt:tt) => {
impl $id {
/// Writes the values of the vector to the `slice`.
///
/// # Panics
///
/// If `slice.len() < Self::lanes()` or `&slice[0]` is not
/// aligned to an `align_of::<Self>()` boundary.
#[inline]
pub fn write_to_slice_aligned(self, slice: &mut [$elem_ty]) {
unsafe {
assert!(slice.len() >= $elem_count);
let target_ptr = slice.as_mut_ptr();
assert_eq!(target_ptr.align_offset(crate::mem::align_of::<Self>()), 0);
self.write_to_slice_aligned_unchecked(slice);
}
}
/// Writes the values of the vector to the `slice`.
///
/// # Panics
///
/// If `slice.len() < Self::lanes()`.
#[inline]
pub fn write_to_slice_unaligned(self, slice: &mut [$elem_ty]) {
unsafe {
assert!(slice.len() >= $elem_count);
self.write_to_slice_unaligned_unchecked(slice);
}
}
/// Writes the values of the vector to the `slice`.
///
/// # Safety
///
/// If `slice.len() < Self::lanes()` or `&slice[0]` is not
/// aligned to an `align_of::<Self>()` boundary, the behavior is
/// undefined.
#[inline]
pub unsafe fn write_to_slice_aligned_unchecked(self, slice: &mut [$elem_ty]) {
debug_assert!(slice.len() >= $elem_count);
let target_ptr = slice.as_mut_ptr();
debug_assert_eq!(target_ptr.align_offset(crate::mem::align_of::<Self>()), 0);
#[allow(clippy::cast_ptr_alignment)]
#[allow(clippy::cast_ptr_alignment)]
#[allow(clippy::cast_ptr_alignment)]
#[allow(clippy::cast_ptr_alignment)]
*(target_ptr as *mut Self) = self;
}
/// Writes the values of the vector to the `slice`.
///
/// # Safety
///
/// If `slice.len() < Self::lanes()` the behavior is undefined.
#[inline]
pub unsafe fn write_to_slice_unaligned_unchecked(self, slice: &mut [$elem_ty]) {
debug_assert!(slice.len() >= $elem_count);
let target_ptr = slice.as_mut_ptr().cast();
let self_ptr = &self as *const Self as *const u8;
crate::ptr::copy_nonoverlapping(self_ptr, target_ptr, crate::mem::size_of::<Self>());
}
}
test_if! {
$test_tt:
paste::item! {
// Comparisons use integer casts within mantissa^1 range.
#[allow(clippy::float_cmp)]
pub mod [<$id _slice_write_to_slice>] {
use super::*;
use crate::iter::Iterator;
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn write_to_slice_unaligned() {
let mut unaligned = [0 as $elem_ty; $id::lanes() + 1];
let vec = $id::splat(42 as $elem_ty);
vec.write_to_slice_unaligned(&mut unaligned[1..]);
for (index, &b) in unaligned.iter().enumerate() {
if index == 0 {
assert_eq!(b, 0 as $elem_ty);
} else {
assert_eq!(b, 42 as $elem_ty);
assert_eq!(b, vec.extract(index - 1));
}
}
}
// FIXME: wasm-bindgen-test does not support #[should_panic]
// #[cfg_attr(not(target_arch = "wasm32"), test)]
// #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg(not(target_arch = "wasm32"))]
#[test]
#[should_panic]
fn write_to_slice_unaligned_fail() {
let mut unaligned = [0 as $elem_ty; $id::lanes() + 1];
let vec = $id::splat(42 as $elem_ty);
vec.write_to_slice_unaligned(&mut unaligned[2..]);
}
union A {
data: [$elem_ty; 2 * $id::lanes()],
_vec: $id,
}
#[cfg_attr(not(target_arch = "wasm32"), test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn write_to_slice_aligned() {
let mut aligned = A {
data: [0 as $elem_ty; 2 * $id::lanes()],
};
let vec = $id::splat(42 as $elem_ty);
unsafe {
vec.write_to_slice_aligned(
&mut aligned.data[$id::lanes()..]
);
for (idx, &b) in aligned.data.iter().enumerate() {
if idx < $id::lanes() {
assert_eq!(b, 0 as $elem_ty);
} else {
assert_eq!(b, 42 as $elem_ty);
assert_eq!(
b, vec.extract(idx - $id::lanes())
);
}
}
}
}
// FIXME: wasm-bindgen-test does not support #[should_panic]
// #[cfg_attr(not(target_arch = "wasm32"), test)]
// #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg(not(target_arch = "wasm32"))]
#[test]
#[should_panic]
fn write_to_slice_aligned_fail_lanes() {
let mut aligned = A {
data: [0 as $elem_ty; 2 * $id::lanes()],
};
let vec = $id::splat(42 as $elem_ty);
unsafe {
vec.write_to_slice_aligned(
&mut aligned.data[2 * $id::lanes()..]
)
};
}
// FIXME: wasm-bindgen-test does not support #[should_panic]
// #[cfg_attr(not(target_arch = "wasm32"), test)]
// #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg(not(target_arch = "wasm32"))]
#[test]
#[should_panic]
fn write_to_slice_aligned_fail_align() {
unsafe {
let mut aligned = A {
data: [0 as $elem_ty; 2 * $id::lanes()],
};
// get a pointer to the front of data
let ptr: *mut $elem_ty
= aligned.data.as_mut_ptr() as *mut $elem_ty;
// offset pointer by one element
let ptr = ptr.wrapping_add(1);
if ptr.align_offset(crate::mem::align_of::<$id>())
== 0 {
// the pointer is properly aligned, so
// write_to_slice_aligned won't fail here (e.g.
// this can happen for i128x1). So we panic to
// make the "should_fail" test pass:
panic!("ok");
}
// create a slice - this is safe, because the
// elements of the slice exist, are properly
// initialized, and properly aligned:
let s: &mut [$elem_ty]
= slice::from_raw_parts_mut(ptr, $id::lanes());
// this should always panic because the slice
// alignment does not match the alignment
// requirements for the vector type:
let vec = $id::splat(42 as $elem_ty);
vec.write_to_slice_aligned(s);
}
}
}
}
}
};
}