use ndarray::{ArrayBase, Data, Dimension, IntoDimension, Ix1, OwnedRepr};
use pyo3::Python;
use std::{mem, os::raw::c_int};
use crate::{
npyffi::{self, npy_intp},
Element, PyArray,
};
pub trait IntoPyArray {
type Item: Element;
type Dim: Dimension;
fn into_pyarray<'py>(self, _: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim>;
}
impl<T: Element> IntoPyArray for Box<[T]> {
type Item = T;
type Dim = Ix1;
fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
let dims = [self.len()];
let strides = [mem::size_of::<T>() as npy_intp];
let data_ptr = self.as_ptr();
unsafe { PyArray::from_raw_parts(py, dims, strides.as_ptr(), data_ptr, self) }
}
}
impl<T: Element> IntoPyArray for Vec<T> {
type Item = T;
type Dim = Ix1;
fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
let dims = [self.len()];
let strides = [mem::size_of::<T>() as npy_intp];
let data_ptr = self.as_ptr();
unsafe { PyArray::from_raw_parts(py, dims, strides.as_ptr(), data_ptr, self) }
}
}
impl<A, D> IntoPyArray for ArrayBase<OwnedRepr<A>, D>
where
A: Element,
D: Dimension,
{
type Item = A;
type Dim = D;
fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
PyArray::from_owned_array(py, self)
}
}
pub trait ToPyArray {
type Item: Element;
type Dim: Dimension;
fn to_pyarray<'py>(&self, _: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim>;
}
impl<T: Element> ToPyArray for [T] {
type Item = T;
type Dim = Ix1;
fn to_pyarray<'py>(&self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
PyArray::from_slice(py, self)
}
}
impl<S, D, A> ToPyArray for ArrayBase<S, D>
where
S: Data<Elem = A>,
D: Dimension,
A: Element,
{
type Item = A;
type Dim = D;
fn to_pyarray<'py>(&self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
let len = self.len();
match self.order() {
Some(order) if A::IS_COPY => {
let strides = self.npy_strides();
unsafe {
let array =
PyArray::new_(py, self.raw_dim(), strides.as_ptr(), order.to_flag());
array.copy_ptr(self.as_ptr(), len);
array
}
}
_ => {
let dim = self.raw_dim();
let strides = NpyStrides::new::<_, A>(
dim.default_strides()
.slice()
.iter()
.map(|&x| x as npyffi::npy_intp),
);
unsafe {
let array = PyArray::<A, _>::new_(py, dim, strides.as_ptr(), 0);
let data_ptr = array.data();
for (i, item) in self.iter().enumerate() {
data_ptr.add(i).write(item.clone());
}
array
}
}
}
}
}
pub(crate) enum Order {
Standard,
Fortran,
}
impl Order {
fn to_flag(&self) -> c_int {
match self {
Order::Standard => 0,
Order::Fortran => 1,
}
}
}
pub(crate) trait ArrayExt {
fn npy_strides(&self) -> NpyStrides;
fn order(&self) -> Option<Order>;
}
impl<A, S, D> ArrayExt for ArrayBase<S, D>
where
S: Data<Elem = A>,
D: Dimension,
{
fn npy_strides(&self) -> NpyStrides {
NpyStrides::new::<_, A>(self.strides().iter().map(|&x| x as npyffi::npy_intp))
}
fn order(&self) -> Option<Order> {
if self.is_standard_layout() {
Some(Order::Standard)
} else if self.ndim() > 1 && self.raw_view().reversed_axes().is_standard_layout() {
Some(Order::Fortran)
} else {
None
}
}
}
pub(crate) struct NpyStrides([npyffi::npy_intp; 32]);
impl NpyStrides {
pub(crate) fn as_ptr(&self) -> *const npy_intp {
self.0.as_ptr()
}
fn new<S, A>(strides: S) -> Self
where
S: Iterator<Item = npyffi::npy_intp>,
{
let type_size = mem::size_of::<A>() as npyffi::npy_intp;
let mut res = [0; 32];
for (i, s) in strides.enumerate() {
*res.get_mut(i)
.expect("Only dimensionalities of up to 32 are supported") = s * type_size;
}
Self(res)
}
}
pub trait ToNpyDims: Dimension {
fn ndim_cint(&self) -> c_int {
self.ndim() as c_int
}
fn as_dims_ptr(&self) -> *mut npyffi::npy_intp {
self.slice().as_ptr() as *mut npyffi::npy_intp
}
fn to_npy_dims(&self) -> npyffi::PyArray_Dims {
npyffi::PyArray_Dims {
ptr: self.as_dims_ptr(),
len: self.ndim_cint(),
}
}
fn __private__(&self) -> PrivateMarker;
}
impl<D: Dimension> ToNpyDims for D {
fn __private__(&self) -> PrivateMarker {
PrivateMarker
}
}
pub trait NpyIndex: IntoDimension {
fn get_checked<T>(self, dims: &[usize], strides: &[isize]) -> Option<isize>;
fn get_unchecked<T>(self, strides: &[isize]) -> isize;
fn __private__(self) -> PrivateMarker;
}
impl<D: IntoDimension> NpyIndex for D {
fn get_checked<T>(self, dims: &[usize], strides: &[isize]) -> Option<isize> {
let indices_ = self.into_dimension();
let indices = indices_.slice();
if indices.len() != dims.len() {
return None;
}
if indices.iter().zip(dims).any(|(i, d)| i >= d) {
return None;
}
Some(get_unchecked_impl(
indices,
strides,
mem::size_of::<T>() as isize,
))
}
fn get_unchecked<T>(self, strides: &[isize]) -> isize {
let indices_ = self.into_dimension();
let indices = indices_.slice();
get_unchecked_impl(indices, strides, mem::size_of::<T>() as isize)
}
fn __private__(self) -> PrivateMarker {
PrivateMarker
}
}
fn get_unchecked_impl(indices: &[usize], strides: &[isize], size: isize) -> isize {
indices
.iter()
.zip(strides)
.map(|(&i, stride)| stride * i as isize / size)
.sum()
}
#[doc(hidden)]
pub struct PrivateMarker;