diff options
author | Mark Wiebe <mwwiebe@gmail.com> | 2011-01-07 11:48:33 -0800 |
---|---|---|
committer | Mark Wiebe <mwwiebe@gmail.com> | 2011-01-09 01:55:03 -0800 |
commit | e8101f5d55541fcd25c5fe0d3dd5f37a5c66a3fc (patch) | |
tree | d65e5117e686dba2d1dcb6d17e56daf5bfe57007 /numpy | |
parent | 3550d410594cc86ae1a3d730bf689b484e0627de (diff) | |
download | numpy-e8101f5d55541fcd25c5fe0d3dd5f37a5c66a3fc.tar.gz |
ENH: iter: Add NPY_ITER_DELAY_BUFALLOC flag, fix Python iterator wrapper issues
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/src/multiarray/new_iterator.c.src | 265 | ||||
-rw-r--r-- | numpy/core/src/multiarray/new_iterator.h | 9 | ||||
-rw-r--r-- | numpy/core/src/multiarray/new_iterator_pywrap.c | 280 | ||||
-rw-r--r-- | numpy/core/tests/test_new_iterator.py | 38 |
4 files changed, 398 insertions, 194 deletions
diff --git a/numpy/core/src/multiarray/new_iterator.c.src b/numpy/core/src/multiarray/new_iterator.c.src index 78d45d2c1..8b9cafb26 100644 --- a/numpy/core/src/multiarray/new_iterator.c.src +++ b/numpy/core/src/multiarray/new_iterator.c.src @@ -37,6 +37,8 @@ #define NPY_ITFLAG_GROWINNER 0x100 /* There is just one iteration, can specialize iternext for that */ #define NPY_ITFLAG_ONEITERATION 0x200 +/* Delay buffer allocation until first Reset* call */ +#define NPY_ITFLAG_DELAYBUF 0x400 /* Internal iterator per-operand iterator flags */ @@ -46,10 +48,8 @@ #define NPY_OP_ITFLAG_READ 0x02 /* The operand may have a temporary copy made */ #define NPY_OP_ITFLAG_COPY 0x04 -/* The operand needs casting */ +/* The operand needs type conversion/byte swapping/alignment */ #define NPY_OP_ITFLAG_CAST 0x08 -/* The operand needs a byte swap and/or alignment operation */ -#define NPY_OP_ITFLAG_COPYSWAP 0x10 /* The operand never needs buffering */ #define NPY_OP_ITFLAG_BUFNEVER 0x20 /* The operand is aligned */ @@ -193,7 +193,7 @@ struct NpyIter_AD { ((niter) + ((itflags&NPY_ITFLAG_HASINDEX) ? 1 : 0)) /* Size of one AXISDATA struct within the iterator */ -#define NIT_SIZEOF_AXISDATA(itflags, ndim, niter) (( \ +#define NIT_AXISDATA_SIZEOF(itflags, ndim, niter) (( \ /* intp shape */ \ 1 + \ /* intp coord */ \ @@ -205,7 +205,7 @@ struct NpyIter_AD { /* * Macro to advance an AXISDATA pointer by a specified count. * Requires that sizeof_axisdata be previously initialized - * to NIT_SIZEOF_AXISDATA(itflags, ndim, niter). + * to NIT_AXISDATA_SIZEOF(itflags, ndim, niter). */ #define NIT_ADVANCE_AXISDATA(axisdata, count) \ (*((char **)(&axisdata))) += (count)*sizeof_axisdata @@ -216,7 +216,7 @@ struct NpyIter_AD { #define NIT_SIZEOF_ITERATOR(itflags, ndim, niter) ( \ sizeof(struct NpyIter_InternalOnly) + \ NIT_AXISDATA_OFFSET(itflags, ndim, niter) + \ - NIT_SIZEOF_AXISDATA(itflags, ndim, niter)*(ndim)) + NIT_AXISDATA_SIZEOF(itflags, ndim, niter)*(ndim)) /* Internal helper functions */ static int @@ -389,6 +389,7 @@ NpyIter_MultiNew(npy_intp niter, PyArrayObject **op_in, npy_uint32 flags, */ if (itflags&NPY_ITFLAG_BUFFER) { bufferdata = NIT_BUFFERDATA(iter); + NBF_SIZE(bufferdata) = 0; memset(NBF_BUFFERS(bufferdata), 0, niter*NPY_SIZEOF_INTP); memset(NBF_READTRANSFERDATA(bufferdata), 0, niter*NPY_SIZEOF_INTP); memset(NBF_WRITETRANSFERDATA(bufferdata), 0, niter*NPY_SIZEOF_INTP); @@ -563,15 +564,22 @@ NpyIter_MultiNew(npy_intp niter, PyArrayObject **op_in, npy_uint32 flags, } } + /* If buffering is set without delayed allocation */ if (itflags&NPY_ITFLAG_BUFFER) { - /* Allocate the buffers */ - if (!npyiter_allocate_buffers(iter)) { - NpyIter_Deallocate(iter); - return NULL; + if (itflags&NPY_ITFLAG_DELAYBUF) { + /* Make the data pointers NULL */ + memset(NBF_PTRS(bufferdata), 0, niter*NPY_SIZEOF_INTP); } + else { + /* Allocate the buffers */ + if (!npyiter_allocate_buffers(iter)) { + NpyIter_Deallocate(iter); + return NULL; + } - /* Prepare the next buffers and set iterend/size */ - npyiter_copy_to_buffers(iter); + /* Prepare the next buffers and set iterend/size */ + npyiter_copy_to_buffers(iter); + } } return iter; @@ -756,7 +764,9 @@ int NpyIter_RemoveCoords(NpyIter *iter) npy_uint32 itflags; /* Make sure the iterator is reset */ - NpyIter_Reset(iter); + if (NpyIter_Reset(iter) != NPY_SUCCEED) { + return NPY_FAIL; + } itflags = NIT_ITFLAGS(iter); if (itflags&NPY_ITFLAG_HASCOORDS) { @@ -781,6 +791,13 @@ int NpyIter_RemoveInnerLoop(NpyIter *iter) "if coords or an index is being tracked"); return NPY_FAIL; } + if ((itflags&(NPY_ITFLAG_BUFFER|NPY_ITFLAG_RANGE|NPY_ITFLAG_NOINNER)) + == (NPY_ITFLAG_RANGE|NPY_ITFLAG_NOINNER)) { + PyErr_SetString(PyExc_ValueError, + "Iterator flag NO_INNER_ITERATION cannot be used " + "with ranged iteration unless buffering is also enabled"); + return NPY_FAIL; + } /* Set the flag */ if (!(itflags&NPY_ITFLAG_NOINNER)) { itflags |= NPY_ITFLAG_NOINNER; @@ -799,13 +816,11 @@ int NpyIter_RemoveInnerLoop(NpyIter *iter) } /* Reset the iterator */ - NpyIter_Reset(iter); - - return NPY_SUCCEED; + return NpyIter_Reset(iter); } /* Resets the iterator to its initial state */ -void NpyIter_Reset(NpyIter *iter) +int NpyIter_Reset(NpyIter *iter) { npy_uint32 itflags = NIT_ITFLAGS(iter); npy_intp ndim = NIT_NDIM(iter); @@ -814,18 +829,28 @@ void NpyIter_Reset(NpyIter *iter) if (itflags&NPY_ITFLAG_BUFFER) { NpyIter_BufferData *bufferdata; - /* - * If the iterindex is already right, no need to - * do anything - */ - bufferdata = NIT_BUFFERDATA(iter); - if (NIT_ITERINDEX(iter) == NIT_ITERSTART(iter) && - NBF_BUFITEREND(bufferdata) <= NIT_ITEREND(iter) && - NBF_SIZE(bufferdata) > 0) { - return; + /* If buffer allocation was delayed, do it now */ + if (itflags&NPY_ITFLAG_DELAYBUF) { + if (!npyiter_allocate_buffers(iter)) { + return NPY_FAIL; + } + NIT_ITFLAGS(iter) &= ~NPY_ITFLAG_DELAYBUF; + } + else { + /* + * If the iterindex is already right, no need to + * do anything + */ + bufferdata = NIT_BUFFERDATA(iter); + if (NIT_ITERINDEX(iter) == NIT_ITERSTART(iter) && + NBF_BUFITEREND(bufferdata) <= NIT_ITEREND(iter) && + NBF_SIZE(bufferdata) > 0) { + return NPY_SUCCEED; + } + + /* Copy any data from the buffers back to the arrays */ + npyiter_copy_from_buffers(iter); } - /* Copy any data from the buffers back to the arrays */ - npyiter_copy_from_buffers(iter); } npyiter_goto_iterindex(iter, NIT_ITERSTART(iter)); @@ -834,10 +859,12 @@ void NpyIter_Reset(NpyIter *iter) /* Prepare the next buffers and set iterend/size */ npyiter_copy_to_buffers(iter); } + + return NPY_SUCCEED; } /* Resets the iterator to its initial state, with new base data pointers */ -void NpyIter_ResetBasePointers(NpyIter *iter, char **baseptrs) +int NpyIter_ResetBasePointers(NpyIter *iter, char **baseptrs) { npy_uint32 itflags = NIT_ITFLAGS(iter); npy_intp ndim = NIT_NDIM(iter); @@ -847,8 +874,17 @@ void NpyIter_ResetBasePointers(NpyIter *iter, char **baseptrs) npy_intp *baseoffsets = NIT_BASEOFFSETS(iter); if (itflags&NPY_ITFLAG_BUFFER) { - /* Copy any data from the buffers back to the arrays */ - npyiter_copy_from_buffers(iter); + /* If buffer allocation was delayed, do it now */ + if (itflags&NPY_ITFLAG_DELAYBUF) { + if (!npyiter_allocate_buffers(iter)) { + return NPY_FAIL; + } + NIT_ITFLAGS(iter) &= ~NPY_ITFLAG_DELAYBUF; + } + else { + /* Copy any data from the buffers back to the arrays */ + npyiter_copy_from_buffers(iter); + } } /* The new data pointers for resetting */ @@ -862,6 +898,8 @@ void NpyIter_ResetBasePointers(NpyIter *iter, char **baseptrs) /* Prepare the next buffers and set iterend/size */ npyiter_copy_to_buffers(iter); } + + return NPY_SUCCEED; } /* Resets the iterator to a new iterator index range */ @@ -896,9 +934,7 @@ NpyIter_ResetToIterIndexRange(NpyIter *iter, NIT_ITERSTART(iter) = istart; NIT_ITEREND(iter) = iend; - NpyIter_Reset(iter); - - return NPY_SUCCEED; + return NpyIter_Reset(iter); } /* @@ -943,7 +979,7 @@ int NpyIter_GotoCoords(NpyIter *iter, npy_intp *coords) perm = NIT_PERM(iter); axisdata = NIT_AXISDATA(iter); - sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); /* Compute the iterindex corresponding to the coordinates */ iterindex = 0; @@ -1032,7 +1068,7 @@ int NpyIter_GotoIndex(NpyIter *iter, npy_intp index) } axisdata = NIT_AXISDATA(iter); - sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); /* Compute the iterindex corresponding to the index */ iterindex = 0; @@ -1155,7 +1191,7 @@ npy_intp NpyIter_GetIterIndex(NpyIter *iter) npy_intp sizeof_axisdata; iterindex = 0; - sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); axisdata = NIT_INDEX_AXISDATA(NIT_AXISDATA(iter), ndim-1); for (idim = ndim-2; idim >= 0; --idim) { @@ -1224,7 +1260,7 @@ npyiter_iternext_itflags@tag_itflags@_dims@tag_ndim@_iters@tag_niter@( #endif nstrides = NAD_NSTRIDES(); - sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); axisdata0 = NIT_AXISDATA(iter); # if !(@const_itflags@&NPY_ITFLAG_NOINNER) @@ -1520,7 +1556,7 @@ npyiter_getcoord_itflags@tag_itflags@(NpyIter *iter, npy_intp *outcoord) #endif axisdata = NIT_AXISDATA(iter); - sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); #if ((@const_itflags@)&NPY_ITFLAG_IDENTPERM) outcoord += ndim-1; for(idim = 0; idim < ndim; ++idim, --outcoord, @@ -1554,11 +1590,21 @@ NpyIter_GetCoords_Fn NpyIter_GetGetCoords(NpyIter *iter) npy_intp ndim = NIT_NDIM(iter); npy_intp niter = NIT_NITER(iter); - if (!(itflags&NPY_ITFLAG_HASCOORDS)) { - PyErr_SetString(PyExc_ValueError, - "Cannot retrieve a GetCoords function for an iterator " - "that doesn't track coordinates."); - return NULL; + /* These flags must be correct */ + if ((itflags&(NPY_ITFLAG_HASCOORDS|NPY_ITFLAG_DELAYBUF)) != + NPY_ITFLAG_HASCOORDS) { + if (!(itflags&NPY_ITFLAG_HASCOORDS)) { + PyErr_SetString(PyExc_ValueError, + "Cannot retrieve a GetCoords function for an iterator " + "that doesn't track coordinates."); + return NULL; + } + else { + PyErr_SetString(PyExc_ValueError, + "Cannot retrieve a GetCoords function for an iterator " + "that used DELAY_BUFALLOC before a Reset call"); + return NULL; + } } /* @@ -1601,6 +1647,11 @@ NpyIter_GetCoords_Fn NpyIter_GetGetCoords(NpyIter *iter) } +int NpyIter_HasDelayedBufAlloc(NpyIter *iter) +{ + return (NIT_ITFLAGS(iter)&NPY_ITFLAG_DELAYBUF) != 0; +} + int NpyIter_HasInnerLoop(NpyIter *iter) { return (NIT_ITFLAGS(iter)&NPY_ITFLAG_NOINNER) == 0; @@ -1658,7 +1709,7 @@ int NpyIter_GetShape(NpyIter *iter, npy_intp *outshape) perm = NIT_PERM(iter); axisdata = NIT_AXISDATA(iter); - sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); for(idim = 0; idim < ndim; ++idim, NIT_ADVANCE_AXISDATA(axisdata, 1)) { npy_intp p = perm[idim]; if (p < 0) { @@ -1738,7 +1789,7 @@ PyArrayObject *NpyIter_GetIterView(NpyIter *iter, npy_intp i) writeable = NIT_OPITFLAGS(iter)[i]&NPY_OP_ITFLAG_WRITE; dataptr = NIT_RESETDATAPTR(iter)[i]; axisdata = NIT_AXISDATA(iter); - sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); /* Retrieve the shape and strides from the axisdata */ for (idim = 0; idim < ndim; ++idim, NIT_ADVANCE_AXISDATA(axisdata, 1)) { @@ -1901,6 +1952,9 @@ npyiter_check_global_flags(npy_uint32 flags, npy_uint32* itflags) if (flags&NPY_ITER_GROWINNER) { (*itflags) |= NPY_ITFLAG_GROWINNER; } + if (flags&NPY_ITER_DELAY_BUFALLOC) { + (*itflags) |= NPY_ITFLAG_DELAYBUF; + } } return 1; @@ -2161,11 +2215,11 @@ npyiter_prepare_one_operand(PyArrayObject **op, *op_dtype = nbo_dtype; /* Indicate that byte order or alignment needs fixing */ - *op_itflags |= NPY_OP_ITFLAG_COPYSWAP; + *op_itflags |= NPY_OP_ITFLAG_CAST; } /* Check alignment */ else if (!PyArray_ISALIGNED(*op)) { - *op_itflags |= NPY_OP_ITFLAG_COPYSWAP; + *op_itflags |= NPY_OP_ITFLAG_CAST; } } } @@ -2412,7 +2466,7 @@ npyiter_fill_axisdata(NpyIter *iter, char **op_dataptr, PyArrayObject **op = NIT_OBJECTS(iter); axisdata0 = NIT_AXISDATA(iter); - sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); /* Process the first operand */ if (op_axes == NULL || op_axes[0] == NULL) { @@ -2683,7 +2737,7 @@ npyiter_replace_axisdata(NpyIter *iter, npy_intp iiter, perm = NIT_PERM(iter); axisdata0 = NIT_AXISDATA(iter); - sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); /* * Replace just the strides which were non-zero, and compute @@ -2770,7 +2824,7 @@ npyiter_compute_index_strides(NpyIter *iter, npy_uint32 flags) } if (flags&NPY_ITER_C_INDEX) { - sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); axisdata = NIT_AXISDATA(iter); indexstride = 1; for(idim = 0; idim < ndim; ++idim, NIT_ADVANCE_AXISDATA(axisdata, 1)) { @@ -2787,7 +2841,7 @@ npyiter_compute_index_strides(NpyIter *iter, npy_uint32 flags) } } else if (flags&NPY_ITER_F_INDEX) { - sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); axisdata = NIT_INDEX_AXISDATA(NIT_AXISDATA(iter), ndim-1); indexstride = 1; for(idim = 0; idim < ndim; ++idim, NIT_ADVANCE_AXISDATA(axisdata, -1)) { @@ -2871,7 +2925,7 @@ npyiter_flip_negative_strides(NpyIter *iter) npy_intp istrides, nstrides = NAD_NSTRIDES(); NpyIter_AxisData *axisdata, *axisdata0; npy_intp *baseoffsets; - npy_intp sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + npy_intp sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); int any_flipped = 0; axisdata0 = axisdata = NIT_AXISDATA(iter); @@ -2956,7 +3010,7 @@ npyiter_reverse_axis_ordering(NpyIter *iter) return; } - size = NIT_SIZEOF_AXISDATA(itflags, ndim, niter)/NPY_SIZEOF_INTP; + size = NIT_AXISDATA_SIZEOF(itflags, ndim, niter)/NPY_SIZEOF_INTP; first = (npy_intp*)NIT_AXISDATA(iter); last = first + (ndim-1)*size; @@ -2995,7 +3049,7 @@ npyiter_find_best_axis_ordering(NpyIter *iter) npy_intp i0, i1, ipos, j0, j1; npy_intp *perm; NpyIter_AxisData *axisdata = NIT_AXISDATA(iter); - npy_intp sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + npy_intp sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); int permuted = 0; perm = NIT_PERM(iter); @@ -3132,7 +3186,7 @@ npyiter_coalesce_axes(NpyIter *iter) npy_intp istrides, nstrides = NAD_NSTRIDES(); NpyIter_AxisData *axisdata = NIT_AXISDATA(iter); - npy_intp sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + npy_intp sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); NpyIter_AxisData *ad_compress; npy_intp new_ndim = 1; @@ -3243,7 +3297,7 @@ npyiter_new_temp_array(NpyIter *iter, PyTypeObject *subtype, stride = op_dtype->elsize; char reversestride[NPY_MAXDIMS], anyreverse = 0; NpyIter_AxisData *axisdata = NIT_AXISDATA(iter); - npy_intp sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + npy_intp sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); PyArrayObject *ret; @@ -3434,10 +3488,9 @@ npyiter_allocate_arrays(NpyIter *iter, /* New arrays are aligned and need no swapping or casting */ op_itflags[iiter] |= NPY_OP_ITFLAG_ALIGNED; - op_itflags[iiter] &= ~(NPY_OP_ITFLAG_COPYSWAP|NPY_OP_ITFLAG_CAST); + op_itflags[iiter] &= ~NPY_OP_ITFLAG_CAST; } - else if ((op_itflags[iiter]& - (NPY_OP_ITFLAG_CAST|NPY_OP_ITFLAG_COPYSWAP)) && + else if ((op_itflags[iiter]&NPY_OP_ITFLAG_CAST) && (op_itflags[iiter]&NPY_OP_ITFLAG_COPY)) { PyArrayObject *temp; npy_intp ondim = PyArray_NDIM(op[iiter]); @@ -3479,16 +3532,15 @@ npyiter_allocate_arrays(NpyIter *iter, /* The temporary copy is aligned and needs no swap or cast */ op_itflags[iiter] |= NPY_OP_ITFLAG_ALIGNED; - op_itflags[iiter] &= ~(NPY_OP_ITFLAG_COPYSWAP|NPY_OP_ITFLAG_CAST); + op_itflags[iiter] &= ~NPY_OP_ITFLAG_CAST; } else { /* * Buffering must be enabled for casting/conversion if copy * wasn't specified. */ - if (op_itflags[iiter]& - (NPY_OP_ITFLAG_CAST|NPY_OP_ITFLAG_COPYSWAP) && - !(itflags&NPY_ITFLAG_BUFFER)) { + if ((op_itflags[iiter]&NPY_OP_ITFLAG_CAST) && + !(itflags&NPY_ITFLAG_BUFFER)) { PyErr_SetString(PyExc_TypeError, "Iterator operand required copying or buffering, " "but neither copying nor buffering was enabled"); @@ -3515,14 +3567,13 @@ npyiter_allocate_arrays(NpyIter *iter, * dimension. */ if ((itflags&NPY_ITFLAG_BUFFER) && - (!(op_itflags[iiter]&(NPY_OP_ITFLAG_CAST| - NPY_OP_ITFLAG_COPYSWAP)) || + (!(op_itflags[iiter]&NPY_OP_ITFLAG_CAST) || (op_itflags[iiter]&NPY_OP_ITFLAG_WRITE))) { int is_one_to_one = 1; npy_intp stride, shape, innerstride = 0, innershape; NpyIter_AxisData *axisdata = NIT_AXISDATA(iter); npy_intp sizeof_axisdata = - NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + NIT_AXISDATA_SIZEOF(itflags, ndim, niter); /* Find stride of the first non-empty shape */ for (idim = 0; idim < ndim; ++idim) { innershape = NAD_SHAPE(axisdata); @@ -3563,9 +3614,7 @@ npyiter_allocate_arrays(NpyIter *iter, * Set that stride, because it may not belong to the first * dimension. */ - if (idim == ndim && - !(op_itflags[iiter]&(NPY_OP_ITFLAG_CAST| - NPY_OP_ITFLAG_COPYSWAP))) { + if (idim == ndim && !(op_itflags[iiter]&NPY_OP_ITFLAG_CAST)) { op_itflags[iiter] |= NPY_OP_ITFLAG_BUFNEVER; NBF_STRIDES(bufferdata)[iiter] = innerstride; } @@ -3789,8 +3838,9 @@ npyiter_allocate_buffers(NpyIter *iter) { npy_uint32 itflags = NIT_ITFLAGS(iter); npy_intp ndim = NIT_NDIM(iter); - npy_intp iiter, niter = NIT_NITER(iter); + npy_intp iiter = 0, niter = NIT_NITER(iter); + npy_intp i; char *op_itflags = NIT_OPITFLAGS(iter); NpyIter_BufferData *bufferdata = NIT_BUFFERDATA(iter); NpyIter_AxisData *axisdata = NIT_AXISDATA(iter); @@ -3798,7 +3848,11 @@ npyiter_allocate_buffers(NpyIter *iter) PyArray_Descr **op_dtype = NIT_DTYPES(iter); npy_intp *strides = NAD_STRIDES(axisdata), op_stride; npy_intp buffersize = NBF_BUFFERSIZE(bufferdata); - char *buffer; + char *buffer, **buffers = NBF_BUFFERS(bufferdata); + PyArray_StridedTransferFn *readtransferfn = NBF_READTRANSFERFN(bufferdata), + *writetransferfn = NBF_WRITETRANSFERFN(bufferdata); + void **readtransferdata = NBF_READTRANSFERDATA(bufferdata), + **writetransferdata = NBF_WRITETRANSFERDATA(bufferdata); PyArray_StridedTransferFn stransfer = NULL; void *transferdata = NULL; @@ -3815,10 +3869,9 @@ npyiter_allocate_buffers(NpyIter *iter) npy_intp itemsize = op_dtype[iiter]->elsize; buffer = PyArray_malloc(itemsize*buffersize); if (buffer == NULL) { - PyErr_NoMemory(); - return 0; + goto fail; } - NBF_BUFFERS(bufferdata)[iiter] = buffer; + buffers[iiter] = buffer; /* Also need to get an appropriate transfer functions */ if (flags&NPY_OP_ITFLAG_READ) { @@ -3830,13 +3883,13 @@ npyiter_allocate_buffers(NpyIter *iter) op_dtype[iiter], &stransfer, &transferdata) != NPY_SUCCEED) { - return 0; + goto fail; } - NBF_READTRANSFERFN(bufferdata)[iiter] = stransfer; - NBF_READTRANSFERDATA(bufferdata)[iiter] = transferdata; + readtransferfn[iiter] = stransfer; + readtransferdata[iiter] = transferdata; } else { - NBF_READTRANSFERFN(bufferdata)[iiter] = NULL; + readtransferfn[iiter] = NULL; } if (flags&NPY_OP_ITFLAG_WRITE) { if (PyArray_GetDTypeTransferFunction( @@ -3847,22 +3900,40 @@ npyiter_allocate_buffers(NpyIter *iter) PyArray_DESCR(op[iiter]), &stransfer, &transferdata) != NPY_SUCCEED) { - return 0; + goto fail; } - NBF_WRITETRANSFERFN(bufferdata)[iiter] = stransfer; - NBF_WRITETRANSFERDATA(bufferdata)[iiter] = transferdata; + writetransferfn[iiter] = stransfer; + writetransferdata[iiter] = transferdata; } else { - NBF_WRITETRANSFERFN(bufferdata)[iiter] = NULL; + writetransferfn[iiter] = NULL; } } else { - NBF_READTRANSFERFN(bufferdata)[iiter] = NULL; - NBF_WRITETRANSFERFN(bufferdata)[iiter] = NULL; + readtransferfn[iiter] = NULL; + writetransferfn[iiter] = NULL; } } return 1; + +fail: + for (i = 0; i < iiter; ++i) { + if (buffers[i] != NULL) { + PyArray_free(buffers[i]); + buffers[i] = NULL; + } + if (readtransferdata[iiter] != NULL) { + PyArray_FreeStridedTransferData(readtransferdata[iiter]); + readtransferdata[iiter] = NULL; + } + if (writetransferdata[iiter] != NULL) { + PyArray_FreeStridedTransferData(writetransferdata[iiter]); + writetransferdata[iiter] = NULL; + } + } + PyErr_NoMemory(); + return 0; } /* @@ -3882,7 +3953,7 @@ static void npyiter_goto_iterindex(NpyIter *iter, npy_intp iterindex) npy_intp istrides, nstrides, i, shape; axisdata = NIT_AXISDATA(iter); - sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); nstrides = NAD_NSTRIDES(); NIT_ITERINDEX(iter) = iterindex; @@ -3973,7 +4044,7 @@ npyiter_copy_from_buffers(NpyIter *iter) PyArray_StridedTransferFn stransfer = NULL; void *transferdata = NULL; - npy_intp axisdata_incr = NIT_SIZEOF_AXISDATA(itflags, ndim, niter) / + npy_intp axisdata_incr = NIT_AXISDATA_SIZEOF(itflags, ndim, niter) / NPY_SIZEOF_INTP; /* If we're past the end, nothing to copy */ @@ -4028,7 +4099,7 @@ npyiter_copy_to_buffers(NpyIter *iter) PyArray_StridedTransferFn stransfer = NULL; void *transferdata = NULL; - npy_intp axisdata_incr = NIT_SIZEOF_AXISDATA(itflags, ndim, niter) / + npy_intp axisdata_incr = NIT_AXISDATA_SIZEOF(itflags, ndim, niter) / NPY_SIZEOF_INTP; /* Calculate the size if using any buffers */ @@ -4058,9 +4129,7 @@ npyiter_copy_to_buffers(NpyIter *iter) stransfer = NBF_READTRANSFERFN(bufferdata)[iiter]; transferdata = NBF_READTRANSFERDATA(bufferdata)[iiter]; switch (op_itflags[iiter]& - (NPY_OP_ITFLAG_BUFNEVER| - NPY_OP_ITFLAG_COPYSWAP| - NPY_OP_ITFLAG_CAST)) { + (NPY_OP_ITFLAG_BUFNEVER|NPY_OP_ITFLAG_CAST)) { /* never need to buffer this operand */ case NPY_OP_ITFLAG_BUFNEVER: ptrs[iiter] = ad_ptrs[iiter]; @@ -4150,10 +4219,16 @@ NpyIter_DebugPrint(NpyIter *iter) printf("FORCEDORDER "); if (itflags&NPY_ITFLAG_NOINNER) printf("NOINNER "); + if (itflags&NPY_ITFLAG_RANGE) + printf("RANGE "); if (itflags&NPY_ITFLAG_BUFFER) printf("BUFFER "); if (itflags&NPY_ITFLAG_GROWINNER) printf("GROWINNER "); + if (itflags&NPY_ITFLAG_ONEITERATION) + printf("ONEITERATION "); + if (itflags&NPY_ITFLAG_DELAYBUF) + printf("DELAYBUF "); printf("\n"); printf("NDim: %d\n", (int)ndim); printf("NIter: %d\n", (int)niter); @@ -4163,8 +4238,10 @@ NpyIter_DebugPrint(NpyIter *iter) printf("IterIndex: %d\n", (int)NIT_ITERINDEX(iter)); printf("Iterator SizeOf: %d\n", (int)NIT_SIZEOF_ITERATOR(itflags, ndim, niter)); + printf("BufferData SizeOf: %d\n", + (int)NIT_BUFFERDATA_SIZEOF(itflags, ndim, niter)); printf("AxisData SizeOf: %d\n", - (int)NIT_SIZEOF_AXISDATA(itflags, ndim, niter)); + (int)NIT_AXISDATA_SIZEOF(itflags, ndim, niter)); printf("\n"); printf("Perm: "); @@ -4216,8 +4293,6 @@ NpyIter_DebugPrint(NpyIter *iter) printf("COPY "); if ((NIT_OPITFLAGS(iter)[iiter])&NPY_OP_ITFLAG_CAST) printf("CAST "); - if ((NIT_OPITFLAGS(iter)[iiter])&NPY_OP_ITFLAG_COPYSWAP) - printf("COPYSWAP "); if ((NIT_OPITFLAGS(iter)[iiter])&NPY_OP_ITFLAG_BUFNEVER) printf("BUFNEVER "); if ((NIT_OPITFLAGS(iter)[iiter])&NPY_OP_ITFLAG_ALIGNED) @@ -4264,7 +4339,7 @@ NpyIter_DebugPrint(NpyIter *iter) } axisdata = NIT_AXISDATA(iter); - sizeof_axisdata = NIT_SIZEOF_AXISDATA(itflags, ndim, niter); + sizeof_axisdata = NIT_AXISDATA_SIZEOF(itflags, ndim, niter); for (idim = 0; idim < ndim; ++idim, NIT_ADVANCE_AXISDATA(axisdata, 1)) { printf("AxisData[%d]:\n", (int)idim); printf(" Shape: %d\n", (int)NAD_SHAPE(axisdata)); diff --git a/numpy/core/src/multiarray/new_iterator.h b/numpy/core/src/multiarray/new_iterator.h index 72e7d87ea..2d54a31af 100644 --- a/numpy/core/src/multiarray/new_iterator.h +++ b/numpy/core/src/multiarray/new_iterator.h @@ -48,6 +48,9 @@ NpyIter_Copy(NpyIter *iter); /* Deallocate an iterator */ int NpyIter_Deallocate(NpyIter* iter); +/* Whether the buffer allocation is being delayed */ +int NpyIter_HasDelayedBufAlloc(NpyIter *iter); + /* Whether the iterator handles the inner loop */ int NpyIter_HasInnerLoop(NpyIter *iter); /* Removes the inner loop handling (so HasInnerLoop returns false) */ @@ -58,9 +61,9 @@ npy_intp *NpyIter_GetInnerStrideArray(NpyIter *iter); npy_intp* NpyIter_GetInnerLoopSizePtr(NpyIter *iter); /* Resets the iterator to its initial state */ -void NpyIter_Reset(NpyIter *iter); +int NpyIter_Reset(NpyIter *iter); /* Resets the iterator to its initial state, with new base data pointers */ -void NpyIter_ResetBasePointers(NpyIter *iter, char **baseptrs); +int NpyIter_ResetBasePointers(NpyIter *iter, char **baseptrs); /* Resets the iterator to a new iterator index range */ int NpyIter_ResetToIterIndexRange(NpyIter *iter, npy_intp istart, npy_intp iend); @@ -137,6 +140,8 @@ NPY_NO_EXPORT void NpyIter_DebugPrint(NpyIter *iter); #define NPY_ITER_BUFFERED 0x00000040 /* When buffering is enabled, grows the inner loop if possible */ #define NPY_ITER_GROWINNER 0x00000080 +/* Delay allocation of buffers until first Reset* call */ +#define NPY_ITER_DELAY_BUFALLOC 0x00000100 /*** Per-operand flags that may be passed to the iterator constructors ***/ diff --git a/numpy/core/src/multiarray/new_iterator_pywrap.c b/numpy/core/src/multiarray/new_iterator_pywrap.c index 9a0b6cbfd..871ab33b7 100644 --- a/numpy/core/src/multiarray/new_iterator_pywrap.c +++ b/numpy/core/src/multiarray/new_iterator_pywrap.c @@ -38,7 +38,7 @@ void npyiter_cache_values(NewNpyArrayIterObject *self) /* iternext and getcoords functions */ self->iternext = NpyIter_GetIterNext(iter); - if (NpyIter_HasCoords(iter)) { + if (NpyIter_HasCoords(iter) && !NpyIter_HasDelayedBufAlloc(iter)) { self->getcoords = NpyIter_GetGetCoords(iter); } else { @@ -87,17 +87,17 @@ NpyIter_GlobalFlagsConverter(PyObject *flags_in, npy_uint32 *flags) PyObject *f; char *str = NULL; Py_ssize_t length = 0; - npy_uint32 flag = 0; + npy_uint32 flag; if (flags_in == NULL || flags_in == Py_None) { *flags = 0; - return NPY_SUCCEED; + return 1; } if (!PyTuple_Check(flags_in) && !PyList_Check(flags_in)) { PyErr_SetString(PyExc_ValueError, "Iterator global flags must be a list or tuple of strings"); - return NPY_FAIL; + return 0; } nflags = PySequence_Size(flags_in); @@ -105,13 +105,14 @@ NpyIter_GlobalFlagsConverter(PyObject *flags_in, npy_uint32 *flags) for (iflags = 0; iflags < nflags; ++iflags) { f = PySequence_GetItem(flags_in, iflags); if (f == NULL) { - return NPY_FAIL; + return 0; } if (PyString_AsStringAndSize(f, &str, &length) == -1) { Py_DECREF(f); - return NPY_FAIL; + return 0; } /* Use switch statements to quickly isolate the right flag */ + flag = 0; switch (str[0]) { case 'b': if (strcmp(str, "buffered") == 0) { @@ -137,6 +138,11 @@ NpyIter_GlobalFlagsConverter(PyObject *flags_in, npy_uint32 *flags) break; } break; + case 'd': + if (strcmp(str, "delay_bufalloc") == 0) { + flag = NPY_ITER_DELAY_BUFALLOC; + } + break; case 'f': if (strcmp(str, "f_index") == 0) { flag = NPY_ITER_F_INDEX; @@ -162,7 +168,7 @@ NpyIter_GlobalFlagsConverter(PyObject *flags_in, npy_uint32 *flags) PyErr_Format(PyExc_ValueError, "Unexpected iterator global flag \"%s\"", str); Py_DECREF(f); - return NPY_FAIL; + return 0; } else { tmpflags |= flag; @@ -171,7 +177,7 @@ NpyIter_GlobalFlagsConverter(PyObject *flags_in, npy_uint32 *flags) } *flags |= tmpflags; - return NPY_SUCCEED; + return 1; } /* TODO: Use PyArray_OrderConverter once 'K' is added there */ @@ -182,27 +188,27 @@ npyiter_order_converter(PyObject *order_in, NPY_ORDER *order) Py_ssize_t length = 0; if (PyString_AsStringAndSize(order_in, &str, &length) == -1) { - return NPY_FAIL; + return 0; } if (length == 1) switch (str[0]) { case 'C': *order = NPY_CORDER; - return NPY_SUCCEED; + return 1; case 'F': *order = NPY_FORTRANORDER; - return NPY_SUCCEED; + return 1; case 'A': *order = NPY_ANYORDER; - return NPY_SUCCEED; + return 1; case 'K': *order = NPY_KEEPORDER; - return NPY_SUCCEED; + return 1; } PyErr_SetString(PyExc_ValueError, "order must be one of 'C', 'F', 'A', or 'K'"); - return NPY_FAIL; + return 0; } /* @@ -216,37 +222,37 @@ PyArray_CastingConverter(PyObject *obj, NPY_CASTING *casting) Py_ssize_t length = 0; if (PyString_AsStringAndSize(obj, &str, &length) == -1) { - return NPY_FAIL; + return 0; } if (length >= 2) switch (str[2]) { case 0: if (strcmp(str, "no") == 0) { *casting = NPY_NO_CASTING; - return NPY_SUCCEED; + return 1; } break; case 'u': if (strcmp(str, "equiv") == 0) { *casting = NPY_EQUIV_CASTING; - return NPY_SUCCEED; + return 1; } break; case 'f': if (strcmp(str, "safe") == 0) { *casting = NPY_SAFE_CASTING; - return NPY_SUCCEED; + return 1; } break; case 'm': if (strcmp(str, "same_kind") == 0) { *casting = NPY_SAME_KIND_CASTING; - return NPY_SUCCEED; + return 1; } break; case 's': if (strcmp(str, "unsafe") == 0) { *casting = NPY_UNSAFE_CASTING; - return NPY_SUCCEED; + return 1; } break; } @@ -254,7 +260,7 @@ PyArray_CastingConverter(PyObject *obj, NPY_CASTING *casting) PyErr_SetString(PyExc_ValueError, "casting must be one of 'no', 'equiv', 'safe', " "'same_kind', or 'unsafe'"); - return NPY_FAIL; + return 0; } @@ -263,11 +269,12 @@ NpyIter_OpFlagsConverter(PyObject *op_flags_in, npy_uint32 *op_flags) { int iflags, nflags; + npy_uint32 flag; if (!PyTuple_Check(op_flags_in) && !PyList_Check(op_flags_in)) { PyErr_SetString(PyExc_ValueError, "op_flags must be a tuple or array of per-op flag-tuples"); - return NPY_FAIL; + return 0; } nflags = PySequence_Size(op_flags_in); @@ -277,21 +284,21 @@ NpyIter_OpFlagsConverter(PyObject *op_flags_in, PyObject *f; char *str = NULL; Py_ssize_t length = 0; - npy_uint32 flag = 0; f = PySequence_GetItem(op_flags_in, iflags); if (f == NULL) { - return NPY_FAIL; + return 0; } if (PyString_AsStringAndSize(f, &str, &length) == -1) { Py_DECREF(f); PyErr_SetString(PyExc_ValueError, "op_flags must be a tuple or array of per-op flag-tuples"); - return NPY_FAIL; + return 0; } /* Use switch statements to quickly isolate the right flag */ + flag = 0; switch (str[0]) { case 'a': if (strcmp(str, "allocate") == 0) { @@ -359,7 +366,7 @@ NpyIter_OpFlagsConverter(PyObject *op_flags_in, PyErr_Format(PyExc_ValueError, "Unexpected per-op iterator flag \"%s\"", str); Py_DECREF(f); - return NPY_FAIL; + return 0; } else { *op_flags |= flag; @@ -367,7 +374,7 @@ NpyIter_OpFlagsConverter(PyObject *op_flags_in, Py_DECREF(f); } - return NPY_SUCCEED; + return 1; } static int @@ -379,7 +386,7 @@ npyiter_convert_op_flags_array(PyObject *op_flags_in, if (!PyTuple_Check(op_flags_in) && !PyList_Check(op_flags_in)) { PyErr_SetString(PyExc_ValueError, "op_flags must be a tuple or array of per-op flag-tuples"); - return NPY_FAIL; + return 0; } if (PySequence_Size(op_flags_in) != niter) { @@ -389,35 +396,35 @@ npyiter_convert_op_flags_array(PyObject *op_flags_in, for (iiter = 0; iiter < niter; ++iiter) { PyObject *f = PySequence_GetItem(op_flags_in, iiter); if (f == NULL) { - return NPY_FAIL; + return 0; } if (NpyIter_OpFlagsConverter(f, - &op_flags_array[iiter]) != NPY_SUCCEED) { + &op_flags_array[iiter]) != 1) { Py_DECREF(f); /* If the first one doesn't work, try the whole thing as flags */ if (iiter == 0) { PyErr_Clear(); goto try_single_flags; } - return NPY_FAIL; + return 0; } Py_DECREF(f); } - return NPY_SUCCEED; + return 1; try_single_flags: if (NpyIter_OpFlagsConverter(op_flags_in, - &op_flags_array[0]) != NPY_SUCCEED) { - return NPY_FAIL; + &op_flags_array[0]) != 1) { + return 0; } for (iiter = 1; iiter < niter; ++iiter) { op_flags_array[iiter] = op_flags_array[0]; } - return NPY_SUCCEED; + return 1; } static int @@ -443,11 +450,11 @@ npyiter_convert_dtypes(PyObject *op_dtypes_in, for (i = 0; i < iiter; ++i ) { Py_XDECREF(op_dtypes[i]); } - return NPY_FAIL; + return 0; } /* Try converting the object to a descr */ - if (PyArray_DescrConverter2(dtype, &op_dtypes[iiter]) != NPY_SUCCEED) { + if (PyArray_DescrConverter2(dtype, &op_dtypes[iiter]) != 1) { npy_intp i; for (i = 0; i < iiter; ++i ) { Py_XDECREF(op_dtypes[i]); @@ -460,18 +467,18 @@ npyiter_convert_dtypes(PyObject *op_dtypes_in, Py_DECREF(dtype); } - return NPY_SUCCEED; + return 1; try_single_dtype: - if (PyArray_DescrConverter2(op_dtypes_in, &op_dtypes[0]) == NPY_SUCCEED) { + if (PyArray_DescrConverter2(op_dtypes_in, &op_dtypes[0]) == 1) { for (iiter = 1; iiter < niter; ++iiter) { op_dtypes[iiter] = op_dtypes[0]; Py_XINCREF(op_dtypes[iiter]); } - return NPY_SUCCEED; + return 1; } - return NPY_FAIL; + return 0; } static int @@ -485,7 +492,7 @@ npyiter_convert_op_axes(PyObject *op_axes_in, npy_intp niter, PySequence_Size(op_axes_in) != niter) { PyErr_SetString(PyExc_ValueError, "op_axes must be a tuple/list matching the number of ops"); - return NPY_FAIL; + return 0; } *oa_ndim = 0; @@ -495,7 +502,7 @@ npyiter_convert_op_axes(PyObject *op_axes_in, npy_intp niter, npy_intp idim; a = PySequence_GetItem(op_axes_in, iiter); if (a == NULL) { - return NPY_FAIL; + return 0; } if (a == Py_None) { op_axes[iiter] = NULL; @@ -505,32 +512,32 @@ npyiter_convert_op_axes(PyObject *op_axes_in, npy_intp niter, "Each entry of op_axes must be None " "or a tuple/list"); Py_DECREF(a); - return NPY_FAIL; + return 0; } if (*oa_ndim == 0) { *oa_ndim = PySequence_Size(a); if (*oa_ndim == 0) { PyErr_SetString(PyExc_ValueError, "op_axes must have at least one dimension"); - return NPY_FAIL; + return 0; } if (*oa_ndim > NPY_MAXDIMS) { PyErr_SetString(PyExc_ValueError, "Too many dimensions in op_axes"); - return NPY_FAIL; + return 0; } } if (PySequence_Size(a) != *oa_ndim) { PyErr_SetString(PyExc_ValueError, "Each entry of op_axes must have the same size"); Py_DECREF(a); - return NPY_FAIL; + return 0; } for (idim = 0; idim < *oa_ndim; ++idim) { PyObject *v = PySequence_GetItem(a, idim); if (v == NULL) { Py_DECREF(a); - return NPY_FAIL; + return 0; } /* numpy.newaxis is None */ if (v == Py_None) { @@ -542,7 +549,7 @@ npyiter_convert_op_axes(PyObject *op_axes_in, npy_intp niter, PyErr_Occurred()) { Py_DECREF(a); Py_DECREF(v); - return NPY_FAIL; + return 0; } } Py_DECREF(v); @@ -555,10 +562,10 @@ npyiter_convert_op_axes(PyObject *op_axes_in, npy_intp niter, PyErr_SetString(PyExc_ValueError, "If op_axes is provided, at least one list of axes " "must be contained within it"); - return NPY_FAIL; + return 0; } - return NPY_SUCCEED; + return 1; } /* @@ -579,11 +586,11 @@ npyiter_convert_ops(PyObject *op_in, PyObject *op_flags_in, if (niter == 0) { PyErr_SetString(PyExc_ValueError, "Must provide at least one operand"); - return NPY_FAIL; + return 0; } if (niter > NPY_MAXARGS) { PyErr_SetString(PyExc_ValueError, "Too many operands"); - return NPY_FAIL; + return 0; } for (iiter = 0; iiter < niter; ++iiter) { @@ -593,7 +600,7 @@ npyiter_convert_ops(PyObject *op_in, PyObject *op_flags_in, for (i = 0; i < iiter; ++i) { Py_XDECREF(op[i]); } - return NPY_FAIL; + return 0; } else if (item == Py_None) { Py_DECREF(item); @@ -633,11 +640,11 @@ npyiter_convert_ops(PyObject *op_in, PyObject *op_flags_in, } } else if (npyiter_convert_op_flags_array(op_flags_in, - op_flags, niter) != NPY_SUCCEED) { + op_flags, niter) != 1) { for (iiter = 0; iiter < niter; ++iiter) { Py_XDECREF(op[iiter]); } - return NPY_FAIL; + return 0; } /* Now that we have the flags - convert all the ops to arrays */ @@ -662,14 +669,14 @@ npyiter_convert_ops(PyObject *op_in, PyObject *op_flags_in, for (iiter = 0; iiter < niter; ++iiter) { Py_DECREF(op[iiter]); } - return NPY_FAIL; + return 0; } Py_DECREF(op[iiter]); op[iiter] = ao; } } - return NPY_SUCCEED; + return 1; } static int @@ -679,7 +686,7 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds) "order", "casting", "op_axes", "buffersize", NULL}; - PyObject *op_in = NULL, *flags_in = NULL, *op_flags_in = NULL, + PyObject *op_in = NULL, *op_flags_in = NULL, *op_dtypes_in = NULL, *op_axes_in = NULL; npy_intp iiter, niter = 0; @@ -700,9 +707,9 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds) return -1; } - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOOO&O&Oi", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&OOO&O&Oi", kwlist, &op_in, - &flags_in, + NpyIter_GlobalFlagsConverter, &flags, &op_flags_in, &op_dtypes_in, npyiter_order_converter, &order, @@ -712,14 +719,9 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds) return -1; } - /* flags */ - if (NpyIter_GlobalFlagsConverter(flags_in, &flags) != NPY_SUCCEED) { - return -1; - } - /* op and op_flags */ if (npyiter_convert_ops(op_in, op_flags_in, op, op_flags, &niter) - != NPY_SUCCEED) { + != 1) { return -1; } @@ -729,7 +731,7 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds) /* op_request_dtypes */ if (op_dtypes_in != NULL && op_dtypes_in != Py_None && npyiter_convert_dtypes(op_dtypes_in, - op_request_dtypes, niter) != NPY_SUCCEED) { + op_request_dtypes, niter) != 1) { goto fail; } @@ -741,7 +743,7 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds) } if (npyiter_convert_op_axes(op_axes_in, niter, - op_axes, &oa_ndim) != NPY_SUCCEED) { + op_axes, &oa_ndim) != 1) { goto fail; } } @@ -786,7 +788,7 @@ NpyIter_NestedIters(PyObject *NPY_UNUSED(self), "casting", "buffersize", NULL}; - PyObject *op_in = NULL, *axes_in = NULL, *flags_in = NULL, + PyObject *op_in = NULL, *axes_in = NULL, *op_flags_in = NULL, *op_dtypes_in = NULL; npy_intp iiter, niter = 0, inest, nnest = 0; @@ -806,10 +808,10 @@ NpyIter_NestedIters(PyObject *NPY_UNUSED(self), PyObject *ret = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|OOOO&O&i", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O&OOO&O&i", kwlist, &op_in, &axes_in, - &flags_in, + NpyIter_GlobalFlagsConverter, &flags, &op_flags_in, &op_dtypes_in, npyiter_order_converter, &order, @@ -884,14 +886,9 @@ NpyIter_NestedIters(PyObject *NPY_UNUSED(self), Py_DECREF(item); } - /* flags */ - if (NpyIter_GlobalFlagsConverter(flags_in, &flags) != NPY_SUCCEED) { - return NULL; - } - /* op and op_flags */ if (npyiter_convert_ops(op_in, op_flags_in, op, op_flags, &niter) - != NPY_SUCCEED) { + != 1) { return NULL; } @@ -903,7 +900,7 @@ NpyIter_NestedIters(PyObject *NPY_UNUSED(self), /* op_request_dtypes */ if (op_dtypes_in != NULL && op_dtypes_in != Py_None && npyiter_convert_dtypes(op_dtypes_in, - op_request_dtypes, niter) != NPY_SUCCEED) { + op_request_dtypes, niter) != 1) { goto fail; } @@ -1069,7 +1066,11 @@ NpyIter_NestedIters(PyObject *NPY_UNUSED(self), * Need to do a nested reset so all the iterators point * at the right data */ - NpyIter_ResetBasePointers(iter->nested_child->iter, iter->dataptrs); + if (NpyIter_ResetBasePointers(iter->nested_child->iter, + iter->dataptrs) != NPY_SUCCEED) { + Py_DECREF(ret); + return NULL; + } } return ret; @@ -1095,16 +1096,20 @@ npyiter_dealloc(NewNpyArrayIterObject *self) self->ob_type->tp_free((PyObject*)self); } -static void +static int npyiter_resetbasepointers(NewNpyArrayIterObject *self) { while (self->nested_child) { - NpyIter_ResetBasePointers(self->nested_child->iter, - self->dataptrs); + if (NpyIter_ResetBasePointers(self->nested_child->iter, + self->dataptrs) != NPY_SUCCEED) { + return NPY_FAIL; + } self = self->nested_child; self->started = 0; self->finished = 0; } + + return NPY_SUCCEED; } static PyObject * @@ -1116,12 +1121,20 @@ npyiter_reset(NewNpyArrayIterObject *self) return NULL; } - NpyIter_Reset(self->iter); + if (NpyIter_Reset(self->iter) != NPY_SUCCEED) { + return NULL; + } self->started = 0; self->finished = 0; + if (self->getcoords == NULL && NpyIter_HasCoords(self->iter)) { + self->getcoords = NpyIter_GetGetCoords(self->iter); + } + /* If there is nesting, the nested iterators should be reset */ - npyiter_resetbasepointers(self); + if (npyiter_resetbasepointers(self) != NPY_SUCCEED) { + return NULL; + } Py_RETURN_NONE; } @@ -1168,7 +1181,9 @@ npyiter_iternext(NewNpyArrayIterObject *self) { if (self->iter != NULL && !self->finished && self->iternext(self->iter)) { /* If there is nesting, the nested iterators should be reset */ - npyiter_resetbasepointers(self); + if (npyiter_resetbasepointers(self) != NPY_SUCCEED) { + return NULL; + } Py_RETURN_TRUE; } @@ -1346,7 +1361,9 @@ npyiter_next(NewNpyArrayIterObject *self) } /* If there is nesting, the nested iterators should be reset */ - npyiter_resetbasepointers(self); + if (npyiter_resetbasepointers(self) != NPY_SUCCEED) { + return NULL; + } } self->started = 1; @@ -1390,7 +1407,7 @@ static PyObject *npyiter_coords_get(NewNpyArrayIterObject *self) return NULL; } - if (NpyIter_HasCoords(self->iter)) { + if (self->getcoords != NULL) { ndim = NpyIter_GetNDim(self->iter); self->getcoords(self->iter, coords); ret = PyTuple_New(ndim); @@ -1401,9 +1418,22 @@ static PyObject *npyiter_coords_get(NewNpyArrayIterObject *self) return ret; } else { - PyErr_SetString(PyExc_ValueError, - "Iterator does not have coordinates"); - return NULL; + if (!NpyIter_HasCoords(self->iter)) { + PyErr_SetString(PyExc_ValueError, + "Iterator does not have coordinates"); + return NULL; + } + else if (NpyIter_HasDelayedBufAlloc(self->iter)) { + PyErr_SetString(PyExc_ValueError, + "Iterator construction used delayed buffer allocation, " + "and no reset has been done yet"); + return NULL; + } + else { + PyErr_SetString(PyExc_ValueError, + "Iterator is in an invalid state"); + return NULL; + } } } @@ -1449,7 +1479,9 @@ static int npyiter_coords_set(NewNpyArrayIterObject *self, PyObject *value) self->finished = 0; /* If there is nesting, the nested iterators should be reset */ - npyiter_resetbasepointers(self); + if (npyiter_resetbasepointers(self) != NPY_SUCCEED) { + return -1; + } return 0; } @@ -1506,7 +1538,9 @@ static int npyiter_index_set(NewNpyArrayIterObject *self, PyObject *value) self->finished = 0; /* If there is nesting, the nested iterators should be reset */ - npyiter_resetbasepointers(self); + if (npyiter_resetbasepointers(self) != NPY_SUCCEED) { + return -1; + } return 0; } @@ -1555,7 +1589,9 @@ static int npyiter_iterindex_set(NewNpyArrayIterObject *self, PyObject *value) self->finished = 0; /* If there is nesting, the nested iterators should be reset */ - npyiter_resetbasepointers(self); + if (npyiter_resetbasepointers(self) != NPY_SUCCEED) { + return -1; + } return 0; } @@ -1615,12 +1651,34 @@ static int npyiter_iterrange_set(NewNpyArrayIterObject *self, PyObject *value) self->started = self->finished = 1; } + if (self->getcoords == NULL && NpyIter_HasCoords(self->iter)) { + self->getcoords = NpyIter_GetGetCoords(self->iter); + } + /* If there is nesting, the nested iterators should be reset */ - npyiter_resetbasepointers(self); + if (npyiter_resetbasepointers(self) != NPY_SUCCEED) { + return -1; + } return 0; } +static PyObject *npyiter_hasdelayedbufalloc_get(NewNpyArrayIterObject *self) +{ + if (self->iter == NULL) { + PyErr_SetString(PyExc_ValueError, + "Iterator is invalid"); + return NULL; + } + + if (NpyIter_HasDelayedBufAlloc(self->iter)) { + Py_RETURN_TRUE; + } + else { + Py_RETURN_FALSE; + } +} + static PyObject *npyiter_hascoords_get(NewNpyArrayIterObject *self) { if (self->iter == NULL) { @@ -1752,6 +1810,14 @@ npyiter_seq_item(NewNpyArrayIterObject *self, Py_ssize_t i) "Iterator is past the end"); return NULL; } + + if (NpyIter_HasDelayedBufAlloc(self->iter)) { + PyErr_SetString(PyExc_ValueError, + "Iterator construction used delayed buffer allocation, " + "and no reset has been done yet"); + return NULL; + } + niter = NpyIter_GetNIter(self->iter); if (i < 0 || i >= niter) { PyErr_Format(PyExc_IndexError, @@ -1815,6 +1881,14 @@ npyiter_seq_slice(NewNpyArrayIterObject *self, "Iterator is past the end"); return NULL; } + + if (NpyIter_HasDelayedBufAlloc(self->iter)) { + PyErr_SetString(PyExc_ValueError, + "Iterator construction used delayed buffer allocation, " + "and no reset has been done yet"); + return NULL; + } + niter = NpyIter_GetNIter(self->iter); if (ilow < 0) { ilow = 0; @@ -1825,8 +1899,8 @@ npyiter_seq_slice(NewNpyArrayIterObject *self, if (ihigh < ilow) { ihigh = ilow; } - else if (ihigh >= niter) { - ihigh = niter-1; + else if (ihigh > niter) { + ihigh = niter; } ret = PyTuple_New(ihigh-ilow); @@ -1859,11 +1933,20 @@ npyiter_seq_ass_item(NewNpyArrayIterObject *self, Py_ssize_t i, PyObject *v) "can't delete iterator operands"); return -1; } + if (self->iter == NULL || self->finished) { PyErr_SetString(PyExc_ValueError, "Iterator is past the end"); return -1; } + + if (NpyIter_HasDelayedBufAlloc(self->iter)) { + PyErr_SetString(PyExc_ValueError, + "Iterator construction used delayed buffer allocation, " + "and no reset has been done yet"); + return -1; + } + niter = NpyIter_GetNIter(self->iter); if (i < 0 || i >= niter) { PyErr_Format(PyExc_IndexError, @@ -1948,6 +2031,9 @@ static PyGetSetDef npyiter_getsets[] = { {"itviews", (getter)npyiter_itviews_get, NULL, NULL, NULL}, + {"hasdelayedbufalloc", + (getter)npyiter_hasdelayedbufalloc_get, + NULL, NULL, NULL}, {"hascoords", (getter)npyiter_hascoords_get, NULL, NULL, NULL}, diff --git a/numpy/core/tests/test_new_iterator.py b/numpy/core/tests/test_new_iterator.py index b2b9bcb59..25e96a73d 100644 --- a/numpy/core/tests/test_new_iterator.py +++ b/numpy/core/tests/test_new_iterator.py @@ -603,6 +603,14 @@ def test_iter_flags_errors(): assert_raises(ValueError, newiter, [], [], []) # Too many operands assert_raises(ValueError, newiter, [a]*100, [], [['readonly']]*100) + # Bad global flag + assert_raises(ValueError, newiter, [a], ['bad flag'], [['readonly']]) + # Bad op flag + assert_raises(ValueError, newiter, [a], [], [['readonly','bad flag']]) + # Bad order parameter + assert_raises(ValueError, newiter, [a], [], [['readonly']], order='G') + # Bad casting parameter + assert_raises(ValueError, newiter, [a], [], [['readonly']], casting='noon') # op_flags must match ops assert_raises(ValueError, newiter, [a]*3, [], [['readonly']]*2) # Cannot track both a C and an F index @@ -1035,6 +1043,12 @@ def test_iter_copy(): i = None assert_equal([x[()] for x in j], a.ravel(order='F')) + a = arange(24, dtype='<i4').reshape(2,3,4) + i = newiter(a, ['buffered'], order='F', casting='unsafe', + op_dtypes='>f8', buffersize=5) + j = i.copy() + i = None + assert_equal([x[()] for x in j], a.ravel(order='F')) def test_iter_allocate_output_simple(): # Check that the iterator will properly allocate outputs @@ -1327,6 +1341,30 @@ def test_iter_write_buffering(): i.iternext() assert_equal(a.ravel(order='C'), np.arange(24)) +def test_iter_buffering_delayed_alloc(): + # Test that delaying buffer allocation works + + a = np.arange(6) + b = np.arange(1, dtype='f4') + i = np.newiter([a,b], ['buffered','delay_bufalloc','coords'], + casting='unsafe', + op_dtypes='f4') + assert_(i.hasdelayedbufalloc) + assert_raises(ValueError, lambda i:i.coords, i) + assert_raises(ValueError, lambda i:i[0], i) + assert_raises(ValueError, lambda i:i[0:2], i) + def assign_iter(i): + i[0] = 0 + assert_raises(ValueError, assign_iter, i) + + i.reset() + assert_(not i.hasdelayedbufalloc) + assert_equal(i.coords, (0,)) + assert_equal(i[0], 0) + i[1] = 1 + assert_equal(i[0:2], [0,1]) + assert_equal([[x[0][()],x[1][()]] for x in i], zip(range(6), [1]*6)) + def test_iter_buffered_cast_simple(): # Test that buffering can handle a simple cast |