diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2015-03-19 22:29:36 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2015-03-19 22:29:36 (GMT) |
commit | 31084ba52856329b972b84299b438bf5b73be292 (patch) | |
tree | 0aa36746b0aefcf57c9895e9593d2cc63fca3fe0 /Objects | |
parent | 9eb57c5fa50ed2f57d9320bb575371868316b5f2 (diff) | |
download | cpython-31084ba52856329b972b84299b438bf5b73be292.zip cpython-31084ba52856329b972b84299b438bf5b73be292.tar.gz cpython-31084ba52856329b972b84299b438bf5b73be292.tar.bz2 |
Issue #23632: Memoryviews now allow tuple indexing (including for multi-dimensional memoryviews).
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/memoryobject.c | 149 |
1 files changed, 120 insertions, 29 deletions
diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index 6142b4c..b6951ab 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -192,10 +192,10 @@ PyTypeObject _PyManagedBuffer_Type = { #define VIEW_ADDR(mv) (&((PyMemoryViewObject *)mv)->view) /* Check for the presence of suboffsets in the first dimension. */ -#define HAVE_PTR(suboffsets) (suboffsets && suboffsets[0] >= 0) +#define HAVE_PTR(suboffsets, dim) (suboffsets && suboffsets[dim] >= 0) /* Adjust ptr if suboffsets are present. */ -#define ADJUST_PTR(ptr, suboffsets) \ - (HAVE_PTR(suboffsets) ? *((char**)ptr) + suboffsets[0] : ptr) +#define ADJUST_PTR(ptr, suboffsets, dim) \ + (HAVE_PTR(suboffsets, dim) ? *((char**)ptr) + suboffsets[dim] : ptr) /* Memoryview buffer properties */ #define MV_C_CONTIGUOUS(flags) (flags&(_Py_MEMORYVIEW_SCALAR|_Py_MEMORYVIEW_C)) @@ -332,11 +332,11 @@ copy_base(const Py_ssize_t *shape, Py_ssize_t itemsize, char *p; Py_ssize_t i; for (i=0, p=mem; i < shape[0]; p+=itemsize, sptr+=sstrides[0], i++) { - char *xsptr = ADJUST_PTR(sptr, ssuboffsets); + char *xsptr = ADJUST_PTR(sptr, ssuboffsets, 0); memcpy(p, xsptr, itemsize); } for (i=0, p=mem; i < shape[0]; p+=itemsize, dptr+=dstrides[0], i++) { - char *xdptr = ADJUST_PTR(dptr, dsuboffsets); + char *xdptr = ADJUST_PTR(dptr, dsuboffsets, 0); memcpy(xdptr, p, itemsize); } } @@ -364,8 +364,8 @@ copy_rec(const Py_ssize_t *shape, Py_ssize_t ndim, Py_ssize_t itemsize, } for (i = 0; i < shape[0]; dptr+=dstrides[0], sptr+=sstrides[0], i++) { - char *xdptr = ADJUST_PTR(dptr, dsuboffsets); - char *xsptr = ADJUST_PTR(sptr, ssuboffsets); + char *xdptr = ADJUST_PTR(dptr, dsuboffsets, 0); + char *xsptr = ADJUST_PTR(sptr, ssuboffsets, 0); copy_rec(shape+1, ndim-1, itemsize, xdptr, dstrides+1, dsuboffsets ? dsuboffsets+1 : NULL, @@ -2057,7 +2057,7 @@ tolist_base(const char *ptr, const Py_ssize_t *shape, return NULL; for (i = 0; i < shape[0]; ptr+=strides[0], i++) { - const char *xptr = ADJUST_PTR(ptr, suboffsets); + const char *xptr = ADJUST_PTR(ptr, suboffsets, 0); item = unpack_single(xptr, fmt); if (item == NULL) { Py_DECREF(lst); @@ -2091,7 +2091,7 @@ tolist_rec(const char *ptr, Py_ssize_t ndim, const Py_ssize_t *shape, return NULL; for (i = 0; i < shape[0]; ptr+=strides[0], i++) { - const char *xptr = ADJUST_PTR(ptr, suboffsets); + const char *xptr = ADJUST_PTR(ptr, suboffsets, 0); item = tolist_rec(xptr, ndim-1, shape+1, strides+1, suboffsets ? suboffsets+1 : NULL, fmt); @@ -2171,33 +2171,66 @@ memory_repr(PyMemoryViewObject *self) /* Indexing and slicing */ /**************************************************************************/ -/* Get the pointer to the item at index. */ static char * -ptr_from_index(Py_buffer *view, Py_ssize_t index) +lookup_dimension(Py_buffer *view, char *ptr, int dim, Py_ssize_t index) { - char *ptr; - Py_ssize_t nitems; /* items in the first dimension */ + Py_ssize_t nitems; /* items in the given dimension */ assert(view->shape); assert(view->strides); - nitems = view->shape[0]; + nitems = view->shape[dim]; if (index < 0) { index += nitems; } if (index < 0 || index >= nitems) { - PyErr_SetString(PyExc_IndexError, "index out of bounds"); + PyErr_Format(PyExc_IndexError, + "index out of bounds on dimension %d", dim + 1); return NULL; } - ptr = (char *)view->buf; - ptr += view->strides[0] * index; + ptr += view->strides[dim] * index; - ptr = ADJUST_PTR(ptr, view->suboffsets); + ptr = ADJUST_PTR(ptr, view->suboffsets, dim); return ptr; } +/* Get the pointer to the item at index. */ +static char * +ptr_from_index(Py_buffer *view, Py_ssize_t index) +{ + char *ptr = (char *)view->buf; + return lookup_dimension(view, ptr, 0, index); +} + +/* Get the pointer to the item at tuple. */ +static char * +ptr_from_tuple(Py_buffer *view, PyObject *tup) +{ + char *ptr = (char *)view->buf; + Py_ssize_t dim, nindices = PyTuple_GET_SIZE(tup); + + if (nindices > view->ndim) { + PyErr_Format(PyExc_TypeError, + "cannot index %zd-dimension view with %zd-element tuple", + view->ndim, nindices); + return NULL; + } + + for (dim = 0; dim < nindices; dim++) { + Py_ssize_t index; + index = PyNumber_AsSsize_t(PyTuple_GET_ITEM(tup, dim), + PyExc_IndexError); + if (index == -1 && PyErr_Occurred()) + return NULL; + ptr = lookup_dimension(view, ptr, dim, index); + if (ptr == NULL) + return NULL; + } + return ptr; +} + /* Return the item at index. In a one-dimensional view, this is an object with the type specified by view->format. Otherwise, the item is a sub-view. The function is used in memory_subscript() and memory_as_sequence. */ @@ -2229,6 +2262,32 @@ memory_item(PyMemoryViewObject *self, Py_ssize_t index) return NULL; } +/* Return the item at position *key* (a tuple of indices). */ +static PyObject * +memory_item_multi(PyMemoryViewObject *self, PyObject *tup) +{ + Py_buffer *view = &(self->view); + const char *fmt; + Py_ssize_t nindices = PyTuple_GET_SIZE(tup); + char *ptr; + + CHECK_RELEASED(self); + + fmt = adjust_fmt(view); + if (fmt == NULL) + return NULL; + + if (nindices < view->ndim) { + PyErr_SetString(PyExc_NotImplementedError, + "sub-views are not implemented"); + return NULL; + } + ptr = ptr_from_tuple(view, tup); + if (ptr == NULL) + return NULL; + return unpack_single(ptr, fmt); +} + Py_LOCAL_INLINE(int) init_slice(Py_buffer *base, PyObject *key, int dim) { @@ -2277,6 +2336,22 @@ is_multislice(PyObject *key) return 1; } +static Py_ssize_t +is_multiindex(PyObject *key) +{ + Py_ssize_t size, i; + + if (!PyTuple_Check(key)) + return 0; + size = PyTuple_GET_SIZE(key); + for (i = 0; i < size; i++) { + PyObject *x = PyTuple_GET_ITEM(key, i); + if (!PyIndex_Check(x)) + return 0; + } + return 1; +} + /* mv[obj] returns an object holding the data for one element if obj fully indexes the memoryview or another memoryview object if it does not. @@ -2332,6 +2407,9 @@ memory_subscript(PyMemoryViewObject *self, PyObject *key) return (PyObject *)sliced; } + else if (is_multiindex(key)) { + return memory_item_multi(self, key); + } else if (is_multislice(key)) { PyErr_SetString(PyExc_NotImplementedError, "multi-dimensional slicing is not implemented"); @@ -2376,14 +2454,15 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value) return -1; } } - if (view->ndim != 1) { - PyErr_SetString(PyExc_NotImplementedError, - "memoryview assignments are currently restricted to ndim = 1"); - return -1; - } if (PyIndex_Check(key)) { - Py_ssize_t index = PyNumber_AsSsize_t(key, PyExc_IndexError); + Py_ssize_t index; + if (1 < view->ndim) { + PyErr_SetString(PyExc_NotImplementedError, + "sub-views are not implemented"); + return -1; + } + index = PyNumber_AsSsize_t(key, PyExc_IndexError); if (index == -1 && PyErr_Occurred()) return -1; ptr = ptr_from_index(view, index); @@ -2418,7 +2497,19 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value) PyBuffer_Release(&src); return ret; } - else if (PySlice_Check(key) || is_multislice(key)) { + if (is_multiindex(key)) { + char *ptr; + if (PyTuple_GET_SIZE(key) < view->ndim) { + PyErr_SetString(PyExc_NotImplementedError, + "sub-views are not implemented"); + return -1; + } + ptr = ptr_from_tuple(view, key); + if (ptr == NULL) + return -1; + return pack_single(ptr, value, fmt); + } + if (PySlice_Check(key) || is_multislice(key)) { /* Call memory_subscript() to produce a sliced lvalue, then copy rvalue into lvalue. This is already implemented in _testbuffer.c. */ PyErr_SetString(PyExc_NotImplementedError, @@ -2591,8 +2682,8 @@ cmp_base(const char *p, const char *q, const Py_ssize_t *shape, int equal; for (i = 0; i < shape[0]; p+=pstrides[0], q+=qstrides[0], i++) { - const char *xp = ADJUST_PTR(p, psuboffsets); - const char *xq = ADJUST_PTR(q, qsuboffsets); + const char *xp = ADJUST_PTR(p, psuboffsets, 0); + const char *xq = ADJUST_PTR(q, qsuboffsets, 0); equal = unpack_cmp(xp, xq, fmt, unpack_p, unpack_q); if (equal <= 0) return equal; @@ -2626,8 +2717,8 @@ cmp_rec(const char *p, const char *q, } for (i = 0; i < shape[0]; p+=pstrides[0], q+=qstrides[0], i++) { - const char *xp = ADJUST_PTR(p, psuboffsets); - const char *xq = ADJUST_PTR(q, qsuboffsets); + const char *xp = ADJUST_PTR(p, psuboffsets, 0); + const char *xq = ADJUST_PTR(q, qsuboffsets, 0); equal = cmp_rec(xp, xq, ndim-1, shape+1, pstrides+1, psuboffsets ? psuboffsets+1 : NULL, qstrides+1, qsuboffsets ? qsuboffsets+1 : NULL, |