diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 53730d4fb..19775809b 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1218,6 +1218,48 @@ where D::is_contiguous(&self.dim, &self.strides) } + /// Return a standard-layout array containing the data, cloning if + /// necessary. + /// + /// If `self` is in standard layout, a COW view of the data is returned + /// without cloning. Otherwise, the data is cloned, and the returned array + /// owns the cloned data. + /// + /// ``` + /// use ndarray::Array2; + /// + /// let standard = Array2::::zeros((3, 4)); + /// assert!(standard.is_standard_layout()); + /// let cow_view = standard.as_standard_layout(); + /// assert!(cow_view.is_view()); + /// assert!(cow_view.is_standard_layout()); + /// + /// let fortran = standard.reversed_axes(); + /// assert!(!fortran.is_standard_layout()); + /// let cow_owned = fortran.as_standard_layout(); + /// assert!(cow_owned.is_owned()); + /// assert!(cow_owned.is_standard_layout()); + /// ``` + pub fn as_standard_layout(&self) -> CowArray<'_, A, D> + where + S: Data, + A: Clone, + { + if self.is_standard_layout() { + CowArray::from(self.view()) + } else { + let v: Vec = self.iter().cloned().collect(); + let dim = self.dim.clone(); + assert_eq!(v.len(), dim.size()); + let owned_array: Array = unsafe { + // Safe because the shape and element type are from the existing array + // and the strides are the default strides. + Array::from_shape_vec_unchecked(dim, v) + }; + CowArray::from(owned_array) + } + } + /// Return a pointer to the first element in the array. /// /// Raw access to array elements needs to follow the strided indexing diff --git a/tests/array.rs b/tests/array.rs index 000057b07..178381a66 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1970,6 +1970,74 @@ fn array_macros() { assert_eq!(empty2, array![[]]); } +#[cfg(test)] +mod as_standard_layout_tests { + use super::*; + use ndarray::Data; + use std::fmt::Debug; + + fn test_as_standard_layout_for(orig: ArrayBase) + where + S: Data, + S::Elem: Clone + Debug + PartialEq, + D: Dimension, + { + let orig_is_standard = orig.is_standard_layout(); + let out = orig.as_standard_layout(); + assert!(out.is_standard_layout()); + assert_eq!(out, orig); + assert_eq!(orig_is_standard, out.is_view()); + } + + #[test] + fn test_f_layout() { + let shape = (2, 2).f(); + let arr = Array::::from_shape_vec(shape, vec![1, 2, 3, 4]).unwrap(); + assert!(!arr.is_standard_layout()); + test_as_standard_layout_for(arr); + } + + #[test] + fn test_c_layout() { + let arr = Array::::from_shape_vec((2, 2), vec![1, 2, 3, 4]).unwrap(); + assert!(arr.is_standard_layout()); + test_as_standard_layout_for(arr); + } + + #[test] + fn test_f_layout_view() { + let shape = (2, 2).f(); + let arr = Array::::from_shape_vec(shape, vec![1, 2, 3, 4]).unwrap(); + let arr_view = arr.view(); + assert!(!arr_view.is_standard_layout()); + test_as_standard_layout_for(arr); + } + + #[test] + fn test_c_layout_view() { + let arr = Array::::from_shape_vec((2, 2), vec![1, 2, 3, 4]).unwrap(); + let arr_view = arr.view(); + assert!(arr_view.is_standard_layout()); + test_as_standard_layout_for(arr_view); + } + + #[test] + fn test_zero_dimensional_array() { + let arr_view = ArrayView1::::from(&[]); + assert!(arr_view.is_standard_layout()); + test_as_standard_layout_for(arr_view); + } + + #[test] + fn test_custom_layout() { + let shape = (1, 2, 3, 2).strides((12, 1, 2, 6)); + let arr_data: Vec = (0..12).collect(); + let arr = Array::::from_shape_vec(shape, arr_data).unwrap(); + assert!(!arr.is_standard_layout()); + test_as_standard_layout_for(arr); + } +} + #[cfg(test)] mod array_cow_tests { use super::*;