1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 /** \file
18  * \ingroup pythonintern
19  *
20  * This file deals with array access for 'BPy_PropertyArrayRNA' from bpy_rna.c
21  */
22 
23 #include <Python.h>
24 
25 #include "CLG_log.h"
26 
27 #include "BLI_utildefines.h"
28 
29 #include "RNA_types.h"
30 
31 #include "bpy_rna.h"
32 
33 #include "MEM_guardedalloc.h"
34 
35 #include "RNA_access.h"
36 
37 #include "BPY_extern_clog.h"
38 
39 #include "../generic/py_capi_utils.h"
40 
41 #define USE_MATHUTILS
42 
43 #ifdef USE_MATHUTILS
44 #  include "../mathutils/mathutils.h" /* so we can have mathutils callbacks */
45 #endif
46 
47 #define MAX_ARRAY_DIMENSION 10
48 
49 struct ItemConvertArgData;
50 
51 typedef void (*ItemConvertFunc)(const struct ItemConvertArgData *arg, PyObject *, char *);
52 typedef int (*ItemTypeCheckFunc)(PyObject *);
53 typedef void (*RNA_SetArrayFunc)(PointerRNA *, PropertyRNA *, const char *);
54 typedef void (*RNA_SetIndexFunc)(PointerRNA *, PropertyRNA *, int index, void *);
55 
56 struct ItemConvertArgData {
57   union {
58     struct {
59       int range[2];
60     } int_data;
61     struct {
62       float range[2];
63     } float_data;
64   };
65 };
66 
67 /**
68  * Callback and args needed to apply the value (clamp range for now)
69  */
70 typedef struct ItemConvert_FuncArg {
71   ItemConvertFunc func;
72   struct ItemConvertArgData arg;
73 } ItemConvert_FuncArg;
74 
75 /*
76  * arr[3][4][5]
77  *     0  1  2  <- dimension index
78  */
79 
80 /*
81  *  arr[2] = x
82  *
83  *  py_to_array_index(arraydim=0, arrayoffset=0, index=2)
84  *      validate_array(lvalue_dim=0)
85  *      ... make real index ...
86  */
87 
88 /* arr[3] = x, self->arraydim is 0, lvalue_dim is 1 */
89 /* Ensures that a python sequence has expected number of
90  * items/sub-items and items are of desired type. */
validate_array_type(PyObject * seq,int dim,int totdim,int dimsize[],const bool is_dynamic,ItemTypeCheckFunc check_item_type,const char * item_type_str,const char * error_prefix)91 static int validate_array_type(PyObject *seq,
92                                int dim,
93                                int totdim,
94                                int dimsize[],
95                                const bool is_dynamic,
96                                ItemTypeCheckFunc check_item_type,
97                                const char *item_type_str,
98                                const char *error_prefix)
99 {
100   Py_ssize_t i;
101 
102   /* not the last dimension */
103   if (dim + 1 < totdim) {
104     /* check that a sequence contains dimsize[dim] items */
105     const int seq_size = PySequence_Size(seq);
106     if (seq_size == -1) {
107       PyErr_Format(PyExc_ValueError,
108                    "%s sequence expected at dimension %d, not '%s'",
109                    error_prefix,
110                    dim + 1,
111                    Py_TYPE(seq)->tp_name);
112       return -1;
113     }
114     for (i = 0; i < seq_size; i++) {
115       Py_ssize_t item_seq_size;
116       PyObject *item;
117       bool ok = true;
118       item = PySequence_GetItem(seq, i);
119 
120       if (item == NULL) {
121         PyErr_Format(PyExc_TypeError,
122                      "%s sequence type '%s' failed to retrieve index %d",
123                      error_prefix,
124                      Py_TYPE(seq)->tp_name,
125                      i);
126         ok = 0;
127       }
128       else if ((item_seq_size = PySequence_Size(item)) == -1) {
129         /* BLI_snprintf(error_str, error_str_size, "expected a sequence of %s", item_type_str); */
130         PyErr_Format(PyExc_TypeError,
131                      "%s expected a sequence of %s, not %s",
132                      error_prefix,
133                      item_type_str,
134                      Py_TYPE(item)->tp_name);
135         ok = 0;
136       }
137       /* arr[3][4][5]
138        * dimsize[1] = 4
139        * dimsize[2] = 5
140        *
141        * dim = 0 */
142       else if (item_seq_size != dimsize[dim + 1]) {
143         /* BLI_snprintf(error_str, error_str_size,
144          *              "sequences of dimension %d should contain %d items",
145          *              dim + 1, dimsize[dim + 1]); */
146         PyErr_Format(PyExc_ValueError,
147                      "%s sequences of dimension %d should contain %d items, not %d",
148                      error_prefix,
149                      dim + 1,
150                      dimsize[dim + 1],
151                      item_seq_size);
152         ok = 0;
153       }
154       else if (validate_array_type(item,
155                                    dim + 1,
156                                    totdim,
157                                    dimsize,
158                                    is_dynamic,
159                                    check_item_type,
160                                    item_type_str,
161                                    error_prefix) == -1) {
162         ok = 0;
163       }
164 
165       Py_XDECREF(item);
166 
167       if (!ok) {
168         return -1;
169       }
170     }
171   }
172   else {
173     /* check that items are of correct type */
174     const int seq_size = PySequence_Size(seq);
175     if (seq_size == -1) {
176       PyErr_Format(PyExc_ValueError,
177                    "%s sequence expected at dimension %d, not '%s'",
178                    error_prefix,
179                    dim + 1,
180                    Py_TYPE(seq)->tp_name);
181       return -1;
182     }
183     if ((seq_size != dimsize[dim]) && (is_dynamic == false)) {
184       PyErr_Format(PyExc_ValueError,
185                    "%s sequences of dimension %d should contain %d items, not %d",
186                    error_prefix,
187                    dim,
188                    dimsize[dim],
189                    seq_size);
190       return -1;
191     }
192 
193     for (i = 0; i < seq_size; i++) {
194       PyObject *item = PySequence_GetItem(seq, i);
195 
196       if (item == NULL) {
197         PyErr_Format(PyExc_TypeError,
198                      "%s sequence type '%s' failed to retrieve index %d",
199                      error_prefix,
200                      Py_TYPE(seq)->tp_name,
201                      i);
202         return -1;
203       }
204       if (!check_item_type(item)) {
205         Py_DECREF(item);
206 
207 #if 0
208         BLI_snprintf(
209             error_str, error_str_size, "sequence items should be of type %s", item_type_str);
210 #endif
211         PyErr_Format(PyExc_TypeError,
212                      "%s expected sequence items of type %s, not %s",
213                      error_prefix,
214                      item_type_str,
215                      Py_TYPE(item)->tp_name);
216         return -1;
217       }
218 
219       Py_DECREF(item);
220     }
221   }
222 
223   return 0; /* ok */
224 }
225 
226 /* Returns the number of items in a single- or multi-dimensional sequence. */
count_items(PyObject * seq,int dim)227 static int count_items(PyObject *seq, int dim)
228 {
229   int totitem = 0;
230 
231   if (dim > 1) {
232     const Py_ssize_t seq_size = PySequence_Size(seq);
233     Py_ssize_t i;
234     for (i = 0; i < seq_size; i++) {
235       PyObject *item = PySequence_GetItem(seq, i);
236       if (item) {
237         const int tot = count_items(item, dim - 1);
238         Py_DECREF(item);
239         if (tot != -1) {
240           totitem += tot;
241         }
242         else {
243           totitem = -1;
244           break;
245         }
246       }
247       else {
248         totitem = -1;
249         break;
250       }
251     }
252   }
253   else {
254     totitem = PySequence_Size(seq);
255   }
256 
257   return totitem;
258 }
259 
260 /* Modifies property array length if needed and PROP_DYNAMIC flag is set. */
validate_array_length(PyObject * rvalue,PointerRNA * ptr,PropertyRNA * prop,int lvalue_dim,int * r_totitem,const char * error_prefix)261 static int validate_array_length(PyObject *rvalue,
262                                  PointerRNA *ptr,
263                                  PropertyRNA *prop,
264                                  int lvalue_dim,
265                                  int *r_totitem,
266                                  const char *error_prefix)
267 {
268   int dimsize[MAX_ARRAY_DIMENSION];
269   int tot, totdim, len;
270 
271   totdim = RNA_property_array_dimension(ptr, prop, dimsize);
272   tot = count_items(rvalue, totdim - lvalue_dim);
273 
274   if (tot == -1) {
275     PyErr_Format(PyExc_ValueError,
276                  "%s %.200s.%.200s, error validating the sequence length",
277                  error_prefix,
278                  RNA_struct_identifier(ptr->type),
279                  RNA_property_identifier(prop));
280     return -1;
281   }
282   if ((RNA_property_flag(prop) & PROP_DYNAMIC) && lvalue_dim == 0) {
283     if (RNA_property_array_length(ptr, prop) != tot) {
284 #if 0
285       /* length is flexible */
286       if (!RNA_property_dynamic_array_set_length(ptr, prop, tot)) {
287         /* BLI_snprintf(error_str, error_str_size,
288          *              "%s.%s: array length cannot be changed to %d",
289          *              RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), tot); */
290         PyErr_Format(PyExc_ValueError,
291                      "%s %s.%s: array length cannot be changed to %d",
292                      error_prefix,
293                      RNA_struct_identifier(ptr->type),
294                      RNA_property_identifier(prop),
295                      tot);
296         return -1;
297       }
298 #else
299       *r_totitem = tot;
300       return 0;
301 
302 #endif
303     }
304 
305     len = tot;
306   }
307   else {
308     /* length is a constraint */
309     if (!lvalue_dim) {
310       len = RNA_property_array_length(ptr, prop);
311     }
312     /* array item assignment */
313     else {
314       int i;
315 
316       len = 1;
317 
318       /* arr[3][4][5]
319        *
320        *    arr[2] = x
321        *    dimsize = {4, 5}
322        *    dimsize[1] = 4
323        *    dimsize[2] = 5
324        *    lvalue_dim = 0, totdim = 3
325        *
326        *    arr[2][3] = x
327        *    lvalue_dim = 1
328        *
329        *    arr[2][3][4] = x
330        *    lvalue_dim = 2 */
331       for (i = lvalue_dim; i < totdim; i++) {
332         len *= dimsize[i];
333       }
334     }
335 
336     if (tot != len) {
337       /* BLI_snprintf(error_str, error_str_size, "sequence must have length of %d", len); */
338       PyErr_Format(PyExc_ValueError,
339                    "%s %.200s.%.200s, sequence must have %d items total, not %d",
340                    error_prefix,
341                    RNA_struct_identifier(ptr->type),
342                    RNA_property_identifier(prop),
343                    len,
344                    tot);
345       return -1;
346     }
347   }
348 
349   *r_totitem = len;
350 
351   return 0;
352 }
353 
validate_array(PyObject * rvalue,PointerRNA * ptr,PropertyRNA * prop,int lvalue_dim,ItemTypeCheckFunc check_item_type,const char * item_type_str,int * r_totitem,const char * error_prefix)354 static int validate_array(PyObject *rvalue,
355                           PointerRNA *ptr,
356                           PropertyRNA *prop,
357                           int lvalue_dim,
358                           ItemTypeCheckFunc check_item_type,
359                           const char *item_type_str,
360                           int *r_totitem,
361                           const char *error_prefix)
362 {
363   int dimsize[MAX_ARRAY_DIMENSION];
364   const int totdim = RNA_property_array_dimension(ptr, prop, dimsize);
365 
366   /* validate type first because length validation may modify property array length */
367 
368 #ifdef USE_MATHUTILS
369   if (lvalue_dim == 0) { /* only valid for first level array */
370     if (MatrixObject_Check(rvalue)) {
371       MatrixObject *pymat = (MatrixObject *)rvalue;
372 
373       if (BaseMath_ReadCallback(pymat) == -1) {
374         return -1;
375       }
376 
377       if (RNA_property_type(prop) != PROP_FLOAT) {
378         PyErr_Format(PyExc_ValueError,
379                      "%s %.200s.%.200s, matrix assign to non float array",
380                      error_prefix,
381                      RNA_struct_identifier(ptr->type),
382                      RNA_property_identifier(prop));
383         return -1;
384       }
385       if (totdim != 2) {
386         PyErr_Format(PyExc_ValueError,
387                      "%s %.200s.%.200s, matrix assign array with %d dimensions",
388                      error_prefix,
389                      RNA_struct_identifier(ptr->type),
390                      RNA_property_identifier(prop),
391                      totdim);
392         return -1;
393       }
394       if (pymat->num_col != dimsize[0] || pymat->num_row != dimsize[1]) {
395         PyErr_Format(PyExc_ValueError,
396                      "%s %.200s.%.200s, matrix assign dimension size mismatch, "
397                      "is %dx%d, expected be %dx%d",
398                      error_prefix,
399                      RNA_struct_identifier(ptr->type),
400                      RNA_property_identifier(prop),
401                      pymat->num_col,
402                      pymat->num_row,
403                      dimsize[0],
404                      dimsize[1]);
405         return -1;
406       }
407 
408       *r_totitem = dimsize[0] * dimsize[1];
409       return 0;
410     }
411   }
412 #endif /* USE_MATHUTILS */
413 
414   {
415     const int prop_flag = RNA_property_flag(prop);
416     if (validate_array_type(rvalue,
417                             lvalue_dim,
418                             totdim,
419                             dimsize,
420                             (prop_flag & PROP_DYNAMIC) != 0,
421                             check_item_type,
422                             item_type_str,
423                             error_prefix) == -1) {
424       return -1;
425     }
426 
427     return validate_array_length(rvalue, ptr, prop, lvalue_dim, r_totitem, error_prefix);
428   }
429 }
430 
copy_value_single(PyObject * item,PointerRNA * ptr,PropertyRNA * prop,char * data,uint item_size,int * index,const ItemConvert_FuncArg * convert_item,RNA_SetIndexFunc rna_set_index)431 static char *copy_value_single(PyObject *item,
432                                PointerRNA *ptr,
433                                PropertyRNA *prop,
434                                char *data,
435                                uint item_size,
436                                int *index,
437                                const ItemConvert_FuncArg *convert_item,
438                                RNA_SetIndexFunc rna_set_index)
439 {
440   if (!data) {
441     union {
442       float fl;
443       int i;
444     } value_buf;
445     char *value = (void *)&value_buf;
446 
447     convert_item->func(&convert_item->arg, item, value);
448     rna_set_index(ptr, prop, *index, value);
449     (*index) += 1;
450   }
451   else {
452     convert_item->func(&convert_item->arg, item, data);
453     data += item_size;
454   }
455 
456   return data;
457 }
458 
copy_values(PyObject * seq,PointerRNA * ptr,PropertyRNA * prop,int dim,char * data,uint item_size,int * index,const ItemConvert_FuncArg * convert_item,RNA_SetIndexFunc rna_set_index)459 static char *copy_values(PyObject *seq,
460                          PointerRNA *ptr,
461                          PropertyRNA *prop,
462                          int dim,
463                          char *data,
464                          uint item_size,
465                          int *index,
466                          const ItemConvert_FuncArg *convert_item,
467                          RNA_SetIndexFunc rna_set_index)
468 {
469   const int totdim = RNA_property_array_dimension(ptr, prop, NULL);
470   const Py_ssize_t seq_size = PySequence_Size(seq);
471   Py_ssize_t i;
472 
473   /* Regarding PySequence_GetItem() failing.
474    *
475    * This should never be NULL since we validated it, _but_ some tricky python
476    * developer could write their own sequence type which succeeds on
477    * validating but fails later somehow, so include checks for safety.
478    */
479 
480   /* Note that 'data can be NULL' */
481 
482   if (seq_size == -1) {
483     return NULL;
484   }
485 
486 #ifdef USE_MATHUTILS
487   if (dim == 0) {
488     if (MatrixObject_Check(seq)) {
489       MatrixObject *pymat = (MatrixObject *)seq;
490       const size_t allocsize = pymat->num_col * pymat->num_row * sizeof(float);
491 
492       /* read callback already done by validate */
493       /* since this is the first iteration we can assume data is allocated */
494       memcpy(data, pymat->matrix, allocsize);
495 
496       /* not really needed but do for completeness */
497       data += allocsize;
498 
499       return data;
500     }
501   }
502 #endif /* USE_MATHUTILS */
503 
504   for (i = 0; i < seq_size; i++) {
505     PyObject *item = PySequence_GetItem(seq, i);
506     if (item) {
507       if (dim + 1 < totdim) {
508         data = copy_values(
509             item, ptr, prop, dim + 1, data, item_size, index, convert_item, rna_set_index);
510       }
511       else {
512         data = copy_value_single(
513             item, ptr, prop, data, item_size, index, convert_item, rna_set_index);
514       }
515 
516       Py_DECREF(item);
517 
518       /* data may be NULL, but the for loop checks */
519     }
520     else {
521       return NULL;
522     }
523   }
524 
525   return data;
526 }
527 
py_to_array(PyObject * seq,PointerRNA * ptr,PropertyRNA * prop,char * param_data,ItemTypeCheckFunc check_item_type,const char * item_type_str,int item_size,const ItemConvert_FuncArg * convert_item,RNA_SetArrayFunc rna_set_array,const char * error_prefix)528 static int py_to_array(PyObject *seq,
529                        PointerRNA *ptr,
530                        PropertyRNA *prop,
531                        char *param_data,
532                        ItemTypeCheckFunc check_item_type,
533                        const char *item_type_str,
534                        int item_size,
535                        const ItemConvert_FuncArg *convert_item,
536                        RNA_SetArrayFunc rna_set_array,
537                        const char *error_prefix)
538 {
539   /*int totdim, dim_size[MAX_ARRAY_DIMENSION];*/
540   int totitem;
541   char *data = NULL;
542 
543   /*totdim = RNA_property_array_dimension(ptr, prop, dim_size);*/ /*UNUSED*/
544 
545   if (validate_array(seq, ptr, prop, 0, check_item_type, item_type_str, &totitem, error_prefix) ==
546       -1) {
547     return -1;
548   }
549 
550   if (totitem) {
551     /* note: this code is confusing */
552     if (param_data && RNA_property_flag(prop) & PROP_DYNAMIC) {
553       /* not freeing allocated mem, RNA_parameter_list_free() will do this */
554       ParameterDynAlloc *param_alloc = (ParameterDynAlloc *)param_data;
555       param_alloc->array_tot = (int)totitem;
556 
557       /* freeing param list will free */
558       param_alloc->array = MEM_callocN(item_size * totitem, "py_to_array dyn");
559 
560       data = param_alloc->array;
561     }
562     else if (param_data) {
563       data = param_data;
564     }
565     else {
566       data = PyMem_MALLOC(item_size * totitem);
567     }
568 
569     /* will only fail in very rare cases since we already validated the
570      * python data, the check here is mainly for completeness. */
571     if (copy_values(seq, ptr, prop, 0, data, item_size, NULL, convert_item, NULL) != NULL) {
572       if (param_data == NULL) {
573         /* NULL can only pass through in case RNA property arraylength is 0 (impossible?) */
574         rna_set_array(ptr, prop, data);
575         PyMem_FREE(data);
576       }
577     }
578     else {
579       if (param_data == NULL) {
580         PyMem_FREE(data);
581       }
582 
583       PyErr_Format(PyExc_TypeError,
584                    "%s internal error parsing sequence of type '%s' after successful validation",
585                    error_prefix,
586                    Py_TYPE(seq)->tp_name);
587       return -1;
588     }
589   }
590 
591   return 0;
592 }
593 
py_to_array_index(PyObject * py,PointerRNA * ptr,PropertyRNA * prop,int lvalue_dim,int arrayoffset,int index,ItemTypeCheckFunc check_item_type,const char * item_type_str,const ItemConvert_FuncArg * convert_item,RNA_SetIndexFunc rna_set_index,const char * error_prefix)594 static int py_to_array_index(PyObject *py,
595                              PointerRNA *ptr,
596                              PropertyRNA *prop,
597                              int lvalue_dim,
598                              int arrayoffset,
599                              int index,
600                              ItemTypeCheckFunc check_item_type,
601                              const char *item_type_str,
602                              const ItemConvert_FuncArg *convert_item,
603                              RNA_SetIndexFunc rna_set_index,
604                              const char *error_prefix)
605 {
606   int totdim, dimsize[MAX_ARRAY_DIMENSION];
607   int totitem, i;
608 
609   totdim = RNA_property_array_dimension(ptr, prop, dimsize);
610 
611   /* convert index */
612 
613   /* arr[3][4][5]
614    *
615    *    arr[2] = x
616    *    lvalue_dim = 0, index = 0 + 2 * 4 * 5
617    *
618    *    arr[2][3] = x
619    *    lvalue_dim = 1, index = 40 + 3 * 5 */
620 
621   lvalue_dim++;
622 
623   for (i = lvalue_dim; i < totdim; i++) {
624     index *= dimsize[i];
625   }
626 
627   index += arrayoffset;
628 
629   if (lvalue_dim == totdim) { /* single item, assign directly */
630     if (!check_item_type(py)) {
631       PyErr_Format(PyExc_TypeError,
632                    "%s %.200s.%.200s, expected a %s type, not %s",
633                    error_prefix,
634                    RNA_struct_identifier(ptr->type),
635                    RNA_property_identifier(prop),
636                    item_type_str,
637                    Py_TYPE(py)->tp_name);
638       return -1;
639     }
640     copy_value_single(py, ptr, prop, NULL, 0, &index, convert_item, rna_set_index);
641   }
642   else {
643     if (validate_array(
644             py, ptr, prop, lvalue_dim, check_item_type, item_type_str, &totitem, error_prefix) ==
645         -1) {
646       return -1;
647     }
648 
649     if (totitem) {
650       copy_values(py, ptr, prop, lvalue_dim, NULL, 0, &index, convert_item, rna_set_index);
651     }
652   }
653   return 0;
654 }
655 
py_to_float(const struct ItemConvertArgData * arg,PyObject * py,char * data)656 static void py_to_float(const struct ItemConvertArgData *arg, PyObject *py, char *data)
657 {
658   const float *range = arg->float_data.range;
659   float value = (float)PyFloat_AsDouble(py);
660   CLAMP(value, range[0], range[1]);
661   *(float *)data = value;
662 }
663 
py_to_int(const struct ItemConvertArgData * arg,PyObject * py,char * data)664 static void py_to_int(const struct ItemConvertArgData *arg, PyObject *py, char *data)
665 {
666   const int *range = arg->int_data.range;
667   int value = PyC_Long_AsI32(py);
668   CLAMP(value, range[0], range[1]);
669   *(int *)data = value;
670 }
671 
py_to_bool(const struct ItemConvertArgData * UNUSED (arg),PyObject * py,char * data)672 static void py_to_bool(const struct ItemConvertArgData *UNUSED(arg), PyObject *py, char *data)
673 {
674   *(bool *)data = (bool)PyObject_IsTrue(py);
675 }
676 
py_float_check(PyObject * py)677 static int py_float_check(PyObject *py)
678 {
679   /* accept both floats and integers */
680   return PyNumber_Check(py);
681 }
682 
py_int_check(PyObject * py)683 static int py_int_check(PyObject *py)
684 {
685   /* accept only integers */
686   return PyLong_Check(py);
687 }
688 
py_bool_check(PyObject * py)689 static int py_bool_check(PyObject *py)
690 {
691   return PyBool_Check(py);
692 }
693 
float_set_index(PointerRNA * ptr,PropertyRNA * prop,int index,void * value)694 static void float_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, void *value)
695 {
696   RNA_property_float_set_index(ptr, prop, index, *(float *)value);
697 }
698 
int_set_index(PointerRNA * ptr,PropertyRNA * prop,int index,void * value)699 static void int_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, void *value)
700 {
701   RNA_property_int_set_index(ptr, prop, index, *(int *)value);
702 }
703 
bool_set_index(PointerRNA * ptr,PropertyRNA * prop,int index,void * value)704 static void bool_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, void *value)
705 {
706   RNA_property_boolean_set_index(ptr, prop, index, *(bool *)value);
707 }
708 
convert_item_init_float(PointerRNA * ptr,PropertyRNA * prop,ItemConvert_FuncArg * convert_item)709 static void convert_item_init_float(PointerRNA *ptr,
710                                     PropertyRNA *prop,
711                                     ItemConvert_FuncArg *convert_item)
712 {
713   float *range = convert_item->arg.float_data.range;
714   convert_item->func = py_to_float;
715   RNA_property_float_range(ptr, prop, &range[0], &range[1]);
716 }
717 
convert_item_init_int(PointerRNA * ptr,PropertyRNA * prop,ItemConvert_FuncArg * convert_item)718 static void convert_item_init_int(PointerRNA *ptr,
719                                   PropertyRNA *prop,
720                                   ItemConvert_FuncArg *convert_item)
721 {
722   int *range = convert_item->arg.int_data.range;
723   convert_item->func = py_to_int;
724   RNA_property_int_range(ptr, prop, &range[0], &range[1]);
725 }
726 
convert_item_init_bool(PointerRNA * UNUSED (ptr),PropertyRNA * UNUSED (prop),ItemConvert_FuncArg * convert_item)727 static void convert_item_init_bool(PointerRNA *UNUSED(ptr),
728                                    PropertyRNA *UNUSED(prop),
729                                    ItemConvert_FuncArg *convert_item)
730 {
731   convert_item->func = py_to_bool;
732 }
733 
pyrna_py_to_array(PointerRNA * ptr,PropertyRNA * prop,char * param_data,PyObject * py,const char * error_prefix)734 int pyrna_py_to_array(
735     PointerRNA *ptr, PropertyRNA *prop, char *param_data, PyObject *py, const char *error_prefix)
736 {
737   int ret;
738   switch (RNA_property_type(prop)) {
739     case PROP_FLOAT: {
740       ItemConvert_FuncArg convert_item;
741       convert_item_init_float(ptr, prop, &convert_item);
742 
743       ret = py_to_array(py,
744                         ptr,
745                         prop,
746                         param_data,
747                         py_float_check,
748                         "float",
749                         sizeof(float),
750                         &convert_item,
751                         (RNA_SetArrayFunc)RNA_property_float_set_array,
752                         error_prefix);
753       break;
754     }
755     case PROP_INT: {
756       ItemConvert_FuncArg convert_item;
757       convert_item_init_int(ptr, prop, &convert_item);
758 
759       ret = py_to_array(py,
760                         ptr,
761                         prop,
762                         param_data,
763                         py_int_check,
764                         "int",
765                         sizeof(int),
766                         &convert_item,
767                         (RNA_SetArrayFunc)RNA_property_int_set_array,
768                         error_prefix);
769       break;
770     }
771     case PROP_BOOLEAN: {
772       ItemConvert_FuncArg convert_item;
773       convert_item_init_bool(ptr, prop, &convert_item);
774 
775       ret = py_to_array(py,
776                         ptr,
777                         prop,
778                         param_data,
779                         py_bool_check,
780                         "boolean",
781                         sizeof(bool),
782                         &convert_item,
783                         (RNA_SetArrayFunc)RNA_property_boolean_set_array,
784                         error_prefix);
785       break;
786     }
787     default: {
788       PyErr_SetString(PyExc_TypeError, "not an array type");
789       ret = -1;
790       break;
791     }
792   }
793 
794   return ret;
795 }
796 
pyrna_py_to_array_index(PointerRNA * ptr,PropertyRNA * prop,int arraydim,int arrayoffset,int index,PyObject * py,const char * error_prefix)797 int pyrna_py_to_array_index(PointerRNA *ptr,
798                             PropertyRNA *prop,
799                             int arraydim,
800                             int arrayoffset,
801                             int index,
802                             PyObject *py,
803                             const char *error_prefix)
804 {
805   int ret;
806   switch (RNA_property_type(prop)) {
807     case PROP_FLOAT: {
808       ItemConvert_FuncArg convert_item;
809       convert_item_init_float(ptr, prop, &convert_item);
810 
811       ret = py_to_array_index(py,
812                               ptr,
813                               prop,
814                               arraydim,
815                               arrayoffset,
816                               index,
817                               py_float_check,
818                               "float",
819                               &convert_item,
820                               float_set_index,
821                               error_prefix);
822       break;
823     }
824     case PROP_INT: {
825       ItemConvert_FuncArg convert_item;
826       convert_item_init_int(ptr, prop, &convert_item);
827 
828       ret = py_to_array_index(py,
829                               ptr,
830                               prop,
831                               arraydim,
832                               arrayoffset,
833                               index,
834                               py_int_check,
835                               "int",
836                               &convert_item,
837                               int_set_index,
838                               error_prefix);
839       break;
840     }
841     case PROP_BOOLEAN: {
842       ItemConvert_FuncArg convert_item;
843       convert_item_init_bool(ptr, prop, &convert_item);
844 
845       ret = py_to_array_index(py,
846                               ptr,
847                               prop,
848                               arraydim,
849                               arrayoffset,
850                               index,
851                               py_bool_check,
852                               "boolean",
853                               &convert_item,
854                               bool_set_index,
855                               error_prefix);
856       break;
857     }
858     default: {
859       PyErr_SetString(PyExc_TypeError, "not an array type");
860       ret = -1;
861       break;
862     }
863   }
864 
865   return ret;
866 }
867 
pyrna_array_index(PointerRNA * ptr,PropertyRNA * prop,int index)868 PyObject *pyrna_array_index(PointerRNA *ptr, PropertyRNA *prop, int index)
869 {
870   PyObject *item;
871 
872   switch (RNA_property_type(prop)) {
873     case PROP_FLOAT:
874       item = PyFloat_FromDouble(RNA_property_float_get_index(ptr, prop, index));
875       break;
876     case PROP_BOOLEAN:
877       item = PyBool_FromLong(RNA_property_boolean_get_index(ptr, prop, index));
878       break;
879     case PROP_INT:
880       item = PyLong_FromLong(RNA_property_int_get_index(ptr, prop, index));
881       break;
882     default:
883       PyErr_SetString(PyExc_TypeError, "not an array type");
884       item = NULL;
885       break;
886   }
887 
888   return item;
889 }
890 
891 #if 0
892 /* XXX this is not used (and never will?) */
893 /* Given an array property, creates an N-dimensional tuple of values. */
894 static PyObject *pyrna_py_from_array_internal(PointerRNA *ptr,
895                                               PropertyRNA *prop,
896                                               int dim,
897                                               int *index)
898 {
899   PyObject *tuple;
900   int i, len;
901   int totdim = RNA_property_array_dimension(ptr, prop, NULL);
902 
903   len = RNA_property_multi_array_length(ptr, prop, dim);
904 
905   tuple = PyTuple_New(len);
906 
907   for (i = 0; i < len; i++) {
908     PyObject *item;
909 
910     if (dim + 1 < totdim) {
911       item = pyrna_py_from_array_internal(ptr, prop, dim + 1, index);
912     }
913     else {
914       item = pyrna_array_index(ptr, prop, *index);
915       *index = *index + 1;
916     }
917 
918     if (!item) {
919       Py_DECREF(tuple);
920       return NULL;
921     }
922 
923     PyTuple_SET_ITEM(tuple, i, item);
924   }
925 
926   return tuple;
927 }
928 #endif
929 
pyrna_py_from_array_index(BPy_PropertyArrayRNA * self,PointerRNA * ptr,PropertyRNA * prop,int index)930 PyObject *pyrna_py_from_array_index(BPy_PropertyArrayRNA *self,
931                                     PointerRNA *ptr,
932                                     PropertyRNA *prop,
933                                     int index)
934 {
935   int totdim, arraydim, arrayoffset, dimsize[MAX_ARRAY_DIMENSION], i, len;
936   BPy_PropertyArrayRNA *ret = NULL;
937 
938   arraydim = self ? self->arraydim : 0;
939   arrayoffset = self ? self->arrayoffset : 0;
940 
941   /* just in case check */
942   len = RNA_property_multi_array_length(ptr, prop, arraydim);
943   if (index >= len || index < 0) {
944     /* this shouldn't happen because higher level funcs must check for invalid index */
945     CLOG_WARN(BPY_LOG_RNA, "invalid index %d for array with length=%d", index, len);
946 
947     PyErr_SetString(PyExc_IndexError, "out of range");
948     return NULL;
949   }
950 
951   totdim = RNA_property_array_dimension(ptr, prop, dimsize);
952 
953   if (arraydim + 1 < totdim) {
954     ret = (BPy_PropertyArrayRNA *)pyrna_prop_CreatePyObject(ptr, prop);
955     ret->arraydim = arraydim + 1;
956 
957     /* arr[3][4][5]
958      *
959      *    x = arr[2]
960      *    index = 0 + 2 * 4 * 5
961      *
962      *    x = arr[2][3]
963      *    index = offset + 3 * 5 */
964 
965     for (i = arraydim + 1; i < totdim; i++) {
966       index *= dimsize[i];
967     }
968 
969     ret->arrayoffset = arrayoffset + index;
970   }
971   else {
972     index = arrayoffset + index;
973     ret = (BPy_PropertyArrayRNA *)pyrna_array_index(ptr, prop, index);
974   }
975 
976   return (PyObject *)ret;
977 }
978 
pyrna_py_from_array(PointerRNA * ptr,PropertyRNA * prop)979 PyObject *pyrna_py_from_array(PointerRNA *ptr, PropertyRNA *prop)
980 {
981   PyObject *ret;
982 
983   ret = pyrna_math_object_from_array(ptr, prop);
984 
985   /* is this a maths object? */
986   if (ret) {
987     return ret;
988   }
989 
990   return pyrna_prop_CreatePyObject(ptr, prop);
991 }
992 
993 /* TODO, multi-dimensional arrays */
pyrna_array_contains_py(PointerRNA * ptr,PropertyRNA * prop,PyObject * value)994 int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value)
995 {
996   const int len = RNA_property_array_length(ptr, prop);
997   int type;
998   int i;
999 
1000   if (len == 0) {
1001     /* possible with dynamic arrays */
1002     return 0;
1003   }
1004 
1005   if (RNA_property_array_dimension(ptr, prop, NULL) > 1) {
1006     PyErr_SetString(PyExc_TypeError, "PropertyRNA - multi dimensional arrays not supported yet");
1007     return -1;
1008   }
1009 
1010   type = RNA_property_type(prop);
1011 
1012   switch (type) {
1013     case PROP_FLOAT: {
1014       const float value_f = PyFloat_AsDouble(value);
1015       if (value_f == -1 && PyErr_Occurred()) {
1016         PyErr_Clear();
1017         return 0;
1018       }
1019 
1020       float tmp[32];
1021       float *tmp_arr;
1022 
1023       if (len * sizeof(float) > sizeof(tmp)) {
1024         tmp_arr = PyMem_MALLOC(len * sizeof(float));
1025       }
1026       else {
1027         tmp_arr = tmp;
1028       }
1029 
1030       RNA_property_float_get_array(ptr, prop, tmp_arr);
1031 
1032       for (i = 0; i < len; i++) {
1033         if (tmp_arr[i] == value_f) {
1034           break;
1035         }
1036       }
1037 
1038       if (tmp_arr != tmp) {
1039         PyMem_FREE(tmp_arr);
1040       }
1041 
1042       return i < len ? 1 : 0;
1043 
1044       break;
1045     }
1046     case PROP_INT: {
1047       const int value_i = PyC_Long_AsI32(value);
1048       if (value_i == -1 && PyErr_Occurred()) {
1049         PyErr_Clear();
1050         return 0;
1051       }
1052 
1053       int tmp[32];
1054       int *tmp_arr;
1055 
1056       if (len * sizeof(int) > sizeof(tmp)) {
1057         tmp_arr = PyMem_MALLOC(len * sizeof(int));
1058       }
1059       else {
1060         tmp_arr = tmp;
1061       }
1062 
1063       RNA_property_int_get_array(ptr, prop, tmp_arr);
1064 
1065       for (i = 0; i < len; i++) {
1066         if (tmp_arr[i] == value_i) {
1067           break;
1068         }
1069       }
1070 
1071       if (tmp_arr != tmp) {
1072         PyMem_FREE(tmp_arr);
1073       }
1074 
1075       return i < len ? 1 : 0;
1076 
1077       break;
1078     }
1079     case PROP_BOOLEAN: {
1080       const int value_i = PyC_Long_AsBool(value);
1081       if (value_i == -1 && PyErr_Occurred()) {
1082         PyErr_Clear();
1083         return 0;
1084       }
1085 
1086       bool tmp[32];
1087       bool *tmp_arr;
1088 
1089       if (len * sizeof(bool) > sizeof(tmp)) {
1090         tmp_arr = PyMem_MALLOC(len * sizeof(bool));
1091       }
1092       else {
1093         tmp_arr = tmp;
1094       }
1095 
1096       RNA_property_boolean_get_array(ptr, prop, tmp_arr);
1097 
1098       for (i = 0; i < len; i++) {
1099         if (tmp_arr[i] == value_i) {
1100           break;
1101         }
1102       }
1103 
1104       if (tmp_arr != tmp) {
1105         PyMem_FREE(tmp_arr);
1106       }
1107 
1108       return i < len ? 1 : 0;
1109 
1110       break;
1111     }
1112   }
1113 
1114   /* should never reach this */
1115   PyErr_SetString(PyExc_TypeError, "PropertyRNA - type not in float/bool/int");
1116   return -1;
1117 }
1118