Skip to content

Commit 36aa2be

Browse files
jturner314bluss
authored andcommitted
Add try_into_owned_nocopy method
1 parent 493674d commit 36aa2be

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

src/data_traits.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@ pub unsafe trait Data: RawData {
109109
Self::Elem: Clone,
110110
D: Dimension;
111111

112+
/// Converts the array into `Array<A, D>` if this is possible without
113+
/// cloning the array elements. Otherwise, returns `self_` unchanged.
114+
#[doc(hidden)]
115+
fn try_into_owned_nocopy<D>(
116+
self_: ArrayBase<Self, D>,
117+
) -> Result<ArrayBase<OwnedRepr<Self::Elem>, D>, ArrayBase<Self, D>>
118+
where
119+
D: Dimension;
120+
112121
/// Return a shared ownership (copy on write) array based on the existing one,
113122
/// cloning elements if necessary.
114123
#[doc(hidden)]
@@ -276,6 +285,27 @@ unsafe impl<A> Data for OwnedArcRepr<A> {
276285
}
277286
}
278287

288+
fn try_into_owned_nocopy<D>(
289+
self_: ArrayBase<Self, D>,
290+
) -> Result<ArrayBase<OwnedRepr<Self::Elem>, D>, ArrayBase<Self, D>>
291+
where
292+
D: Dimension,
293+
{
294+
match Arc::try_unwrap(self_.data.0) {
295+
Ok(owned_data) => unsafe {
296+
// Safe because the data is equivalent.
297+
Ok(ArrayBase::from_data_ptr(owned_data, self_.ptr)
298+
.with_strides_dim(self_.strides, self_.dim))
299+
},
300+
Err(arc_data) => unsafe {
301+
// Safe because the data is equivalent; we're just
302+
// reconstructing `self_`.
303+
Err(ArrayBase::from_data_ptr(OwnedArcRepr(arc_data), self_.ptr)
304+
.with_strides_dim(self_.strides, self_.dim))
305+
},
306+
}
307+
}
308+
279309
#[allow(clippy::wrong_self_convention)]
280310
fn to_shared<D>(self_: &ArrayBase<Self, D>) -> ArrayBase<OwnedArcRepr<Self::Elem>, D>
281311
where
@@ -337,6 +367,16 @@ unsafe impl<A> Data for OwnedRepr<A> {
337367
{
338368
self_
339369
}
370+
371+
#[inline]
372+
fn try_into_owned_nocopy<D>(
373+
self_: ArrayBase<Self, D>,
374+
) -> Result<ArrayBase<Self, D>, ArrayBase<Self, D>>
375+
where
376+
D: Dimension,
377+
{
378+
Ok(self_)
379+
}
340380
}
341381

342382
unsafe impl<A> DataMut for OwnedRepr<A> {}
@@ -393,6 +433,15 @@ unsafe impl<'a, A> Data for ViewRepr<&'a A> {
393433
{
394434
self_.to_owned()
395435
}
436+
437+
fn try_into_owned_nocopy<D>(
438+
self_: ArrayBase<Self, D>,
439+
) -> Result<ArrayBase<OwnedRepr<Self::Elem>, D>, ArrayBase<Self, D>>
440+
where
441+
D: Dimension,
442+
{
443+
Err(self_)
444+
}
396445
}
397446

398447
unsafe impl<'a, A> RawDataClone for ViewRepr<&'a A> {
@@ -438,6 +487,15 @@ unsafe impl<'a, A> Data for ViewRepr<&'a mut A> {
438487
{
439488
self_.to_owned()
440489
}
490+
491+
fn try_into_owned_nocopy<D>(
492+
self_: ArrayBase<Self, D>,
493+
) -> Result<ArrayBase<OwnedRepr<Self::Elem>, D>, ArrayBase<Self, D>>
494+
where
495+
D: Dimension,
496+
{
497+
Err(self_)
498+
}
441499
}
442500

443501
unsafe impl<'a, A> DataMut for ViewRepr<&'a mut A> {}
@@ -609,6 +667,22 @@ unsafe impl<'a, A> Data for CowRepr<'a, A> {
609667
},
610668
}
611669
}
670+
671+
fn try_into_owned_nocopy<D>(
672+
self_: ArrayBase<Self, D>,
673+
) -> Result<ArrayBase<OwnedRepr<Self::Elem>, D>, ArrayBase<Self, D>>
674+
where
675+
D: Dimension,
676+
{
677+
match self_.data {
678+
CowRepr::View(_) => Err(self_),
679+
CowRepr::Owned(data) => unsafe {
680+
// safe because the data is equivalent so ptr, dims remain valid
681+
Ok(ArrayBase::from_data_ptr(data, self_.ptr)
682+
.with_strides_dim(self_.strides, self_.dim))
683+
},
684+
}
685+
}
612686
}
613687

614688
unsafe impl<'a, A> DataMut for CowRepr<'a, A> where A: Clone {}

src/impl_methods.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,34 @@ where
241241
S::into_owned(self)
242242
}
243243

244+
/// Converts the array into `Array<A, D>` if this is possible without
245+
/// cloning the array elements. Otherwise, returns `self` unchanged.
246+
///
247+
/// ```
248+
/// use ndarray::{array, rcarr2, ArcArray2, Array2};
249+
///
250+
/// // Reference-counted, clone-on-write `ArcArray`.
251+
/// let a: ArcArray2<_> = rcarr2(&[[1., 2.], [3., 4.]]);
252+
/// {
253+
/// // Another reference to the same data.
254+
/// let b: ArcArray2<_> = a.clone();
255+
/// // Since there are two references to the same data, `.into_owned()`
256+
/// // would require cloning the data, so `.try_into_owned_nocopy()`
257+
/// // returns `Err`.
258+
/// assert!(b.try_into_owned_nocopy().is_err());
259+
/// }
260+
/// // Here, since the second reference has been dropped, the `ArcArray`
261+
/// // can be converted into an `Array` without cloning the data.
262+
/// let unique: Array2<_> = a.try_into_owned_nocopy().unwrap();
263+
/// assert_eq!(unique, array![[1., 2.], [3., 4.]]);
264+
/// ```
265+
pub fn try_into_owned_nocopy(self) -> Result<Array<A, D>, Self>
266+
where
267+
S: Data,
268+
{
269+
S::try_into_owned_nocopy(self)
270+
}
271+
244272
/// Turn the array into a shared ownership (copy on write) array,
245273
/// without any copying.
246274
pub fn into_shared(self) -> ArcArray<A, D>

0 commit comments

Comments
 (0)