use std::sync::Arc;
use crate::dimension::Dimension;
use crate::dtype::Element;
use crate::layout::MemoryLayout;
use super::ArrayFlags;
use super::owned::Array;
use super::view::ArrayView;
pub struct ArcArray<T: Element, D: Dimension> {
data: Arc<Vec<T>>,
dim: D,
strides: Vec<isize>,
offset: usize,
}
impl<T: Element, D: Dimension> ArcArray<T, D> {
pub fn from_owned(arr: Array<T, D>) -> Self {
let dim = arr.dim.clone();
let original_strides: Vec<isize> = arr.inner.strides().to_vec();
if arr.inner.is_standard_layout() {
let data = arr.inner.into_raw_vec_and_offset().0;
Self {
data: Arc::new(data),
dim,
strides: original_strides,
offset: 0,
}
} else {
let shape = dim.as_slice();
let is_f = crate::layout::detect_layout(shape, &original_strides)
== crate::layout::MemoryLayout::Fortran;
if is_f {
let data = arr.inner.into_raw_vec_and_offset().0;
Self {
data: Arc::new(data),
dim,
strides: original_strides,
offset: 0,
}
} else {
let contiguous = arr.inner.as_standard_layout().into_owned();
let data = contiguous.into_raw_vec_and_offset().0;
let strides = compute_c_strides(shape);
Self {
data: Arc::new(data),
dim,
strides,
offset: 0,
}
}
}
}
#[inline]
pub fn shape(&self) -> &[usize] {
self.dim.as_slice()
}
#[inline]
pub fn ndim(&self) -> usize {
self.dim.ndim()
}
#[inline]
pub fn size(&self) -> usize {
self.dim.size()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.size() == 0
}
#[inline]
pub fn strides(&self) -> &[isize] {
&self.strides
}
pub fn layout(&self) -> MemoryLayout {
crate::layout::detect_layout(self.dim.as_slice(), &self.strides)
}
#[inline]
pub const fn dim(&self) -> &D {
&self.dim
}
pub fn ref_count(&self) -> usize {
Arc::strong_count(&self.data)
}
pub fn is_unique(&self) -> bool {
Arc::strong_count(&self.data) == 1
}
pub fn as_slice(&self) -> &[T] {
&self.data[self.offset..self.offset + self.size()]
}
#[inline]
pub fn as_ptr(&self) -> *const T {
self.as_slice().as_ptr()
}
pub fn view(&self) -> ArrayView<'_, T, D> {
let nd_dim = self.dim.to_ndarray_dim();
let slice = self.as_slice();
let nd_view = ndarray::ArrayView::from_shape(nd_dim, slice)
.expect("ArcArray data should be consistent with shape");
ArrayView::from_ndarray(nd_view)
}
fn make_unique(&mut self) {
if Arc::strong_count(&self.data) > 1 {
let slice = &self.data[self.offset..self.offset + self.size()];
self.data = Arc::new(slice.to_vec());
self.offset = 0;
}
}
pub fn as_slice_mut(&mut self) -> &mut [T] {
self.make_unique();
let size = self.size();
let offset = self.offset;
Arc::get_mut(&mut self.data)
.expect("make_unique should ensure refcount == 1")
.get_mut(offset..offset + size)
.expect("offset + size should be in bounds")
}
pub fn mapv_inplace(&mut self, f: impl Fn(T) -> T) {
self.make_unique();
let size = self.size();
let offset = self.offset;
let data = Arc::get_mut(&mut self.data).expect("unique after make_unique");
for elem in &mut data[offset..offset + size] {
*elem = f(elem.clone());
}
}
pub fn into_owned(self) -> Array<T, D> {
let data: Vec<T> = if self.offset == 0 && self.data.len() == self.size() {
match Arc::try_unwrap(self.data) {
Ok(v) => v,
Err(arc) => arc[..].to_vec(),
}
} else {
self.data[self.offset..self.offset + self.size()].to_vec()
};
Array::from_vec(self.dim, data).expect("data should match shape")
}
#[must_use]
pub fn copy(&self) -> Self {
let data = self.as_slice().to_vec();
Self {
data: Arc::new(data),
dim: self.dim.clone(),
strides: self.strides.clone(),
offset: 0,
}
}
pub fn flags(&self) -> ArrayFlags {
let layout = self.layout();
ArrayFlags {
c_contiguous: layout.is_c_contiguous(),
f_contiguous: layout.is_f_contiguous(),
owndata: true, writeable: true,
aligned: true,
}
}
}
impl<T: Element, D: Dimension> Clone for ArcArray<T, D> {
fn clone(&self) -> Self {
Self {
data: Arc::clone(&self.data),
dim: self.dim.clone(),
strides: self.strides.clone(),
offset: self.offset,
}
}
}
impl<T: Element, D: Dimension> From<Array<T, D>> for ArcArray<T, D> {
fn from(arr: Array<T, D>) -> Self {
Self::from_owned(arr)
}
}
fn compute_c_strides(shape: &[usize]) -> Vec<isize> {
let ndim = shape.len();
if ndim == 0 {
return vec![];
}
let mut strides = vec![0isize; ndim];
strides[ndim - 1] = 1;
for i in (0..ndim - 1).rev() {
strides[i] = strides[i + 1] * shape[i + 1] as isize;
}
strides
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dimension::{Ix1, Ix2};
#[test]
fn arc_from_owned() {
let arr = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
let arc = ArcArray::from_owned(arr);
assert_eq!(arc.shape(), &[3]);
assert_eq!(arc.as_slice(), &[1.0, 2.0, 3.0]);
assert_eq!(arc.ref_count(), 1);
}
#[test]
fn arc_clone_shares() {
let arr = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
let arc1 = ArcArray::from_owned(arr);
let arc2 = arc1.clone();
assert_eq!(arc1.ref_count(), 2);
assert_eq!(arc2.ref_count(), 2);
assert_eq!(arc1.as_ptr(), arc2.as_ptr());
}
#[test]
fn arc_cow_on_mutation() {
let arr = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
let arc1 = ArcArray::from_owned(arr);
let mut arc2 = arc1.clone();
assert_eq!(arc1.as_ptr(), arc2.as_ptr());
assert_eq!(arc1.ref_count(), 2);
arc2.as_slice_mut()[0] = 99.0;
assert_ne!(arc1.as_ptr(), arc2.as_ptr());
assert_eq!(arc1.as_slice(), &[1.0, 2.0, 3.0]);
assert_eq!(arc2.as_slice(), &[99.0, 2.0, 3.0]);
assert_eq!(arc1.ref_count(), 1);
assert_eq!(arc2.ref_count(), 1);
}
#[test]
fn arc_view_sees_old_data_after_cow() {
let arr = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
let mut arc = ArcArray::from_owned(arr);
let arc_clone = arc.clone();
let view = arc_clone.view();
assert_eq!(view.as_slice().unwrap(), &[1.0, 2.0, 3.0]);
arc.as_slice_mut()[0] = 99.0;
assert_eq!(view.as_slice().unwrap(), &[1.0, 2.0, 3.0]);
assert_eq!(arc.as_slice(), &[99.0, 2.0, 3.0]);
}
#[test]
fn arc_unique_no_clone() {
let arr = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
let mut arc = ArcArray::from_owned(arr);
let ptr_before = arc.as_ptr();
arc.as_slice_mut()[0] = 99.0;
assert_eq!(arc.as_ptr(), ptr_before);
assert_eq!(arc.as_slice(), &[99.0, 2.0, 3.0]);
}
#[test]
fn arc_into_owned() {
let arr = Array::<f64, Ix2>::from_vec(Ix2::new([2, 3]), vec![1.0; 6]).unwrap();
let arc = ArcArray::from_owned(arr);
let owned = arc.into_owned();
assert_eq!(owned.shape(), &[2, 3]);
}
#[test]
fn arc_mapv_inplace() {
let arr = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
let mut arc = ArcArray::from_owned(arr);
arc.mapv_inplace(|x| x * 2.0);
assert_eq!(arc.as_slice(), &[2.0, 4.0, 6.0]);
}
#[test]
fn arc_copy_is_independent() {
let arr = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
let arc = ArcArray::from_owned(arr);
let copy = arc.copy();
assert_ne!(arc.as_ptr(), copy.as_ptr());
assert_eq!(arc.ref_count(), 1); assert_eq!(copy.ref_count(), 1);
}
#[test]
fn arc_from_owned_after_transpose_is_standard_layout() {
use crate::dimension::Ix2;
let arr = Array::<f64, Ix2>::from_vec(Ix2::new([2, 3]), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
.unwrap();
let transposed = crate::manipulation::transpose(&arr, None).unwrap();
assert_eq!(transposed.shape(), &[3, 2]);
let arc = ArcArray::<f64, crate::dimension::IxDyn>::from_owned(transposed);
assert_eq!(arc.as_slice(), &[1.0, 4.0, 2.0, 5.0, 3.0, 6.0]);
}
#[test]
fn arc_from_owned_with_broadcast_to_materializes_data() {
use crate::dimension::Ix1;
let a = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
let b = crate::manipulation::broadcast_to(&a, &[2, 3]).unwrap();
assert_eq!(b.shape(), &[2, 3]);
let arc = ArcArray::<f64, crate::dimension::IxDyn>::from_owned(b);
assert_eq!(arc.as_slice(), &[1.0, 2.0, 3.0, 1.0, 2.0, 3.0]);
}
}