Skip to content

Commit 8bc9535

Browse files
authored
Merge pull request #194 from PyO3/intopyarray-pointer-fix
Fix IntoPyArray for arrays of which the starting pointer is not equal…
2 parents 7e77c5e + 9e856a2 commit 8bc9535

File tree

3 files changed

+32
-8
lines changed

3 files changed

+32
-8
lines changed

src/array.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -415,14 +415,15 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
415415
py: Python<'py>,
416416
dims: ID,
417417
strides: *const npy_intp,
418-
slice: Box<[T]>,
418+
boxed_slice: Box<[T]>,
419+
data_ptr: Option<*const T>,
419420
) -> &'py Self
420421
where
421422
ID: IntoDimension<Dim = D>,
422423
{
423424
let dims = dims.into_dimension();
424-
let container = SliceBox::new(slice);
425-
let data_ptr = container.data.as_ptr();
425+
let container = SliceBox::new(boxed_slice);
426+
let data_ptr = data_ptr.unwrap_or_else(|| container.data.as_ptr());
426427
let cell = pyo3::PyClassInitializer::from(container)
427428
.create_cell(py)
428429
.expect("Object creation failed.");

src/convert.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ impl<T: Element> IntoPyArray for Box<[T]> {
3939
fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
4040
let len = self.len();
4141
let strides = [mem::size_of::<T>() as npy_intp];
42-
unsafe { PyArray::from_boxed_slice(py, [len], strides.as_ptr(), self) }
42+
unsafe { PyArray::from_boxed_slice(py, [len], strides.as_ptr(), self, None) }
4343
}
4444
}
4545

@@ -59,10 +59,20 @@ where
5959
type Item = A;
6060
type Dim = D;
6161
fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
62-
let strides = self.npy_strides();
63-
let dim = self.raw_dim();
64-
let boxed = self.into_raw_vec().into_boxed_slice();
65-
unsafe { PyArray::from_boxed_slice(py, dim, strides.as_ptr(), boxed) }
62+
let (strides, dim) = (self.npy_strides(), self.raw_dim());
63+
let orig_ptr = self.as_ptr();
64+
let is_empty_or_size0 = self.is_empty() || std::mem::size_of::<Self>() == 0;
65+
let vec = self.into_raw_vec();
66+
let offset = if is_empty_or_size0 {
67+
0
68+
} else {
69+
unsafe { orig_ptr.offset_from(vec.as_ptr()) as usize }
70+
};
71+
let mut boxed_slice = vec.into_boxed_slice();
72+
// data_ptr is not always the pointer to the 1st element.
73+
// See https://github.com/PyO3/rust-numpy/issues/182 for the detail.
74+
let data_ptr = unsafe { boxed_slice.as_mut_ptr().add(offset) };
75+
unsafe { PyArray::from_boxed_slice(py, dim, strides.as_ptr(), boxed_slice, Some(data_ptr)) }
6676
}
6777
}
6878

tests/to_py.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,19 @@ fn into_pyarray_cant_resize() {
115115
})
116116
}
117117

118+
/// Check that into_pyarray works for ndarray of which the pointer of the first element is
119+
/// not at the start. See https://github.com/PyO3/rust-numpy/issues/182 for more
120+
#[test]
121+
fn into_pyarray_collapsed() {
122+
let mut arr = Array2::<f64>::from_shape_fn([3, 4], |(i, j)| (i * 10 + j) as f64);
123+
arr.slice_collapse(s![1.., ..]);
124+
let copy = arr.clone();
125+
pyo3::Python::with_gil(|py| {
126+
let py_arr = arr.into_pyarray(py);
127+
assert_eq!(py_arr.readonly().as_array(), copy);
128+
})
129+
}
130+
118131
#[test]
119132
fn forder_to_pyarray() {
120133
pyo3::Python::with_gil(|py| {

0 commit comments

Comments
 (0)