diff --git a/src/data_traits.rs b/src/data_traits.rs index 137de0bc4..f4df024c6 100644 --- a/src/data_traits.rs +++ b/src/data_traits.rs @@ -414,9 +414,7 @@ unsafe impl DataOwned for OwnedArcRepr { } } -unsafe impl<'a, A> RawData for CowRepr<'a, A> - where A: Clone -{ +unsafe impl<'a, A> RawData for CowRepr<'a, A> { type Elem = A; fn _data_slice(&self) -> Option<&[A]> { match self { @@ -435,7 +433,16 @@ unsafe impl<'a, A> RawDataMut for CowRepr<'a, A> where Self: Sized, D: Dimension { - array.ensure_is_owned(); + match array.data { + CowRepr::View(_) => { + let owned = array.to_owned(); + array.data = CowRepr::Owned(owned.data); + array.ptr = owned.ptr; + array.dim = owned.dim; + array.strides = owned.strides; + } + CowRepr::Owned(_) => {} + } } #[inline] @@ -445,7 +452,7 @@ unsafe impl<'a, A> RawDataMut for CowRepr<'a, A> } unsafe impl<'a, A> RawDataClone for CowRepr<'a, A> - where A: Copy + where A: Clone { unsafe fn clone_with_ptr(&self, ptr: *mut Self::Elem) -> (Self, *mut Self::Elem) { match self { @@ -462,36 +469,44 @@ unsafe impl<'a, A> RawDataClone for CowRepr<'a, A> #[doc(hidden)] unsafe fn clone_from_with_ptr(&mut self, other: &Self, ptr: *mut Self::Elem) -> *mut Self::Elem { - match self { - CowRepr::View(view) => { - match other { - CowRepr::View(other_view) => view.clone_from_with_ptr(other_view, ptr), - CowRepr::Owned(_) => panic!("Cannot copy `CowRepr::View` from `CowRepr::Temp`"), - } + match (&mut *self, other) { + (CowRepr::View(self_), CowRepr::View(other)) => { + self_.clone_from_with_ptr(other, ptr) }, - CowRepr::Owned(data) => { - match other { - CowRepr::View(_) => panic!("Cannot copy `CowRepr::Temp` from `CowRepr::View`"), - CowRepr::Owned(other_data) => data.clone_from_with_ptr(other_data, ptr), - } + (CowRepr::Owned(self_), CowRepr::Owned(other)) => { + self_.clone_from_with_ptr(other, ptr) }, + (_, CowRepr::Owned(other)) => { + let (cloned, ptr) = other.clone_with_ptr(ptr); + *self = CowRepr::Owned(cloned); + ptr + }, + (_, CowRepr::View(other)) => { + let (cloned, ptr) = other.clone_with_ptr(ptr); + *self = CowRepr::View(cloned); + ptr + } } } } -unsafe impl<'a, A> Data for CowRepr<'a, A> - where A: Clone -{ +unsafe impl<'a, A> Data for CowRepr<'a, A> { #[inline] fn into_owned(self_: ArrayBase, D>) -> ArrayBase, D> where A: Clone, D: Dimension, { - if self_.data.is_view() { - ViewRepr::into_owned(self_.into_view_array().unwrap()) - } else { - OwnedRepr::into_owned(self_.into_owned_array().unwrap()) + match self_.data { + CowRepr::View(_) => self_.to_owned(), + CowRepr::Owned(data) => ArrayBase { + data, + ptr: self_.ptr, + dim: self_.dim, + strides: self_.strides, + }, } } } + +unsafe impl<'a, A> DataMut for CowRepr<'a, A> where A: Clone {} diff --git a/src/impl_cow.rs b/src/impl_cow.rs new file mode 100644 index 000000000..47b2af574 --- /dev/null +++ b/src/impl_cow.rs @@ -0,0 +1,55 @@ +// Copyright 2019 ndarray developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::imp_prelude::*; + +/// Methods specific to `ArrayCow`. +/// +/// ***See also all methods for [`ArrayBase`]*** +/// +/// [`ArrayBase`]: struct.ArrayBase.html +impl<'a, A, D> ArrayCow<'a, A, D> +where + D: Dimension, +{ + pub fn is_view(&self) -> bool { + self.data.is_view() + } + + pub fn is_owned(&self) -> bool { + self.data.is_owned() + } +} + +impl<'a, A, D> From> for ArrayCow<'a, A, D> +where + D: Dimension, +{ + fn from(view: ArrayView<'a, A, D>) -> ArrayCow<'a, A, D> { + ArrayBase { + data: CowRepr::View(view.data), + ptr: view.ptr, + dim: view.dim, + strides: view.strides, + } + } +} + +impl<'a, A, D> From> for ArrayCow<'a, A, D> +where + D: Dimension, +{ + fn from(array: Array) -> ArrayCow<'a, A, D> { + ArrayBase { + data: CowRepr::Owned(array.data), + ptr: array.ptr, + dim: array.dim, + strides: array.strides, + } + } +} diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 4dd85bf58..a8a4ecea6 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -1230,14 +1230,14 @@ where A: Clone { if self.is_standard_layout() { - ArrayCow::from_view_array(self.view()) + ArrayCow::from(self.view()) } else { - let v = self.iter().map(|x| x.clone()).collect::>(); + let v = self.iter().cloned().collect::>(); let owned_array: Array = unsafe { // Safe because we use shape and content of existing array here. - ArrayBase::from_shape_vec_unchecked(self.dim(), v) + ArrayBase::from_shape_vec_unchecked(self.raw_dim(), v) }; - ArrayCow::from_owned_array(owned_array) + ArrayCow::from(owned_array) } } diff --git a/src/lib.rs b/src/lib.rs index a85307c7b..a96c3d63b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -205,6 +205,7 @@ mod imp_prelude { DataShared, RawViewRepr, ViewRepr, + CowRepr, Ix, Ixs, }; pub use crate::dimension::DimensionExt; @@ -1230,6 +1231,20 @@ pub type ArcArray = ArrayBase, D>; /// and so on. pub type Array = ArrayBase, D>; +/// An array with copy-on-write behavior. +/// +/// An `ArrayCow` represents either a uniquely owned array or a view of an +/// array. The `'a` corresponds to the lifetime of the view variant. +/// +/// Array views have all the methods of an array (see [`ArrayBase`][ab]). +/// +/// See also [`ArcArray`](type.ArcArray.html), which also provides +/// copy-on-write behavior but has a reference-counted pointer to the data +/// instead of either a view or a uniquely owned copy. +/// +/// [ab]: struct.ArrayBase.html +pub type ArrayCow<'a, A, D> = ArrayBase, D>; + /// A read-only array view. /// /// An array view represents an array or a part of it, created from @@ -1374,16 +1389,12 @@ impl ViewRepr { } } -pub enum CowRepr<'a, A> - where A: Clone -{ +pub enum CowRepr<'a, A> { View(ViewRepr<&'a A>), Owned(OwnedRepr), } -impl<'a, A> CowRepr<'a, A> - where A: Clone -{ +impl<'a, A> CowRepr<'a, A> { pub fn is_view(&self) -> bool { match self { CowRepr::View(_) => true, @@ -1396,8 +1407,6 @@ impl<'a, A> CowRepr<'a, A> } } -pub type ArrayCow<'a, A, D> = ArrayBase, D>; - mod impl_clone; mod impl_constructors; @@ -1496,69 +1505,6 @@ impl ArrayBase } } -impl<'a, A, D> ArrayCow<'a, A, D> - where A: Clone, - D: Dimension -{ - fn from_view_array(array: ArrayView<'a, A, D>) -> ArrayCow<'a, A, D> { - ArrayBase { - data: CowRepr::View(array.data), - ptr: array.ptr, - dim: array.dim, - strides: array.strides, - } - } - - fn from_owned_array(array: Array) -> ArrayCow<'a, A, D> { - ArrayBase { - data: CowRepr::Owned(array.data), - ptr: array.ptr, - dim: array.dim, - strides: array.strides, - } - } - - fn into_view_array(self) -> Option> { - match self.data { - CowRepr::View(view) => Some(ArrayBase { - data: view, - ptr: self.ptr, - dim: self.dim, - strides: self.strides, - }), - CowRepr::Owned(_) => None, - } - } - - fn into_owned_array(self) -> Option> { - match self.data { - CowRepr::View(_) => None, - CowRepr::Owned(data) => Some(ArrayBase { - data, - ptr: self.ptr, - dim: self.dim, - strides: self.strides, - }) - } - } - - fn ensure_is_owned(&mut self) { - if self.data.is_view() { - let mut copied_data: Vec = self.iter().map(|x| x.clone()).collect(); - self.ptr = copied_data.as_mut_ptr(); - self.data = CowRepr::Owned(OwnedRepr(copied_data)); - } - } - - pub fn is_view(&self) -> bool { - self.data.is_view() - } - - pub fn is_owned(&self) -> bool { - self.data.is_owned() - } -} - // parallel methods #[cfg(feature="rayon")] @@ -1581,6 +1527,9 @@ mod impl_views; // Array raw view methods mod impl_raw_views; +// Copy-on-write array methods +mod impl_cow; + /// A contiguous array shape of n dimensions. /// /// Either c- or f- memory ordered (*c* a.k.a *row major* is the default). diff --git a/src/prelude.rs b/src/prelude.rs index 80c98e34c..25ab3ae65 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -25,6 +25,7 @@ pub use crate::{ Array, ArcArray, RcArray, + ArrayCow, ArrayView, ArrayViewMut, RawArrayView,