Skip to content

Commit d400f60

Browse files
Cris Luengodean0x7d
Cris Luengo
authored andcommitted
Python buffer objects can have negative strides.
1 parent 2b941b3 commit d400f60

File tree

6 files changed

+40
-33
lines changed

6 files changed

+40
-33
lines changed

docs/advanced/pycpp/numpy.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ completely avoid copy operations with Python expressions like
4141
py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
4242
2, /* Number of dimensions */
4343
{ m.rows(), m.cols() }, /* Buffer dimensions */
44-
{ sizeof(float) * m.rows(), /* Strides (in bytes) for each index */
45-
sizeof(float) }
44+
{ (ssize_t)( sizeof(float) * m.rows() ),/* Strides (in bytes) for each index */
45+
(ssize_t)( sizeof(float) ) }
4646
);
4747
});
4848
@@ -61,7 +61,7 @@ specification.
6161
std::string format;
6262
int ndim;
6363
std::vector<size_t> shape;
64-
std::vector<size_t> strides;
64+
std::vector<ssize_t> strides;
6565
};
6666
6767
To create a C++ function that can take a Python buffer object as an argument,
@@ -121,8 +121,8 @@ as follows:
121121
{ (size_t) m.rows(),
122122
(size_t) m.cols() },
123123
/* Strides (in bytes) for each index */
124-
{ sizeof(Scalar) * (rowMajor ? m.cols() : 1),
125-
sizeof(Scalar) * (rowMajor ? 1 : m.rows()) }
124+
{ (ssize_t)( sizeof(Scalar) * (rowMajor ? m.cols() : 1) ),
125+
(ssize_t)( sizeof(Scalar) * (rowMajor ? 1 : m.rows()) ) }
126126
);
127127
})
128128

