summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgfyoung <gfyoung@mit.edu>2016-01-13 13:15:21 +0000
committergfyoung <gfyoung@mit.edu>2016-03-15 15:04:05 +0100
commit9128ed5198cc8544406467a00824e5fe6791f090 (patch)
treeca3dc831ff1566a2794e940d1a358a17774fd59b
parente034b86d1f40af6e15a829d093232febd4a708c3 (diff)
downloadnumpy-9128ed5198cc8544406467a00824e5fe6791f090.tar.gz
BUG: Fix string copying for np.place
Fixes bug in string copying in np.place in which replacements strings that were smaller than their replaced elements would only partially replace the element instead of the entire element. Closes gh-6974. Addendum: this commit also appears to have fixed issue with overflow for very large input arrays. Closes gh-7207.
-rw-r--r--numpy/core/src/multiarray/compiled_base.c224
-rw-r--r--numpy/lib/function_base.py3
-rw-r--r--numpy/lib/tests/test_function_base.py5
3 files changed, 78 insertions, 154 deletions
diff --git a/numpy/core/src/multiarray/compiled_base.c b/numpy/core/src/multiarray/compiled_base.c
index 932b94f15..711a0ab91 100644
--- a/numpy/core/src/multiarray/compiled_base.c
+++ b/numpy/core/src/multiarray/compiled_base.c
@@ -302,193 +302,111 @@ arr_digitize(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds)
}
/*
- * Insert values from an input array into an output array, at positions
- * indicated by a mask. If the arrays are of dtype object (indicated by
- * the objarray flag), take care of reference counting.
- *
- * This function implements the copying logic of arr_insert() defined
- * below.
- */
-static void
-arr_insert_loop(char *mptr, char *vptr, char *input_data, char *zero,
- char *avals_data, int melsize, int delsize, int objarray,
- int totmask, int numvals, int nd, npy_intp *instrides,
- npy_intp *inshape)
-{
- int mindx, rem_indx, indx, i, copied;
-
- /*
- * Walk through mask array, when non-zero is encountered
- * copy next value in the vals array to the input array.
- * If we get through the value array, repeat it as necessary.
- */
- copied = 0;
- for (mindx = 0; mindx < totmask; mindx++) {
- if (memcmp(mptr,zero,melsize) != 0) {
- /* compute indx into input array */
- rem_indx = mindx;
- indx = 0;
- for (i = nd - 1; i > 0; --i) {
- indx += (rem_indx % inshape[i]) * instrides[i];
- rem_indx /= inshape[i];
- }
- indx += rem_indx * instrides[0];
- /* fprintf(stderr, "mindx = %d, indx=%d\n", mindx, indx); */
- /* Copy value element over to input array */
- memcpy(input_data+indx,vptr,delsize);
- if (objarray) {
- Py_INCREF(*((PyObject **)vptr));
- }
- vptr += delsize;
- copied += 1;
- /* If we move past value data. Reset */
- if (copied >= numvals) {
- vptr = avals_data;
- copied = 0;
- }
- }
- mptr += melsize;
- }
-}
-
-/*
* Returns input array with values inserted sequentially into places
* indicated by the mask
*/
NPY_NO_EXPORT PyObject *
arr_insert(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict)
{
- PyObject *mask = NULL, *vals = NULL;
- PyArrayObject *ainput = NULL, *amask = NULL, *avals = NULL, *tmp = NULL;
- int numvals, totmask, sameshape;
- char *input_data, *mptr, *vptr, *zero = NULL;
- int melsize, delsize, nd, objarray, k;
- npy_intp *instrides, *inshape;
+ char *src, *dest;
+ npy_bool *mask_data;
+ PyArray_Descr *dtype;
+ PyArray_CopySwapFunc *copyswap;
+ PyObject *array0, *mask0, *values0;
+ PyArrayObject *array, *mask, *values;
+ npy_intp i, j, chunk, nm, ni, nv;
static char *kwlist[] = {"input", "mask", "vals", NULL};
+ NPY_BEGIN_THREADS_DEF;
+ values = mask = NULL;
- if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O&OO", kwlist,
- PyArray_Converter, &ainput,
- &mask, &vals)) {
- goto fail;
+ if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O!OO:place", kwlist,
+ &PyArray_Type, &array0, &mask0, &values0)) {
+ return NULL;
}
- amask = (PyArrayObject *)PyArray_FROM_OF(mask, NPY_ARRAY_CARRAY);
- if (amask == NULL) {
+ array = (PyArrayObject *)PyArray_FromArray((PyArrayObject *)array0, NULL,
+ NPY_ARRAY_CARRAY | NPY_ARRAY_UPDATEIFCOPY);
+ if (array == NULL) {
goto fail;
}
- /* Cast an object array */
- if (PyArray_DESCR(amask)->type_num == NPY_OBJECT) {
- tmp = (PyArrayObject *)PyArray_Cast(amask, NPY_INTP);
- if (tmp == NULL) {
- goto fail;
- }
- Py_DECREF(amask);
- amask = tmp;
- }
- sameshape = 1;
- if (PyArray_NDIM(amask) == PyArray_NDIM(ainput)) {
- for (k = 0; k < PyArray_NDIM(amask); k++) {
- if (PyArray_DIMS(amask)[k] != PyArray_DIMS(ainput)[k]) {
- sameshape = 0;
- }
- }
- }
- else {
- /* Test to see if amask is 1d */
- if (PyArray_NDIM(amask) != 1) {
- sameshape = 0;
- }
- else if ((PyArray_SIZE(ainput)) != PyArray_SIZE(amask)) {
- sameshape = 0;
- }
- }
- if (!sameshape) {
- PyErr_SetString(PyExc_TypeError,
- "mask array must be 1-d or same shape as input array");
+ ni = PyArray_SIZE(array);
+ dest = PyArray_DATA(array);
+ chunk = PyArray_DESCR(array)->elsize;
+ mask = (PyArrayObject *)PyArray_FROM_OTF(mask0, NPY_BOOL,
+ NPY_ARRAY_CARRAY | NPY_ARRAY_FORCECAST);
+ if (mask == NULL) {
goto fail;
}
- avals = (PyArrayObject *)PyArray_FromObject(vals,
- PyArray_DESCR(ainput)->type_num, 0, 1);
- if (avals == NULL) {
+ nm = PyArray_SIZE(mask);
+ if (nm != ni) {
+ PyErr_SetString(PyExc_ValueError,
+ "place: mask and data must be "
+ "the same size");
goto fail;
}
- numvals = PyArray_SIZE(avals);
- nd = PyArray_NDIM(ainput);
- input_data = PyArray_DATA(ainput);
- mptr = PyArray_DATA(amask);
- melsize = PyArray_DESCR(amask)->elsize;
- vptr = PyArray_DATA(avals);
- delsize = PyArray_DESCR(avals)->elsize;
- zero = PyArray_Zero(amask);
- if (zero == NULL) {
+
+ mask_data = PyArray_DATA(mask);
+ dtype = PyArray_DESCR(array);
+ Py_INCREF(dtype);
+
+ values = (PyArrayObject *)PyArray_FromAny(values0, dtype,
+ 0, 0, NPY_ARRAY_CARRAY, NULL);
+ if (values == NULL) {
goto fail;
}
- objarray = (PyArray_DESCR(ainput)->type_num == NPY_OBJECT);
- if (!numvals) {
- /* nothing to insert! fail unless none of mask is true */
- const char *iter = mptr;
- const char *const last = iter + PyArray_NBYTES(amask);
- while (iter != last && !memcmp(iter, zero, melsize)) {
- iter += melsize;
+ nv = PyArray_SIZE(values); /* zero if null array */
+ if (nv <= 0) {
+ npy_bool allFalse = 1;
+ i = 0;
+
+ while (allFalse && i < ni) {
+ if (mask_data[i]) {
+ allFalse = 0;
+ } else {
+ i++;
+ }
}
- if (iter != last) {
+ if (!allFalse) {
PyErr_SetString(PyExc_ValueError,
- "Cannot insert from an empty array!");
+ "Cannot insert from an empty array!");
goto fail;
+ } else {
+ Py_XDECREF(values);
+ Py_XDECREF(mask);
+ Py_RETURN_NONE;
}
- goto finish;
}
- /* Handle zero-dimensional case separately */
- if (nd == 0) {
- if (memcmp(mptr,zero,melsize) != 0) {
- /* Copy value element over to input array */
- memcpy(input_data,vptr,delsize);
- if (objarray) {
- Py_INCREF(*((PyObject **)vptr));
+ src = PyArray_DATA(values);
+ j = 0;
+
+ copyswap = PyArray_DESCR(array)->f->copyswap;
+ NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(array));
+ for (i = 0; i < ni; i++) {
+ if (mask_data[i]) {
+ if (j >= nv) {
+ j = 0;
}
- }
- Py_DECREF(amask);
- Py_DECREF(avals);
- PyDataMem_FREE(zero);
- Py_DECREF(ainput);
- Py_RETURN_NONE;
- }
- totmask = (int) PyArray_SIZE(amask);
- instrides = PyArray_STRIDES(ainput);
- inshape = PyArray_DIMS(ainput);
- if (objarray) {
- /* object array, need to refcount, can't release the GIL */
- arr_insert_loop(mptr, vptr, input_data, zero, PyArray_DATA(avals),
- melsize, delsize, objarray, totmask, numvals, nd,
- instrides, inshape);
- }
- else {
- /* No increfs take place in arr_insert_loop, so release the GIL */
- NPY_BEGIN_ALLOW_THREADS;
- arr_insert_loop(mptr, vptr, input_data, zero, PyArray_DATA(avals),
- melsize, delsize, objarray, totmask, numvals, nd,
- instrides, inshape);
- NPY_END_ALLOW_THREADS;
+ copyswap(dest + i*chunk, src + j*chunk, 0, array);
+ j++;
+ }
}
+ NPY_END_THREADS;
-finish:
- Py_DECREF(amask);
- Py_DECREF(avals);
- PyDataMem_FREE(zero);
- Py_DECREF(ainput);
+ Py_XDECREF(values);
+ Py_XDECREF(mask);
+ Py_DECREF(array);
Py_RETURN_NONE;
-fail:
- PyDataMem_FREE(zero);
- Py_XDECREF(ainput);
- Py_XDECREF(amask);
- Py_XDECREF(avals);
+ fail:
+ Py_XDECREF(mask);
+ Py_XDECREF(array);
+ Py_XDECREF(values);
return NULL;
}
diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py
index c155babef..648eb5019 100644
--- a/numpy/lib/function_base.py
+++ b/numpy/lib/function_base.py
@@ -2026,7 +2026,8 @@ def place(arr, mask, vals):
vals : 1-D sequence
Values to put into `a`. Only the first N elements are used, where
N is the number of True values in `mask`. If `vals` is smaller
- than N it will be repeated.
+ than N, it will be repeated, and if elements of `a` are to be masked,
+ this sequence must be non-empty.
See Also
--------
diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py
index e04a497c1..945992fc0 100644
--- a/numpy/lib/tests/test_function_base.py
+++ b/numpy/lib/tests/test_function_base.py
@@ -805,6 +805,11 @@ class TestExtins(TestCase):
assert_raises_regex(ValueError, "Cannot insert from an empty array",
lambda: place(a, [0, 0, 0, 0, 0, 1, 0], []))
+ # See Issue #6974
+ a = np.array(['12', '34'])
+ place(a, [0, 1], '9')
+ assert_array_equal(a, ['12', '9'])
+
def test_both(self):
a = rand(10)
mask = a > 0.5