Repository: rust-ndarray/ndarray
Pull request #1015 (2021-05-29 01:41:41)

Add in/to_slice_memory_order for ArrayView/Mut
This adds a `.to_slice_memory_order()` method to `ArrayView` and an `.into_slice_memory_order()` method to `ArrayViewMut`. This has unfortunate code duplication with `.as_slice_memory_order/_mut()`, but the quantity of duplicated code isn't very large.

bluss: Rebased and got the newest spelling from #1119 so we're up to date on that too :wink: 
bluss: Thanks I'll merge and future improvements can come later - the Result question is left here and it sounds like it could be a good idea! Nice to have the negative tests included here.

Patch:
diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs
index 918b131a..1e9a1499 100644
--- a/src/impl_views/conversions.rs
+++ b/src/impl_views/conversions.rs
@@ -7,12 +7,14 @@
 // except according to those terms.
 
 use alloc::slice;
+use rawpointer::PointerExt;
 use std::mem::MaybeUninit;
 
 use crate::imp_prelude::*;
 
 use crate::{Baseiter, ElementsBase, ElementsBaseMut, Iter, IterMut};
 
+use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr;
 use crate::iter::{self, AxisIter, AxisIterMut};
 use crate::math_cell::MathCell;
 use crate::IndexLonger;
@@ -44,6 +46,26 @@ where
         }
     }
 
+    /// Return the array’s data as a slice, if it is contiguous.
+    /// Return `None` otherwise.
+    ///
+    /// Note that while the method is similar to
+    /// [`ArrayBase::as_slice_memory_order()`], this method transfers the view's
+    /// lifetime to the slice, so it is a bit more powerful.
+    pub fn to_slice_memory_order(&self) -> Option<&'a [A]> {
+        if self.is_contiguous() {
+            let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides);
+            unsafe {
+                Some(slice::from_raw_parts(
+                    self.ptr.sub(offset).as_ptr(),
+                    self.len(),
+                ))
+            }
+        } else {
+            None
+        }
+    }
+
     /// Converts to a raw array view.
     pub(crate) fn into_raw_view(self) -> RawArrayView<A, D> {
         unsafe { RawArrayView::new(self.ptr, self.dim, self.strides) }
@@ -114,6 +136,16 @@ where
         self.try_into_slice().ok()
     }
 
+    /// Return the array’s data as a slice, if it is contiguous.
+    /// Return `None` otherwise.
+    ///
+    /// Note that while this is similar to
+    /// [`ArrayBase::as_slice_memory_order_mut()`], this method transfers the
+    /// view's lifetime to the slice.
+    pub fn into_slice_memory_order(self) -> Option<&'a mut [A]> {
+        self.try_into_slice_memory_order().ok()
+    }
+
     /// Return a shared view of the array with elements as if they were embedded in cells.
     ///
     /// The cell view itself can be copied and accessed without exclusivity.
@@ -215,6 +247,22 @@ where
         }
     }
 
+    /// Return the array’s data as a slice, if it is contiguous.
+    /// Otherwise return self in the Err branch of the result.
+    fn try_into_slice_memory_order(self) -> Result<&'a mut [A], Self> {
+        if self.is_contiguous() {
+            let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides);
+            unsafe {
+                Ok(slice::from_raw_parts_mut(
+                    self.ptr.sub(offset).as_ptr(),
+                    self.len(),
+                ))
+            }
+        } else {
+            Err(self)
+        }
+    }
+
     pub(crate) fn into_iter_(self) -> IterMut<'a, A, D> {
         IterMut::new(self)
     }


Test patch:
diff --git a/tests/array.rs b/tests/array.rs
index 16d90156..44565965 100644
--- a/tests/array.rs
+++ b/tests/array.rs
@@ -1048,6 +1048,42 @@ fn as_slice_memory_order_mut_contiguous_cowarray() {
     assert_eq!(b.strides(), &[1, 2]);
 }
 
+#[test]
+fn to_slice_memory_order() {
+    for shape in vec![[2, 0, 3, 5], [2, 1, 3, 5], [2, 4, 3, 5]] {
+        let data: Vec<usize> = (0..shape.iter().product()).collect();
+        let mut orig = Array1::from(data.clone()).into_shape(shape).unwrap();
+        for perm in vec![[0, 1, 2, 3], [0, 2, 1, 3], [2, 0, 1, 3]] {
+            let mut a = orig.view_mut().permuted_axes(perm);
+            assert_eq!(a.as_slice_memory_order().unwrap(), &data);
+            assert_eq!(a.as_slice_memory_order_mut().unwrap(), &data);
+            assert_eq!(a.view().to_slice_memory_order().unwrap(), &data);
+            assert_eq!(a.view_mut().into_slice_memory_order().unwrap(), &data);
+        }
+    }
+}
+
+#[test]
+fn to_slice_memory_order_discontiguous() {
+    let mut orig = Array3::<u8>::zeros([3, 2, 4]);
+    assert!(orig
+        .slice(s![.., 1.., ..])
+        .as_slice_memory_order()
+        .is_none());
+    assert!(orig
+        .slice_mut(s![.., 1.., ..])
+        .as_slice_memory_order_mut()
+        .is_none());
+    assert!(orig
+        .slice(s![.., 1.., ..])
+        .to_slice_memory_order()
+        .is_none());
+    assert!(orig
+        .slice_mut(s![.., 1.., ..])
+        .into_slice_memory_order()
+        .is_none());
+}
+
 #[test]
 fn array0_into_scalar() {
     // With this kind of setup, the `Array`'s pointer is not the same as the