diff --git a/src/data_traits.rs b/src/data_traits.rs index 535c83c2d..5dfa57316 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -14,6 +14,7 @@ use std::sync::Arc; use { ArrayBase, Dimension, + RawViewRepr, ViewRepr, OwnedRepr, OwnedRcRepr, @@ -22,92 +23,177 @@ use { /// Array representation trait. /// -/// ***Note:*** `Data` is not an extension interface at this point. +/// For an array that meets the invariants of the `ArrayBase` type. This trait +/// does not imply any ownership or lifetime; pointers to elements in the array +/// may not be safe to dereference. +/// +/// ***Note:*** `RawData` is not an extension interface at this point. /// Traits in Rust can serve many different roles. This trait is public because /// it is used as a bound on public methods. -pub unsafe trait Data : Sized { +pub unsafe trait RawData : Sized { /// The array element type. type Elem; #[doc(hidden)] // This method is only used for debugging - fn _data_slice(&self) -> &[Self::Elem]; + fn _data_slice(&self) -> Option<&[Self::Elem]>; + + private_decl!{} +} + +/// Array representation trait. +/// +/// For an array with writable elements. +/// +/// ***Internal trait, see `RawData`.*** +pub unsafe trait RawDataMut : RawData { + /// If possible, ensures that the array has unique access to its data. + /// + /// If `Self` provides safe mutable access to array elements, then it + /// **must** panic or ensure that the data is unique. + #[doc(hidden)] + fn try_ensure_unique(&mut ArrayBase) + where Self: Sized, + D: Dimension; + /// If possible, returns whether the array has unique access to its data. + /// + /// If `Self` provides safe mutable access to array elements, then it + /// **must** return `Some(_)`. + #[doc(hidden)] + fn try_is_unique(&mut self) -> Option; +} + +/// Array representation trait. +/// +/// An array representation that can be cloned. +/// +/// ***Internal trait, see `RawData`.*** +pub unsafe trait RawDataClone : RawData { + #[doc(hidden)] + /// Unsafe because, `ptr` must point inside the current storage. + unsafe fn clone_with_ptr(&self, ptr: *mut Self::Elem) -> (Self, *mut Self::Elem); + + #[doc(hidden)] + unsafe fn clone_from_with_ptr(&mut self, other: &Self, ptr: *mut Self::Elem) -> *mut Self::Elem { + let (data, ptr) = other.clone_with_ptr(ptr); + *self = data; + ptr + } +} + +/// Array representation trait. +/// +/// For an array with elements that can be accessed with safe code. +/// +/// ***Internal trait, see `RawData`.*** +pub unsafe trait Data : RawData { /// Converts the array to a uniquely owned array, cloning elements if necessary. #[doc(hidden)] fn into_owned(self_: ArrayBase) -> ArrayBase, D> where Self::Elem: Clone, D: Dimension; - - private_decl!{} } /// Array representation trait. /// -/// For an array with writable elements. +/// For an array with writable elements that can be accessed with safe code. /// /// ***Internal trait, see `Data`.*** -pub unsafe trait DataMut : Data { +// +// # For implementers +// +// If you implement the `DataMut` trait, you are guaranteeing that the +// `RawDataMut::try_ensure_unique` implementation always panics or ensures that +// the data is unique. You are also guaranteeing that `try_is_unique` always +// returns `Some(_)`. +pub unsafe trait DataMut : Data + RawDataMut { + /// Ensures that the array has unique access to its data. #[doc(hidden)] #[inline] - fn ensure_unique(&mut ArrayBase) - where Self: Sized, - D: Dimension - { } + fn ensure_unique(self_: &mut ArrayBase) + where Self: Sized, + D: Dimension + { + Self::try_ensure_unique(self_) + } + /// Returns whether the array has unique access to its data. #[doc(hidden)] #[inline] fn is_unique(&mut self) -> bool { - true + self.try_is_unique().unwrap() } } /// Array representation trait. /// -/// An array representation that can be cloned. +/// An array representation that can be cloned and allows elements to be +/// accessed with safe code. /// /// ***Internal trait, see `Data`.*** -pub unsafe trait DataClone : Data { - #[doc(hidden)] - /// Unsafe because, `ptr` must point inside the current storage. - unsafe fn clone_with_ptr(&self, ptr: *mut Self::Elem) -> (Self, *mut Self::Elem); +#[deprecated(note="use `Data + RawDataClone` instead", since="0.13")] +pub trait DataClone : Data + RawDataClone {} - #[doc(hidden)] - unsafe fn clone_from_with_ptr(&mut self, other: &Self, ptr: *mut Self::Elem) -> *mut Self::Elem { - let (data, ptr) = other.clone_with_ptr(ptr); - *self = data; - ptr +#[allow(deprecated)] +impl DataClone for T where T: Data + RawDataClone {} + +unsafe impl RawData for RawViewRepr<*const A> { + type Elem = A; + fn _data_slice(&self) -> Option<&[A]> { + None } + private_impl!{} } -unsafe impl Data for OwnedArcRepr { +unsafe impl RawDataClone for RawViewRepr<*const A> { + unsafe fn clone_with_ptr(&self, ptr: *mut Self::Elem) -> (Self, *mut Self::Elem) { + (*self, ptr) + } +} + +unsafe impl RawData for RawViewRepr<*mut A> { type Elem = A; - fn _data_slice(&self) -> &[A] { - &self.0 + fn _data_slice(&self) -> Option<&[A]> { + None } - fn into_owned(mut self_: ArrayBase) -> ArrayBase, D> - where - A: Clone, - D: Dimension, - { - Self::ensure_unique(&mut self_); - let data = OwnedRepr(Arc::try_unwrap(self_.data.0).ok().unwrap()); - ArrayBase { - data: data, - ptr: self_.ptr, - dim: self_.dim, - strides: self_.strides, - } + private_impl!{} +} + +unsafe impl RawDataMut for RawViewRepr<*mut A> { + #[inline] + fn try_ensure_unique(_: &mut ArrayBase) + where Self: Sized, + D: Dimension + {} + + #[inline] + fn try_is_unique(&mut self) -> Option { + None + } +} + +unsafe impl RawDataClone for RawViewRepr<*mut A> { + unsafe fn clone_with_ptr(&self, ptr: *mut Self::Elem) -> (Self, *mut Self::Elem) { + (*self, ptr) + } +} + +unsafe impl RawData for OwnedArcRepr { + type Elem = A; + fn _data_slice(&self) -> Option<&[A]> { + Some(&self.0) } private_impl!{} } // NOTE: Copy on write -unsafe impl DataMut for OwnedArcRepr - where A: Clone +unsafe impl RawDataMut for OwnedArcRepr +where + A: Clone, { - fn ensure_unique(self_: &mut ArrayBase) + fn try_ensure_unique(self_: &mut ArrayBase) where Self: Sized, D: Dimension { @@ -136,23 +222,59 @@ unsafe impl DataMut for OwnedArcRepr } } - fn is_unique(&mut self) -> bool { - Arc::get_mut(&mut self.0).is_some() + fn try_is_unique(&mut self) -> Option { + Some(Arc::get_mut(&mut self.0).is_some()) } } -unsafe impl DataClone for OwnedArcRepr { +unsafe impl Data for OwnedArcRepr { + fn into_owned(mut self_: ArrayBase) -> ArrayBase, D> + where + A: Clone, + D: Dimension, + { + Self::ensure_unique(&mut self_); + let data = OwnedRepr(Arc::try_unwrap(self_.data.0).ok().unwrap()); + ArrayBase { + data: data, + ptr: self_.ptr, + dim: self_.dim, + strides: self_.strides, + } + } +} + +unsafe impl DataMut for OwnedArcRepr where A: Clone {} + +unsafe impl RawDataClone for OwnedArcRepr { unsafe fn clone_with_ptr(&self, ptr: *mut Self::Elem) -> (Self, *mut Self::Elem) { // pointer is preserved (self.clone(), ptr) } } -unsafe impl Data for OwnedRepr { +unsafe impl RawData for OwnedRepr { type Elem = A; - fn _data_slice(&self) -> &[A] { - &self.0 + fn _data_slice(&self) -> Option<&[A]> { + Some(&self.0) } + private_impl!{} +} + +unsafe impl RawDataMut for OwnedRepr { + #[inline] + fn try_ensure_unique(_: &mut ArrayBase) + where Self: Sized, + D: Dimension + {} + + #[inline] + fn try_is_unique(&mut self) -> Option { + Some(true) + } +} + +unsafe impl Data for OwnedRepr { #[inline] fn into_owned(self_: ArrayBase) -> ArrayBase, D> where @@ -161,12 +283,11 @@ unsafe impl Data for OwnedRepr { { self_ } - private_impl!{} } unsafe impl DataMut for OwnedRepr { } -unsafe impl DataClone for OwnedRepr +unsafe impl RawDataClone for OwnedRepr where A: Clone { unsafe fn clone_with_ptr(&self, ptr: *mut Self::Elem) -> (Self, *mut Self::Elem) { @@ -192,11 +313,15 @@ unsafe impl DataClone for OwnedRepr } } -unsafe impl<'a, A> Data for ViewRepr<&'a A> { +unsafe impl<'a, A> RawData for ViewRepr<&'a A> { type Elem = A; - fn _data_slice(&self) -> &[A] { - &[] + fn _data_slice(&self) -> Option<&[A]> { + None } + private_impl!{} +} + +unsafe impl<'a, A> Data for ViewRepr<&'a A> { fn into_owned(self_: ArrayBase) -> ArrayBase, D> where Self::Elem: Clone, @@ -204,20 +329,35 @@ unsafe impl<'a, A> Data for ViewRepr<&'a A> { { self_.to_owned() } - private_impl!{} } -unsafe impl<'a, A> DataClone for ViewRepr<&'a A> { +unsafe impl<'a, A> RawDataClone for ViewRepr<&'a A> { unsafe fn clone_with_ptr(&self, ptr: *mut Self::Elem) -> (Self, *mut Self::Elem) { (*self, ptr) } } -unsafe impl<'a, A> Data for ViewRepr<&'a mut A> { +unsafe impl<'a, A> RawData for ViewRepr<&'a mut A> { type Elem = A; - fn _data_slice(&self) -> &[A] { - &[] + fn _data_slice(&self) -> Option<&[A]> { + None } + private_impl!{} +} + +unsafe impl<'a, A> RawDataMut for ViewRepr<&'a mut A> { + #[inline] + fn try_ensure_unique(_: &mut ArrayBase) + where Self: Sized, + D: Dimension {} + + #[inline] + fn try_is_unique(&mut self) -> Option { + Some(true) + } +} + +unsafe impl<'a, A> Data for ViewRepr<&'a mut A> { fn into_owned(self_: ArrayBase) -> ArrayBase, D> where Self::Elem: Clone, @@ -225,7 +365,6 @@ unsafe impl<'a, A> Data for ViewRepr<&'a mut A> { { self_.to_owned() } - private_impl!{} } unsafe impl<'a, A> DataMut for ViewRepr<&'a mut A> { } @@ -250,7 +389,7 @@ pub unsafe trait DataOwned : Data { /// A representation that is a lightweight view. /// /// ***Internal trait, see `Data`.*** -pub unsafe trait DataShared : Clone + DataClone { } +pub unsafe trait DataShared : Clone + Data + RawDataClone { } unsafe impl DataShared for OwnedRcRepr {} unsafe impl<'a, A> DataShared for ViewRepr<&'a A> {} diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs index 16e758e29..ee612efea 100644 --- a/src/dimension/mod.rs +++ b/src/dimension/mod.rs @@ -303,13 +303,12 @@ impl<'a> DimensionExt for [Ix] /// /// **Panics** if `index` is larger than the size of the axis // FIXME: Move to Dimension trait -pub fn do_collapse_axis( +pub fn do_collapse_axis( dims: &mut D, - ptr: &mut *mut A, strides: &D, axis: usize, index: usize, -) { +) -> isize { let dim = dims.slice()[axis]; let stride = strides.slice()[axis]; ndassert!(index < dim, @@ -317,10 +316,7 @@ pub fn do_collapse_axis( array with shape {:?}", index, dim, *dims); dims.slice_mut()[axis] = 1; - let off = stride_offset(index, stride); - unsafe { - *ptr = ptr.offset(off); - } + stride_offset(index, stride) } /// Compute the equivalent unsigned index given the axis length and signed index. diff --git a/src/impl_1d.rs b/src/impl_1d.rs index 519a5ffc4..e891e2d4e 100644 --- a/src/impl_1d.rs +++ b/src/impl_1d.rs @@ -12,11 +12,13 @@ use imp_prelude::*; /// # Methods For 1-D Arrays impl ArrayBase - where S: Data, + where S: RawData, { /// Return an vector with the elements of the one-dimensional array. pub fn to_vec(&self) -> Vec - where A: Clone, + where + A: Clone, + S: Data, { if let Some(slc) = self.as_slice() { slc.to_vec() diff --git a/src/impl_2d.rs b/src/impl_2d.rs index 428da0f23..9f42d27b5 100644 --- a/src/impl_2d.rs +++ b/src/impl_2d.rs @@ -12,12 +12,14 @@ use imp_prelude::*; /// # Methods For 2-D Arrays impl ArrayBase - where S: Data, + where S: RawData, { /// Return an array view of row `index`. /// /// **Panics** if `index` is out of bounds. pub fn row(&self, index: Ix) -> ArrayView1 + where + S: Data, { self.index_axis(Axis(0), index) } @@ -40,6 +42,8 @@ impl ArrayBase /// /// **Panics** if `index` is out of bounds. pub fn column(&self, index: Ix) -> ArrayView1 + where + S: Data, { self.index_axis(Axis(1), index) } diff --git a/src/impl_clone.rs b/src/impl_clone.rs index 9626d945d..b980f05f4 100644 --- a/src/impl_clone.rs +++ b/src/impl_clone.rs @@ -6,9 +6,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. use imp_prelude::*; -use DataClone; +use RawDataClone; -impl Clone for ArrayBase { +impl Clone for ArrayBase { fn clone(&self) -> ArrayBase { unsafe { let (data, ptr) = self.data.clone_with_ptr(self.ptr); @@ -33,5 +33,4 @@ impl Clone for ArrayBase { } } -impl Copy for ArrayBase {} - +impl Copy for ArrayBase {} diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 350bd9172..562517dae 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -45,7 +45,10 @@ use iter::{ use stacking::stack; /// # Methods For All Array Types -impl ArrayBase where S: Data, D: Dimension +impl ArrayBase +where + S: RawData, + D: Dimension, { /// Return the total number of elements in the array. pub fn len(&self) -> usize { @@ -110,7 +113,10 @@ impl ArrayBase where S: Data, D: Dimension } /// Return a read-only view of the array - pub fn view(&self) -> ArrayView { + pub fn view(&self) -> ArrayView + where + S: Data, + { debug_assert!(self.pointer_is_inbounds()); unsafe { ArrayView::new_(self.ptr, self.dim.clone(), self.strides.clone()) @@ -158,7 +164,9 @@ impl ArrayBase where S: Data, D: Dimension /// # assert_eq!(arr, owned); /// ``` pub fn to_owned(&self) -> Array - where A: Clone + where + A: Clone, + S: Data, { if let Some(slc) = self.as_slice_memory_order() { unsafe { @@ -173,7 +181,9 @@ impl ArrayBase where S: Data, D: Dimension /// Return a shared ownership (copy on write) array. pub fn to_shared(&self) -> ArcArray - where A: Clone + where + A: Clone, + S: Data, { // FIXME: Avoid copying if it’s already an ArcArray. self.to_owned().into_shared() @@ -182,7 +192,9 @@ impl ArrayBase where S: Data, D: Dimension /// Turn the array into a uniquely owned array, cloning the array elements /// if necessary. pub fn into_owned(self) -> Array - where A: Clone, + where + A: Clone, + S: Data, { S::into_owned(self) } @@ -203,7 +215,10 @@ impl ArrayBase where S: Data, D: Dimension /// Returns a reference to the first element of the array, or `None` if it /// is empty. - pub fn first(&self) -> Option<&A> { + pub fn first(&self) -> Option<&A> + where + S: Data, + { if self.is_empty() { None } else { @@ -230,7 +245,10 @@ impl ArrayBase where S: Data, D: Dimension /// is where the rightmost index is varying the fastest. /// /// Iterator element type is `&A`. - pub fn iter(&self) -> Iter { + pub fn iter(&self) -> Iter + where + S: Data, + { debug_assert!(self.pointer_is_inbounds()); self.view().into_iter_() } @@ -255,7 +273,10 @@ impl ArrayBase where S: Data, D: Dimension /// Iterator element type is `(D::Pattern, &A)`. /// /// See also [`Zip::indexed`](struct.Zip.html) - pub fn indexed_iter(&self) -> IndexedIter { + pub fn indexed_iter(&self) -> IndexedIter + where + S: Data, + { IndexedIter::new(self.view().into_elements_base()) } @@ -285,6 +306,7 @@ impl ArrayBase where S: Data, D: Dimension pub fn slice(&self, info: &SliceInfo) -> ArrayView where Do: Dimension, + S: Data, { self.view().slice_move(info) } @@ -393,7 +415,10 @@ impl ArrayBase where S: Data, D: Dimension /// /// **Panics** if an index is out of bounds or step size is zero.
/// **Panics** if `axis` is out of bounds. - pub fn slice_axis(&self, axis: Axis, indices: Slice) -> ArrayView { + pub fn slice_axis(&self, axis: Axis, indices: Slice) -> ArrayView + where + S: Data, + { let mut view = self.view(); view.slice_axis_inplace(axis, indices); view @@ -430,8 +455,6 @@ impl ArrayBase where S: Data, D: Dimension debug_assert!(self.pointer_is_inbounds()); } - - /// Return a reference to the element at `index`, or return `None` /// if the index is out of bounds. /// @@ -451,7 +474,9 @@ impl ArrayBase where S: Data, D: Dimension /// ); /// ``` pub fn get(&self, index: I) -> Option<&A> - where I: NdIndex, + where + I: NdIndex, + S: Data, { unsafe { self.get_ptr(index).map(|ptr| &*ptr) @@ -478,7 +503,7 @@ impl ArrayBase where S: Data, D: Dimension } pub(crate) fn get_ptr_mut(&mut self, index: I) -> Option<*mut A> - where S: DataMut, + where S: RawDataMut, I: NdIndex, { // const and mut are separate to enforce &mutness as well as the @@ -495,7 +520,9 @@ impl ArrayBase where S: Data, D: Dimension /// **Note:** only unchecked for non-debug builds of ndarray. #[inline] pub unsafe fn uget(&self, index: I) -> &A - where I: NdIndex, + where + S: Data, + I: NdIndex, { arraytraits::debug_bounds_check(self, &index); let off = index.index_unchecked(&self.strides); @@ -555,7 +582,10 @@ impl ArrayBase where S: Data, D: Dimension // `get` for zero-dimensional arrays // panics if dimension is not zero. otherwise an element is always present. - fn get_0d(&self) -> &A { + fn get_0d(&self) -> &A + where + S: Data, + { assert!(self.ndim() == 0); unsafe { &*self.as_ptr() @@ -585,6 +615,7 @@ impl ArrayBase where S: Data, D: Dimension /// ``` pub fn index_axis(&self, axis: Axis, index: usize) -> ArrayView where + S: Data, D: RemoveAxis, { self.view().index_axis_move(axis, index) @@ -646,13 +677,9 @@ impl ArrayBase where S: Data, D: Dimension /// /// **Panics** if `axis` or `index` is out of bounds. pub fn collapse_axis(&mut self, axis: Axis, index: usize) { - dimension::do_collapse_axis( - &mut self.dim, - &mut self.ptr, - &self.strides, - axis.index(), - index, - ) + let offset = dimension::do_collapse_axis(&mut self.dim, &self.strides, axis.index(), index); + self.ptr = unsafe { self.ptr.offset(offset) }; + debug_assert!(self.pointer_is_inbounds()); } /// Along `axis`, select the subview `index` and return a @@ -661,7 +688,9 @@ impl ArrayBase where S: Data, D: Dimension /// **Panics** if `axis` or `index` is out of bounds. #[deprecated(note="renamed to `index_axis`", since="0.12.1")] pub fn subview(&self, axis: Axis, index: Ix) -> ArrayView - where D: RemoveAxis, + where + S: Data, + D: RemoveAxis, { self.index_axis(axis, index) } @@ -719,8 +748,10 @@ impl ArrayBase where S: Data, D: Dimension ///); /// ``` pub fn select(&self, axis: Axis, indices: &[Ix]) -> Array - where A: Copy, - D: RemoveAxis, + where + A: Copy, + S: Data, + D: RemoveAxis, { let mut subs = vec![self.view(); indices.len()]; for (&i, sub) in zip(indices, &mut subs[..]) { @@ -763,7 +794,10 @@ impl ArrayBase where S: Data, D: Dimension /// /* loop body */ /// } /// ``` - pub fn genrows(&self) -> Lanes { + pub fn genrows(&self) -> Lanes + where + S: Data, + { let mut n = self.ndim(); if n == 0 { n += 1; } Lanes::new(self.view(), Axis(n - 1)) @@ -807,7 +841,10 @@ impl ArrayBase where S: Data, D: Dimension /// /* loop body */ /// } /// ``` - pub fn gencolumns(&self) -> Lanes { + pub fn gencolumns(&self) -> Lanes + where + S: Data, + { Lanes::new(self.view(), Axis(0)) } @@ -849,7 +886,10 @@ impl ArrayBase where S: Data, D: Dimension /// // The first lane for axis 2 is [0, 1, 2] /// assert_eq!(inner2.into_iter().next().unwrap(), aview1(&[0, 1, 2])); /// ``` - pub fn lanes(&self, axis: Axis) -> Lanes { + pub fn lanes(&self, axis: Axis) -> Lanes + where + S: Data, + { Lanes::new(self.view(), axis) } @@ -872,7 +912,9 @@ impl ArrayBase where S: Data, D: Dimension /// Iterator element is `ArrayView` (read-only array view). #[allow(deprecated)] pub fn outer_iter(&self) -> AxisIter - where D: RemoveAxis, + where + S: Data, + D: RemoveAxis, { self.view().into_outer_iter() } @@ -907,7 +949,9 @@ impl ArrayBase where S: Data, D: Dimension /// /// pub fn axis_iter(&self, axis: Axis) -> AxisIter - where D: RemoveAxis, + where + S: Data, + D: RemoveAxis, { AxisIter::new(self.view(), axis) } @@ -954,7 +998,10 @@ impl ArrayBase where S: Data, D: Dimension /// assert_eq!(iter.next_back().unwrap(), arr3(&[[[12, 13]], /// [[26, 27]]])); /// ``` - pub fn axis_chunks_iter(&self, axis: Axis, size: usize) -> AxisChunksIter { + pub fn axis_chunks_iter(&self, axis: Axis, size: usize) -> AxisChunksIter + where + S: Data, + { AxisChunksIter::new(self.view(), axis, size) } @@ -983,7 +1030,9 @@ impl ArrayBase where S: Data, D: Dimension /// (**Panics** if `D` is `IxDyn` and `chunk_size` does not match the /// number of array axes.) pub fn exact_chunks(&self, chunk_size: E) -> ExactChunks - where E: IntoDimension, + where + E: IntoDimension, + S: Data, { ExactChunks::new(self.view(), chunk_size) } @@ -1042,7 +1091,9 @@ impl ArrayBase where S: Data, D: Dimension /// (**Panics** if `D` is `IxDyn` and `window_size` does not match the /// number of array axes.) pub fn windows(&self, window_size: E) -> Windows - where E: IntoDimension + where + E: IntoDimension, + S: Data, { Windows::new(self.view(), window_size) } @@ -1061,7 +1112,10 @@ impl ArrayBase where S: Data, D: Dimension /// /// The diagonal is simply the sequence indexed by *(0, 0, .., 0)*, /// *(1, 1, ..., 1)* etc as long as all axes have elements. - pub fn diag(&self) -> ArrayView1
{ + pub fn diag(&self) -> ArrayView1 + where + S: Data, + { self.view().into_diag() } @@ -1083,6 +1137,19 @@ impl ArrayBase where S: Data, D: Dimension } } + /// Try to make the array unshared. + /// + /// This is equivalent to `.ensure_unique()` if `S: DataMut`. + /// + /// This method is mostly only useful with unsafe code. + fn try_ensure_unique(&mut self) + where S: RawDataMut + { + debug_assert!(self.pointer_is_inbounds()); + S::try_ensure_unique(self); + debug_assert!(self.pointer_is_inbounds()); + } + /// Make the array unshared. /// /// This method is mostly only useful with unsafe code. @@ -1141,18 +1208,36 @@ impl ArrayBase where S: Data, D: Dimension /// Return a mutable pointer to the first element in the array. #[inline(always)] pub fn as_mut_ptr(&mut self) -> *mut A - where S: DataMut + where S: RawDataMut { - self.ensure_unique(); // for RcArray + self.try_ensure_unique(); // for RcArray self.ptr } + /// Return a raw view of the array. + #[inline] + pub fn raw_view(&self) -> RawArrayView { + unsafe { RawArrayView::new_(self.ptr, self.dim.clone(), self.strides.clone()) } + } + + /// Return a raw mutable view of the array. + #[inline] + pub fn raw_view_mut(&mut self) -> RawArrayViewMut + where S: RawDataMut + { + self.try_ensure_unique(); // for RcArray + unsafe { RawArrayViewMut::new_(self.ptr, self.dim.clone(), self.strides.clone()) } + } + /// Return the array’s data as a slice, if it is contiguous and in standard order. /// Return `None` otherwise. /// /// If this function returns `Some(_)`, then the element order in the slice /// corresponds to the logical order of the array’s elements. - pub fn as_slice(&self) -> Option<&[A]> { + pub fn as_slice(&self) -> Option<&[A]> + where + S: Data, + { if self.is_standard_layout() { unsafe { Some(slice::from_raw_parts(self.ptr, self.len())) @@ -1184,7 +1269,10 @@ impl ArrayBase where S: Data, D: Dimension /// have whatever order the elements have in memory. /// /// Implementation notes: Does not yet support negatively strided arrays. - pub fn as_slice_memory_order(&self) -> Option<&[A]> { + pub fn as_slice_memory_order(&self) -> Option<&[A]> + where + S: Data, + { if self.is_contiguous() { unsafe { Some(slice::from_raw_parts(self.ptr, self.len())) @@ -1240,7 +1328,7 @@ impl ArrayBase where S: Data, D: Dimension strides: shape.default_strides(), dim: shape, }) - } else if self.ndim() > 1 && self.view().reversed_axes().is_standard_layout() { + } else if self.ndim() > 1 && self.raw_view().reversed_axes().is_standard_layout() { Ok(ArrayBase { data: self.data, ptr: self.ptr, @@ -1377,7 +1465,9 @@ impl ArrayBase where S: Data, D: Dimension /// ); /// ``` pub fn broadcast(&self, dim: E) -> Option> - where E: IntoDimension + where + E: IntoDimension, + S: Data, { /// Return new stride when trying to grow `from` into shape `to` /// @@ -1519,7 +1609,10 @@ impl ArrayBase where S: Data, D: Dimension /// This is a shorthand for `self.view().reversed_axes()`. /// /// See also the more general methods `.reversed_axes()` and `.swap_axes()`. - pub fn t(&self) -> ArrayView { + pub fn t(&self) -> ArrayView + where + S: Data, + { self.view().reversed_axes() } @@ -1638,16 +1731,19 @@ impl ArrayBase where S: Data, D: Dimension } fn pointer_is_inbounds(&self) -> bool { - let slc = self.data._data_slice(); - if slc.is_empty() { - // special case for data-less views - return true; + match self.data._data_slice() { + None => { + // special case for non-owned views + true + } + Some(slc) => { + let ptr = slc.as_ptr() as *mut A; + let end = unsafe { + ptr.offset(slc.len() as isize) + }; + self.ptr >= ptr && self.ptr <= end + } } - let ptr = slc.as_ptr() as *mut A; - let end = unsafe { - ptr.offset(slc.len() as isize) - }; - self.ptr >= ptr && self.ptr <= end } /// Perform an elementwise assigment to `self` from `rhs`. @@ -1750,7 +1846,10 @@ impl ArrayBase where S: Data, D: Dimension /// /// Elements are visited in arbitrary order. pub fn fold<'a, F, B>(&'a self, init: B, f: F) -> B - where F: FnMut(B, &'a A) -> B, A: 'a + where + F: FnMut(B, &'a A) -> B, + A: 'a, + S: Data, { if let Some(slc) = self.as_slice_memory_order() { slc.iter().fold(init, f) @@ -1790,6 +1889,7 @@ impl ArrayBase where S: Data, D: Dimension pub fn map<'a, B, F>(&'a self, f: F) -> Array where F: FnMut(&'a A) -> B, A: 'a, + S: Data, { if let Some(slc) = self.as_slice_memory_order() { let v = ::iterators::to_vec_mapped(slc.iter(), f); @@ -1853,6 +1953,7 @@ impl ArrayBase where S: Data, D: Dimension pub fn mapv(&self, mut f: F) -> Array where F: FnMut(A) -> B, A: Clone, + S: Data, { self.map(move |x| f(x.clone())) } @@ -1911,6 +2012,7 @@ impl ArrayBase where S: Data, D: Dimension pub fn visit<'a, F>(&'a self, mut f: F) where F: FnMut(&'a A), A: 'a, + S: Data, { self.fold((), move |(), elt| f(elt)) } @@ -1928,6 +2030,7 @@ impl ArrayBase where S: Data, D: Dimension where D: RemoveAxis, F: FnMut(&B, &A) -> B, B: Clone, + S: Data, { let mut res = Array::from_elem(self.raw_dim().remove_axis(axis), init); for subview in self.axis_iter(axis) { @@ -1949,6 +2052,7 @@ impl ArrayBase where S: Data, D: Dimension where D: RemoveAxis, F: FnMut(ArrayView1<'a, A>) -> B, A: 'a, + S: Data, { let view_len = self.len_of(axis); let view_stride = self.strides.axis(axis); diff --git a/src/impl_raw_views.rs b/src/impl_raw_views.rs new file mode 100644 index 000000000..76396109d --- /dev/null +++ b/src/impl_raw_views.rs @@ -0,0 +1,214 @@ +use dimension::{self, stride_offset}; +use imp_prelude::*; +use {is_aligned, StrideShape}; + +impl RawArrayView +where + D: Dimension, +{ + /// Create a new `RawArrayView`. + /// + /// Unsafe because caller is responsible for ensuring that the array will + /// meet all of the invariants of the `ArrayBase` type. + #[inline(always)] + pub(crate) unsafe fn new_(ptr: *const A, dim: D, strides: D) -> Self { + RawArrayView { + data: RawViewRepr::new(), + ptr: ptr as *mut A, + dim: dim, + strides: strides, + } + } + + /// Create an `RawArrayView` from shape information and a raw pointer + /// to the elements. + /// + /// Unsafe because caller is responsible for ensuring all of the following: + /// + /// * `ptr` must be non-null and aligned, and it must be safe to + /// [`.offset()`] `ptr` by zero. + /// + /// * It must be safe to [`.offset()`] the pointer repeatedly along all + /// axes and calculate the `count`s for the `.offset()` calls without + /// overflow, even if the array is empty or the elements are zero-sized. + /// + /// In other words, + /// + /// * All possible pointers generated by moving along all axes must be in + /// bounds or one byte past the end of a single allocation with element + /// type `A`. The only exceptions are if the array is empty or the element + /// type is zero-sized. In these cases, `ptr` may be dangling, but it must + /// still be safe to [`.offset()`] the pointer along the axes. + /// + /// * The offset in units of bytes between the least address and greatest + /// address by moving along all axes must not exceed `isize::MAX`. This + /// constraint prevents the computed offset, in bytes, from overflowing + /// `isize` regardless of the starting point due to past offsets. + /// + /// * The offset in units of `A` between the least address and greatest + /// address by moving along all axes must not exceed `isize::MAX`. This + /// constraint prevents overflow when calculating the `count` parameter to + /// [`.offset()`] regardless of the starting point due to past offsets. + /// + /// * The product of non-zero axis lengths must not exceed `isize::MAX`. + /// + /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset + pub unsafe fn from_shape_ptr(shape: Sh, ptr: *const A) -> Self + where + Sh: Into>, + { + let shape = shape.into(); + let dim = shape.dim; + let strides = shape.strides; + if cfg!(debug_assertions) { + assert!(!ptr.is_null(), "The pointer must be non-null."); + assert!(is_aligned(ptr), "The pointer must be aligned."); + dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); + } + RawArrayView::new_(ptr, dim, strides) + } + + /// Converts to a read-only view of the array. + /// + /// **Warning** from a safety standpoint, this is equivalent to + /// dereferencing a raw pointer for every element in the array. You must + /// ensure that all of the data is valid and choose the correct lifetime. + #[inline] + pub unsafe fn deref_into_view<'a>(self) -> ArrayView<'a, A, D> { + ArrayView::new_(self.ptr, self.dim, self.strides) + } + + /// Split the array view along `axis` and return one array pointer strictly + /// before the split and one array pointer after the split. + /// + /// **Panics** if `axis` or `index` is out of bounds. + pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { + assert!(index <= self.len_of(axis)); + let left_ptr = self.ptr; + let right_ptr = if index == self.len_of(axis) { + self.ptr + } else { + let offset = stride_offset(index, self.strides.axis(axis)); + // The `.offset()` is safe due to the guarantees of `RawData`. + unsafe { self.ptr.offset(offset) } + }; + + let mut dim_left = self.dim.clone(); + dim_left.set_axis(axis, index); + let left = unsafe { Self::new_(left_ptr, dim_left, self.strides.clone()) }; + + let mut dim_right = self.dim; + let right_len = dim_right.axis(axis) - index; + dim_right.set_axis(axis, right_len); + let right = unsafe { Self::new_(right_ptr, dim_right, self.strides) }; + + (left, right) + } +} + +impl RawArrayViewMut +where + D: Dimension, +{ + /// Create a new `RawArrayViewMut`. + /// + /// Unsafe because caller is responsible for ensuring that the array will + /// meet all of the invariants of the `ArrayBase` type. + #[inline(always)] + pub(crate) unsafe fn new_(ptr: *mut A, dim: D, strides: D) -> Self { + RawArrayViewMut { + data: RawViewRepr::new(), + ptr: ptr, + dim: dim, + strides: strides, + } + } + + /// Create an `RawArrayViewMut` from shape information and a raw + /// pointer to the elements. + /// + /// Unsafe because caller is responsible for ensuring all of the following: + /// + /// * `ptr` must be non-null and aligned, and it must be safe to + /// [`.offset()`] `ptr` by zero. + /// + /// * It must be safe to [`.offset()`] the pointer repeatedly along all + /// axes and calculate the `count`s for the `.offset()` calls without + /// overflow, even if the array is empty or the elements are zero-sized. + /// + /// In other words, + /// + /// * All possible pointers generated by moving along all axes must be in + /// bounds or one byte past the end of a single allocation with element + /// type `A`. The only exceptions are if the array is empty or the element + /// type is zero-sized. In these cases, `ptr` may be dangling, but it must + /// still be safe to [`.offset()`] the pointer along the axes. + /// + /// * The offset in units of bytes between the least address and greatest + /// address by moving along all axes must not exceed `isize::MAX`. This + /// constraint prevents the computed offset, in bytes, from overflowing + /// `isize` regardless of the starting point due to past offsets. + /// + /// * The offset in units of `A` between the least address and greatest + /// address by moving along all axes must not exceed `isize::MAX`. This + /// constraint prevents overflow when calculating the `count` parameter to + /// [`.offset()`] regardless of the starting point due to past offsets. + /// + /// * The product of non-zero axis lengths must not exceed `isize::MAX`. + /// + /// [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset + pub unsafe fn from_shape_ptr(shape: Sh, ptr: *mut A) -> Self + where + Sh: Into>, + { + let shape = shape.into(); + let dim = shape.dim; + let strides = shape.strides; + if cfg!(debug_assertions) { + assert!(!ptr.is_null(), "The pointer must be non-null."); + assert!(is_aligned(ptr), "The pointer must be aligned."); + dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); + } + RawArrayViewMut::new_(ptr, dim, strides) + } + + /// Converts to a non-mutable `RawArrayView`. + #[inline] + pub(crate) fn into_raw_view(self) -> RawArrayView { + unsafe { RawArrayView::new_(self.ptr, self.dim, self.strides) } + } + + /// Converts to a read-only view of the array. + /// + /// **Warning** from a safety standpoint, this is equivalent to + /// dereferencing a raw pointer for every element in the array. You must + /// ensure that all of the data is valid and choose the correct lifetime. + #[inline] + pub unsafe fn deref_into_view<'a>(self) -> ArrayView<'a, A, D> { + ArrayView::new_(self.ptr, self.dim, self.strides) + } + + /// Converts to a mutable view of the array. + /// + /// **Warning** from a safety standpoint, this is equivalent to + /// dereferencing a raw pointer for every element in the array. You must + /// ensure that all of the data is valid and choose the correct lifetime. + #[inline] + pub unsafe fn deref_into_view_mut<'a>(self) -> ArrayViewMut<'a, A, D> { + ArrayViewMut::new_(self.ptr, self.dim, self.strides) + } + + /// Split the array view along `axis` and return one array pointer strictly + /// before the split and one array pointer after the split. + /// + /// **Panics** if `axis` or `index` is out of bounds. + pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { + let (left, right) = self.into_raw_view().split_at(axis, index); + unsafe { + ( + Self::new_(left.ptr, left.dim, left.strides), + Self::new_(right.ptr, right.dim, right.strides), + ) + } + } +} diff --git a/src/impl_views.rs b/src/impl_views.rs index 560c2ff12..489e26cc6 100644 --- a/src/impl_views.rs +++ b/src/impl_views.rs @@ -9,11 +9,10 @@ use std::slice; use imp_prelude::*; -use dimension::{self, stride_offset}; +use dimension; use error::ShapeError; -use NdIndex; use arraytraits::array_out_of_bounds; -use StrideShape; +use {is_aligned, NdIndex, StrideShape}; use { ElementsBase, @@ -25,11 +24,6 @@ use { use iter::{self, AxisIter, AxisIterMut}; -/// Returns `true` if the pointer is aligned. -fn is_aligned(ptr: *const T) -> bool { - (ptr as usize) % ::std::mem::align_of::() == 0 -} - /// Methods for read-only array views. impl<'a, A, D> ArrayView<'a, A, D> where D: Dimension, @@ -117,15 +111,7 @@ impl<'a, A, D> ArrayView<'a, A, D> pub unsafe fn from_shape_ptr(shape: Sh, ptr: *const A) -> Self where Sh: Into> { - let shape = shape.into(); - let dim = shape.dim; - let strides = shape.strides; - if cfg!(debug_assertions) { - assert!(!ptr.is_null(), "The pointer must be non-null."); - assert!(is_aligned(ptr), "The pointer must be aligned."); - dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); - } - ArrayView::new_(ptr, dim, strides) + RawArrayView::from_shape_ptr(shape, ptr).deref_into_view() } /// Convert the view into an `ArrayView<'b, A, D>` where `'b` is a lifetime @@ -147,35 +133,11 @@ impl<'a, A, D> ArrayView<'a, A, D> /// an array with shape 3 × 5 × 5. /// /// - pub fn split_at(self, axis: Axis, index: Ix) - -> (Self, Self) - { - // NOTE: Keep this in sync with the ArrayViewMut version - assert!(index <= self.len_of(axis)); - let left_ptr = self.ptr; - let right_ptr = if index == self.len_of(axis) { - self.ptr - } else { - let offset = stride_offset(index, self.strides.axis(axis)); - unsafe { - self.ptr.offset(offset) - } - }; - - let mut dim_left = self.dim.clone(); - dim_left.set_axis(axis, index); - let left = unsafe { - Self::new_(left_ptr, dim_left, self.strides.clone()) - }; - - let mut dim_right = self.dim; - let right_len = dim_right.axis(axis) - index; - dim_right.set_axis(axis, right_len); - let right = unsafe { - Self::new_(right_ptr, dim_right, self.strides) - }; - - (left, right) + pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { + unsafe { + let (left, right) = self.into_raw_view().split_at(axis, index); + (left.deref_into_view(), right.deref_into_view()) + } } /// Return the array’s data as a slice, if it is contiguous and in standard order. @@ -189,6 +151,11 @@ impl<'a, A, D> ArrayView<'a, A, D> None } } + + /// Converts to a raw array view. + pub(crate) fn into_raw_view(self) -> RawArrayView { + unsafe { RawArrayView::new_(self.ptr, self.dim, self.strides) } + } } @@ -414,15 +381,7 @@ impl<'a, A, D> ArrayViewMut<'a, A, D> pub unsafe fn from_shape_ptr(shape: Sh, ptr: *mut A) -> Self where Sh: Into> { - let shape = shape.into(); - let dim = shape.dim; - let strides = shape.strides; - if cfg!(debug_assertions) { - assert!(!ptr.is_null(), "The pointer must be non-null."); - assert!(is_aligned(ptr), "The pointer must be aligned."); - dimension::max_abs_offset_check_overflow::(&dim, &strides).unwrap(); - } - ArrayViewMut::new_(ptr, dim, strides) + RawArrayViewMut::from_shape_ptr(shape, ptr).deref_into_view_mut() } /// Convert the view into an `ArrayViewMut<'b, A, D>` where `'b` is a lifetime @@ -439,35 +398,11 @@ impl<'a, A, D> ArrayViewMut<'a, A, D> /// before the split and one mutable view after the split. /// /// **Panics** if `axis` or `index` is out of bounds. - pub fn split_at(self, axis: Axis, index: Ix) - -> (Self, Self) - { - // NOTE: Keep this in sync with the ArrayView version - assert!(index <= self.len_of(axis)); - let left_ptr = self.ptr; - let right_ptr = if index == self.len_of(axis) { - self.ptr - } else { - let offset = stride_offset(index, self.strides.axis(axis)); - unsafe { - self.ptr.offset(offset) - } - }; - - let mut dim_left = self.dim.clone(); - dim_left.set_axis(axis, index); - let left = unsafe { - Self::new_(left_ptr, dim_left, self.strides.clone()) - }; - - let mut dim_right = self.dim; - let right_len = dim_right.axis(axis) - index; - dim_right.set_axis(axis, right_len); - let right = unsafe { - Self::new_(right_ptr, dim_right, self.strides) - }; - - (left, right) + pub fn split_at(self, axis: Axis, index: Ix) -> (Self, Self) { + unsafe { + let (left, right) = self.into_raw_view_mut().split_at(axis, index); + (left.deref_into_view_mut(), right.deref_into_view_mut()) + } } /// Return the array’s data as a slice, if it is contiguous and in standard order. @@ -611,6 +546,11 @@ impl<'a, A, D> ArrayViewMut<'a, A, D> } } + /// Converts to a mutable raw array view. + pub(crate) fn into_raw_view_mut(self) -> RawArrayViewMut { + unsafe { RawArrayViewMut::new_(self.ptr, self.dim, self.strides) } + } + #[inline] pub(crate) fn into_base_iter(self) -> Baseiter { unsafe { diff --git a/src/lib.rs b/src/lib.rs index 4aa83e20d..c56df3d02 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,7 +143,11 @@ mod data_traits; pub use aliases::*; +#[allow(deprecated)] pub use data_traits::{ + RawData, + RawDataMut, + RawDataClone, Data, DataMut, DataOwned, @@ -185,10 +189,13 @@ mod imp_prelude { pub use ArcArray; pub use { RemoveAxis, + RawData, + RawDataMut, Data, DataMut, DataOwned, DataShared, + RawViewRepr, ViewRepr, Ix, Ixs, }; @@ -1014,7 +1021,7 @@ pub type Ixs = isize; // // [`.offset()`]: https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.offset-1 pub struct ArrayBase - where S: Data + where S: RawData { /// Data buffer / ownership information. (If owned, contains the data /// buffer; if borrowed, contains the lifetime and mutability.) @@ -1113,6 +1120,57 @@ pub type ArrayView<'a, A, D> = ArrayBase, D>; /// [ab]: struct.ArrayBase.html pub type ArrayViewMut<'a, A, D> = ArrayBase, D>; +/// A read-only array view without a lifetime. +/// +/// This is similar to [`ArrayView`] but does not carry any lifetime or +/// ownership information, and its data cannot be read without an unsafe +/// conversion into an [`ArrayView`]. The relationship between `RawArrayView` +/// and [`ArrayView`] is somewhat analogous to the relationship between `*const +/// T` and `&T`, but `RawArrayView` has additional requirements that `*const T` +/// does not, such as alignment and non-nullness. +/// +/// [`ArrayView`]: type.ArrayView.html +/// +/// The `RawArrayView` is parameterized by `A` for the element type and +/// `D` for the dimensionality. +/// +/// Raw array views have all the methods of an array (see +/// [`ArrayBase`](struct.ArrayBase.html)). +/// +/// See also [`RawArrayViewMut`](type.RawArrayViewMut.html). +/// +/// # Warning +/// +/// You can't use this type wih an arbitrary raw pointer; see +/// [`from_shape_ptr`](#method.from_shape_ptr) for details. +pub type RawArrayView = ArrayBase, D>; + +/// A mutable array view without a lifetime. +/// +/// This is similar to [`ArrayViewMut`] but does not carry any lifetime or +/// ownership information, and its data cannot be read/written without an +/// unsafe conversion into an [`ArrayViewMut`]. The relationship between +/// `RawArrayViewMut` and [`ArrayViewMut`] is somewhat analogous to the +/// relationship between `*mut T` and `&mut T`, but `RawArrayViewMut` has +/// additional requirements that `*mut T` does not, such as alignment and +/// non-nullness. +/// +/// [`ArrayViewMut`]: type.ArrayViewMut.html +/// +/// The `RawArrayViewMut` is parameterized by `A` for the element type +/// and `D` for the dimensionality. +/// +/// Raw array views have all the methods of an array (see +/// [`ArrayBase`](struct.ArrayBase.html)). +/// +/// See also [`RawArrayView`](type.RawArrayView.html). +/// +/// # Warning +/// +/// You can't use this type wih an arbitrary raw pointer; see +/// [`from_shape_ptr`](#method.from_shape_ptr) for details. +pub type RawArrayViewMut = ArrayBase, D>; + /// Array's representation. /// /// *Don’t use this type directly—use the type alias @@ -1140,6 +1198,23 @@ impl Clone for OwnedArcRepr { } } +/// Array pointer’s representation. +/// +/// *Don’t use this type directly—use the type aliases +/// [`RawArrayView`](type.RawArrayView.html) / +/// [`RawArrayViewMut`](type.RawArrayViewMut.html) for the array type!* +#[derive(Copy, Clone)] +// This is just a marker type, to carry the mutability and element type. +pub struct RawViewRepr { + ptr: PhantomData, +} + +impl RawViewRepr { + #[inline(always)] + fn new() -> Self { + RawViewRepr { ptr: PhantomData } + } +} /// Array view’s representation. /// @@ -1272,6 +1347,9 @@ pub use impl_ops::ScalarOperand; // Array view methods mod impl_views; +// Array raw view methods +mod impl_raw_views; + /// A contiguous array shape of n dimensions. /// /// Either c- or f- memory ordered (*c* a.k.a *row major* is the default). @@ -1288,3 +1366,8 @@ pub struct StrideShape { strides: D, custom: bool, } + +/// Returns `true` if the pointer is aligned. +pub(crate) fn is_aligned(ptr: *const T) -> bool { + (ptr as usize) % ::std::mem::align_of::() == 0 +} diff --git a/src/prelude.rs b/src/prelude.rs index 6778ec20e..20e1600c8 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -27,6 +27,8 @@ pub use { RcArray, ArrayView, ArrayViewMut, + RawArrayView, + RawArrayViewMut, }; #[doc(no_inline)]