include/pybind11/numpy.h

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -265,14 +265,14 @@ class unchecked_reference {
265265
const unsigned char *data_;
266266
// Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to
267267
// make large performance gains on big, nested loops, but requires compile-time dimensions
268-
conditional_t<Dynamic, const size_t *, std::array<size_t, (size_t) Dims>>
269-
shape_, strides_;
268+
conditional_t<Dynamic, const size_t *, std::array<size_t, (size_t) Dims>> shape_;
269+
conditional_t<Dynamic, const ssize_t *, std::array<ssize_t, (size_t) Dims>> strides_;
270270
const size_t dims_;
271271

272272
friend class pybind11::array;
273273
// Constructor for compile-time dimensions:
274274
template <bool Dyn = Dynamic>
275-
unchecked_reference(const void *data, const size_t *shape, const size_t *strides, enable_if_t<!Dyn, size_t>)
275+
unchecked_reference(const void *data, const size_t *shape, const ssize_t *strides, enable_if_t<!Dyn, size_t>)
276276
: data_{reinterpret_cast<const unsigned char *>(data)}, dims_{Dims} {
277277
for (size_t i = 0; i < dims_; i++) {
278278
shape_[i] = shape[i];
@@ -281,7 +281,7 @@ class unchecked_reference {
281281
}
282282
// Constructor for runtime dimensions:
283283
template <bool Dyn = Dynamic>
284-
unchecked_reference(const void *data, const size_t *shape, const size_t *strides, enable_if_t<Dyn, size_t> dims)
284+
unchecked_reference(const void *data, const size_t *shape, const ssize_t *strides, enable_if_t<Dyn, size_t> dims)
285285
: data_{reinterpret_cast<const unsigned char *>(data)}, shape_{shape}, strides_{strides}, dims_{dims} {}
286286

287287
public:
@@ -573,12 +573,12 @@ class array : public buffer {
573573
}
574574

575575
/// Strides of the array
576-
const size_t* strides() const {
577-
return reinterpret_cast<const size_t *>(detail::array_proxy(m_ptr)->strides);
576+
const ssize_t* strides() const {
577+
return reinterpret_cast<const ssize_t *>(detail::array_proxy(m_ptr)->strides);
578578
}
579579

580580
/// Stride along a given axis
581-
size_t strides(size_t dim) const {
581+
ssize_t strides(size_t dim) const {
582582
if (dim >= ndim())
583583
fail_dim_check(dim, "invalid axis");
584584
return strides()[dim];
@@ -702,9 +702,9 @@ class array : public buffer {
702702
throw std::domain_error("array is not writeable");
703703
}
704704

705-
static std::vector<Py_intptr_t> default_strides(const std::vector<Py_intptr_t>& shape, size_t itemsize) {
705+
static std::vector<ssize_t> default_strides(const std::vector<size_t>& shape, size_t itemsize) {
706706
auto ndim = shape.size();
707-
std::vector<Py_intptr_t> strides(ndim);
707+
std::vector<ssize_t> strides(ndim);
708708
if (ndim) {
709709
std::fill(strides.begin(), strides.end(), itemsize);
710710
for (size_t i = 0; i < ndim - 1; i++)
@@ -1133,7 +1133,7 @@ array_iterator<T> array_end(const buffer_info& buffer) {
11331133

11341134
class common_iterator {
11351135
public:
1136-
using container_type = std::vector<size_t>;
1136+
using container_type = std::vector<ssize_t>;
11371137
using value_type = container_type::value_type;
11381138
using size_type = container_type::size_type;
11391139

@@ -1175,7 +1175,7 @@ template <size_t N> class multi_array_iterator {
11751175
for (size_t i = 0; i < shape.size(); ++i)
11761176
m_shape[i] = static_cast<container_type::value_type>(shape[i]);
11771177

1178-
container_type strides(shape.size());
1178+
std::vector<ssize_t> strides(shape.size());
11791179
for (size_t i = 0; i < N; ++i)
11801180
init_common_iterator(buffers[i], shape, m_common_iterator[i], strides);
11811181
}
@@ -1203,15 +1203,15 @@ template <size_t N> class multi_array_iterator {
12031203

12041204
void init_common_iterator(const buffer_info &buffer,
12051205
const std::vector<size_t> &shape,
1206-
common_iter &iterator, container_type &strides) {
1206+
common_iter &iterator, std::vector<ssize_t> &strides) {
12071207
auto buffer_shape_iter = buffer.shape.rbegin();
12081208
auto buffer_strides_iter = buffer.strides.rbegin();
12091209
auto shape_iter = shape.rbegin();
12101210
auto strides_iter = strides.rbegin();
12111211

12121212
while (buffer_shape_iter != buffer.shape.rend()) {
12131213
if (*shape_iter == *buffer_shape_iter)
1214-
*strides_iter = static_cast<size_t>(*buffer_strides_iter);
1214+
*strides_iter = *buffer_strides_iter;
12151215
else
12161216
*strides_iter = 0;
12171217

@@ -1283,10 +1283,11 @@ broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &n
12831283

12841284
// Check for C contiguity (but only if previous inputs were also C contiguous)
12851285
if (trivial_broadcast_c) {
1286-
size_t expect_stride = buffers[i].itemsize;
1286+
ssize_t expect_stride = static_cast<ssize_t>(buffers[i].itemsize);
12871287
auto end = buffers[i].shape.crend();
1288-
for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin();
1289-
trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) {
1288+
auto shape_iter = buffers[i].shape.crbegin();
1289+
auto stride_iter = buffers[i].strides.crbegin();
1290+
for (; trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) {
12901291
if (expect_stride == *stride_iter)
12911292
expect_stride *= *shape_iter;
12921293
else
@@ -1296,10 +1297,11 @@ broadcast_trivial broadcast(const std::array<buffer_info, N> &buffers, size_t &n
12961297

12971298
// Check for Fortran contiguity (if previous inputs were also F contiguous)
12981299
if (trivial_broadcast_f) {
1299-
size_t expect_stride = buffers[i].itemsize;
1300+
ssize_t expect_stride = static_cast<ssize_t>(buffers[i].itemsize);
13001301
auto end = buffers[i].shape.cend();
1301-
for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin();
1302-
trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) {
1302+
auto shape_iter = buffers[i].shape.cbegin();
1303+
auto stride_iter = buffers[i].strides.cbegin();
1304+
for (; trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) {
13031305
if (expect_stride == *stride_iter)
13041306
expect_stride *= *shape_iter;
13051307
else
@@ -1336,20 +1338,20 @@ struct vectorize_helper {
13361338
auto trivial = broadcast(buffers, ndim, shape);
13371339

13381340
size_t size = 1;
1339-
std::vector<size_t> strides(ndim);
1341+
std::vector<ssize_t> strides(ndim);
13401342
if (ndim > 0) {
13411343
if (trivial == broadcast_trivial::f_trivial) {
1342-
strides[0] = sizeof(Return);
1344+
strides[0] = static_cast<ssize_t>(sizeof(Return));
13431345
for (size_t i = 1; i < ndim; ++i) {
1344-
strides[i] = strides[i - 1] * shape[i - 1];
1346+
strides[i] = strides[i - 1] * static_cast<ssize_t>(shape[i - 1]);
13451347
size *= shape[i - 1];
13461348
}
13471349
size *= shape[ndim - 1];
13481350
}
13491351
else {
1350-
strides[ndim-1] = sizeof(Return);
1352+
strides[ndim-1] = static_cast<ssize_t>(sizeof(Return));
13511353
for (size_t i = ndim - 1; i > 0; --i) {
1352-
strides[i - 1] = strides[i] * shape[i];
1354+
strides[i - 1] = strides[i] * static_cast<ssize_t>(shape[i]);
13531355
size *= shape[i];
13541356
}
13551357
size *= shape[0];

tests/test_buffers.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ test_initializer buffers([](py::module &m) {
109109
py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
110110
2, /* Number of dimensions */
111111
{ m.rows(), m.cols() }, /* Buffer dimensions */
112-
{ sizeof(float) * m.rows(), /* Strides (in bytes) for each index */
113-
sizeof(float) }
112+
{ static_cast<ssize_t>(sizeof(float) * m.rows()), /* Strides (in bytes) for each index */
113+
static_cast<ssize_t>(sizeof(float)) }
114114
);
115115
})
116116
;

tests/test_numpy_array.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <pybind11/stl.h>
1414

1515
#include <cstdint>
16+
#include <vector>
1617

1718
using arr = py::array;
1819
using arr_t = py::array_t<uint16_t, 0>;
@@ -294,4 +295,4 @@ test_initializer numpy_array([](py::module &m) {
294295
std::fill(a.mutable_data(), a.mutable_data() + a.size(), 42.);
295296
return a;
296297
});
297-
});
298+
});

tests/test_numpy_array.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ def assert_references(a, b, base=None):
203203
a2 = wrap(a1d)
204204
assert_references(a1d, a2, a1)
205205

206+
a1m = a1[::-1, ::-1, ::-1]
207+
a2 = wrap(a1m)
208+
assert_references(a1m, a2, a1)
209+
206210

207211
def test_numpy_view(capture):
208212
from pybind11_tests.array import ArrayClass

tests/test_numpy_dtypes.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ py::array_t<int32_t, 0> test_array_ctors(int i) {
226226

227227
std::vector<int32_t> data { 1, 2, 3, 4, 5, 6 };
228228
std::vector<size_t> shape { 3, 2 };
229-
std::vector<size_t> strides { 8, 4 };
229+
std::vector<ssize_t> strides { 8, 4 };
230230

231231
auto ptr = data.data();
232232
auto vptr = (void *) ptr;

0 commit comments

Comments
 (0)