From ef115c6e92262fb46c3d837a98df313b604110e2 Mon Sep 17 00:00:00 2001 From: bluss Date: Tue, 2 Feb 2021 19:08:04 +0100 Subject: [PATCH] FIX: Add fast case for .into_dimensionality() no-op. This case has most impact for IxDyn, the benchmark for const dims is unchanged (it was already fast). --- src/impl_methods.rs | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index f8c0ee919..e5af35b01 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() +}