diff --git a/src/array.rs b/src/array.rs index 512829ed0..58cc91dd1 100644 --- a/src/array.rs +++ b/src/array.rs @@ -415,14 +415,15 @@ impl PyArray { py: Python<'py>, dims: ID, strides: *const npy_intp, - slice: Box<[T]>, + boxed_slice: Box<[T]>, + data_ptr: Option<*const T>, ) -> &'py Self where ID: IntoDimension, { let dims = dims.into_dimension(); - let container = SliceBox::new(slice); - let data_ptr = container.data.as_ptr(); + let container = SliceBox::new(boxed_slice); + let data_ptr = data_ptr.unwrap_or_else(|| container.data.as_ptr()); let cell = pyo3::PyClassInitializer::from(container) .create_cell(py) .expect("Object creation failed."); diff --git a/src/convert.rs b/src/convert.rs index a53d8a808..894976f5f 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -39,7 +39,7 @@ impl IntoPyArray for Box<[T]> { fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray { let len = self.len(); let strides = [mem::size_of::() as npy_intp]; - unsafe { PyArray::from_boxed_slice(py, [len], strides.as_ptr(), self) } + unsafe { PyArray::from_boxed_slice(py, [len], strides.as_ptr(), self, None) } } } @@ -59,10 +59,20 @@ where type Item = A; type Dim = D; fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray { - let strides = self.npy_strides(); - let dim = self.raw_dim(); - let boxed = self.into_raw_vec().into_boxed_slice(); - unsafe { PyArray::from_boxed_slice(py, dim, strides.as_ptr(), boxed) } + let (strides, dim) = (self.npy_strides(), self.raw_dim()); + let orig_ptr = self.as_ptr(); + let is_empty_or_size0 = self.is_empty() || std::mem::size_of::() == 0; + let vec = self.into_raw_vec(); + let offset = if is_empty_or_size0 { + 0 + } else { + unsafe { orig_ptr.offset_from(vec.as_ptr()) as usize } + }; + let mut boxed_slice = vec.into_boxed_slice(); + // data_ptr is not always the pointer to the 1st element. + // See https://github.com/PyO3/rust-numpy/issues/182 for the detail. + let data_ptr = unsafe { boxed_slice.as_mut_ptr().add(offset) }; + unsafe { PyArray::from_boxed_slice(py, dim, strides.as_ptr(), boxed_slice, Some(data_ptr)) } } } diff --git a/tests/to_py.rs b/tests/to_py.rs index f11cec1dc..224b51c77 100644 --- a/tests/to_py.rs +++ b/tests/to_py.rs @@ -115,6 +115,19 @@ fn into_pyarray_cant_resize() { }) } +/// Check that into_pyarray works for ndarray of which the pointer of the first element is +/// not at the start. See https://github.com/PyO3/rust-numpy/issues/182 for more +#[test] +fn into_pyarray_collapsed() { + let mut arr = Array2::::from_shape_fn([3, 4], |(i, j)| (i * 10 + j) as f64); + arr.slice_collapse(s![1.., ..]); + let copy = arr.clone(); + pyo3::Python::with_gil(|py| { + let py_arr = arr.into_pyarray(py); + assert_eq!(py_arr.readonly().as_array(), copy); + }) +} + #[test] fn forder_to_pyarray() { pyo3::Python::with_gil(|py| {