summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/src/multiarray/lowlevel_strided_loops.c.src66
-rw-r--r--numpy/core/src/multiarray/lowlevel_strided_loops.h14
-rw-r--r--numpy/core/src/multiarray/new_iterator.c.src106
-rw-r--r--numpy/core/src/multiarray/new_iterator.h8
-rw-r--r--numpy/core/src/multiarray/new_iterator_pywrap.c40
-rw-r--r--numpy/core/tests/test_new_iterator.py60
6 files changed, 282 insertions, 12 deletions
diff --git a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src
index 880659f2a..2eb0229a8 100644
--- a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src
+++ b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src
@@ -658,10 +658,24 @@ NPY_NO_EXPORT PyArray_StridedTransferFn
/* Does a simple aligned cast */
typedef struct {
- void *freefunc;
+ void *freefunc, *copyfunc;
PyArray_VectorUnaryFunc *castfunc;
} _strided_cast_data;
+/* strided cast data copy function */
+_strided_cast_data *_strided_cast_data_copy(_strided_cast_data *data)
+{
+ _strided_cast_data *newdata =
+ (_strided_cast_data *)PyArray_malloc(sizeof(_strided_cast_data));
+ if (newdata == NULL) {
+ return NULL;
+ }
+
+ memcpy(newdata, data, sizeof(_strided_cast_data));
+
+ return newdata;
+}
+
static void
_aligned_strided_to_strided_cast(char *dst, npy_intp dst_stride,
char *src, npy_intp src_stride,
@@ -691,7 +705,7 @@ _aligned_contig_to_contig_cast(char *dst, npy_intp NPY_UNUSED(dst_stride),
/* Wraps a transfer function + data in alignment code */
typedef struct {
- void *freefunc;
+ void *freefunc, *copyfunc;
PyArray_StridedTransferFn wrapped,
tobuffer, frombuffer;
void *wrappeddata;
@@ -706,6 +720,37 @@ void _align_wrap_data_free(_align_wrap_data *data)
PyArray_free(data);
}
+/* transfer data copy function */
+_align_wrap_data *_align_wrap_data_copy(_align_wrap_data *data)
+{
+ _align_wrap_data *newdata;
+ npy_intp basedatasize, datasize;
+
+ /* Round up the structure size to 16-byte boundary */
+ basedatasize = (sizeof(_align_wrap_data)+15)&(-0x10);
+ /* Add space for two 32-element buffers */
+ datasize = basedatasize + 32*data->src_itemsize + 32*data->dst_itemsize;
+
+ /* Allocate the data, and populate it */
+ newdata = (_align_wrap_data *)PyArray_malloc(datasize);
+ if (newdata == NULL) {
+ return NULL;
+ }
+ memcpy(newdata, data, basedatasize);
+ newdata->bufferin = (char *)newdata + basedatasize;
+ newdata->bufferout = newdata->bufferin + 32*newdata->src_itemsize;
+ if (newdata->wrappeddata != NULL) {
+ newdata->wrappeddata =
+ PyArray_CopyStridedTransferData(data->wrappeddata);
+ if (newdata->wrappeddata == NULL) {
+ PyArray_free(newdata);
+ return NULL;
+ }
+ }
+
+ return newdata;
+}
+
static void
_strided_to_strided_contig_align_wrap(char *dst, npy_intp dst_stride,
char *src, npy_intp src_stride,
@@ -775,6 +820,7 @@ PyArray_WrapTransferFunction(npy_intp src_itemsize, npy_intp dst_itemsize,
/* Allocate the data, and populate it */
data = (_align_wrap_data *)PyArray_malloc(datasize);
data->freefunc = (void *)&_align_wrap_data_free;
+ data->copyfunc = (void *)&_align_wrap_data_copy;
data->tobuffer = tobuffer;
data->frombuffer = frombuffer;
data->wrapped = wrapped;
@@ -847,7 +893,7 @@ PyArray_GetDTypeTransferFunction(int aligned,
}
/* Allocate the data for the casting */
- data = PyArray_malloc(sizeof(_strided_cast_data));
+ data = (_strided_cast_data *)PyArray_malloc(sizeof(_strided_cast_data));
if (data == NULL) {
PyErr_NoMemory();
*outstransfer = NULL;
@@ -855,6 +901,7 @@ PyArray_GetDTypeTransferFunction(int aligned,
return NPY_FAIL;
}
data->freefunc = (void*)&(PyArray_free);
+ data->copyfunc = (void*)&(_strided_cast_data_copy);
data->castfunc = castfunc;
@@ -944,6 +991,19 @@ PyArray_FreeStridedTransferData(void *transferdata)
}
}
+typedef void *(*_npy_stridedtransfer_copy)(void *);
+NPY_NO_EXPORT void *
+PyArray_CopyStridedTransferData(void *transferdata)
+{
+ if (transferdata != NULL) {
+ _npy_stridedtransfer_copy copy =
+ *((_npy_stridedtransfer_copy *)transferdata + 1);
+ return copy(transferdata);
+ }
+
+ return NULL;
+}
+
NPY_NO_EXPORT npy_intp
diff --git a/numpy/core/src/multiarray/lowlevel_strided_loops.h b/numpy/core/src/multiarray/lowlevel_strided_loops.h
index 66cf60f02..52a941b1b 100644
--- a/numpy/core/src/multiarray/lowlevel_strided_loops.h
+++ b/numpy/core/src/multiarray/lowlevel_strided_loops.h
@@ -11,9 +11,10 @@
* and a casting operation,
*
* The 'transferdata' parameter is slightly special, and must always contain
- * a pointer to a deallocation routine at its beginning. The function
- * PyArray_FreeStridedTransferFn should be used to deallocate such
- * pointers, and calls the function pointer.
+ * pointer to deallocation and copying routines at its beginning. The function
+ * PyArray_FreeStridedTransferData should be used to deallocate such
+ * pointers, and calls the first function pointer, while the function
+ * PyArray_CopyStridedTransferData should be used to copy it.
*
*/
typedef void (*PyArray_StridedTransferFn)(char *dst, npy_intp dst_stride,
@@ -29,6 +30,13 @@ NPY_NO_EXPORT void
PyArray_FreeStridedTransferData(void *transferdata);
/*
+ * Copies a PyArray_StridedTransferFunction data object. See
+ * the comment with the function typedef for more details.
+ */
+NPY_NO_EXPORT void *
+PyArray_CopyStridedTransferData(void *transferdata);
+
+/*
* Gives back a function pointer to a specialized function for copying
* strided memory. Returns NULL if there is a problem with the inputs.
*
diff --git a/numpy/core/src/multiarray/new_iterator.c.src b/numpy/core/src/multiarray/new_iterator.c.src
index b7f5a04fd..78d45d2c1 100644
--- a/numpy/core/src/multiarray/new_iterator.c.src
+++ b/numpy/core/src/multiarray/new_iterator.c.src
@@ -72,7 +72,7 @@ struct NpyIter_InternalOnly {
npy_uint32 itflags;
npy_uint16 ndim, niter;
npy_intp itersize, iterstart, iterend;
- /* iterindex is only used if NPY_ITER_RANGED was set */
+ /* iterindex is only used if RANGED or BUFFERED was set */
npy_intp iterindex;
/* The rest is variable */
char iter_flexdata;
@@ -302,7 +302,7 @@ static void
npyiter_copy_to_buffers(NpyIter *iter);
/* The constructor for an iterator over multiple objects */
-NpyIter*
+NpyIter *
NpyIter_MultiNew(npy_intp niter, PyArrayObject **op_in, npy_uint32 flags,
NPY_ORDER order, NPY_CASTING casting,
npy_uint32 *op_flags,
@@ -578,7 +578,7 @@ NpyIter_MultiNew(npy_intp niter, PyArrayObject **op_in, npy_uint32 flags,
}
/* The constructor for an iterator over one object */
-NpyIter*
+NpyIter *
NpyIter_New(PyArrayObject *op, npy_uint32 flags,
NPY_ORDER order, NPY_CASTING casting,
PyArray_Descr* dtype,
@@ -600,6 +600,106 @@ NpyIter_New(PyArrayObject *op, npy_uint32 flags,
}
}
+NpyIter *
+NpyIter_Copy(NpyIter *iter)
+{
+ npy_uint32 itflags = NIT_ITFLAGS(iter);
+ npy_intp ndim = NIT_NDIM(iter);
+ npy_intp iiter, niter = NIT_NITER(iter);
+ int out_of_memory = 0;
+
+ npy_intp size;
+ NpyIter *newiter;
+ PyArrayObject **objects;
+ PyArray_Descr **dtypes;
+
+ /* Allocate memory for the new iterator */
+ size = NIT_SIZEOF_ITERATOR(itflags, ndim, niter);
+ newiter = (NpyIter*)PyArray_malloc(size);
+
+ /* Copy the raw values to the new iterator */
+ memcpy(newiter, iter, size);
+
+ /* Take ownership of references to the operands and dtypes */
+ objects = NIT_OBJECTS(newiter);
+ dtypes = NIT_DTYPES(newiter);
+ for (iiter = 0; iiter < niter; ++iiter) {
+ Py_INCREF(objects[iiter]);
+ Py_INCREF(dtypes[iiter]);
+ }
+
+ /* Allocate buffers and make copies of the transfer data if necessary */
+ if (itflags&NPY_ITFLAG_BUFFER) {
+ NpyIter_BufferData *bufferdata;
+ npy_intp buffersize, itemsize;
+ char **buffers;
+ void **readtransferdata, **writetransferdata;
+
+ bufferdata = NIT_BUFFERDATA(newiter);
+ buffers = NBF_BUFFERS(bufferdata);
+ readtransferdata = NBF_READTRANSFERDATA(bufferdata);
+ writetransferdata = NBF_WRITETRANSFERDATA(bufferdata);
+ buffersize = NBF_BUFFERSIZE(bufferdata);
+
+ for (iiter = 0; iiter < niter; ++iiter) {
+ if (buffers[iiter] != NULL) {
+ if (out_of_memory) {
+ buffers[iiter] = NULL;
+ }
+ else {
+ itemsize = dtypes[iiter]->elsize;
+ buffers[iiter] = PyArray_malloc(itemsize*buffersize);
+ if (buffers[iiter] == NULL) {
+ out_of_memory = 1;
+ }
+ }
+ }
+
+ if (readtransferdata[iiter] != NULL) {
+ if (out_of_memory) {
+ readtransferdata[iiter] = NULL;
+ }
+ else {
+ readtransferdata[iiter] =
+ PyArray_CopyStridedTransferData(readtransferdata[iiter]);
+ if (readtransferdata[iiter] == NULL) {
+ out_of_memory = 1;
+ }
+ }
+ }
+
+ if (writetransferdata[iiter] != NULL) {
+ if (out_of_memory) {
+ writetransferdata[iiter] = NULL;
+ }
+ else {
+ writetransferdata[iiter] =
+ PyArray_CopyStridedTransferData(writetransferdata[iiter]);
+ if (writetransferdata[iiter] == NULL) {
+ out_of_memory = 1;
+ }
+ }
+ }
+ }
+
+ /* Initialize the buffers to the current iterindex */
+ if (!out_of_memory && NBF_SIZE(bufferdata) > 0) {
+ npyiter_goto_iterindex(newiter, NIT_ITERINDEX(newiter));
+
+ /* Prepare the next buffers and set iterend/size */
+ npyiter_copy_to_buffers(newiter);
+ }
+ }
+
+ if (out_of_memory) {
+ NpyIter_Deallocate(newiter);
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ return newiter;
+}
+
int NpyIter_Deallocate(NpyIter *iter)
{
npy_uint32 itflags = NIT_ITFLAGS(iter);
diff --git a/numpy/core/src/multiarray/new_iterator.h b/numpy/core/src/multiarray/new_iterator.h
index 89d0be055..72e7d87ea 100644
--- a/numpy/core/src/multiarray/new_iterator.h
+++ b/numpy/core/src/multiarray/new_iterator.h
@@ -28,19 +28,23 @@ typedef enum {
/* Allocate a new iterator for one array object */
-NpyIter*
+NpyIter *
NpyIter_New(PyArrayObject* op, npy_uint32 flags,
NPY_ORDER order, NPY_CASTING casting,
PyArray_Descr* dtype,
npy_intp a_ndim, npy_intp *axes, npy_intp buffersize);
/* Allocate a new iterator for multiple array objects */
-NpyIter*
+NpyIter *
NpyIter_MultiNew(npy_intp niter, PyArrayObject **op_in, npy_uint32 flags,
NPY_ORDER order, NPY_CASTING casting,
npy_uint32 *op_flags, PyArray_Descr **op_request_dtypes,
npy_intp oa_ndim, npy_intp **op_axes, npy_intp buffersize);
+/* Makes a copy of the iterator */
+NpyIter *
+NpyIter_Copy(NpyIter *iter);
+
/* Deallocate an iterator */
int NpyIter_Deallocate(NpyIter* iter);
diff --git a/numpy/core/src/multiarray/new_iterator_pywrap.c b/numpy/core/src/multiarray/new_iterator_pywrap.c
index 49c6c3d6c..9a0b6cbfd 100644
--- a/numpy/core/src/multiarray/new_iterator_pywrap.c
+++ b/numpy/core/src/multiarray/new_iterator_pywrap.c
@@ -1126,6 +1126,43 @@ npyiter_reset(NewNpyArrayIterObject *self)
Py_RETURN_NONE;
}
+/*
+ * Makes a copy of the iterator. Note that the nesting is not
+ * copied.
+ */
+static PyObject *
+npyiter_copy(NewNpyArrayIterObject *self)
+{
+ NewNpyArrayIterObject *iter;
+
+ if (self->iter == NULL) {
+ PyErr_SetString(PyExc_ValueError,
+ "Iterator is invalid");
+ return NULL;
+ }
+
+ /* Allocate the iterator */
+ iter = (NewNpyArrayIterObject *)npyiter_new(&NpyIter_Type, NULL, NULL);
+ if (iter == NULL) {
+ return NULL;
+ }
+
+ /* Copy the C iterator */
+ iter->iter = NpyIter_Copy(self->iter);
+ if (iter->iter == NULL) {
+ Py_DECREF(iter);
+ return NULL;
+ }
+
+ /* Cache some values for the member functions to use */
+ npyiter_cache_values(iter);
+
+ iter->started = self->started;
+ iter->finished = self->finished;
+
+ return (PyObject *)iter;
+}
+
static PyObject *
npyiter_iternext(NewNpyArrayIterObject *self)
{
@@ -1550,7 +1587,6 @@ static PyObject *npyiter_iterrange_get(NewNpyArrayIterObject *self)
static int npyiter_iterrange_set(NewNpyArrayIterObject *self, PyObject *value)
{
npy_intp istart = 0, iend = 0;
- PyObject *item;
if (self->iter == NULL) {
PyErr_SetString(PyExc_ValueError,
@@ -1869,6 +1905,8 @@ npyiter_seq_ass_item(NewNpyArrayIterObject *self, Py_ssize_t i, PyObject *v)
static PyMethodDef npyiter_methods[] = {
{"reset", (PyCFunction)npyiter_reset, METH_NOARGS, NULL},
+ {"copy", (PyCFunction)npyiter_copy, METH_NOARGS, NULL},
+ {"__copy__", (PyCFunction)npyiter_copy, METH_NOARGS, NULL},
{"iternext", (PyCFunction)npyiter_iternext, METH_NOARGS, NULL},
{"remove_coords", (PyCFunction)npyiter_remove_coords, METH_NOARGS, NULL},
{"remove_inner_loop", (PyCFunction)npyiter_remove_inner_loop,
diff --git a/numpy/core/tests/test_new_iterator.py b/numpy/core/tests/test_new_iterator.py
index 3d492cab5..b2b9bcb59 100644
--- a/numpy/core/tests/test_new_iterator.py
+++ b/numpy/core/tests/test_new_iterator.py
@@ -29,6 +29,7 @@ def iter_iterindices(i):
def test_iter_refcount():
# Make sure the iterator doesn't leak
+ # Basic
a = arange(6)
dt = np.dtype('f4').newbyteorder()
rc_a = sys.getrefcount(a)
@@ -43,6 +44,26 @@ def test_iter_refcount():
assert_equal(sys.getrefcount(a), rc_a)
assert_equal(sys.getrefcount(dt), rc_dt)
+ # With a copy
+ a = arange(6, dtype='f4')
+ dt = np.dtype('f4')
+ rc_a = sys.getrefcount(a)
+ rc_dt = sys.getrefcount(dt)
+ it = newiter(a, [],
+ [['readwrite']],
+ op_dtypes=[dt])
+ rc2_a = sys.getrefcount(a)
+ rc2_dt = sys.getrefcount(dt)
+ it2 = it.copy()
+ assert_(sys.getrefcount(a) > rc2_a)
+ assert_(sys.getrefcount(dt) > rc2_dt)
+ it = None
+ assert_equal(sys.getrefcount(a), rc2_a)
+ assert_equal(sys.getrefcount(dt), rc2_dt)
+ it2 = None
+ assert_equal(sys.getrefcount(a), rc_a)
+ assert_equal(sys.getrefcount(dt), rc_dt)
+
def test_iter_best_order():
# The iterator should always find the iteration order
# with increasing memory addresses
@@ -976,6 +997,45 @@ def test_iter_op_axes_errors():
assert_raises(ValueError, newiter, [a,a], [], [['readonly']]*2,
op_axes=[[0,1],[1,0]])
+def test_iter_copy():
+ # Check that copying the iterator works correctly
+ a = arange(24).reshape(2,3,4)
+
+ # Simple iterator
+ i = newiter(a)
+ j = i.copy()
+ assert_equal([x[()] for x in i], [x[()] for x in j])
+
+ i.iterindex = 3
+ j = i.copy()
+ assert_equal([x[()] for x in i], [x[()] for x in j])
+
+ # Buffered iterator
+ i = newiter(a, ['buffered','ranged'], order='F', buffersize=3)
+ j = i.copy()
+ assert_equal([x[()] for x in i], [x[()] for x in j])
+
+ i.iterindex = 3
+ j = i.copy()
+ assert_equal([x[()] for x in i], [x[()] for x in j])
+
+ i.iterrange = (3,9)
+ j = i.copy()
+ assert_equal([x[()] for x in i], [x[()] for x in j])
+
+ i.iterrange = (2,18)
+ i.next(); i.next()
+ j = i.copy()
+ assert_equal([x[()] for x in i], [x[()] for x in j])
+
+ # Casting iterator
+ 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