diff --git a/benches/bench1.rs b/benches/bench1.rs index 0486bd092..a6fa86deb 100644 --- a/benches/bench1.rs +++ b/benches/bench1.rs @@ -14,6 +14,7 @@ use std::mem::MaybeUninit; use ndarray::ShapeBuilder; use ndarray::{arr0, arr1, arr2, azip, s}; use ndarray::{Array, Array1, Array2, Axis, Ix, Zip}; +use ndarray::{Ix1, Ix2, Ix3, Ix5, IxDyn}; use test::black_box; @@ -941,3 +942,59 @@ fn sum_axis1(bench: &mut test::Bencher) { let a = range_mat(MEAN_SUM_N, MEAN_SUM_N); bench.iter(|| a.sum_axis(Axis(1))); } + +#[bench] +fn into_dimensionality_ix1_ok(bench: &mut test::Bencher) { + let a = Array::::zeros(Ix1(10)); + let a = a.view(); + bench.iter(|| a.into_dimensionality::()); +} + +#[bench] +fn into_dimensionality_ix3_ok(bench: &mut test::Bencher) { + let a = Array::::zeros(Ix3(10, 10, 10)); + let a = a.view(); + bench.iter(|| a.into_dimensionality::()); +} + +#[bench] +fn into_dimensionality_ix3_err(bench: &mut test::Bencher) { + let a = Array::::zeros(Ix3(10, 10, 10)); + let a = a.view(); + bench.iter(|| a.into_dimensionality::()); +} + +#[bench] +fn into_dimensionality_dyn_to_ix3(bench: &mut test::Bencher) { + let a = Array::::zeros(IxDyn(&[10, 10, 10])); + let a = a.view(); + bench.iter(|| a.clone().into_dimensionality::()); +} + +#[bench] +fn into_dimensionality_dyn_to_dyn(bench: &mut test::Bencher) { + let a = Array::::zeros(IxDyn(&[10, 10, 10])); + let a = a.view(); + bench.iter(|| a.clone().into_dimensionality::()); +} + +#[bench] +fn into_dyn_ix3(bench: &mut test::Bencher) { + let a = Array::::zeros(Ix3(10, 10, 10)); + let a = a.view(); + bench.iter(|| a.into_dyn()); +} + +#[bench] +fn into_dyn_ix5(bench: &mut test::Bencher) { + let a = Array::::zeros(Ix5(2, 2, 2, 2, 2)); + let a = a.view(); + bench.iter(|| a.into_dyn()); +} + +#[bench] +fn into_dyn_dyn(bench: &mut test::Bencher) { + let a = Array::::zeros(IxDyn(&[10, 10, 10])); + let a = a.view(); + bench.iter(|| a.clone().into_dyn()); +} diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs index aa1f7f95a..18a10c3f0 100644 --- a/src/dimension/dimension_trait.rs +++ b/src/dimension/dimension_trait.rs @@ -929,6 +929,11 @@ impl Dimension for IxDyn { fn from_dimension(d: &D2) -> Option { Some(IxDyn(d.slice())) } + + fn into_dyn(self) -> IxDyn { + self + } + private_impl! {} } diff --git a/src/impl_methods.rs b/src/impl_methods.rs index f8c0ee919..341a90286 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -6,6 +6,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::mem::{size_of, ManuallyDrop}; use alloc::slice; use alloc::vec; use alloc::vec::Vec; @@ -1583,8 +1584,11 @@ where } } - /// Convert an array or array view to another with the same type, but - /// different dimensionality type. Errors if the dimensions don't agree. + /// Convert an array or array view to another with the same type, but different dimensionality + /// type. Errors if the dimensions don't agree (the number of axes must match). + /// + /// Note that conversion to a dynamic dimensional array will never fail (and is equivalent to + /// the `into_dyn` method). /// /// ``` /// use ndarray::{ArrayD, Ix2, IxDyn}; @@ -1600,8 +1604,11 @@ where where D2: Dimension, { - if let Some(dim) = D2::from_dimension(&self.dim) { - if let Some(strides) = D2::from_dimension(&self.strides) { + if D::NDIM == D2::NDIM { + // safe because D == D2 + unsafe { + let dim = unlimited_transmute::(self.dim); + let strides = unlimited_transmute::(self.strides); return Ok(ArrayBase { data: self.data, ptr: self.ptr, @@ -1609,6 +1616,17 @@ where strides, }); } + } else if D::NDIM == None || D2::NDIM == None { // one is dynamic dim + if let Some(dim) = D2::from_dimension(&self.dim) { + if let Some(strides) = D2::from_dimension(&self.strides) { + return Ok(ArrayBase { + data: self.data, + ptr: self.ptr, + dim, + strides, + }); + } + } } Err(ShapeError::from_kind(ErrorKind::IncompatibleShape)) } @@ -2375,3 +2393,18 @@ where }); } } + + +/// Transmute from A to B. +/// +/// Like transmute, but does not have the compile-time size check which blocks +/// using regular transmute in some cases. +/// +/// **Panics** if the size of A and B are different. +#[inline] +unsafe fn unlimited_transmute(data: A) -> B { + // safe when sizes are equal and caller guarantees that representations are equal + assert_eq!(size_of::(), size_of::()); + let old_data = ManuallyDrop::new(data); + (&*old_data as *const A as *const B).read() +}