diff options
author | David Cournapeau <cournape@gmail.com> | 2009-04-30 08:36:36 +0000 |
---|---|---|
committer | David Cournapeau <cournape@gmail.com> | 2009-04-30 08:36:36 +0000 |
commit | efd5b424216fb0ce6aa3c7529d8c3e1d5ae34116 (patch) | |
tree | f2a4e44ebcdaf4a90fb59c1f0c392728d9846239 /numpy/core/src | |
parent | fe1f93464b6115ad441bd328ca87f78f2852de49 (diff) | |
download | numpy-efd5b424216fb0ce6aa3c7529d8c3e1d5ae34116.tar.gz |
Put mapping protocol in separate file.
Diffstat (limited to 'numpy/core/src')
-rw-r--r-- | numpy/core/src/arraymapping.c | 990 | ||||
-rw-r--r-- | numpy/core/src/arraymapping.h | 35 | ||||
-rw-r--r-- | numpy/core/src/arrayobject.c | 996 |
3 files changed, 1026 insertions, 995 deletions
diff --git a/numpy/core/src/arraymapping.c b/numpy/core/src/arraymapping.c new file mode 100644 index 000000000..3c4520e94 --- /dev/null +++ b/numpy/core/src/arraymapping.c @@ -0,0 +1,990 @@ +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include "structmember.h" + +/*#include <stdio.h>*/ +#define _MULTIARRAYMODULE +#define NPY_NO_PREFIX +#include "numpy/arrayobject.h" + +#include "arrayobject.h" +#include "arrayiterators.h" +#include "arraymapping.h" + +/************************************************************************* + **************** Implement Mapping Protocol *************************** + *************************************************************************/ + +NPY_NO_EXPORT Py_ssize_t +array_length(PyArrayObject *self) +{ + if (self->nd != 0) { + return self->dimensions[0]; + } else { + PyErr_SetString(PyExc_TypeError, "len() of unsized object"); + return -1; + } +} + +NPY_NO_EXPORT PyObject * +array_big_item(PyArrayObject *self, intp i) +{ + char *item; + PyArrayObject *r; + + if(self->nd == 0) { + PyErr_SetString(PyExc_IndexError, + "0-d arrays can't be indexed"); + return NULL; + } + if ((item = index2ptr(self, i)) == NULL) { + return NULL; + } + Py_INCREF(self->descr); + r = (PyArrayObject *)PyArray_NewFromDescr(self->ob_type, + self->descr, + self->nd-1, + self->dimensions+1, + self->strides+1, item, + self->flags, + (PyObject *)self); + if (r == NULL) { + return NULL; + } + Py_INCREF(self); + r->base = (PyObject *)self; + PyArray_UpdateFlags(r, CONTIGUOUS | FORTRAN); + return (PyObject *)r; +} + +/* contains optimization for 1-d arrays */ +NPY_NO_EXPORT PyObject * +array_item_nice(PyArrayObject *self, Py_ssize_t i) +{ + if (self->nd == 1) { + char *item; + if ((item = index2ptr(self, i)) == NULL) { + return NULL; + } + return PyArray_Scalar(item, self->descr, (PyObject *)self); + } + else { + return PyArray_Return( + (PyArrayObject *) array_big_item(self, (intp) i)); + } +} + +NPY_NO_EXPORT int +array_ass_big_item(PyArrayObject *self, intp i, PyObject *v) +{ + PyArrayObject *tmp; + char *item; + int ret; + + if (v == NULL) { + PyErr_SetString(PyExc_ValueError, + "can't delete array elements"); + return -1; + } + if (!PyArray_ISWRITEABLE(self)) { + PyErr_SetString(PyExc_RuntimeError, + "array is not writeable"); + return -1; + } + if (self->nd == 0) { + PyErr_SetString(PyExc_IndexError, + "0-d arrays can't be indexed."); + return -1; + } + + + if (self->nd > 1) { + if((tmp = (PyArrayObject *)array_big_item(self, i)) == NULL) { + return -1; + } + ret = PyArray_CopyObject(tmp, v); + Py_DECREF(tmp); + return ret; + } + + if ((item = index2ptr(self, i)) == NULL) { + return -1; + } + if (self->descr->f->setitem(v, item, self) == -1) { + return -1; + } + return 0; +} + +/* -------------------------------------------------------------- */ + +static void +_swap_axes(PyArrayMapIterObject *mit, PyArrayObject **ret, int getmap) +{ + PyObject *new; + int n1, n2, n3, val, bnd; + int i; + PyArray_Dims permute; + intp d[MAX_DIMS]; + PyArrayObject *arr; + + permute.ptr = d; + permute.len = mit->nd; + + /* + * arr might not have the right number of dimensions + * and need to be reshaped first by pre-pending ones + */ + arr = *ret; + if (arr->nd != mit->nd) { + for (i = 1; i <= arr->nd; i++) { + permute.ptr[mit->nd-i] = arr->dimensions[arr->nd-i]; + } + for (i = 0; i < mit->nd-arr->nd; i++) { + permute.ptr[i] = 1; + } + new = PyArray_Newshape(arr, &permute, PyArray_ANYORDER); + Py_DECREF(arr); + *ret = (PyArrayObject *)new; + if (new == NULL) { + return; + } + } + + /* + * Setting and getting need to have different permutations. + * On the get we are permuting the returned object, but on + * setting we are permuting the object-to-be-set. + * The set permutation is the inverse of the get permutation. + */ + + /* + * For getting the array the tuple for transpose is + * (n1,...,n1+n2-1,0,...,n1-1,n1+n2,...,n3-1) + * n1 is the number of dimensions of the broadcast index array + * n2 is the number of dimensions skipped at the start + * n3 is the number of dimensions of the result + */ + + /* + * For setting the array the tuple for transpose is + * (n2,...,n1+n2-1,0,...,n2-1,n1+n2,...n3-1) + */ + n1 = mit->iters[0]->nd_m1 + 1; + n2 = mit->iteraxes[0]; + n3 = mit->nd; + + /* use n1 as the boundary if getting but n2 if setting */ + bnd = getmap ? n1 : n2; + val = bnd; + i = 0; + while (val < n1 + n2) { + permute.ptr[i++] = val++; + } + val = 0; + while (val < bnd) { + permute.ptr[i++] = val++; + } + val = n1 + n2; + while (val < n3) { + permute.ptr[i++] = val++; + } + new = PyArray_Transpose(*ret, &permute); + Py_DECREF(*ret); + *ret = (PyArrayObject *)new; +} + +static PyObject * +PyArray_GetMap(PyArrayMapIterObject *mit) +{ + + PyArrayObject *ret, *temp; + PyArrayIterObject *it; + int index; + int swap; + PyArray_CopySwapFunc *copyswap; + + /* Unbound map iterator --- Bind should have been called */ + if (mit->ait == NULL) { + return NULL; + } + + /* This relies on the map iterator object telling us the shape + of the new array in nd and dimensions. + */ + temp = mit->ait->ao; + Py_INCREF(temp->descr); + ret = (PyArrayObject *) + PyArray_NewFromDescr(temp->ob_type, + temp->descr, + mit->nd, mit->dimensions, + NULL, NULL, + PyArray_ISFORTRAN(temp), + (PyObject *)temp); + if (ret == NULL) { + return NULL; + } + + /* + * Now just iterate through the new array filling it in + * with the next object from the original array as + * defined by the mapping iterator + */ + + if ((it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)ret)) == NULL) { + Py_DECREF(ret); + return NULL; + } + index = it->size; + swap = (PyArray_ISNOTSWAPPED(temp) != PyArray_ISNOTSWAPPED(ret)); + copyswap = ret->descr->f->copyswap; + PyArray_MapIterReset(mit); + while (index--) { + copyswap(it->dataptr, mit->dataptr, swap, ret); + PyArray_MapIterNext(mit); + PyArray_ITER_NEXT(it); + } + Py_DECREF(it); + + /* check for consecutive axes */ + if ((mit->subspace != NULL) && (mit->consec)) { + if (mit->iteraxes[0] > 0) { /* then we need to swap */ + _swap_axes(mit, &ret, 1); + } + } + return (PyObject *)ret; +} + +static int +PyArray_SetMap(PyArrayMapIterObject *mit, PyObject *op) +{ + PyObject *arr = NULL; + PyArrayIterObject *it; + int index; + int swap; + PyArray_CopySwapFunc *copyswap; + PyArray_Descr *descr; + + /* Unbound Map Iterator */ + if (mit->ait == NULL) { + return -1; + } + descr = mit->ait->ao->descr; + Py_INCREF(descr); + arr = PyArray_FromAny(op, descr, 0, 0, FORCECAST, NULL); + if (arr == NULL) { + return -1; + } + if ((mit->subspace != NULL) && (mit->consec)) { + if (mit->iteraxes[0] > 0) { /* then we need to swap */ + _swap_axes(mit, (PyArrayObject **)&arr, 0); + if (arr == NULL) { + return -1; + } + } + } + + /* Be sure values array is "broadcastable" + to shape of mit->dimensions, mit->nd */ + + if ((it = (PyArrayIterObject *)\ + PyArray_BroadcastToShape(arr, mit->dimensions, mit->nd))==NULL) { + Py_DECREF(arr); + return -1; + } + + index = mit->size; + swap = (PyArray_ISNOTSWAPPED(mit->ait->ao) != + (PyArray_ISNOTSWAPPED(arr))); + copyswap = PyArray_DESCR(arr)->f->copyswap; + PyArray_MapIterReset(mit); + /* Need to decref hasobject arrays */ + if (PyDataType_FLAGCHK(descr, NPY_ITEM_REFCOUNT)) { + while (index--) { + PyArray_Item_XDECREF(mit->dataptr, PyArray_DESCR(arr)); + PyArray_Item_INCREF(it->dataptr, PyArray_DESCR(arr)); + memmove(mit->dataptr, it->dataptr, sizeof(PyObject *)); + /* ignored unless VOID array with object's */ + if (swap) { + copyswap(mit->dataptr, NULL, swap, arr); + } + PyArray_MapIterNext(mit); + PyArray_ITER_NEXT(it); + } + Py_DECREF(arr); + Py_DECREF(it); + return 0; + } + while(index--) { + memmove(mit->dataptr, it->dataptr, PyArray_ITEMSIZE(arr)); + if (swap) { + copyswap(mit->dataptr, NULL, swap, arr); + } + PyArray_MapIterNext(mit); + PyArray_ITER_NEXT(it); + } + Py_DECREF(arr); + Py_DECREF(it); + return 0; +} + +NPY_NO_EXPORT int +count_new_axes_0d(PyObject *tuple) +{ + int i, argument_count; + int ellipsis_count = 0; + int newaxis_count = 0; + + argument_count = PyTuple_GET_SIZE(tuple); + for (i = 0; i < argument_count; ++i) { + PyObject *arg = PyTuple_GET_ITEM(tuple, i); + if (arg == Py_Ellipsis && !ellipsis_count) { + ellipsis_count++; + } + else if (arg == Py_None) { + newaxis_count++; + } + else { + break; + } + } + if (i < argument_count) { + PyErr_SetString(PyExc_IndexError, + "0-d arrays can only use a single ()" + " or a list of newaxes (and a single ...)" + " as an index"); + return -1; + } + if (newaxis_count > MAX_DIMS) { + PyErr_SetString(PyExc_IndexError, "too many dimensions"); + return -1; + } + return newaxis_count; +} + +NPY_NO_EXPORT PyObject * +add_new_axes_0d(PyArrayObject *arr, int newaxis_count) +{ + PyArrayObject *other; + intp dimensions[MAX_DIMS]; + int i; + + for (i = 0; i < newaxis_count; ++i) { + dimensions[i] = 1; + } + Py_INCREF(arr->descr); + if ((other = (PyArrayObject *) + PyArray_NewFromDescr(arr->ob_type, arr->descr, + newaxis_count, dimensions, + NULL, arr->data, + arr->flags, + (PyObject *)arr)) == NULL) + return NULL; + other->base = (PyObject *)arr; + Py_INCREF(arr); + return (PyObject *)other; +} + + +/* This checks the args for any fancy indexing objects */ + +static int +fancy_indexing_check(PyObject *args) +{ + int i, n; + PyObject *obj; + int retval = SOBJ_NOTFANCY; + + if (PyTuple_Check(args)) { + n = PyTuple_GET_SIZE(args); + if (n >= MAX_DIMS) { + return SOBJ_TOOMANY; + } + for (i = 0; i < n; i++) { + obj = PyTuple_GET_ITEM(args,i); + if (PyArray_Check(obj)) { + if (PyArray_ISINTEGER(obj) || + PyArray_ISBOOL(obj)) { + retval = SOBJ_ISFANCY; + } + else { + retval = SOBJ_BADARRAY; + break; + } + } + else if (PySequence_Check(obj)) { + retval = SOBJ_ISFANCY; + } + } + } + else if (PyArray_Check(args)) { + if ((PyArray_TYPE(args)==PyArray_BOOL) || + (PyArray_ISINTEGER(args))) { + return SOBJ_ISFANCY; + } + else { + return SOBJ_BADARRAY; + } + } + else if (PySequence_Check(args)) { + /* + * Sequences < MAX_DIMS with any slice objects + * or newaxis, or Ellipsis is considered standard + * as long as there are also no Arrays and or additional + * sequences embedded. + */ + retval = SOBJ_ISFANCY; + n = PySequence_Size(args); + if (n < 0 || n >= MAX_DIMS) { + return SOBJ_ISFANCY; + } + for (i = 0; i < n; i++) { + obj = PySequence_GetItem(args, i); + if (obj == NULL) { + return SOBJ_ISFANCY; + } + if (PyArray_Check(obj)) { + if (PyArray_ISINTEGER(obj) || PyArray_ISBOOL(obj)) { + retval = SOBJ_LISTTUP; + } + else { + retval = SOBJ_BADARRAY; + } + } + else if (PySequence_Check(obj)) { + retval = SOBJ_LISTTUP; + } + else if (PySlice_Check(obj) || obj == Py_Ellipsis || + obj == Py_None) { + retval = SOBJ_NOTFANCY; + } + Py_DECREF(obj); + if (retval > SOBJ_ISFANCY) { + return retval; + } + } + } + return retval; +} + +/* + * Called when treating array object like a mapping -- called first from + * Python when using a[object] unless object is a standard slice object + * (not an extended one). + * + * There are two situations: + * + * 1 - the subscript is a standard view and a reference to the + * array can be returned + * + * 2 - the subscript uses Boolean masks or integer indexing and + * therefore a new array is created and returned. + */ + +NPY_NO_EXPORT PyObject * +array_subscript_simple(PyArrayObject *self, PyObject *op) +{ + intp dimensions[MAX_DIMS], strides[MAX_DIMS]; + intp offset; + int nd; + PyArrayObject *other; + intp value; + + value = PyArray_PyIntAsIntp(op); + if (!PyErr_Occurred()) { + return array_big_item(self, value); + } + PyErr_Clear(); + + /* Standard (view-based) Indexing */ + if ((nd = parse_index(self, op, dimensions, strides, &offset)) == -1) { + return NULL; + } + /* This will only work if new array will be a view */ + Py_INCREF(self->descr); + if ((other = (PyArrayObject *) + PyArray_NewFromDescr(self->ob_type, self->descr, + nd, dimensions, + strides, self->data+offset, + self->flags, + (PyObject *)self)) == NULL) { + return NULL; + } + other->base = (PyObject *)self; + Py_INCREF(self); + PyArray_UpdateFlags(other, UPDATE_ALL); + return (PyObject *)other; +} + +NPY_NO_EXPORT PyObject * +array_subscript(PyArrayObject *self, PyObject *op) +{ + int nd, fancy; + PyArrayObject *other; + PyArrayMapIterObject *mit; + PyObject *obj; + + if (PyString_Check(op) || PyUnicode_Check(op)) { + if (self->descr->names) { + obj = PyDict_GetItem(self->descr->fields, op); + if (obj != NULL) { + PyArray_Descr *descr; + int offset; + PyObject *title; + + if (PyArg_ParseTuple(obj, "Oi|O", &descr, &offset, &title)) { + Py_INCREF(descr); + return PyArray_GetField(self, descr, offset); + } + } + } + + PyErr_Format(PyExc_ValueError, + "field named %s not found.", + PyString_AsString(op)); + return NULL; + } + + /* Check for multiple field access */ + if (self->descr->names && PySequence_Check(op) && !PyTuple_Check(op)) { + int seqlen, i; + seqlen = PySequence_Size(op); + for (i = 0; i < seqlen; i++) { + obj = PySequence_GetItem(op, i); + if (!PyString_Check(obj) && !PyUnicode_Check(obj)) { + Py_DECREF(obj); + break; + } + Py_DECREF(obj); + } + /* + * extract multiple fields if all elements in sequence + * are either string or unicode (i.e. no break occurred). + */ + fancy = ((seqlen > 0) && (i == seqlen)); + if (fancy) { + PyObject *_numpy_internal; + _numpy_internal = PyImport_ImportModule("numpy.core._internal"); + if (_numpy_internal == NULL) { + return NULL; + } + obj = PyObject_CallMethod(_numpy_internal, + "_index_fields", "OO", self, op); + Py_DECREF(_numpy_internal); + return obj; + } + } + + if (op == Py_Ellipsis) { + Py_INCREF(self); + return (PyObject *)self; + } + + if (self->nd == 0) { + if (op == Py_None) { + return add_new_axes_0d(self, 1); + } + if (PyTuple_Check(op)) { + if (0 == PyTuple_GET_SIZE(op)) { + Py_INCREF(self); + return (PyObject *)self; + } + if ((nd = count_new_axes_0d(op)) == -1) { + return NULL; + } + return add_new_axes_0d(self, nd); + } + /* Allow Boolean mask selection also */ + if ((PyArray_Check(op) && (PyArray_DIMS(op)==0) + && PyArray_ISBOOL(op))) { + if (PyObject_IsTrue(op)) { + Py_INCREF(self); + return (PyObject *)self; + } + else { + intp oned = 0; + Py_INCREF(self->descr); + return PyArray_NewFromDescr(self->ob_type, + self->descr, + 1, &oned, + NULL, NULL, + NPY_DEFAULT, + NULL); + } + } + PyErr_SetString(PyExc_IndexError, "0-d arrays can't be indexed."); + return NULL; + } + + fancy = fancy_indexing_check(op); + if (fancy != SOBJ_NOTFANCY) { + int oned; + + oned = ((self->nd == 1) && + !(PyTuple_Check(op) && PyTuple_GET_SIZE(op) > 1)); + + /* wrap arguments into a mapiter object */ + mit = (PyArrayMapIterObject *) PyArray_MapIterNew(op, oned, fancy); + if (mit == NULL) { + return NULL; + } + if (oned) { + PyArrayIterObject *it; + PyObject *rval; + it = (PyArrayIterObject *) PyArray_IterNew((PyObject *)self); + if (it == NULL) { + Py_DECREF(mit); + return NULL; + } + rval = iter_subscript(it, mit->indexobj); + Py_DECREF(it); + Py_DECREF(mit); + return rval; + } + PyArray_MapIterBind(mit, self); + other = (PyArrayObject *)PyArray_GetMap(mit); + Py_DECREF(mit); + return (PyObject *)other; + } + + return array_subscript_simple(self, op); +} + + +/* + * Another assignment hacked by using CopyObject. + * This only works if subscript returns a standard view. + * Again there are two cases. In the first case, PyArray_CopyObject + * can be used. In the second case, a new indexing function has to be + * used. + */ + +static int +array_ass_sub_simple(PyArrayObject *self, PyObject *index, PyObject *op) +{ + int ret; + PyArrayObject *tmp; + intp value; + + value = PyArray_PyIntAsIntp(index); + if (!error_converting(value)) { + return array_ass_big_item(self, value, op); + } + PyErr_Clear(); + + /* Rest of standard (view-based) indexing */ + + if (PyArray_CheckExact(self)) { + tmp = (PyArrayObject *)array_subscript_simple(self, index); + if (tmp == NULL) { + return -1; + } + } + else { + PyObject *tmp0; + tmp0 = PyObject_GetItem((PyObject *)self, index); + if (tmp0 == NULL) { + return -1; + } + if (!PyArray_Check(tmp0)) { + PyErr_SetString(PyExc_RuntimeError, + "Getitem not returning array."); + Py_DECREF(tmp0); + return -1; + } + tmp = (PyArrayObject *)tmp0; + } + + if (PyArray_ISOBJECT(self) && (tmp->nd == 0)) { + ret = tmp->descr->f->setitem(op, tmp->data, tmp); + } + else { + ret = PyArray_CopyObject(tmp, op); + } + Py_DECREF(tmp); + return ret; +} + + +/* return -1 if tuple-object seq is not a tuple of integers. + otherwise fill vals with converted integers +*/ +static int +_tuple_of_integers(PyObject *seq, intp *vals, int maxvals) +{ + int i; + PyObject *obj; + intp temp; + + for(i=0; i<maxvals; i++) { + obj = PyTuple_GET_ITEM(seq, i); + if ((PyArray_Check(obj) && PyArray_NDIM(obj) > 0) + || PyList_Check(obj)) { + return -1; + } + temp = PyArray_PyIntAsIntp(obj); + if (error_converting(temp)) { + return -1; + } + vals[i] = temp; + } + return 0; +} + + +static int +array_ass_sub(PyArrayObject *self, PyObject *index, PyObject *op) +{ + int ret, oned, fancy; + PyArrayMapIterObject *mit; + intp vals[MAX_DIMS]; + + if (op == NULL) { + PyErr_SetString(PyExc_ValueError, + "cannot delete array elements"); + return -1; + } + if (!PyArray_ISWRITEABLE(self)) { + PyErr_SetString(PyExc_RuntimeError, + "array is not writeable"); + return -1; + } + + if (PyInt_Check(index) || PyArray_IsScalar(index, Integer) || + PyLong_Check(index) || (PyIndex_Check(index) && + !PySequence_Check(index))) { + intp value; + value = PyArray_PyIntAsIntp(index); + if (PyErr_Occurred()) { + PyErr_Clear(); + } + else { + return array_ass_big_item(self, value, op); + } + } + + if (PyString_Check(index) || PyUnicode_Check(index)) { + if (self->descr->names) { + PyObject *obj; + + obj = PyDict_GetItem(self->descr->fields, index); + if (obj != NULL) { + PyArray_Descr *descr; + int offset; + PyObject *title; + + if (PyArg_ParseTuple(obj, "Oi|O", &descr, &offset, &title)) { + Py_INCREF(descr); + return PyArray_SetField(self, descr, offset, op); + } + } + } + + PyErr_Format(PyExc_ValueError, + "field named %s not found.", + PyString_AsString(index)); + return -1; + } + + if (self->nd == 0) { + /* + * Several different exceptions to the 0-d no-indexing rule + * + * 1) ellipses + * 2) empty tuple + * 3) Using newaxis (None) + * 4) Boolean mask indexing + */ + if (index == Py_Ellipsis || index == Py_None || + (PyTuple_Check(index) && (0 == PyTuple_GET_SIZE(index) || + count_new_axes_0d(index) > 0))) { + return self->descr->f->setitem(op, self->data, self); + } + if (PyBool_Check(index) || PyArray_IsScalar(index, Bool) || + (PyArray_Check(index) && (PyArray_DIMS(index)==0) && + PyArray_ISBOOL(index))) { + if (PyObject_IsTrue(index)) { + return self->descr->f->setitem(op, self->data, self); + } + else { /* don't do anything */ + return 0; + } + } + PyErr_SetString(PyExc_IndexError, "0-d arrays can't be indexed."); + return -1; + } + + /* optimization for integer-tuple */ + if (self->nd > 1 && + (PyTuple_Check(index) && (PyTuple_GET_SIZE(index) == self->nd)) + && (_tuple_of_integers(index, vals, self->nd) >= 0)) { + int i; + char *item; + + for (i = 0; i < self->nd; i++) { + if (vals[i] < 0) { + vals[i] += self->dimensions[i]; + } + if ((vals[i] < 0) || (vals[i] >= self->dimensions[i])) { + PyErr_Format(PyExc_IndexError, + "index (%"INTP_FMT") out of range "\ + "(0<=index<%"INTP_FMT") in dimension %d", + vals[i], self->dimensions[i], i); + return -1; + } + } + item = PyArray_GetPtr(self, vals); + return self->descr->f->setitem(op, item, self); + } + PyErr_Clear(); + + fancy = fancy_indexing_check(index); + if (fancy != SOBJ_NOTFANCY) { + oned = ((self->nd == 1) && + !(PyTuple_Check(index) && PyTuple_GET_SIZE(index) > 1)); + mit = (PyArrayMapIterObject *) PyArray_MapIterNew(index, oned, fancy); + if (mit == NULL) { + return -1; + } + if (oned) { + PyArrayIterObject *it; + int rval; + + it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)self); + if (it == NULL) { + Py_DECREF(mit); + return -1; + } + rval = iter_ass_subscript(it, mit->indexobj, op); + Py_DECREF(it); + Py_DECREF(mit); + return rval; + } + PyArray_MapIterBind(mit, self); + ret = PyArray_SetMap(mit, op); + Py_DECREF(mit); + return ret; + } + + return array_ass_sub_simple(self, index, op); +} + + +/* + * There are places that require that array_subscript return a PyArrayObject + * and not possibly a scalar. Thus, this is the function exposed to + * Python so that 0-dim arrays are passed as scalars + */ + + +static PyObject * +array_subscript_nice(PyArrayObject *self, PyObject *op) +{ + + PyArrayObject *mp; + intp vals[MAX_DIMS]; + + if (PyInt_Check(op) || PyArray_IsScalar(op, Integer) || + PyLong_Check(op) || (PyIndex_Check(op) && + !PySequence_Check(op))) { + intp value; + value = PyArray_PyIntAsIntp(op); + if (PyErr_Occurred()) { + PyErr_Clear(); + } + else { + return array_item_nice(self, (Py_ssize_t) value); + } + } + /* optimization for a tuple of integers */ + if (self->nd > 1 && PyTuple_Check(op) && + (PyTuple_GET_SIZE(op) == self->nd) + && (_tuple_of_integers(op, vals, self->nd) >= 0)) { + int i; + char *item; + + for (i = 0; i < self->nd; i++) { + if (vals[i] < 0) { + vals[i] += self->dimensions[i]; + } + if ((vals[i] < 0) || (vals[i] >= self->dimensions[i])) { + PyErr_Format(PyExc_IndexError, + "index (%"INTP_FMT") out of range "\ + "(0<=index<%"INTP_FMT") in dimension %d", + vals[i], self->dimensions[i], i); + return NULL; + } + } + item = PyArray_GetPtr(self, vals); + return PyArray_Scalar(item, self->descr, (PyObject *)self); + } + PyErr_Clear(); + + mp = (PyArrayObject *)array_subscript(self, op); + /* + * mp could be a scalar if op is not an Int, Scalar, Long or other Index + * object and still convertable to an integer (so that the code goes to + * array_subscript_simple). So, this cast is a bit dangerous.. + */ + + /* + * The following is just a copy of PyArray_Return with an + * additional logic in the nd == 0 case. + */ + + if (mp == NULL) { + return NULL; + } + if (PyErr_Occurred()) { + Py_XDECREF(mp); + return NULL; + } + if (PyArray_Check(mp) && mp->nd == 0) { + Bool noellipses = TRUE; + if ((op == Py_Ellipsis) || PyString_Check(op) || PyUnicode_Check(op)) { + noellipses = FALSE; + } + else if (PyBool_Check(op) || PyArray_IsScalar(op, Bool) || + (PyArray_Check(op) && (PyArray_DIMS(op)==0) && + PyArray_ISBOOL(op))) { + noellipses = FALSE; + } + else if (PySequence_Check(op)) { + Py_ssize_t n, i; + PyObject *temp; + + n = PySequence_Size(op); + i = 0; + while (i < n && noellipses) { + temp = PySequence_GetItem(op, i); + if (temp == Py_Ellipsis) { + noellipses = FALSE; + } + Py_DECREF(temp); + i++; + } + } + if (noellipses) { + PyObject *ret; + ret = PyArray_ToScalar(mp->data, mp); + Py_DECREF(mp); + return ret; + } + } + return (PyObject *)mp; +} + + +NPY_NO_EXPORT PyMappingMethods array_as_mapping = { +#if PY_VERSION_HEX >= 0x02050000 + (lenfunc)array_length, /*mp_length*/ +#else + (inquiry)array_length, /*mp_length*/ +#endif + (binaryfunc)array_subscript_nice, /*mp_subscript*/ + (objobjargproc)array_ass_sub, /*mp_ass_subscript*/ +}; + +/****************** End of Mapping Protocol ******************************/ + + diff --git a/numpy/core/src/arraymapping.h b/numpy/core/src/arraymapping.h new file mode 100644 index 000000000..0b9b712fe --- /dev/null +++ b/numpy/core/src/arraymapping.h @@ -0,0 +1,35 @@ +#ifndef _NPY_ARRAYMAPPING_H_ +#define _NPY_ARRAYMAPPING_H_ + +extern NPY_NO_EXPORT PyMappingMethods array_as_mapping; + +NPY_NO_EXPORT Py_ssize_t +array_length(PyArrayObject *self); + +NPY_NO_EXPORT PyObject * +array_item_nice(PyArrayObject *self, Py_ssize_t i); + +NPY_NO_EXPORT PyObject * +array_subscript(PyArrayObject *self, PyObject *op); + +NPY_NO_EXPORT int +array_ass_big_item(PyArrayObject *self, intp i, PyObject *v); + +#if PY_VERSION_HEX < 0x02050000 +#if SIZEOF_INT == SIZEOF_INTP +#define array_ass_item array_ass_big_item +#endif +#else +#if SIZEOF_SIZE_T == SIZEOF_INTP +#define array_ass_item array_ass_big_item +#endif +#endif +#ifndef array_ass_item +NPY_NO_EXPORT int +array_ass_item(PyArrayObject *self, Py_ssize_t i, PyObject *v) +{ + return array_ass_big_item(self, (intp) i, v); +} +#endif + +#endif diff --git a/numpy/core/src/arrayobject.c b/numpy/core/src/arrayobject.c index e8d6dc541..1d9256b28 100644 --- a/numpy/core/src/arrayobject.c +++ b/numpy/core/src/arrayobject.c @@ -33,6 +33,7 @@ maintainer email: oliphant.travis@ieee.org #include "arrayobject.h" #include "arraydescr.h" #include "arrayiterators.h" +#include "arraymapping.h" #ifndef Py_UNICODE_WIDE #include "ucsnarrow.h" #endif @@ -2145,1001 +2146,6 @@ array_dealloc(PyArrayObject *self) { } /************************************************************************* - **************** Implement Mapping Protocol *************************** - *************************************************************************/ - -static Py_ssize_t -array_length(PyArrayObject *self) -{ - if (self->nd != 0) { - return self->dimensions[0]; - } else { - PyErr_SetString(PyExc_TypeError, "len() of unsized object"); - return -1; - } -} - -NPY_NO_EXPORT PyObject * -array_big_item(PyArrayObject *self, intp i) -{ - char *item; - PyArrayObject *r; - - if(self->nd == 0) { - PyErr_SetString(PyExc_IndexError, - "0-d arrays can't be indexed"); - return NULL; - } - if ((item = index2ptr(self, i)) == NULL) { - return NULL; - } - Py_INCREF(self->descr); - r = (PyArrayObject *)PyArray_NewFromDescr(self->ob_type, - self->descr, - self->nd-1, - self->dimensions+1, - self->strides+1, item, - self->flags, - (PyObject *)self); - if (r == NULL) { - return NULL; - } - Py_INCREF(self); - r->base = (PyObject *)self; - PyArray_UpdateFlags(r, CONTIGUOUS | FORTRAN); - return (PyObject *)r; -} - -/* contains optimization for 1-d arrays */ -static PyObject * -array_item_nice(PyArrayObject *self, Py_ssize_t i) -{ - if (self->nd == 1) { - char *item; - if ((item = index2ptr(self, i)) == NULL) { - return NULL; - } - return PyArray_Scalar(item, self->descr, (PyObject *)self); - } - else { - return PyArray_Return( - (PyArrayObject *) array_big_item(self, (intp) i)); - } -} - -static int -array_ass_big_item(PyArrayObject *self, intp i, PyObject *v) -{ - PyArrayObject *tmp; - char *item; - int ret; - - if (v == NULL) { - PyErr_SetString(PyExc_ValueError, - "can't delete array elements"); - return -1; - } - if (!PyArray_ISWRITEABLE(self)) { - PyErr_SetString(PyExc_RuntimeError, - "array is not writeable"); - return -1; - } - if (self->nd == 0) { - PyErr_SetString(PyExc_IndexError, - "0-d arrays can't be indexed."); - return -1; - } - - - if (self->nd > 1) { - if((tmp = (PyArrayObject *)array_big_item(self, i)) == NULL) { - return -1; - } - ret = PyArray_CopyObject(tmp, v); - Py_DECREF(tmp); - return ret; - } - - if ((item = index2ptr(self, i)) == NULL) { - return -1; - } - if (self->descr->f->setitem(v, item, self) == -1) { - return -1; - } - return 0; -} - -#if PY_VERSION_HEX < 0x02050000 -#if SIZEOF_INT == SIZEOF_INTP -#define array_ass_item array_ass_big_item -#endif -#else -#if SIZEOF_SIZE_T == SIZEOF_INTP -#define array_ass_item array_ass_big_item -#endif -#endif -#ifndef array_ass_item -static int -array_ass_item(PyArrayObject *self, Py_ssize_t i, PyObject *v) -{ - return array_ass_big_item(self, (intp) i, v); -} -#endif - - -/* -------------------------------------------------------------- */ - -static void -_swap_axes(PyArrayMapIterObject *mit, PyArrayObject **ret, int getmap) -{ - PyObject *new; - int n1, n2, n3, val, bnd; - int i; - PyArray_Dims permute; - intp d[MAX_DIMS]; - PyArrayObject *arr; - - permute.ptr = d; - permute.len = mit->nd; - - /* - * arr might not have the right number of dimensions - * and need to be reshaped first by pre-pending ones - */ - arr = *ret; - if (arr->nd != mit->nd) { - for (i = 1; i <= arr->nd; i++) { - permute.ptr[mit->nd-i] = arr->dimensions[arr->nd-i]; - } - for (i = 0; i < mit->nd-arr->nd; i++) { - permute.ptr[i] = 1; - } - new = PyArray_Newshape(arr, &permute, PyArray_ANYORDER); - Py_DECREF(arr); - *ret = (PyArrayObject *)new; - if (new == NULL) { - return; - } - } - - /* - * Setting and getting need to have different permutations. - * On the get we are permuting the returned object, but on - * setting we are permuting the object-to-be-set. - * The set permutation is the inverse of the get permutation. - */ - - /* - * For getting the array the tuple for transpose is - * (n1,...,n1+n2-1,0,...,n1-1,n1+n2,...,n3-1) - * n1 is the number of dimensions of the broadcast index array - * n2 is the number of dimensions skipped at the start - * n3 is the number of dimensions of the result - */ - - /* - * For setting the array the tuple for transpose is - * (n2,...,n1+n2-1,0,...,n2-1,n1+n2,...n3-1) - */ - n1 = mit->iters[0]->nd_m1 + 1; - n2 = mit->iteraxes[0]; - n3 = mit->nd; - - /* use n1 as the boundary if getting but n2 if setting */ - bnd = getmap ? n1 : n2; - val = bnd; - i = 0; - while (val < n1 + n2) { - permute.ptr[i++] = val++; - } - val = 0; - while (val < bnd) { - permute.ptr[i++] = val++; - } - val = n1 + n2; - while (val < n3) { - permute.ptr[i++] = val++; - } - new = PyArray_Transpose(*ret, &permute); - Py_DECREF(*ret); - *ret = (PyArrayObject *)new; -} - -static PyObject * -PyArray_GetMap(PyArrayMapIterObject *mit) -{ - - PyArrayObject *ret, *temp; - PyArrayIterObject *it; - int index; - int swap; - PyArray_CopySwapFunc *copyswap; - - /* Unbound map iterator --- Bind should have been called */ - if (mit->ait == NULL) { - return NULL; - } - - /* This relies on the map iterator object telling us the shape - of the new array in nd and dimensions. - */ - temp = mit->ait->ao; - Py_INCREF(temp->descr); - ret = (PyArrayObject *) - PyArray_NewFromDescr(temp->ob_type, - temp->descr, - mit->nd, mit->dimensions, - NULL, NULL, - PyArray_ISFORTRAN(temp), - (PyObject *)temp); - if (ret == NULL) { - return NULL; - } - - /* - * Now just iterate through the new array filling it in - * with the next object from the original array as - * defined by the mapping iterator - */ - - if ((it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)ret)) == NULL) { - Py_DECREF(ret); - return NULL; - } - index = it->size; - swap = (PyArray_ISNOTSWAPPED(temp) != PyArray_ISNOTSWAPPED(ret)); - copyswap = ret->descr->f->copyswap; - PyArray_MapIterReset(mit); - while (index--) { - copyswap(it->dataptr, mit->dataptr, swap, ret); - PyArray_MapIterNext(mit); - PyArray_ITER_NEXT(it); - } - Py_DECREF(it); - - /* check for consecutive axes */ - if ((mit->subspace != NULL) && (mit->consec)) { - if (mit->iteraxes[0] > 0) { /* then we need to swap */ - _swap_axes(mit, &ret, 1); - } - } - return (PyObject *)ret; -} - -static int -PyArray_SetMap(PyArrayMapIterObject *mit, PyObject *op) -{ - PyObject *arr = NULL; - PyArrayIterObject *it; - int index; - int swap; - PyArray_CopySwapFunc *copyswap; - PyArray_Descr *descr; - - /* Unbound Map Iterator */ - if (mit->ait == NULL) { - return -1; - } - descr = mit->ait->ao->descr; - Py_INCREF(descr); - arr = PyArray_FromAny(op, descr, 0, 0, FORCECAST, NULL); - if (arr == NULL) { - return -1; - } - if ((mit->subspace != NULL) && (mit->consec)) { - if (mit->iteraxes[0] > 0) { /* then we need to swap */ - _swap_axes(mit, (PyArrayObject **)&arr, 0); - if (arr == NULL) { - return -1; - } - } - } - - /* Be sure values array is "broadcastable" - to shape of mit->dimensions, mit->nd */ - - if ((it = (PyArrayIterObject *)\ - PyArray_BroadcastToShape(arr, mit->dimensions, mit->nd))==NULL) { - Py_DECREF(arr); - return -1; - } - - index = mit->size; - swap = (PyArray_ISNOTSWAPPED(mit->ait->ao) != - (PyArray_ISNOTSWAPPED(arr))); - copyswap = PyArray_DESCR(arr)->f->copyswap; - PyArray_MapIterReset(mit); - /* Need to decref hasobject arrays */ - if (PyDataType_FLAGCHK(descr, NPY_ITEM_REFCOUNT)) { - while (index--) { - PyArray_Item_XDECREF(mit->dataptr, PyArray_DESCR(arr)); - PyArray_Item_INCREF(it->dataptr, PyArray_DESCR(arr)); - memmove(mit->dataptr, it->dataptr, sizeof(PyObject *)); - /* ignored unless VOID array with object's */ - if (swap) { - copyswap(mit->dataptr, NULL, swap, arr); - } - PyArray_MapIterNext(mit); - PyArray_ITER_NEXT(it); - } - Py_DECREF(arr); - Py_DECREF(it); - return 0; - } - while(index--) { - memmove(mit->dataptr, it->dataptr, PyArray_ITEMSIZE(arr)); - if (swap) { - copyswap(mit->dataptr, NULL, swap, arr); - } - PyArray_MapIterNext(mit); - PyArray_ITER_NEXT(it); - } - Py_DECREF(arr); - Py_DECREF(it); - return 0; -} - -NPY_NO_EXPORT int -count_new_axes_0d(PyObject *tuple) -{ - int i, argument_count; - int ellipsis_count = 0; - int newaxis_count = 0; - - argument_count = PyTuple_GET_SIZE(tuple); - for (i = 0; i < argument_count; ++i) { - PyObject *arg = PyTuple_GET_ITEM(tuple, i); - if (arg == Py_Ellipsis && !ellipsis_count) { - ellipsis_count++; - } - else if (arg == Py_None) { - newaxis_count++; - } - else { - break; - } - } - if (i < argument_count) { - PyErr_SetString(PyExc_IndexError, - "0-d arrays can only use a single ()" - " or a list of newaxes (and a single ...)" - " as an index"); - return -1; - } - if (newaxis_count > MAX_DIMS) { - PyErr_SetString(PyExc_IndexError, "too many dimensions"); - return -1; - } - return newaxis_count; -} - -NPY_NO_EXPORT PyObject * -add_new_axes_0d(PyArrayObject *arr, int newaxis_count) -{ - PyArrayObject *other; - intp dimensions[MAX_DIMS]; - int i; - - for (i = 0; i < newaxis_count; ++i) { - dimensions[i] = 1; - } - Py_INCREF(arr->descr); - if ((other = (PyArrayObject *) - PyArray_NewFromDescr(arr->ob_type, arr->descr, - newaxis_count, dimensions, - NULL, arr->data, - arr->flags, - (PyObject *)arr)) == NULL) - return NULL; - other->base = (PyObject *)arr; - Py_INCREF(arr); - return (PyObject *)other; -} - - -/* This checks the args for any fancy indexing objects */ - -static int -fancy_indexing_check(PyObject *args) -{ - int i, n; - PyObject *obj; - int retval = SOBJ_NOTFANCY; - - if (PyTuple_Check(args)) { - n = PyTuple_GET_SIZE(args); - if (n >= MAX_DIMS) { - return SOBJ_TOOMANY; - } - for (i = 0; i < n; i++) { - obj = PyTuple_GET_ITEM(args,i); - if (PyArray_Check(obj)) { - if (PyArray_ISINTEGER(obj) || - PyArray_ISBOOL(obj)) { - retval = SOBJ_ISFANCY; - } - else { - retval = SOBJ_BADARRAY; - break; - } - } - else if (PySequence_Check(obj)) { - retval = SOBJ_ISFANCY; - } - } - } - else if (PyArray_Check(args)) { - if ((PyArray_TYPE(args)==PyArray_BOOL) || - (PyArray_ISINTEGER(args))) { - return SOBJ_ISFANCY; - } - else { - return SOBJ_BADARRAY; - } - } - else if (PySequence_Check(args)) { - /* - * Sequences < MAX_DIMS with any slice objects - * or newaxis, or Ellipsis is considered standard - * as long as there are also no Arrays and or additional - * sequences embedded. - */ - retval = SOBJ_ISFANCY; - n = PySequence_Size(args); - if (n < 0 || n >= MAX_DIMS) { - return SOBJ_ISFANCY; - } - for (i = 0; i < n; i++) { - obj = PySequence_GetItem(args, i); - if (obj == NULL) { - return SOBJ_ISFANCY; - } - if (PyArray_Check(obj)) { - if (PyArray_ISINTEGER(obj) || PyArray_ISBOOL(obj)) { - retval = SOBJ_LISTTUP; - } - else { - retval = SOBJ_BADARRAY; - } - } - else if (PySequence_Check(obj)) { - retval = SOBJ_LISTTUP; - } - else if (PySlice_Check(obj) || obj == Py_Ellipsis || - obj == Py_None) { - retval = SOBJ_NOTFANCY; - } - Py_DECREF(obj); - if (retval > SOBJ_ISFANCY) { - return retval; - } - } - } - return retval; -} - -/* - * Called when treating array object like a mapping -- called first from - * Python when using a[object] unless object is a standard slice object - * (not an extended one). - * - * There are two situations: - * - * 1 - the subscript is a standard view and a reference to the - * array can be returned - * - * 2 - the subscript uses Boolean masks or integer indexing and - * therefore a new array is created and returned. - */ - -NPY_NO_EXPORT PyObject * -array_subscript_simple(PyArrayObject *self, PyObject *op) -{ - intp dimensions[MAX_DIMS], strides[MAX_DIMS]; - intp offset; - int nd; - PyArrayObject *other; - intp value; - - value = PyArray_PyIntAsIntp(op); - if (!PyErr_Occurred()) { - return array_big_item(self, value); - } - PyErr_Clear(); - - /* Standard (view-based) Indexing */ - if ((nd = parse_index(self, op, dimensions, strides, &offset)) == -1) { - return NULL; - } - /* This will only work if new array will be a view */ - Py_INCREF(self->descr); - if ((other = (PyArrayObject *) - PyArray_NewFromDescr(self->ob_type, self->descr, - nd, dimensions, - strides, self->data+offset, - self->flags, - (PyObject *)self)) == NULL) { - return NULL; - } - other->base = (PyObject *)self; - Py_INCREF(self); - PyArray_UpdateFlags(other, UPDATE_ALL); - return (PyObject *)other; -} - -static PyObject * -array_subscript(PyArrayObject *self, PyObject *op) -{ - int nd, fancy; - PyArrayObject *other; - PyArrayMapIterObject *mit; - PyObject *obj; - - if (PyString_Check(op) || PyUnicode_Check(op)) { - if (self->descr->names) { - obj = PyDict_GetItem(self->descr->fields, op); - if (obj != NULL) { - PyArray_Descr *descr; - int offset; - PyObject *title; - - if (PyArg_ParseTuple(obj, "Oi|O", &descr, &offset, &title)) { - Py_INCREF(descr); - return PyArray_GetField(self, descr, offset); - } - } - } - - PyErr_Format(PyExc_ValueError, - "field named %s not found.", - PyString_AsString(op)); - return NULL; - } - - /* Check for multiple field access */ - if (self->descr->names && PySequence_Check(op) && !PyTuple_Check(op)) { - int seqlen, i; - seqlen = PySequence_Size(op); - for (i = 0; i < seqlen; i++) { - obj = PySequence_GetItem(op, i); - if (!PyString_Check(obj) && !PyUnicode_Check(obj)) { - Py_DECREF(obj); - break; - } - Py_DECREF(obj); - } - /* - * extract multiple fields if all elements in sequence - * are either string or unicode (i.e. no break occurred). - */ - fancy = ((seqlen > 0) && (i == seqlen)); - if (fancy) { - PyObject *_numpy_internal; - _numpy_internal = PyImport_ImportModule("numpy.core._internal"); - if (_numpy_internal == NULL) { - return NULL; - } - obj = PyObject_CallMethod(_numpy_internal, - "_index_fields", "OO", self, op); - Py_DECREF(_numpy_internal); - return obj; - } - } - - if (op == Py_Ellipsis) { - Py_INCREF(self); - return (PyObject *)self; - } - - if (self->nd == 0) { - if (op == Py_None) { - return add_new_axes_0d(self, 1); - } - if (PyTuple_Check(op)) { - if (0 == PyTuple_GET_SIZE(op)) { - Py_INCREF(self); - return (PyObject *)self; - } - if ((nd = count_new_axes_0d(op)) == -1) { - return NULL; - } - return add_new_axes_0d(self, nd); - } - /* Allow Boolean mask selection also */ - if ((PyArray_Check(op) && (PyArray_DIMS(op)==0) - && PyArray_ISBOOL(op))) { - if (PyObject_IsTrue(op)) { - Py_INCREF(self); - return (PyObject *)self; - } - else { - intp oned = 0; - Py_INCREF(self->descr); - return PyArray_NewFromDescr(self->ob_type, - self->descr, - 1, &oned, - NULL, NULL, - NPY_DEFAULT, - NULL); - } - } - PyErr_SetString(PyExc_IndexError, "0-d arrays can't be indexed."); - return NULL; - } - - fancy = fancy_indexing_check(op); - if (fancy != SOBJ_NOTFANCY) { - int oned; - - oned = ((self->nd == 1) && - !(PyTuple_Check(op) && PyTuple_GET_SIZE(op) > 1)); - - /* wrap arguments into a mapiter object */ - mit = (PyArrayMapIterObject *) PyArray_MapIterNew(op, oned, fancy); - if (mit == NULL) { - return NULL; - } - if (oned) { - PyArrayIterObject *it; - PyObject *rval; - it = (PyArrayIterObject *) PyArray_IterNew((PyObject *)self); - if (it == NULL) { - Py_DECREF(mit); - return NULL; - } - rval = iter_subscript(it, mit->indexobj); - Py_DECREF(it); - Py_DECREF(mit); - return rval; - } - PyArray_MapIterBind(mit, self); - other = (PyArrayObject *)PyArray_GetMap(mit); - Py_DECREF(mit); - return (PyObject *)other; - } - - return array_subscript_simple(self, op); -} - - -/* - * Another assignment hacked by using CopyObject. - * This only works if subscript returns a standard view. - * Again there are two cases. In the first case, PyArray_CopyObject - * can be used. In the second case, a new indexing function has to be - * used. - */ - -static int -array_ass_sub_simple(PyArrayObject *self, PyObject *index, PyObject *op) -{ - int ret; - PyArrayObject *tmp; - intp value; - - value = PyArray_PyIntAsIntp(index); - if (!error_converting(value)) { - return array_ass_big_item(self, value, op); - } - PyErr_Clear(); - - /* Rest of standard (view-based) indexing */ - - if (PyArray_CheckExact(self)) { - tmp = (PyArrayObject *)array_subscript_simple(self, index); - if (tmp == NULL) { - return -1; - } - } - else { - PyObject *tmp0; - tmp0 = PyObject_GetItem((PyObject *)self, index); - if (tmp0 == NULL) { - return -1; - } - if (!PyArray_Check(tmp0)) { - PyErr_SetString(PyExc_RuntimeError, - "Getitem not returning array."); - Py_DECREF(tmp0); - return -1; - } - tmp = (PyArrayObject *)tmp0; - } - - if (PyArray_ISOBJECT(self) && (tmp->nd == 0)) { - ret = tmp->descr->f->setitem(op, tmp->data, tmp); - } - else { - ret = PyArray_CopyObject(tmp, op); - } - Py_DECREF(tmp); - return ret; -} - - -/* return -1 if tuple-object seq is not a tuple of integers. - otherwise fill vals with converted integers -*/ -static int -_tuple_of_integers(PyObject *seq, intp *vals, int maxvals) -{ - int i; - PyObject *obj; - intp temp; - - for(i=0; i<maxvals; i++) { - obj = PyTuple_GET_ITEM(seq, i); - if ((PyArray_Check(obj) && PyArray_NDIM(obj) > 0) - || PyList_Check(obj)) { - return -1; - } - temp = PyArray_PyIntAsIntp(obj); - if (error_converting(temp)) { - return -1; - } - vals[i] = temp; - } - return 0; -} - - -static int -array_ass_sub(PyArrayObject *self, PyObject *index, PyObject *op) -{ - int ret, oned, fancy; - PyArrayMapIterObject *mit; - intp vals[MAX_DIMS]; - - if (op == NULL) { - PyErr_SetString(PyExc_ValueError, - "cannot delete array elements"); - return -1; - } - if (!PyArray_ISWRITEABLE(self)) { - PyErr_SetString(PyExc_RuntimeError, - "array is not writeable"); - return -1; - } - - if (PyInt_Check(index) || PyArray_IsScalar(index, Integer) || - PyLong_Check(index) || (PyIndex_Check(index) && - !PySequence_Check(index))) { - intp value; - value = PyArray_PyIntAsIntp(index); - if (PyErr_Occurred()) { - PyErr_Clear(); - } - else { - return array_ass_big_item(self, value, op); - } - } - - if (PyString_Check(index) || PyUnicode_Check(index)) { - if (self->descr->names) { - PyObject *obj; - - obj = PyDict_GetItem(self->descr->fields, index); - if (obj != NULL) { - PyArray_Descr *descr; - int offset; - PyObject *title; - - if (PyArg_ParseTuple(obj, "Oi|O", &descr, &offset, &title)) { - Py_INCREF(descr); - return PyArray_SetField(self, descr, offset, op); - } - } - } - - PyErr_Format(PyExc_ValueError, - "field named %s not found.", - PyString_AsString(index)); - return -1; - } - - if (self->nd == 0) { - /* - * Several different exceptions to the 0-d no-indexing rule - * - * 1) ellipses - * 2) empty tuple - * 3) Using newaxis (None) - * 4) Boolean mask indexing - */ - if (index == Py_Ellipsis || index == Py_None || - (PyTuple_Check(index) && (0 == PyTuple_GET_SIZE(index) || - count_new_axes_0d(index) > 0))) { - return self->descr->f->setitem(op, self->data, self); - } - if (PyBool_Check(index) || PyArray_IsScalar(index, Bool) || - (PyArray_Check(index) && (PyArray_DIMS(index)==0) && - PyArray_ISBOOL(index))) { - if (PyObject_IsTrue(index)) { - return self->descr->f->setitem(op, self->data, self); - } - else { /* don't do anything */ - return 0; - } - } - PyErr_SetString(PyExc_IndexError, "0-d arrays can't be indexed."); - return -1; - } - - /* optimization for integer-tuple */ - if (self->nd > 1 && - (PyTuple_Check(index) && (PyTuple_GET_SIZE(index) == self->nd)) - && (_tuple_of_integers(index, vals, self->nd) >= 0)) { - int i; - char *item; - - for (i = 0; i < self->nd; i++) { - if (vals[i] < 0) { - vals[i] += self->dimensions[i]; - } - if ((vals[i] < 0) || (vals[i] >= self->dimensions[i])) { - PyErr_Format(PyExc_IndexError, - "index (%"INTP_FMT") out of range "\ - "(0<=index<%"INTP_FMT") in dimension %d", - vals[i], self->dimensions[i], i); - return -1; - } - } - item = PyArray_GetPtr(self, vals); - return self->descr->f->setitem(op, item, self); - } - PyErr_Clear(); - - fancy = fancy_indexing_check(index); - if (fancy != SOBJ_NOTFANCY) { - oned = ((self->nd == 1) && - !(PyTuple_Check(index) && PyTuple_GET_SIZE(index) > 1)); - mit = (PyArrayMapIterObject *) PyArray_MapIterNew(index, oned, fancy); - if (mit == NULL) { - return -1; - } - if (oned) { - PyArrayIterObject *it; - int rval; - - it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)self); - if (it == NULL) { - Py_DECREF(mit); - return -1; - } - rval = iter_ass_subscript(it, mit->indexobj, op); - Py_DECREF(it); - Py_DECREF(mit); - return rval; - } - PyArray_MapIterBind(mit, self); - ret = PyArray_SetMap(mit, op); - Py_DECREF(mit); - return ret; - } - - return array_ass_sub_simple(self, index, op); -} - - -/* - * There are places that require that array_subscript return a PyArrayObject - * and not possibly a scalar. Thus, this is the function exposed to - * Python so that 0-dim arrays are passed as scalars - */ - - -static PyObject * -array_subscript_nice(PyArrayObject *self, PyObject *op) -{ - - PyArrayObject *mp; - intp vals[MAX_DIMS]; - - if (PyInt_Check(op) || PyArray_IsScalar(op, Integer) || - PyLong_Check(op) || (PyIndex_Check(op) && - !PySequence_Check(op))) { - intp value; - value = PyArray_PyIntAsIntp(op); - if (PyErr_Occurred()) { - PyErr_Clear(); - } - else { - return array_item_nice(self, (Py_ssize_t) value); - } - } - /* optimization for a tuple of integers */ - if (self->nd > 1 && PyTuple_Check(op) && - (PyTuple_GET_SIZE(op) == self->nd) - && (_tuple_of_integers(op, vals, self->nd) >= 0)) { - int i; - char *item; - - for (i = 0; i < self->nd; i++) { - if (vals[i] < 0) { - vals[i] += self->dimensions[i]; - } - if ((vals[i] < 0) || (vals[i] >= self->dimensions[i])) { - PyErr_Format(PyExc_IndexError, - "index (%"INTP_FMT") out of range "\ - "(0<=index<%"INTP_FMT") in dimension %d", - vals[i], self->dimensions[i], i); - return NULL; - } - } - item = PyArray_GetPtr(self, vals); - return PyArray_Scalar(item, self->descr, (PyObject *)self); - } - PyErr_Clear(); - - mp = (PyArrayObject *)array_subscript(self, op); - /* - * mp could be a scalar if op is not an Int, Scalar, Long or other Index - * object and still convertable to an integer (so that the code goes to - * array_subscript_simple). So, this cast is a bit dangerous.. - */ - - /* - * The following is just a copy of PyArray_Return with an - * additional logic in the nd == 0 case. - */ - - if (mp == NULL) { - return NULL; - } - if (PyErr_Occurred()) { - Py_XDECREF(mp); - return NULL; - } - if (PyArray_Check(mp) && mp->nd == 0) { - Bool noellipses = TRUE; - if ((op == Py_Ellipsis) || PyString_Check(op) || PyUnicode_Check(op)) { - noellipses = FALSE; - } - else if (PyBool_Check(op) || PyArray_IsScalar(op, Bool) || - (PyArray_Check(op) && (PyArray_DIMS(op)==0) && - PyArray_ISBOOL(op))) { - noellipses = FALSE; - } - else if (PySequence_Check(op)) { - Py_ssize_t n, i; - PyObject *temp; - - n = PySequence_Size(op); - i = 0; - while (i < n && noellipses) { - temp = PySequence_GetItem(op, i); - if (temp == Py_Ellipsis) { - noellipses = FALSE; - } - Py_DECREF(temp); - i++; - } - } - if (noellipses) { - PyObject *ret; - ret = PyArray_ToScalar(mp->data, mp); - Py_DECREF(mp); - return ret; - } - } - return (PyObject *)mp; -} - - -static PyMappingMethods array_as_mapping = { -#if PY_VERSION_HEX >= 0x02050000 - (lenfunc)array_length, /*mp_length*/ -#else - (inquiry)array_length, /*mp_length*/ -#endif - (binaryfunc)array_subscript_nice, /*mp_subscript*/ - (objobjargproc)array_ass_sub, /*mp_ass_subscript*/ -}; - -/****************** End of Mapping Protocol ******************************/ - - -/************************************************************************* **************** Implement Buffer Protocol **************************** *************************************************************************/ |