summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/neps/missing-data.rst4
-rw-r--r--numpy/core/include/numpy/ndarraytypes.h6
-rw-r--r--numpy/core/src/multiarray/nditer_api.c2
-rw-r--r--numpy/core/src/multiarray/nditer_constr.c11
-rw-r--r--numpy/core/src/multiarray/nditer_impl.h2
-rw-r--r--numpy/core/src/multiarray/nditer_pywrap.c39
6 files changed, 57 insertions, 7 deletions
diff --git a/doc/neps/missing-data.rst b/doc/neps/missing-data.rst
index 478d019a5..de00cbb74 100644
--- a/doc/neps/missing-data.rst
+++ b/doc/neps/missing-data.rst
@@ -738,6 +738,10 @@ NPY_ITER_WRITEMASKED
to know the mask ahead of time, and copying everything into
the buffer will never destroy data.
+ The code using the iterator should only write to values which
+ are not masked by the mask specified, otherwise the result will
+ be different depending on whether buffering is enabled or not.
+
NPY_ITER_ARRAYMASK
Indicates that this array is a boolean mask to use when copying
any WRITEMASKED argument from a buffer back to the array. There
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h
index 5e32c9d7c..11ea47c8f 100644
--- a/numpy/core/include/numpy/ndarraytypes.h
+++ b/numpy/core/include/numpy/ndarraytypes.h
@@ -893,6 +893,12 @@ typedef void (NpyIter_GetMultiIndexFunc)(NpyIter *iter,
#define NPY_ITER_NO_SUBTYPE 0x02000000
/* Require that the dimension match the iterator dimensions exactly */
#define NPY_ITER_NO_BROADCAST 0x08000000
+/* A mask is being used on this array, affects buffer -> array copy */
+#define NPY_ITER_WRITEMASKED 0x04000000
+/* This array is the mask for all WRITEMASKED operands */
+#define NPY_ITER_ARRAYMASK 0x10000000
+/* This is a virtual array slot of the mask for all WRITEMASKED operands */
+#define NPY_ITER_VIRTUALMASK 0x20000000
#define NPY_ITER_GLOBAL_FLAGS 0x0000ffff
#define NPY_ITER_PER_OP_FLAGS 0xffff0000
diff --git a/numpy/core/src/multiarray/nditer_api.c b/numpy/core/src/multiarray/nditer_api.c
index 10f4afd8b..30990fff1 100644
--- a/numpy/core/src/multiarray/nditer_api.c
+++ b/numpy/core/src/multiarray/nditer_api.c
@@ -1418,6 +1418,8 @@ NpyIter_DebugPrint(NpyIter *iter)
printf("ALIGNED ");
if ((NIT_OPITFLAGS(iter)[iop])&NPY_OP_ITFLAG_REDUCE)
printf("REDUCE ");
+ if ((NIT_OPITFLAGS(iter)[iop])&NPY_OP_ITFLAG_WRITEMASKED)
+ printf("WRITEMASKED ");
printf("\n");
}
printf("|\n");
diff --git a/numpy/core/src/multiarray/nditer_constr.c b/numpy/core/src/multiarray/nditer_constr.c
index 774ed65e4..53b46c019 100644
--- a/numpy/core/src/multiarray/nditer_constr.c
+++ b/numpy/core/src/multiarray/nditer_constr.c
@@ -899,6 +899,17 @@ npyiter_check_per_op_flags(npy_uint32 op_flags, char *op_itflags)
return 0;
}
+ /* Check the flag for a write masked operands */
+ if (op_flags&NPY_ITER_WRITEMASKED) {
+ if (!(*op_itflags)&NPY_OP_ITFLAG_WRITE) {
+ PyErr_SetString(PyExc_ValueError,
+ "The iterator flag WRITEMASKED may only "
+ "be used with READWRITE or WRITEONLY");
+ return 0;
+ }
+ *op_itflags |= NPY_OP_ITFLAG_WRITEMASKED;
+ }
+
return 1;
}
diff --git a/numpy/core/src/multiarray/nditer_impl.h b/numpy/core/src/multiarray/nditer_impl.h
index f79ac3415..3494d3937 100644
--- a/numpy/core/src/multiarray/nditer_impl.h
+++ b/numpy/core/src/multiarray/nditer_impl.h
@@ -116,6 +116,8 @@
#define NPY_OP_ITFLAG_ALIGNED 0x10
/* The operand is being reduced */
#define NPY_OP_ITFLAG_REDUCE 0x20
+/* The operand requires masking when copying buffer -> array */
+#define NPY_OP_ITFLAG_WRITEMASKED 0x40
/*
* The data layout of the iterator is fully specified by
diff --git a/numpy/core/src/multiarray/nditer_pywrap.c b/numpy/core/src/multiarray/nditer_pywrap.c
index 8b2f3a0c0..7f902d6fa 100644
--- a/numpy/core/src/multiarray/nditer_pywrap.c
+++ b/numpy/core/src/multiarray/nditer_pywrap.c
@@ -370,11 +370,22 @@ NpyIter_OpFlagsConverter(PyObject *op_flags_in,
flag = 0;
switch (str[0]) {
case 'a':
- if (strcmp(str, "allocate") == 0) {
- flag = NPY_ITER_ALLOCATE;
- }
- if (strcmp(str, "aligned") == 0) {
- flag = NPY_ITER_ALIGNED;
+ if (length > 2) switch(str[2]) {
+ case 'i':
+ if (strcmp(str, "aligned") == 0) {
+ flag = NPY_ITER_ALIGNED;
+ }
+ break;
+ case 'l':
+ if (strcmp(str, "allocate") == 0) {
+ flag = NPY_ITER_ALLOCATE;
+ }
+ break;
+ case 'r':
+ if (strcmp(str, "arraymask") == 0) {
+ flag = NPY_ITER_ARRAYMASK;
+ }
+ break;
}
break;
case 'c':
@@ -421,9 +432,23 @@ NpyIter_OpFlagsConverter(PyObject *op_flags_in,
flag = NPY_ITER_UPDATEIFCOPY;
}
break;
+ case 'v':
+ if (strcmp(str, "virtualmask") == 0) {
+ flag = NPY_ITER_VIRTUALMASK;
+ }
+ break;
case 'w':
- if (strcmp(str, "writeonly") == 0) {
- flag = NPY_ITER_WRITEONLY;
+ if (length > 5) switch (str[5]) {
+ case 'o':
+ if (strcmp(str, "writeonly") == 0) {
+ flag = NPY_ITER_WRITEONLY;
+ }
+ break;
+ case 'm':
+ if (strcmp(str, "writemasked") == 0) {
+ flag = NPY_ITER_WRITEMASKED;
+ }
+ break;
}
break;
}