1 /*
2  * This module corresponds to the `Special functions for NPY_OBJECT`
3  * section in the numpy reference for C-API.
4  */
5 
6 #define PY_SSIZE_T_CLEAN
7 #include <Python.h>
8 #include "structmember.h"
9 
10 #define NPY_NO_DEPRECATED_API NPY_API_VERSION
11 #define _MULTIARRAYMODULE
12 #include "numpy/arrayobject.h"
13 #include "numpy/arrayscalars.h"
14 #include "iterators.h"
15 
16 #include "npy_config.h"
17 
18 #include "npy_pycompat.h"
19 
20 static void
21 _fillobject(char *optr, PyObject *obj, PyArray_Descr *dtype);
22 
23 
24 /*NUMPY_API
25  * XINCREF all objects in a single array item. This is complicated for
26  * structured datatypes where the position of objects needs to be extracted.
27  * The function is execute recursively for each nested field or subarrays dtype
28  * such as as `np.dtype([("field1", "O"), ("field2", "f,O", (3,2))])`
29  */
30 NPY_NO_EXPORT void
PyArray_Item_INCREF(char * data,PyArray_Descr * descr)31 PyArray_Item_INCREF(char *data, PyArray_Descr *descr)
32 {
33     PyObject *temp;
34 
35     if (!PyDataType_REFCHK(descr)) {
36         return;
37     }
38     if (descr->type_num == NPY_OBJECT) {
39         memcpy(&temp, data, sizeof(temp));
40         Py_XINCREF(temp);
41     }
42     else if (PyDataType_HASFIELDS(descr)) {
43         PyObject *key, *value, *title = NULL;
44         PyArray_Descr *new;
45         int offset;
46         Py_ssize_t pos = 0;
47 
48         while (PyDict_Next(descr->fields, &pos, &key, &value)) {
49             if (NPY_TITLE_KEY(key, value)) {
50                 continue;
51             }
52             if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset,
53                                   &title)) {
54                 return;
55             }
56             PyArray_Item_INCREF(data + offset, new);
57         }
58     }
59     else if (PyDataType_HASSUBARRAY(descr)) {
60         int size, i, inner_elsize;
61 
62         inner_elsize = descr->subarray->base->elsize;
63         if (inner_elsize == 0) {
64             /* There cannot be any elements, so return */
65             return;
66         }
67         /* Subarrays are always contiguous in memory */
68         size = descr->elsize / inner_elsize;
69 
70         for (i = 0; i < size; i++){
71             /* Recursively increment the reference count of subarray elements */
72             PyArray_Item_INCREF(data + i * inner_elsize,
73                                 descr->subarray->base);
74         }
75     }
76     else {
77         /* This path should not be reachable. */
78         assert(0);
79     }
80     return;
81 }
82 
83 
84 /*NUMPY_API
85  *
86  * XDECREF all objects in a single array item. This is complicated for
87  * structured datatypes where the position of objects needs to be extracted.
88  * The function is execute recursively for each nested field or subarrays dtype
89  * such as as `np.dtype([("field1", "O"), ("field2", "f,O", (3,2))])`
90  */
91 NPY_NO_EXPORT void
PyArray_Item_XDECREF(char * data,PyArray_Descr * descr)92 PyArray_Item_XDECREF(char *data, PyArray_Descr *descr)
93 {
94     PyObject *temp;
95 
96     if (!PyDataType_REFCHK(descr)) {
97         return;
98     }
99 
100     if (descr->type_num == NPY_OBJECT) {
101         memcpy(&temp, data, sizeof(temp));
102         Py_XDECREF(temp);
103     }
104     else if (PyDataType_HASFIELDS(descr)) {
105             PyObject *key, *value, *title = NULL;
106             PyArray_Descr *new;
107             int offset;
108             Py_ssize_t pos = 0;
109 
110             while (PyDict_Next(descr->fields, &pos, &key, &value)) {
111                 if (NPY_TITLE_KEY(key, value)) {
112                     continue;
113                 }
114                 if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset,
115                                       &title)) {
116                     return;
117                 }
118                 PyArray_Item_XDECREF(data + offset, new);
119             }
120         }
121     else if (PyDataType_HASSUBARRAY(descr)) {
122         int size, i, inner_elsize;
123 
124         inner_elsize = descr->subarray->base->elsize;
125         if (inner_elsize == 0) {
126             /* There cannot be any elements, so return */
127             return;
128         }
129         /* Subarrays are always contiguous in memory */
130         size = descr->elsize / inner_elsize;
131 
132         for (i = 0; i < size; i++){
133             /* Recursively decrement the reference count of subarray elements */
134             PyArray_Item_XDECREF(data + i * inner_elsize,
135                                  descr->subarray->base);
136         }
137     }
138     else {
139         /* This path should not be reachable. */
140         assert(0);
141     }
142     return;
143 }
144 
145 /* Used for arrays of python objects to increment the reference count of */
146 /* every python object in the array. */
147 /*NUMPY_API
148   For object arrays, increment all internal references.
149 */
150 NPY_NO_EXPORT int
PyArray_INCREF(PyArrayObject * mp)151 PyArray_INCREF(PyArrayObject *mp)
152 {
153     npy_intp i, n;
154     PyObject **data;
155     PyObject *temp;
156     PyArrayIterObject *it;
157 
158     if (!PyDataType_REFCHK(PyArray_DESCR(mp))) {
159         return 0;
160     }
161     if (PyArray_DESCR(mp)->type_num != NPY_OBJECT) {
162         it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)mp);
163         if (it == NULL) {
164             return -1;
165         }
166         while(it->index < it->size) {
167             PyArray_Item_INCREF(it->dataptr, PyArray_DESCR(mp));
168             PyArray_ITER_NEXT(it);
169         }
170         Py_DECREF(it);
171         return 0;
172     }
173 
174     if (PyArray_ISONESEGMENT(mp)) {
175         data = (PyObject **)PyArray_DATA(mp);
176         n = PyArray_SIZE(mp);
177         if (PyArray_ISALIGNED(mp)) {
178             for (i = 0; i < n; i++, data++) {
179                 Py_XINCREF(*data);
180             }
181         }
182         else {
183             for( i = 0; i < n; i++, data++) {
184                 memcpy(&temp, data, sizeof(temp));
185                 Py_XINCREF(temp);
186             }
187         }
188     }
189     else { /* handles misaligned data too */
190         it = (PyArrayIterObject *)PyArray_IterNew((PyObject *)mp);
191         if (it == NULL) {
192             return -1;
193         }
194         while(it->index < it->size) {
195             memcpy(&temp, it->dataptr, sizeof(temp));
196             Py_XINCREF(temp);
197             PyArray_ITER_NEXT(it);
198         }
199         Py_DECREF(it);
200     }
201     return 0;
202 }
203 
204 /*NUMPY_API
205   Decrement all internal references for object arrays.
206   (or arrays with object fields)
207 */
208 NPY_NO_EXPORT int
PyArray_XDECREF(PyArrayObject * mp)209 PyArray_XDECREF(PyArrayObject *mp)
210 {
211     npy_intp i, n;
212     PyObject **data;
213     PyObject *temp;
214     /*
215      * statically allocating it allows this function to not modify the
216      * reference count of the array for use during dealloc.
217      * (statically is not necessary as such)
218      */
219     PyArrayIterObject it;
220 
221     if (!PyDataType_REFCHK(PyArray_DESCR(mp))) {
222         return 0;
223     }
224     if (PyArray_DESCR(mp)->type_num != NPY_OBJECT) {
225         PyArray_RawIterBaseInit(&it, mp);
226         while(it.index < it.size) {
227             PyArray_Item_XDECREF(it.dataptr, PyArray_DESCR(mp));
228             PyArray_ITER_NEXT(&it);
229         }
230         return 0;
231     }
232 
233     if (PyArray_ISONESEGMENT(mp)) {
234         data = (PyObject **)PyArray_DATA(mp);
235         n = PyArray_SIZE(mp);
236         if (PyArray_ISALIGNED(mp)) {
237             for (i = 0; i < n; i++, data++) Py_XDECREF(*data);
238         }
239         else {
240             for (i = 0; i < n; i++, data++) {
241                 memcpy(&temp, data, sizeof(temp));
242                 Py_XDECREF(temp);
243             }
244         }
245     }
246     else { /* handles misaligned data too */
247         PyArray_RawIterBaseInit(&it, mp);
248         while(it.index < it.size) {
249             memcpy(&temp, it.dataptr, sizeof(temp));
250             Py_XDECREF(temp);
251             PyArray_ITER_NEXT(&it);
252         }
253     }
254     return 0;
255 }
256 
257 /*NUMPY_API
258  * Assumes contiguous
259  */
260 NPY_NO_EXPORT void
PyArray_FillObjectArray(PyArrayObject * arr,PyObject * obj)261 PyArray_FillObjectArray(PyArrayObject *arr, PyObject *obj)
262 {
263     npy_intp i,n;
264     n = PyArray_SIZE(arr);
265     if (PyArray_DESCR(arr)->type_num == NPY_OBJECT) {
266         PyObject **optr;
267         optr = (PyObject **)(PyArray_DATA(arr));
268         n = PyArray_SIZE(arr);
269         if (obj == NULL) {
270             for (i = 0; i < n; i++) {
271                 *optr++ = NULL;
272             }
273         }
274         else {
275             for (i = 0; i < n; i++) {
276                 Py_INCREF(obj);
277                 *optr++ = obj;
278             }
279         }
280     }
281     else {
282         char *optr;
283         optr = PyArray_DATA(arr);
284         for (i = 0; i < n; i++) {
285             _fillobject(optr, obj, PyArray_DESCR(arr));
286             optr += PyArray_DESCR(arr)->elsize;
287         }
288     }
289 }
290 
291 static void
_fillobject(char * optr,PyObject * obj,PyArray_Descr * dtype)292 _fillobject(char *optr, PyObject *obj, PyArray_Descr *dtype)
293 {
294     if (!PyDataType_FLAGCHK(dtype, NPY_ITEM_REFCOUNT)) {
295         PyObject *arr;
296 
297         if ((obj == Py_None) ||
298                 (PyLong_Check(obj) && PyLong_AsLong(obj) == 0)) {
299             return;
300         }
301         /* Clear possible long conversion error */
302         PyErr_Clear();
303         Py_INCREF(dtype);
304         arr = PyArray_NewFromDescr(&PyArray_Type, dtype,
305                                    0, NULL, NULL, NULL,
306                                    0, NULL);
307         if (arr!=NULL) {
308             dtype->f->setitem(obj, optr, arr);
309         }
310         Py_XDECREF(arr);
311     }
312     if (dtype->type_num == NPY_OBJECT) {
313         Py_XINCREF(obj);
314         memcpy(optr, &obj, sizeof(obj));
315     }
316     else if (PyDataType_HASFIELDS(dtype)) {
317         PyObject *key, *value, *title = NULL;
318         PyArray_Descr *new;
319         int offset;
320         Py_ssize_t pos = 0;
321 
322         while (PyDict_Next(dtype->fields, &pos, &key, &value)) {
323             if (NPY_TITLE_KEY(key, value)) {
324                 continue;
325             }
326             if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset, &title)) {
327                 return;
328             }
329             _fillobject(optr + offset, obj, new);
330         }
331     }
332     else if (PyDataType_HASSUBARRAY(dtype)) {
333         int size, i, inner_elsize;
334 
335         inner_elsize = dtype->subarray->base->elsize;
336         if (inner_elsize == 0) {
337             /* There cannot be any elements, so return */
338             return;
339         }
340         /* Subarrays are always contiguous in memory */
341         size = dtype->elsize / inner_elsize;
342 
343         /* Call _fillobject on each item recursively. */
344         for (i = 0; i < size; i++){
345             _fillobject(optr, obj, dtype->subarray->base);
346             optr += inner_elsize;
347         }
348     }
349     else {
350         /* This path should not be reachable. */
351         assert(0);
352     }
353     return;
354 }
355