use crate::error::{JlrsError, JlrsResult};
use crate::traits::{valid_layout::ValidLayout, Cast, Frame, JuliaTypecheck};
use crate::value::datatype::DataType;
use crate::value::Value;
use jl_sys::{
jl_array_data, jl_array_dim, jl_array_dims, jl_array_eltype, jl_array_ndims, jl_array_nrows,
jl_array_ptr_set, jl_array_t, jl_is_array_type, jl_tparam0, jl_typeof,
};
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::marker::PhantomData;
use std::ops::{Index, IndexMut};
#[derive(Copy, Clone, Debug)]
#[repr(transparent)]
pub struct Array<'frame, 'data>(
*mut jl_array_t,
PhantomData<&'frame ()>,
PhantomData<&'data ()>,
);
impl<'frame, 'data> Array<'frame, 'data> {
pub(crate) unsafe fn wrap(array: *mut jl_array_t) -> Self {
Array(array, PhantomData, PhantomData)
}
#[doc(hidden)]
pub unsafe fn ptr(self) -> *mut jl_array_t {
self.0
}
pub fn into_typed_array<T>(self) -> JlrsResult<TypedArray<'frame, 'data, T>>
where
T: Copy + ValidLayout,
{
if self.contains::<T>() {
unsafe { Ok(TypedArray::wrap(self.ptr())) }
} else {
Err(JlrsError::WrongType)?
}
}
pub fn dimensions(self) -> Dimensions {
unsafe { Dimensions::from_array(self.ptr().cast()) }
}
pub fn element_type(self) -> Value<'frame, 'static> {
unsafe { Value::wrap(jl_array_eltype(self.ptr().cast()).cast()) }
}
pub fn contains<T: ValidLayout>(self) -> bool {
unsafe { T::valid_layout(Value::wrap(jl_array_eltype(self.ptr().cast()).cast())) }
}
pub fn contains_inline<T: ValidLayout>(self) -> bool {
self.contains::<T>() && self.is_inline_array()
}
pub fn is_inline_array(self) -> bool {
unsafe { (&*self.ptr()).flags.ptrarray() == 0 }
}
pub fn has_inlined_pointers(self) -> bool {
unsafe {
let flags = (&*self.ptr()).flags;
self.is_inline_array() && flags.hasptr() != 0
}
}
pub fn is_value_array(self) -> bool {
!self.is_inline_array()
}
pub fn copy_inline_data<T>(self) -> JlrsResult<CopiedArray<T>>
where
T: ValidLayout,
{
if !self.contains::<T>() {
Err(JlrsError::WrongType)?;
}
if !self.is_inline_array() {
Err(JlrsError::NotInline)?;
}
unsafe {
let jl_data = jl_array_data(self.ptr().cast()).cast();
let dimensions = Dimensions::from_array(self.ptr().cast());
let sz = dimensions.size();
let mut data = Vec::with_capacity(sz);
let ptr = data.as_mut_ptr();
std::ptr::copy_nonoverlapping(jl_data, ptr, sz);
data.set_len(sz);
Ok(CopiedArray::new(data, dimensions))
}
}
pub fn inline_data<'borrow, 'fr, T, F>(
self,
frame: &'borrow F,
) -> JlrsResult<ArrayData<'borrow, 'fr, T, F>>
where
T: ValidLayout,
F: Frame<'fr>,
{
if !self.contains::<T>() {
Err(JlrsError::WrongType)?;
}
if !self.is_inline_array() {
Err(JlrsError::NotInline)?;
}
unsafe {
let jl_data = jl_array_data(self.ptr().cast()).cast();
let dimensions = Dimensions::from_array(self.ptr().cast());
let data = std::slice::from_raw_parts(jl_data, dimensions.size());
Ok(ArrayData::new(data, dimensions, frame))
}
}
pub fn inline_data_mut<'borrow, 'fr, T, F>(
self,
frame: &'borrow mut F,
) -> JlrsResult<InlineArrayDataMut<'borrow, 'fr, T, F>>
where
T: ValidLayout,
F: Frame<'fr>,
{
if !self.contains::<T>() {
Err(JlrsError::WrongType)?;
}
if !self.is_inline_array() {
Err(JlrsError::NotInline)?;
}
unsafe {
let jl_data = jl_array_data(self.ptr().cast()).cast();
let dimensions = Dimensions::from_array(self.ptr().cast());
let data = std::slice::from_raw_parts_mut(jl_data, dimensions.size());
Ok(InlineArrayDataMut::new(data, dimensions, frame))
}
}
pub unsafe fn unrestricted_inline_data_mut<'borrow, 'fr, T, F>(
self,
frame: &'borrow F,
) -> JlrsResult<UnrestrictedInlineArrayDataMut<'borrow, 'fr, T, F>>
where
T: ValidLayout,
F: Frame<'fr>,
{
if !self.contains::<T>() {
Err(JlrsError::WrongType)?;
}
if !self.is_inline_array() {
Err(JlrsError::NotInline)?;
}
let jl_data = jl_array_data(self.ptr().cast()).cast();
let dimensions = Dimensions::from_array(self.ptr().cast());
let data = std::slice::from_raw_parts_mut(jl_data, dimensions.size());
Ok(UnrestrictedInlineArrayDataMut::new(data, dimensions, frame))
}
pub unsafe fn value_data<'borrow, 'fr, F>(
self,
frame: &'borrow F,
) -> JlrsResult<ArrayData<'borrow, 'fr, Value<'frame, 'data>, F>>
where
F: Frame<'fr>,
{
if !self.is_value_array() {
Err(JlrsError::Inline)?;
}
let jl_data = jl_array_data(self.ptr().cast()).cast();
let dimensions = Dimensions::from_array(self.ptr().cast());
let data = std::slice::from_raw_parts(jl_data, dimensions.size());
Ok(ArrayData::new(data, dimensions, frame))
}
pub unsafe fn value_data_mut<'borrow, 'fr, F>(
self,
frame: &'borrow mut F,
) -> JlrsResult<ValueArrayDataMut<'borrow, 'frame, 'data, 'fr, F>>
where
F: Frame<'fr>,
{
if !self.is_value_array() {
Err(JlrsError::Inline)?;
}
let jl_data = jl_array_data(self.ptr().cast()).cast();
let dimensions = Dimensions::from_array(self.ptr().cast());
let data = std::slice::from_raw_parts_mut(jl_data, dimensions.size());
Ok(ValueArrayDataMut::new(self, data, dimensions, frame))
}
pub unsafe fn unrestricted_value_data_mut<'borrow, 'fr, F>(
self,
frame: &'borrow F,
) -> JlrsResult<UnrestrictedValueArrayDataMut<'borrow, 'frame, 'data, 'fr, F>>
where
F: Frame<'fr>,
{
if !self.is_value_array() {
Err(JlrsError::Inline)?;
}
let jl_data = jl_array_data(self.ptr().cast()).cast();
let dimensions = Dimensions::from_array(self.ptr().cast());
let data = std::slice::from_raw_parts_mut(jl_data, dimensions.size());
Ok(UnrestrictedValueArrayDataMut::new(
self, data, dimensions, frame,
))
}
pub fn as_value(self) -> Value<'frame, 'data> {
self.into()
}
}
unsafe impl<'frame, 'data> JuliaTypecheck for Array<'frame, 'data> {
unsafe fn julia_typecheck(t: DataType) -> bool {
jl_is_array_type(t.ptr().cast())
}
}
impl<'frame, 'data> Into<Value<'frame, 'data>> for Array<'frame, 'data> {
fn into(self) -> Value<'frame, 'data> {
unsafe { Value::wrap(self.ptr().cast()) }
}
}
unsafe impl<'frame, 'data> Cast<'frame, 'data> for Array<'frame, 'data> {
type Output = Self;
fn cast(value: Value<'frame, 'data>) -> JlrsResult<Self::Output> {
if value.is::<Self::Output>() {
return unsafe { Ok(Self::cast_unchecked(value)) };
}
Err(JlrsError::NotAnArray)?
}
unsafe fn cast_unchecked(value: Value<'frame, 'data>) -> Self::Output {
Self::wrap(value.ptr().cast())
}
}
unsafe impl<'frame, 'data> ValidLayout for Array<'frame, 'data> {
unsafe fn valid_layout(v: Value) -> bool {
if let Ok(dt) = v.cast::<DataType>() {
dt.is::<Array>()
} else if let Ok(ua) = v.cast::<super::union_all::UnionAll>() {
ua.base_type().is::<Array>()
} else {
false
}
}
}
#[derive(Copy, Clone, Debug)]
#[repr(transparent)]
pub struct TypedArray<'frame, 'data, T>(
*mut jl_array_t,
PhantomData<&'frame ()>,
PhantomData<&'data ()>,
PhantomData<T>,
)
where
T: Copy + ValidLayout;
impl<'frame, 'data, T: Copy + ValidLayout> TypedArray<'frame, 'data, T> {
pub(crate) unsafe fn wrap(array: *mut jl_array_t) -> Self {
assert!(T::valid_layout(Value::wrap(
jl_array_eltype(array.cast()).cast()
)));
TypedArray(array, PhantomData, PhantomData, PhantomData)
}
#[doc(hidden)]
pub unsafe fn ptr(self) -> *mut jl_array_t {
self.0
}
pub fn dimensions(self) -> Dimensions {
unsafe { Dimensions::from_array(self.ptr().cast()) }
}
pub fn element_type(self) -> Value<'frame, 'static> {
unsafe { Value::wrap(jl_array_eltype(self.ptr().cast()).cast()) }
}
pub fn is_inline_array(self) -> bool {
unsafe { (&*self.ptr()).flags.ptrarray() == 0 }
}
pub fn has_inlined_pointers(self) -> bool {
unsafe {
let flags = (&*self.ptr()).flags;
self.is_inline_array() && flags.hasptr() != 0
}
}
pub fn is_value_array(self) -> bool {
!self.is_inline_array()
}
pub fn copy_inline_data(self) -> JlrsResult<CopiedArray<T>> {
if !self.is_inline_array() {
Err(JlrsError::NotInline)?;
}
unsafe {
let jl_data = jl_array_data(self.ptr().cast()).cast();
let dimensions = Dimensions::from_array(self.ptr().cast());
let sz = dimensions.size();
let mut data = Vec::with_capacity(sz);
let ptr = data.as_mut_ptr();
std::ptr::copy_nonoverlapping(jl_data, ptr, sz);
data.set_len(sz);
Ok(CopiedArray::new(data, dimensions))
}
}
pub fn inline_data<'borrow, 'fr, F>(
self,
frame: &'borrow F,
) -> JlrsResult<ArrayData<'borrow, 'fr, T, F>>
where
F: Frame<'fr>,
{
if !self.is_inline_array() {
Err(JlrsError::NotInline)?;
}
unsafe {
let jl_data = jl_array_data(self.ptr().cast()).cast();
let dimensions = Dimensions::from_array(self.ptr().cast());
let data = std::slice::from_raw_parts(jl_data, dimensions.size());
Ok(ArrayData::new(data, dimensions, frame))
}
}
pub fn inline_data_mut<'borrow, 'fr, F>(
self,
frame: &'borrow mut F,
) -> JlrsResult<InlineArrayDataMut<'borrow, 'fr, T, F>>
where
F: Frame<'fr>,
{
if !self.is_inline_array() {
Err(JlrsError::NotInline)?;
}
unsafe {
let jl_data = jl_array_data(self.ptr().cast()).cast();
let dimensions = Dimensions::from_array(self.ptr().cast());
let data = std::slice::from_raw_parts_mut(jl_data, dimensions.size());
Ok(InlineArrayDataMut::new(data, dimensions, frame))
}
}
pub unsafe fn unrestricted_inline_data_mut<'borrow, 'fr, F>(
self,
frame: &'borrow F,
) -> JlrsResult<UnrestrictedInlineArrayDataMut<'borrow, 'fr, T, F>>
where
F: Frame<'fr>,
{
if !self.is_inline_array() {
Err(JlrsError::NotInline)?;
}
let jl_data = jl_array_data(self.ptr().cast()).cast();
let dimensions = Dimensions::from_array(self.ptr().cast());
let data = std::slice::from_raw_parts_mut(jl_data, dimensions.size());
Ok(UnrestrictedInlineArrayDataMut::new(data, dimensions, frame))
}
pub unsafe fn value_data<'borrow, 'fr, F>(
self,
frame: &'borrow F,
) -> JlrsResult<ArrayData<'borrow, 'fr, Value<'frame, 'data>, F>>
where
F: Frame<'fr>,
{
if !self.is_value_array() {
Err(JlrsError::Inline)?;
}
let jl_data = jl_array_data(self.ptr().cast()).cast();
let dimensions = Dimensions::from_array(self.ptr().cast());
let data = std::slice::from_raw_parts(jl_data, dimensions.size());
Ok(ArrayData::new(data, dimensions, frame))
}
pub unsafe fn value_data_mut<'borrow, 'fr, F>(
self,
frame: &'borrow mut F,
) -> JlrsResult<ValueArrayDataMut<'borrow, 'frame, 'data, 'fr, F>>
where
F: Frame<'fr>,
{
if !self.is_value_array() {
Err(JlrsError::Inline)?;
}
let jl_data = jl_array_data(self.ptr().cast()).cast();
let dimensions = Dimensions::from_array(self.ptr().cast());
let data = std::slice::from_raw_parts_mut(jl_data, dimensions.size());
Ok(ValueArrayDataMut::new(self.into(), data, dimensions, frame))
}
pub unsafe fn unrestricted_value_data_mut<'borrow, 'fr, F>(
self,
frame: &'borrow F,
) -> JlrsResult<UnrestrictedValueArrayDataMut<'borrow, 'frame, 'data, 'fr, F>>
where
F: Frame<'fr>,
{
if !self.is_value_array() {
Err(JlrsError::Inline)?;
}
let jl_data = jl_array_data(self.ptr().cast()).cast();
let dimensions = Dimensions::from_array(self.ptr().cast());
let data = std::slice::from_raw_parts_mut(jl_data, dimensions.size());
Ok(UnrestrictedValueArrayDataMut::new(
Array::wrap(self.ptr()),
data,
dimensions,
frame,
))
}
pub fn as_value(self) -> Value<'frame, 'data> {
self.into()
}
}
unsafe impl<'frame, 'data, T: Copy + ValidLayout> JuliaTypecheck for TypedArray<'frame, 'data, T> {
unsafe fn julia_typecheck(t: DataType) -> bool {
jl_is_array_type(t.ptr().cast()) && T::valid_layout(Value::wrap(jl_tparam0(t.ptr()).cast()))
}
}
impl<'frame, 'data, T: Copy + ValidLayout> Into<Value<'frame, 'data>>
for TypedArray<'frame, 'data, T>
{
fn into(self) -> Value<'frame, 'data> {
unsafe { Value::wrap(self.ptr().cast()) }
}
}
impl<'frame, 'data, T: Copy + ValidLayout> Into<Array<'frame, 'data>>
for TypedArray<'frame, 'data, T>
{
fn into(self) -> Array<'frame, 'data> {
unsafe { Array::wrap(self.ptr()) }
}
}
unsafe impl<'frame, 'data, T: Copy + ValidLayout> Cast<'frame, 'data>
for TypedArray<'frame, 'data, T>
{
type Output = Self;
fn cast(value: Value<'frame, 'data>) -> JlrsResult<Self::Output> {
if value.is::<Self::Output>() {
return unsafe { Ok(Self::cast_unchecked(value)) };
}
Err(JlrsError::NotAnArray)?
}
unsafe fn cast_unchecked(value: Value<'frame, 'data>) -> Self::Output {
Self::wrap(value.ptr().cast())
}
}
unsafe impl<'frame, 'data, T: Copy + ValidLayout> ValidLayout for TypedArray<'frame, 'data, T> {
unsafe fn valid_layout(v: Value) -> bool {
if let Ok(dt) = v.cast::<DataType>() {
dt.is::<TypedArray<T>>()
} else if let Ok(ua) = v.cast::<super::union_all::UnionAll>() {
ua.base_type().is::<TypedArray<T>>()
} else {
false
}
}
}
#[derive(Debug)]
pub struct CopiedArray<T> {
data: Vec<T>,
dimensions: Dimensions,
}
impl<T> CopiedArray<T> {
pub(crate) fn new(data: Vec<T>, dimensions: Dimensions) -> Self {
CopiedArray { data, dimensions }
}
pub fn splat(self) -> (Vec<T>, Dimensions) {
(self.data, self.dimensions)
}
pub fn get<D: Into<Dimensions>>(&self, idx: D) -> Option<&T> {
Some(&self.data[self.dimensions.index_of(idx).ok()?])
}
pub fn get_mut<D: Into<Dimensions>>(&mut self, idx: D) -> Option<&mut T> {
Some(&mut self.data[self.dimensions.index_of(idx).ok()?])
}
pub fn as_slice(&self) -> &[T] {
&self.data
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.data
}
pub fn dimensions(&self) -> &Dimensions {
&self.dimensions
}
}
impl<T, D: Into<Dimensions>> Index<D> for CopiedArray<T> {
type Output = T;
fn index(&self, idx: D) -> &T {
&self.data[self.dimensions.index_of(idx).unwrap()]
}
}
impl<T, D: Into<Dimensions>> IndexMut<D> for CopiedArray<T> {
fn index_mut(&mut self, idx: D) -> &mut T {
&mut self.data[self.dimensions.index_of(idx).unwrap()]
}
}
pub struct ArrayData<'borrow, 'frame, T, F>
where
F: Frame<'frame>,
{
data: &'borrow [T],
dimensions: Dimensions,
_notsendsync: PhantomData<*const ()>,
_borrow: PhantomData<&'borrow F>,
_frame: PhantomData<&'frame ()>,
}
impl<'borrow, 'frame, T, F> ArrayData<'borrow, 'frame, T, F>
where
F: Frame<'frame>,
{
pub(crate) unsafe fn new(data: &'borrow [T], dimensions: Dimensions, _: &'borrow F) -> Self {
ArrayData {
data,
dimensions,
_notsendsync: PhantomData,
_borrow: PhantomData,
_frame: PhantomData,
}
}
pub fn get<D: Into<Dimensions>>(&self, index: D) -> Option<&T> {
Some(&self.data[self.dimensions.index_of(index).ok()?])
}
pub fn as_slice(&self) -> &[T] {
self.data
}
pub fn into_slice(self) -> &'borrow [T] {
self.data
}
pub fn dimensions(&self) -> &Dimensions {
&self.dimensions
}
}
impl<'borrow, 'frame, T, D, F> Index<D> for ArrayData<'borrow, 'frame, T, F>
where
D: Into<Dimensions>,
F: Frame<'frame>,
{
type Output = T;
fn index(&self, index: D) -> &Self::Output {
&self.data[self.dimensions.index_of(index).unwrap()]
}
}
pub struct InlineArrayDataMut<'borrow, 'frame, T, F: Frame<'frame>> {
data: &'borrow mut [T],
dimensions: Dimensions,
_notsendsync: PhantomData<*const ()>,
_borrow: PhantomData<&'borrow F>,
_frame: PhantomData<&'frame ()>,
}
impl<'borrow, 'frame, T, F> InlineArrayDataMut<'borrow, 'frame, T, F>
where
F: Frame<'frame>,
{
pub(crate) unsafe fn new(
data: &'borrow mut [T],
dimensions: Dimensions,
_: &'borrow mut F,
) -> Self {
InlineArrayDataMut {
data,
dimensions,
_notsendsync: PhantomData,
_frame: PhantomData,
_borrow: PhantomData,
}
}
pub fn get<D: Into<Dimensions>>(&self, index: D) -> Option<&T> {
Some(&self.data[self.dimensions.index_of(index).ok()?])
}
pub fn get_mut<D: Into<Dimensions>>(&mut self, index: D) -> Option<&mut T> {
Some(&mut self.data[self.dimensions.index_of(index).ok()?])
}
pub fn as_slice(&self) -> &[T] {
self.data
}
pub fn into_slice(self) -> &'borrow [T] {
self.data
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
self.data
}
pub fn into_mut_slice(self) -> &'borrow mut [T] {
self.data
}
pub fn dimensions(&self) -> &Dimensions {
&self.dimensions
}
}
impl<'borrow, 'frame, T, D, F> Index<D> for InlineArrayDataMut<'borrow, 'frame, T, F>
where
D: Into<Dimensions>,
F: Frame<'frame>,
{
type Output = T;
fn index(&self, index: D) -> &Self::Output {
&self.data[self.dimensions.index_of(index).unwrap()]
}
}
impl<'borrow, 'frame, T, D, F> IndexMut<D> for InlineArrayDataMut<'borrow, 'frame, T, F>
where
D: Into<Dimensions>,
F: Frame<'frame>,
{
fn index_mut(&mut self, index: D) -> &mut Self::Output {
&mut self.data[self.dimensions.index_of(index).unwrap()]
}
}
pub struct UnrestrictedInlineArrayDataMut<'borrow, 'frame, T, F: Frame<'frame>> {
data: &'borrow mut [T],
dimensions: Dimensions,
_notsendsync: PhantomData<*const ()>,
_borrow: PhantomData<&'borrow F>,
_frame: PhantomData<&'frame ()>,
}
impl<'borrow, 'frame, T, F> UnrestrictedInlineArrayDataMut<'borrow, 'frame, T, F>
where
F: Frame<'frame>,
{
pub(crate) unsafe fn new(
data: &'borrow mut [T],
dimensions: Dimensions,
_: &'borrow F,
) -> Self {
UnrestrictedInlineArrayDataMut {
data,
dimensions,
_notsendsync: PhantomData,
_frame: PhantomData,
_borrow: PhantomData,
}
}
pub fn get<D: Into<Dimensions>>(&self, index: D) -> Option<&T> {
Some(&self.data[self.dimensions.index_of(index).ok()?])
}
pub fn get_mut<D: Into<Dimensions>>(&mut self, index: D) -> Option<&mut T> {
Some(&mut self.data[self.dimensions.index_of(index).ok()?])
}
pub fn as_slice(&self) -> &[T] {
&self.data
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.data
}
pub fn dimensions(&self) -> &Dimensions {
&self.dimensions
}
}
impl<'borrow, 'frame, T, D, F> Index<D> for UnrestrictedInlineArrayDataMut<'borrow, 'frame, T, F>
where
D: Into<Dimensions>,
F: Frame<'frame>,
{
type Output = T;
fn index(&self, index: D) -> &Self::Output {
&self.data[self.dimensions.index_of(index).unwrap()]
}
}
impl<'borrow, 'frame, T, D, F> IndexMut<D> for UnrestrictedInlineArrayDataMut<'borrow, 'frame, T, F>
where
D: Into<Dimensions>,
F: Frame<'frame>,
{
fn index_mut(&mut self, index: D) -> &mut Self::Output {
&mut self.data[self.dimensions.index_of(index).unwrap()]
}
}
pub struct ValueArrayDataMut<'borrow, 'value, 'data, 'frame, F: Frame<'frame>> {
array: Array<'value, 'data>,
data: &'borrow mut [Value<'value, 'data>],
dimensions: Dimensions,
_notsendsync: PhantomData<*const ()>,
_borrow: PhantomData<&'borrow mut F>,
_frame: PhantomData<&'frame ()>,
}
impl<'borrow, 'frame, 'data, 'fr, F> ValueArrayDataMut<'borrow, 'frame, 'data, 'fr, F>
where
F: Frame<'fr>,
{
pub(crate) unsafe fn new(
array: Array<'frame, 'data>,
data: &'borrow mut [Value<'frame, 'data>],
dimensions: Dimensions,
_: &'borrow mut F,
) -> Self {
ValueArrayDataMut {
array,
data,
dimensions,
_notsendsync: PhantomData,
_borrow: PhantomData,
_frame: PhantomData,
}
}
pub fn get<D: Into<Dimensions>>(&self, index: D) -> Option<&Value<'frame, 'data>> {
Some(&self.data[self.dimensions.index_of(index).ok()?])
}
pub unsafe fn set<'va, 'da: 'data, D: Into<Dimensions>>(
&mut self,
index: D,
value: Value<'frame, 'da>,
) -> JlrsResult<()> {
let ptr = self.array.ptr();
let eltype = jl_array_eltype(ptr.cast());
if eltype != jl_typeof(value.ptr().cast()).cast() {
Err(JlrsError::InvalidArrayType)?;
}
let idx = self.dimensions.index_of(index)?;
jl_array_ptr_set(ptr.cast(), idx, value.ptr().cast());
Ok(())
}
pub fn as_slice(&self) -> &[Value<'frame, 'data>] {
&self.data
}
pub fn dimensions(&self) -> &Dimensions {
&self.dimensions
}
}
impl<'borrow, 'value, 'data, 'frame, D, F> Index<D>
for ValueArrayDataMut<'borrow, 'value, 'data, 'frame, F>
where
D: Into<Dimensions>,
F: Frame<'frame>,
{
type Output = Value<'value, 'data>;
fn index(&self, index: D) -> &Self::Output {
&self.data[self.dimensions.index_of(index).unwrap()]
}
}
pub struct UnrestrictedValueArrayDataMut<'borrow, 'value, 'data, 'frame, F: Frame<'frame>> {
array: Array<'value, 'data>,
data: &'borrow mut [Value<'value, 'data>],
dimensions: Dimensions,
_notsendsync: PhantomData<*const ()>,
_borrow: PhantomData<&'borrow F>,
_frame: PhantomData<&'frame ()>,
}
impl<'borrow, 'frame, 'data, 'fr, F> UnrestrictedValueArrayDataMut<'borrow, 'frame, 'data, 'fr, F>
where
F: Frame<'fr>,
{
pub(crate) unsafe fn new(
array: Array<'frame, 'data>,
data: &'borrow mut [Value<'frame, 'data>],
dimensions: Dimensions,
_: &'borrow F,
) -> Self {
UnrestrictedValueArrayDataMut {
array,
data,
dimensions,
_notsendsync: PhantomData,
_borrow: PhantomData,
_frame: PhantomData,
}
}
pub fn get<D: Into<Dimensions>>(&self, index: D) -> Option<&Value<'frame, 'data>> {
Some(&self.data[self.dimensions.index_of(index).ok()?])
}
pub unsafe fn set<'va, 'da: 'data, D: Into<Dimensions>>(
&mut self,
index: D,
value: Value<'frame, 'da>,
) -> JlrsResult<()> {
let ptr = self.array.ptr();
let eltype = jl_array_eltype(ptr.cast());
if eltype != jl_typeof(value.ptr().cast()).cast() {
Err(JlrsError::InvalidArrayType)?;
}
jl_array_ptr_set(
ptr.cast(),
self.dimensions.index_of(index)?,
value.ptr().cast(),
);
Ok(())
}
pub fn as_slice(&self) -> &[Value<'frame, 'data>] {
&self.data
}
pub fn dimensions(&self) -> &Dimensions {
&self.dimensions
}
}
impl<'borrow, 'value, 'data, 'frame, D, F> Index<D>
for UnrestrictedValueArrayDataMut<'borrow, 'value, 'data, 'frame, F>
where
D: Into<Dimensions>,
F: Frame<'frame>,
{
type Output = Value<'value, 'data>;
fn index(&self, index: D) -> &Self::Output {
&self.data[self.dimensions.index_of(index).unwrap()]
}
}
#[derive(Clone)]
pub enum Dimensions {
#[doc(hidden)]
Few([usize; 4]),
#[doc(hidden)]
Many(Box<[usize]>),
}
impl Dimensions {
pub(crate) unsafe fn from_array(array: *mut jl_array_t) -> Self {
let n_dims = jl_array_ndims(array);
match n_dims {
0 => Into::into(()),
1 => Into::into(jl_array_nrows(array) as usize),
2 => Into::into((jl_array_dim(array, 0), jl_array_dim(array, 1))),
3 => Into::into((
jl_array_dim(array, 0),
jl_array_dim(array, 1),
jl_array_dim(array, 2),
)),
ndims => Into::into(jl_array_dims(array, ndims as _)),
}
}
pub fn n_dimensions(&self) -> usize {
match self {
Dimensions::Few([n, _, _, _]) => *n,
Dimensions::Many(ref dims) => dims[0],
}
}
pub fn n_elements(&self, dimension: usize) -> usize {
if self.n_dimensions() == 0 && dimension == 0 {
return 0;
}
assert!(dimension < self.n_dimensions());
let dims = match self {
Dimensions::Few(ref dims) => dims,
Dimensions::Many(ref dims) => dims.as_ref(),
};
dims[dimension as usize + 1]
}
pub fn size(&self) -> usize {
if self.n_dimensions() == 0 {
return 0;
}
let dims = match self {
Dimensions::Few(ref dims) => &dims[1..dims[0] as usize + 1],
Dimensions::Many(ref dims) => &dims[1..dims[0] as usize + 1],
};
dims.iter().product()
}
pub fn index_of<D: Into<Dimensions>>(&self, dim_index: D) -> JlrsResult<usize> {
let dim_index = dim_index.into();
self.check_bounds(&dim_index)?;
let idx = match self.n_dimensions() {
0 => 0,
_ => {
let mut d_it = dim_index.as_slice().iter().rev();
let acc = d_it.next().unwrap();
d_it.zip(self.as_slice().iter().rev().skip(1))
.fold(*acc, |acc, (dim, sz)| dim + sz * acc)
}
};
Ok(idx)
}
pub fn as_slice(&self) -> &[usize] {
match self {
Dimensions::Few(ref v) => &v[1..v[0] as usize + 1],
Dimensions::Many(ref v) => &v[1..],
}
}
fn check_bounds(&self, dim_index: &Dimensions) -> JlrsResult<()> {
if self.n_dimensions() != dim_index.n_dimensions() {
Err(JlrsError::InvalidIndex(dim_index.clone(), self.clone()))?;
}
for i in 0..self.n_dimensions() {
if self.n_elements(i) < dim_index.n_elements(i) {
Err(JlrsError::InvalidIndex(dim_index.clone(), self.clone()))?;
}
}
Ok(())
}
}
impl Debug for Dimensions {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let mut f = f.debug_tuple("");
for d in self.as_slice() {
f.field(&d);
}
f.finish()
}
}
impl Display for Dimensions {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let mut f = f.debug_tuple("");
for d in self.as_slice() {
f.field(&d);
}
f.finish()
}
}
impl Into<Dimensions> for usize {
fn into(self) -> Dimensions {
Dimensions::Few([1, self, 0, 0])
}
}
impl Into<Dimensions> for () {
fn into(self) -> Dimensions {
Dimensions::Few([0, 0, 0, 0])
}
}
impl Into<Dimensions> for (usize,) {
fn into(self) -> Dimensions {
Dimensions::Few([1, self.0, 0, 0])
}
}
impl Into<Dimensions> for (usize, usize) {
fn into(self) -> Dimensions {
Dimensions::Few([2, self.0, self.1, 0])
}
}
impl Into<Dimensions> for (usize, usize, usize) {
fn into(self) -> Dimensions {
Dimensions::Few([3, self.0, self.1, self.2])
}
}
impl Into<Dimensions> for (usize, usize, usize, usize) {
fn into(self) -> Dimensions {
Dimensions::Many(Box::new([4, self.0, self.1, self.2, self.3]))
}
}
impl Into<Dimensions> for (usize, usize, usize, usize, usize) {
fn into(self) -> Dimensions {
Dimensions::Many(Box::new([5, self.0, self.1, self.2, self.3, self.4]))
}
}
impl Into<Dimensions> for (usize, usize, usize, usize, usize, usize) {
fn into(self) -> Dimensions {
Dimensions::Many(Box::new([
6, self.0, self.1, self.2, self.3, self.4, self.5,
]))
}
}
impl Into<Dimensions> for (usize, usize, usize, usize, usize, usize, usize) {
fn into(self) -> Dimensions {
Dimensions::Many(Box::new([
7, self.0, self.1, self.2, self.3, self.4, self.5, self.6,
]))
}
}
impl Into<Dimensions> for (usize, usize, usize, usize, usize, usize, usize, usize) {
fn into(self) -> Dimensions {
Dimensions::Many(Box::new([
8, self.0, self.1, self.2, self.3, self.4, self.5, self.6, self.7,
]))
}
}
impl Into<Dimensions> for &[usize] {
fn into(self) -> Dimensions {
let nd = self.len();
let mut v: Vec<usize> = Vec::with_capacity(nd + 1);
v.push(nd);
v.extend_from_slice(self);
Dimensions::Many(v.into_boxed_slice())
}
}
#[cfg(test)]
mod tests {
use super::Dimensions;
#[test]
fn convert_usize() {
let d: Dimensions = 4.into();
assert_eq!(d.n_dimensions(), 1);
assert_eq!(d.n_elements(0), 4);
assert_eq!(d.size(), 4);
}
#[test]
fn convert_tuple_0d() {
let d: Dimensions = ().into();
assert_eq!(d.n_dimensions(), 0);
assert_eq!(d.n_elements(0), 0);
assert_eq!(d.size(), 0);
}
#[test]
fn convert_tuple_1d() {
let d: Dimensions = (4,).into();
assert_eq!(d.n_dimensions(), 1);
assert_eq!(d.n_elements(0), 4);
assert_eq!(d.size(), 4);
}
#[test]
fn convert_tuple_2d() {
let d: Dimensions = (4, 3).into();
assert_eq!(d.n_dimensions(), 2);
assert_eq!(d.n_elements(0), 4);
assert_eq!(d.n_elements(1), 3);
assert_eq!(d.size(), 12);
}
#[test]
fn convert_tuple_3d() {
let d: Dimensions = (4, 3, 2).into();
assert_eq!(d.n_dimensions(), 3);
assert_eq!(d.n_elements(0), 4);
assert_eq!(d.n_elements(1), 3);
assert_eq!(d.n_elements(2), 2);
assert_eq!(d.size(), 24);
}
#[test]
fn convert_tuple_4d() {
let d: Dimensions = (4, 3, 2, 1).into();
assert_eq!(d.n_dimensions(), 4);
assert_eq!(d.n_elements(0), 4);
assert_eq!(d.n_elements(1), 3);
assert_eq!(d.n_elements(2), 2);
assert_eq!(d.n_elements(3), 1);
assert_eq!(d.size(), 24);
}
#[test]
fn convert_tuple_5d() {
let d: Dimensions = (4, 3, 2, 1, 2).into();
assert_eq!(d.n_dimensions(), 5);
assert_eq!(d.n_elements(0), 4);
assert_eq!(d.n_elements(1), 3);
assert_eq!(d.n_elements(2), 2);
assert_eq!(d.n_elements(3), 1);
assert_eq!(d.n_elements(4), 2);
assert_eq!(d.size(), 48);
}
#[test]
fn convert_tuple_6d() {
let d: Dimensions = (4, 3, 2, 1, 2, 3).into();
assert_eq!(d.n_dimensions(), 6);
assert_eq!(d.n_elements(0), 4);
assert_eq!(d.n_elements(1), 3);
assert_eq!(d.n_elements(2), 2);
assert_eq!(d.n_elements(3), 1);
assert_eq!(d.n_elements(4), 2);
assert_eq!(d.n_elements(5), 3);
assert_eq!(d.size(), 144);
}
#[test]
fn convert_tuple_7d() {
let d: Dimensions = (4, 3, 2, 1, 2, 3, 2).into();
assert_eq!(d.n_dimensions(), 7);
assert_eq!(d.n_elements(0), 4);
assert_eq!(d.n_elements(1), 3);
assert_eq!(d.n_elements(2), 2);
assert_eq!(d.n_elements(3), 1);
assert_eq!(d.n_elements(4), 2);
assert_eq!(d.n_elements(5), 3);
assert_eq!(d.n_elements(6), 2);
assert_eq!(d.size(), 288);
}
#[test]
fn convert_tuple_8d() {
let d: Dimensions = (4, 3, 2, 1, 2, 3, 2, 4).into();
assert_eq!(d.n_dimensions(), 8);
assert_eq!(d.n_elements(0), 4);
assert_eq!(d.n_elements(1), 3);
assert_eq!(d.n_elements(2), 2);
assert_eq!(d.n_elements(3), 1);
assert_eq!(d.n_elements(4), 2);
assert_eq!(d.n_elements(5), 3);
assert_eq!(d.n_elements(6), 2);
assert_eq!(d.n_elements(7), 4);
assert_eq!(d.size(), 1152);
}
#[test]
fn convert_tuple_nd() {
let v = [1, 2, 3];
let d: Dimensions = v.as_ref().into();
assert_eq!(d.n_dimensions(), 3);
assert_eq!(d.n_elements(0), 1);
assert_eq!(d.n_elements(1), 2);
assert_eq!(d.n_elements(2), 3);
assert_eq!(d.size(), 6);
}
}