diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index 1cdc771..ecf764c 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -3791,6 +3791,47 @@ class TestBufferProtocol(unittest.TestCase): self.assertNotEqual(m1, a) self.assertEqual(m1, m1) + def test_pybuffer_iscontiguous(self): + if ctypes: + # setup + c_ssize_t_p = ctypes.POINTER(ctypes.c_ssize_t) + class Py_buffer(ctypes.Structure): + _fields_ = [ + ('buf', ctypes.c_void_p), + ('obj', ctypes.py_object), + ('len', ctypes.c_ssize_t), + ('itemsize', ctypes.c_ssize_t), + ('readonly', ctypes.c_int), + ('ndim', ctypes.c_int), + ('format', ctypes.c_char_p), + ('shape', c_ssize_t_p), + ('strides', c_ssize_t_p), + ('suboffsets', c_ssize_t_p), + ('internal', ctypes.c_void_p), + ] + PyBuffer_IsContiguous = ctypes.pythonapi.PyBuffer_IsContiguous + PyBuffer_IsContiguous.argtypes = \ + (ctypes.POINTER(Py_buffer), ctypes.c_char) + + # issue #23352: contiguous buffer but suboffsets != NULL + s = b'testing' + buf = ctypes.create_string_buffer(s, len(s)) + pybuf = Py_buffer( + buf=ctypes.cast(buf, ctypes.c_void_p), + obj=None, + len=len(buf), + itemsize=1, + readonly=1, + ndim=1, + format=b'B', + shape=(ctypes.c_ssize_t * 1)(len(buf)), + strides=(ctypes.c_ssize_t * 1)(1), + suboffsets=(ctypes.c_ssize_t * 1)(-1), + internal=None, + ) + pybufref = ctypes.byref(pybuf) + self.assertNotEqual(0, PyBuffer_IsContiguous(pybufref, b'C')) + def test_memoryview_tobytes(self): # Many implicit tests are already in self.verify(). diff --git a/Objects/abstract.c b/Objects/abstract.c index 9ab60f2..ee06dcd 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -407,7 +407,20 @@ int PyBuffer_IsContiguous(const Py_buffer *view, char order) { - if (view->suboffsets != NULL) return 0; + if (view->suboffsets != NULL) { + /* + * If any suboffset is non-negative, then the buffer has + * indirect array referencing and the buffer is considered + * non-contiguous. If suboffsets is NULL or if all entries + * are negative (which is equivalent to suboffsets == NULL), + * then there is no dereferencing, which means the buffer + * might be contiguous (further checks are done below). + */ + int i; + for (i=0; indim; i++) + if (view->suboffsets[i] >= 0) + return 0; + } if (order == 'C') return _IsCContiguous(view);