forked from rust-ndarray/ndarray
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Moves towards a reference-based implementation.
Provides a new `core` module that lays out a new internal design, and create aliases of the original `ndarray` types using that module. The module can eventually be moved into its own crate.
- Loading branch information
Showing
5 changed files
with
422 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
//! The core types of `ndarray`, redefined in terms of the new infrastructure. | ||
//! | ||
//! The idea is partially that the `core` module / folder could be extracted | ||
//! into its own crate, but these types and implementations would remain here. | ||
|
||
mod backend; | ||
mod array; | ||
mod arrayref; | ||
|
||
use core::{mem, ptr::NonNull}; | ||
use std::sync::Arc; | ||
|
||
pub use array::*; | ||
pub use arrayref::*; | ||
pub use backend::*; | ||
|
||
use crate::Dimension; | ||
|
||
/// Type alias for a dynamically-allocated, unique backend. | ||
pub type VecBackend<T> = (VecOwner<T>, NonNull<T>); | ||
|
||
/// This type should act almost entirely equivalently to the existing `Array` | ||
pub type Array<A, D> = OwnedBase<D, VecBackend<A>>; | ||
|
||
/// Completely equivalent to [`crate::OwnedRepr`]! | ||
pub struct VecOwner<T> | ||
{ | ||
ptr: NonNull<T>, | ||
len: usize, | ||
cap: usize, | ||
} | ||
|
||
/// The glue implementation for this backend. | ||
unsafe impl<E> Backend for VecBackend<E> | ||
{ | ||
type Elem = E; | ||
|
||
type Ref = NonNull<E>; | ||
|
||
type Owned = VecOwner<E>; | ||
|
||
fn ref_from_owner_offset(owner: &Self::Owned, offset: isize) -> Self::Ref | ||
{ | ||
todo!("unsafe {{ owner.ptr.offset(offset) }}") | ||
} | ||
} | ||
|
||
/// A very simple Uniqueable, since this type is always unique. | ||
unsafe impl<A, D> Uniqueable for Array<A, D> | ||
{ | ||
fn try_ensure_unique(&mut self) {} | ||
|
||
fn try_is_unique(&self) -> Option<bool> | ||
{ | ||
Some(true) | ||
} | ||
} | ||
|
||
/// A blanket NdArray implementation for any backend whose reference type is `NonNull<A>`. | ||
/// | ||
/// As described in the `arrayref` module of `core`, this kind of reference-focused `impl` | ||
/// is very ergonomic and makes sense, since most behavior relies on the reference type, | ||
/// not the owned type. | ||
unsafe impl<L, B, A> NdArray<B> for OwnedBase<L, B> | ||
where B: Backend<Ref = NonNull<A>, Elem = A> | ||
{ | ||
fn as_ptr(&self) -> *mut <B as Backend>::Elem | ||
{ | ||
self.aref.storage.as_ptr() | ||
} | ||
} | ||
|
||
/// And now our Arc-based backend | ||
pub type ArcBackend<T> = (Arc<VecOwner<T>>, NonNull<T>); | ||
|
||
/// Again, should be almost entirely equivalent to the existing `ArcArray` | ||
pub type ArcArray<A, D> = OwnedBase<D, ArcBackend<A>>; | ||
|
||
/// A simple backend implementation | ||
unsafe impl<E> Backend for ArcBackend<E> | ||
{ | ||
type Elem = E; | ||
|
||
type Ref = NonNull<E>; | ||
|
||
type Owned = Arc<VecOwner<E>>; | ||
|
||
fn ref_from_owner_offset(owner: &Self::Owned, offset: isize) -> Self::Ref | ||
{ | ||
todo!("unsafe {{ owner.as_ref().ptr.offset(isize) }}") | ||
} | ||
} | ||
|
||
/// Uniqueable implementation, with the uniqueness logic stolen from what already exists | ||
unsafe impl<A, D: Dimension> Uniqueable for ArcArray<A, D> | ||
{ | ||
fn try_ensure_unique(&mut self) | ||
{ | ||
if Arc::get_mut(&mut self.own).is_some() { | ||
return; | ||
} | ||
if self.layout.size() <= self.own.len / 2 { | ||
todo!(".to_owned().to_shared()"); | ||
} | ||
let ptr = self.as_ptr(); | ||
let rcvec = &mut self.own; | ||
let a_size = mem::size_of::<A>() as isize; | ||
let our_off = if a_size != 0 { | ||
(ptr as isize - Arc::as_ptr(&rcvec) as isize) / a_size | ||
} else { | ||
0 | ||
}; | ||
self.storage = ArcBackend::<A>::ref_from_owner_offset(&mut self.own, our_off); | ||
} | ||
|
||
/// We're using strong count here so that we don't need &mut self. | ||
/// This is not as strong as the original `try_is_unique`, but it doesn't matter. | ||
/// The original does not hold on to the mutable reference provided by [`Arc::get_mut`], | ||
/// so the moment the call is over the uniqueness guarantee does not hold. | ||
/// This is a weaker guarantee of instantaneous uniqueness, but neither this nor the | ||
/// original implementation are good enough to safely do anything with that uniqueness anyway. | ||
fn try_is_unique(&self) -> Option<bool> | ||
{ | ||
Some(Arc::strong_count(&self.own) == 1) | ||
} | ||
} | ||
|
||
/// These aliases work for a view into any array whose reference type is `NonNull`: | ||
|
||
pub type ArrayView<'a, A, D> = ViewBase<'a, D, NonNull<A>, A>; | ||
pub type ArrayViewMut<'a, A, D> = ViewBaseMut<'a, D, NonNull<A>, A>; | ||
pub type RawArrayView<A, D> = RawViewBase<D, NonNull<A>, A>; | ||
pub type RawArrayViewMut<A, D> = RawViewBaseMut<D, NonNull<A>, A>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
//! Owning array types. | ||
|
||
use core::{fmt::Debug, marker::PhantomData}; | ||
|
||
use super::{ | ||
arrayref::{RawRefBase, RefBase}, | ||
backend::Backend, | ||
}; | ||
|
||
/// Base type for arrays with owning semantics. | ||
pub struct OwnedBase<L, B: Backend> | ||
{ | ||
pub(super) aref: RefBase<L, B::Ref>, | ||
pub(super) own: B::Owned, | ||
} | ||
|
||
/// Base type for array views. | ||
/// | ||
/// Views are like references; in fact, they're just wrappers for references. | ||
/// The difference is that a reference's layout must be identical to the array | ||
/// from which is has been derived; a view's layout may be different, representing | ||
/// a segment, strided, or otherwise incomplete look at its parent array. | ||
#[derive(Debug)] | ||
pub struct ViewBase<'a, L, R, E> | ||
{ | ||
aref: RefBase<L, R>, | ||
life: PhantomData<&'a E>, | ||
} | ||
|
||
/// Base type for array views with mutable data. | ||
/// | ||
/// All kinds of views can have their layout mutated. However, data mutation | ||
/// is tracked separately via two different types. | ||
#[derive(Debug)] | ||
pub struct ViewBaseMut<'a, L, R, E> | ||
{ | ||
aref: RefBase<L, R>, | ||
life: PhantomData<&'a mut E>, | ||
} | ||
|
||
/// Base type for array views without lifetimes. | ||
#[derive(Debug)] | ||
pub struct RawViewBase<L, R, E> | ||
{ | ||
rref: RawRefBase<L, R>, | ||
life: PhantomData<*const E>, | ||
} | ||
|
||
/// Base type for array views without lifetimes, but with mutable data. | ||
#[derive(Debug)] | ||
pub struct RawViewBaseMut<L, R, E> | ||
{ | ||
rref: RawRefBase<L, R>, | ||
life: PhantomData<*const E>, | ||
} | ||
|
||
/// A trait for arrays with mutable data that can be made unique. | ||
/// | ||
/// Essentially all monomorphizations of [`OwnedBase`], [`ViewBaseMut`], | ||
/// and [`RawViewBaseMut`] should implement Uniqueable; this applies even | ||
/// when the array type does not have any data sharing capabilities. | ||
/// | ||
/// There are already blanket implementations for `ViewBaseMut` and | ||
/// `RawViewBaseMut`; as a result, any creation of these types | ||
/// _must_ ensure that the underlying data is unique (a.k.a, unshared) | ||
/// before creating these mutable views. | ||
pub unsafe trait Uniqueable | ||
{ | ||
fn try_ensure_unique(&mut self); | ||
|
||
fn try_is_unique(&self) -> Option<bool>; | ||
} | ||
|
||
/// Trait implemented by all the non-reference array types. | ||
/// | ||
/// We'll probably want to split trait into multiple trait, for three reasons: | ||
/// 1. Adding a "Raw" and "Mut" variants will likely be necessary | ||
/// 2. We probably don't want a single trait specified by backend, but instead traits | ||
/// that are specified by reference type, owned type, and backend separately. | ||
/// This allows for blanket implementations that would otherwise be annoying. | ||
/// 3. We may want to add subtraits that break the behavior into logical pieces, | ||
/// instead of having a monolith. | ||
pub unsafe trait NdArray<B: Backend> | ||
{ | ||
fn as_ptr(&self) -> *mut B::Elem; | ||
} | ||
|
||
mod array_impls | ||
{ | ||
use core::fmt::Debug; | ||
use core::ops::{Deref, DerefMut}; | ||
|
||
use crate::core::{Backend, RawRefBase, RefBase}; | ||
|
||
use super::{OwnedBase, RawViewBase, RawViewBaseMut, Uniqueable, ViewBase, ViewBaseMut}; | ||
|
||
impl<L, B: Backend> Deref for OwnedBase<L, B> | ||
{ | ||
type Target = RefBase<L, B::Ref>; | ||
|
||
fn deref(&self) -> &Self::Target | ||
{ | ||
&self.aref | ||
} | ||
} | ||
|
||
impl<L, B: Backend> DerefMut for OwnedBase<L, B> | ||
where Self: Uniqueable | ||
{ | ||
fn deref_mut(&mut self) -> &mut Self::Target | ||
{ | ||
self.try_ensure_unique(); | ||
&mut self.aref | ||
} | ||
} | ||
|
||
impl<'a, L, R, E> Deref for ViewBase<'a, L, R, E> | ||
{ | ||
type Target = RefBase<L, R>; | ||
|
||
fn deref(&self) -> &Self::Target | ||
{ | ||
&self.aref | ||
} | ||
} | ||
|
||
impl<'a, L, R, E> Deref for ViewBaseMut<'a, L, R, E> | ||
{ | ||
type Target = RefBase<L, R>; | ||
|
||
fn deref(&self) -> &Self::Target | ||
{ | ||
&self.aref | ||
} | ||
} | ||
|
||
impl<'a, L, R, E> DerefMut for ViewBaseMut<'a, L, R, E> | ||
{ | ||
fn deref_mut(&mut self) -> &mut Self::Target | ||
{ | ||
&mut self.aref | ||
} | ||
} | ||
|
||
impl<L, R, E> Deref for RawViewBase<L, R, E> | ||
{ | ||
type Target = RawRefBase<L, R>; | ||
|
||
fn deref(&self) -> &Self::Target | ||
{ | ||
&self.rref | ||
} | ||
} | ||
|
||
impl<L, R, E> Deref for RawViewBaseMut<L, R, E> | ||
{ | ||
type Target = RawRefBase<L, R>; | ||
|
||
fn deref(&self) -> &Self::Target | ||
{ | ||
&self.rref | ||
} | ||
} | ||
|
||
impl<L, R, E> DerefMut for RawViewBaseMut<L, R, E> | ||
{ | ||
fn deref_mut(&mut self) -> &mut Self::Target | ||
{ | ||
&mut self.rref | ||
} | ||
} | ||
|
||
unsafe impl<'a, L, R, E> Uniqueable for ViewBaseMut<'a, L, R, E> | ||
{ | ||
fn try_ensure_unique(&mut self) {} | ||
|
||
fn try_is_unique(&self) -> Option<bool> | ||
{ | ||
Some(true) | ||
} | ||
} | ||
|
||
unsafe impl<L, R, E> Uniqueable for RawViewBaseMut<L, R, E> | ||
{ | ||
fn try_ensure_unique(&mut self) {} | ||
|
||
fn try_is_unique(&self) -> Option<bool> | ||
{ | ||
None | ||
} | ||
} | ||
|
||
impl<L, B> Debug for OwnedBase<L, B> | ||
where | ||
B: Backend + Debug, | ||
B::Ref: Debug, | ||
B::Owned: Debug, | ||
L: Debug, | ||
{ | ||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result | ||
{ | ||
f.debug_struct("OwnedBase") | ||
.field("aref", &self.aref) | ||
.field("own", &self.own) | ||
.finish() | ||
} | ||
} | ||
} |
Oops, something went wrong.