Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add raw array pointer types #496

Merged
merged 10 commits into from Nov 29, 2018
257 changes: 198 additions & 59 deletions src/data_traits.rs
Expand Up @@ -14,6 +14,7 @@ use std::sync::Arc;
use {
ArrayBase,
Dimension,
RawViewRepr,
ViewRepr,
OwnedRepr,
OwnedRcRepr,
Expand All @@ -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<D>(&mut ArrayBase<Self, D>)
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<bool>;
}

/// 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<D>(self_: ArrayBase<Self, D>) -> ArrayBase<OwnedRepr<Self::Elem>, 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<D>(&mut ArrayBase<Self, D>)
where Self: Sized,
D: Dimension
{ }
fn ensure_unique<D>(self_: &mut ArrayBase<Self, D>)
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<T> DataClone for T where T: Data + RawDataClone {}

unsafe impl<A> RawData for RawViewRepr<*const A> {
type Elem = A;
fn _data_slice(&self) -> Option<&[A]> {
None
}
private_impl!{}
}

unsafe impl<A> Data for OwnedArcRepr<A> {
unsafe impl<A> RawDataClone for RawViewRepr<*const A> {
unsafe fn clone_with_ptr(&self, ptr: *mut Self::Elem) -> (Self, *mut Self::Elem) {
(*self, ptr)
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can be without this trait, and just use DataClone? Clone is the only consumer of the trait

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In current ndarray, DataClone has a Data bound. If we want to be able to clone ArrayPtr/ArrayPtrMut, this is too restrictive given only the current implementation of Clone for ArrayBase. Possible alternatives to the approach in this PR:

  1. Don't add a DataRawClone trait; instead, implement Clone individually for ArrayPtr and ArrayPtrMut. That would be inconsistent and would prevent code with just a DataClone bound from being able to clone ArrayPtr and ArrayPtrMut.

    With a breaking change, though, we could get rid of DataClone entirely and just implement Clone individually for ArcArray, Array, ArrayView, ArrayPtr, and ArrayPtrMut. This actually seems pretty nice, but it does make writing generic code more difficult. (In user code, the bound to allow array.clone() would have to be ArrayBase<S, D>: Clone instead of S: DataRawClone.)

  2. Add the DataRawClone trait and remove the DataClone trait. That would work but would be a breaking change.

Fwiw, DataClone does have a small amount of utility as the combination of Data and DataRawClone, but I agree it does seem like unnecessary complication.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(2) I think a breaking change is ok. We have lots of them queued? We should get on to merging them as soon as 0.12.1 is released (and I can do the releasing if you want).

I'd hope it has very little impact on users, but maybe I underestimate how much these representation traits are used?

I'd use the name DataClone for the new trait.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use the name DataClone for the new trait.

The only thing that I don't like about this is consistency of naming with the other data traits, especially DataMut vs. DataRawMut. I was naming traits without a Data bound DataRaw* and traits with a Data bound Data*.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. We still should use only one trait if we don't need 2, we could use the name DataRawClone and let DataClone be a deprecated reexport of it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DataClone could be a trait alias of DataRawClone + Data? That's a new Rust feature we can transition to.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woah, I had no idea trait aliases were a thing. They look very convenient.

I've added a commit deprecating DataClone. We can switch DataClone to a trait alias once they're available on stable.


unsafe impl<A> 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<D>(mut self_: ArrayBase<Self, D>) -> ArrayBase<OwnedRepr<Self::Elem>, 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<A> RawDataMut for RawViewRepr<*mut A> {
#[inline]
fn try_ensure_unique<D>(_: &mut ArrayBase<Self, D>)
where Self: Sized,
D: Dimension
{}

#[inline]
fn try_is_unique(&mut self) -> Option<bool> {
None
}
}

unsafe impl<A> RawDataClone for RawViewRepr<*mut A> {
unsafe fn clone_with_ptr(&self, ptr: *mut Self::Elem) -> (Self, *mut Self::Elem) {
(*self, ptr)
}
}

unsafe impl<A> RawData for OwnedArcRepr<A> {
type Elem = A;
fn _data_slice(&self) -> Option<&[A]> {
Some(&self.0)
}
private_impl!{}
}

// NOTE: Copy on write
unsafe impl<A> DataMut for OwnedArcRepr<A>
where A: Clone
unsafe impl<A> RawDataMut for OwnedArcRepr<A>
where
A: Clone,
{
fn ensure_unique<D>(self_: &mut ArrayBase<Self, D>)
fn try_ensure_unique<D>(self_: &mut ArrayBase<Self, D>)
where Self: Sized,
D: Dimension
{
Expand Down Expand Up @@ -136,23 +222,59 @@ unsafe impl<A> DataMut for OwnedArcRepr<A>
}
}

fn is_unique(&mut self) -> bool {
Arc::get_mut(&mut self.0).is_some()
fn try_is_unique(&mut self) -> Option<bool> {
Some(Arc::get_mut(&mut self.0).is_some())
}
}

unsafe impl<A> DataClone for OwnedArcRepr<A> {
unsafe impl<A> Data for OwnedArcRepr<A> {
fn into_owned<D>(mut self_: ArrayBase<Self, D>) -> ArrayBase<OwnedRepr<Self::Elem>, 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<A> DataMut for OwnedArcRepr<A> where A: Clone {}

unsafe impl<A> RawDataClone for OwnedArcRepr<A> {
unsafe fn clone_with_ptr(&self, ptr: *mut Self::Elem) -> (Self, *mut Self::Elem) {
// pointer is preserved
(self.clone(), ptr)
}
}

unsafe impl<A> Data for OwnedRepr<A> {
unsafe impl<A> RawData for OwnedRepr<A> {
type Elem = A;
fn _data_slice(&self) -> &[A] {
&self.0
fn _data_slice(&self) -> Option<&[A]> {
Some(&self.0)
}
private_impl!{}
}

unsafe impl<A> RawDataMut for OwnedRepr<A> {
#[inline]
fn try_ensure_unique<D>(_: &mut ArrayBase<Self, D>)
where Self: Sized,
D: Dimension
{}

#[inline]
fn try_is_unique(&mut self) -> Option<bool> {
Some(true)
}
}

unsafe impl<A> Data for OwnedRepr<A> {
#[inline]
fn into_owned<D>(self_: ArrayBase<Self, D>) -> ArrayBase<OwnedRepr<Self::Elem>, D>
where
Expand All @@ -161,12 +283,11 @@ unsafe impl<A> Data for OwnedRepr<A> {
{
self_
}
private_impl!{}
}

unsafe impl<A> DataMut for OwnedRepr<A> { }

unsafe impl<A> DataClone for OwnedRepr<A>
unsafe impl<A> RawDataClone for OwnedRepr<A>
where A: Clone
{
unsafe fn clone_with_ptr(&self, ptr: *mut Self::Elem) -> (Self, *mut Self::Elem) {
Expand All @@ -192,40 +313,58 @@ unsafe impl<A> DataClone for OwnedRepr<A>
}
}

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<D>(self_: ArrayBase<Self, D>) -> ArrayBase<OwnedRepr<Self::Elem>, D>
where
Self::Elem: Clone,
D: Dimension,
{
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<D>(_: &mut ArrayBase<Self, D>)
where Self: Sized,
D: Dimension {}

#[inline]
fn try_is_unique(&mut self) -> Option<bool> {
Some(true)
}
}

unsafe impl<'a, A> Data for ViewRepr<&'a mut A> {
fn into_owned<D>(self_: ArrayBase<Self, D>) -> ArrayBase<OwnedRepr<Self::Elem>, D>
where
Self::Elem: Clone,
D: Dimension,
{
self_.to_owned()
}
private_impl!{}
}

unsafe impl<'a, A> DataMut for ViewRepr<&'a mut A> { }
Expand All @@ -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<A> DataShared for OwnedRcRepr<A> {}
unsafe impl<'a, A> DataShared for ViewRepr<&'a A> {}
Expand Down