Thanks to visit codestin.com
Credit goes to github.com

Skip to content

ENH: Add support for PEP 3118 buffers with offsets (non-contiguous) #7467

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 81 additions & 10 deletions numpy/core/src/multiarray/ctors.c
Original file line number Diff line number Diff line change
Expand Up @@ -1259,6 +1259,57 @@ PyArray_New(PyTypeObject *subtype, int nd, npy_intp *dims, int type_num,
return new;
}

int
_copy_from_pil_style(char* dest, int ndim, char *buf, npy_intp *shape,
npy_intp *strides, npy_intp *suboffsets, npy_intp itemsize)
{
npy_intp indices[NPY_MAXDIMS];
void **pointers[NPY_MAXDIMS];
char *pointer = (char*)buf;
npy_intp blocksize;
int ax;
/* Since suboffsets cannot be all negative, last_axis will be inevitably
* reset in the initialization loop below. However, the compiler cannot
* know this, so we preset last_axis to an arbitrary value. */
int last_axis = 0;

/* Initialize indices and pointers. */
for (ax = 0; ax < ndim; ++ax) {
indices[ax] = 0;
pointers[ax] = (void **)pointer;
if (suboffsets[ax] >= 0) {
last_axis = ax;
pointer = *((char**)pointer) + suboffsets[ax];
}
}
/* compute blocksize */
blocksize = itemsize;
for (ax = last_axis + 1; ax < ndim; ++ax)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If all suboffsets[ax] < 0 then last_axis never gets initialized when we get here. Even if that is not really possible (is it not?) the compiler does not seem able to figure it out, and that's what's making Travis unhappy. I guess the right value to initialize it at the beginning of the function is -1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suboffsets cannot be all negative because this would mean no dereferencing and the PEP requires that suboffsets pointer be NULL in this case. If not for that requirement, the right initial value would indeed be -1, but I don't want to introduce confusion between real -1 and -1 that means ndim - 1, so I just initialize last_axis to 0.

blocksize *= shape[ax];
/* main loop */
for (;;) {
memcpy(dest, pointer, blocksize);
dest += blocksize;
ax = last_axis;
/* Update indices and pointers for the next block. */
while (indices[ax] == shape[ax] - 1) {
if (ax == 0)
return 0;
indices[ax--] = 0;
}
++indices[ax];
/* update pointers */
pointer = (char *)pointers[ax] + strides[ax];
for (; ax <= last_axis; ++ax) {
pointers[ax] = (void **)pointer;
if (suboffsets[ax] >=0 ) {
pointer = *((char**)pointer) + suboffsets[ax];
}
}
}
return -1;
}


NPY_NO_EXPORT int
_array_from_buffer_3118(PyObject *obj, PyObject **out)
Expand Down Expand Up @@ -1344,23 +1395,43 @@ _array_from_buffer_3118(PyObject *obj, PyObject **out)
}
}

flags = NPY_ARRAY_BEHAVED & (view->readonly ? ~NPY_ARRAY_WRITEABLE : ~0);
r = PyArray_NewFromDescr(&PyArray_Type, descr,
nd, shape, strides, view->buf,
flags, NULL);
if (r == NULL ||
PyArray_SetBaseObject((PyArrayObject *)r, memoryview) < 0) {
Py_XDECREF(r);
Py_DECREF(memoryview);
return -1;
if (view->suboffsets == NULL) {
flags = NPY_ARRAY_BEHAVED & (view->readonly ? ~NPY_ARRAY_WRITEABLE : ~0);
r = PyArray_NewFromDescr(&PyArray_Type, descr,
nd, shape, strides, view->buf,
flags, NULL);
if (r == NULL ||
PyArray_SetBaseObject((PyArrayObject *)r, memoryview) < 0) {
Py_XDECREF(r);
Py_DECREF(memoryview);
return -1;
}
PyArray_UpdateFlags((PyArrayObject *)r, NPY_ARRAY_UPDATE_ALL);
}
PyArray_UpdateFlags((PyArrayObject *)r, NPY_ARRAY_UPDATE_ALL);
else {
/* Recompute strides */
npy_intp blocksize = view->itemsize;
for (k = nd - 1; k >= 0; --k) {
if (view->suboffsets[k] >= 0) {
strides[k] = blocksize;
}
blocksize *= shape[k];
}
r = PyArray_NewFromDescr(&PyArray_Type, descr,
nd, shape, strides, NULL, 0, NULL);
if (r == NULL)
goto fail2;
_copy_from_pil_style((char *)PyArray_DATA((PyArrayObject *)r), nd,
(char *)view->buf, shape, view->strides,
view->suboffsets, view->itemsize);

}
*out = r;
return 0;

fail:
Py_XDECREF(descr);
fail2:
Py_DECREF(memoryview);
return -1;

Expand Down
14 changes: 14 additions & 0 deletions numpy/core/tests/test_multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -5921,6 +5921,20 @@ def test_relaxed_strides(self):
shape, strides = get_buffer_info(arr, ['C_CONTIGUOUS'])
assert_(strides[-1] == 8)

def test_pil_style(self):
try:
import _testbuffer
except ImportError:
raise SkipTest("_testbuffer is not available")

for shape in [(2, 3), (2, 3, 4)]:
data = list(range(np.prod(shape)))
buffer = _testbuffer.ndarray(data, shape, format='i',
flags=_testbuffer.ND_PIL)
a = np.array(buffer)
b = np.array(data, dtype='i').reshape(shape)
yield assert_equal, a, b


class TestArrayAttributeDeletion(object):

Expand Down