This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/python/intern/bpy_rna_array.c
Campbell Barton 51e5417bd3 Fix #105678: Crash assigning Image.pixels to an undersized sequence
Now only dynamic function parameters that use ParameterDynAlloc support
dynamically sized parameters arrays.

Add tests for both dynamic arrays that don't support resizing
(Image.pixels) and dynamic sized arguments using
(VertexGroup.add(index=[..])).

Regression in [0] which extended support for dynamic sized function
arguments.

[0]: dfb8c5974e
2023-03-14 16:00:24 +11:00

1120 lines
31 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup pythonintern
*
* This file deals with array access for 'BPy_PropertyArrayRNA' from bpy_rna.c
*/
#include <Python.h>
#include "CLG_log.h"
#include "BLI_utildefines.h"
#include "RNA_types.h"
#include "bpy_rna.h"
#include "MEM_guardedalloc.h"
#include "RNA_access.h"
#include "BPY_extern_clog.h"
#include "../generic/py_capi_utils.h"
#define USE_MATHUTILS
#ifdef USE_MATHUTILS
# include "../mathutils/mathutils.h" /* so we can have mathutils callbacks */
#endif
#define MAX_ARRAY_DIMENSION 10
struct ItemConvertArgData;
typedef void (*ItemConvertFunc)(const struct ItemConvertArgData *arg, PyObject *, char *);
typedef int (*ItemTypeCheckFunc)(PyObject *);
typedef void (*RNA_SetArrayFunc)(PointerRNA *, PropertyRNA *, const char *);
typedef void (*RNA_SetIndexFunc)(PointerRNA *, PropertyRNA *, int index, void *);
struct ItemConvertArgData {
union {
struct {
int range[2];
} int_data;
struct {
float range[2];
} float_data;
};
};
/**
* Callback and args needed to apply the value (clamp range for now)
*/
typedef struct ItemConvert_FuncArg {
ItemConvertFunc func;
struct ItemConvertArgData arg;
} ItemConvert_FuncArg;
/*
* arr[3][4][5]
* 0 1 2 <- dimension index
*/
/*
* arr[2] = x
*
* py_to_array_index(arraydim=0, arrayoffset=0, index=2)
* validate_array(lvalue_dim=0)
* ... make real index ...
*/
/* arr[3] = x, self->arraydim is 0, lvalue_dim is 1 */
/* Ensures that a python sequence has expected number of
* items/sub-items and items are of desired type. */
static int 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)
{
Py_ssize_t i;
/* not the last dimension */
if (dim + 1 < totdim) {
/* check that a sequence contains dimsize[dim] items */
const int seq_size = PySequence_Size(seq);
if (seq_size == -1) {
PyErr_Format(PyExc_ValueError,
"%s sequence expected at dimension %d, not '%s'",
error_prefix,
dim + 1,
Py_TYPE(seq)->tp_name);
return -1;
}
for (i = 0; i < seq_size; i++) {
Py_ssize_t item_seq_size;
PyObject *item;
bool ok = true;
item = PySequence_GetItem(seq, i);
if (item == NULL) {
PyErr_Format(PyExc_TypeError,
"%s sequence type '%s' failed to retrieve index %d",
error_prefix,
Py_TYPE(seq)->tp_name,
i);
ok = 0;
}
else if ((item_seq_size = PySequence_Size(item)) == -1) {
// BLI_snprintf(error_str, error_str_size, "expected a sequence of %s", item_type_str);
PyErr_Format(PyExc_TypeError,
"%s expected a sequence of %s, not %s",
error_prefix,
item_type_str,
Py_TYPE(item)->tp_name);
ok = 0;
}
/* arr[3][4][5]
* dimsize[1] = 4
* dimsize[2] = 5
*
* dim = 0 */
else if (item_seq_size != dimsize[dim + 1]) {
/* BLI_snprintf(error_str, error_str_size,
* "sequences of dimension %d should contain %d items",
* dim + 1, dimsize[dim + 1]); */
PyErr_Format(PyExc_ValueError,
"%s sequences of dimension %d should contain %d items, not %d",
error_prefix,
dim + 1,
dimsize[dim + 1],
item_seq_size);
ok = 0;
}
else if (validate_array_type(item,
dim + 1,
totdim,
dimsize,
is_dynamic,
check_item_type,
item_type_str,
error_prefix) == -1) {
ok = 0;
}
Py_XDECREF(item);
if (!ok) {
return -1;
}
}
}
else {
/* check that items are of correct type */
const int seq_size = PySequence_Size(seq);
if (seq_size == -1) {
PyErr_Format(PyExc_ValueError,
"%s sequence expected at dimension %d, not '%s'",
error_prefix,
dim + 1,
Py_TYPE(seq)->tp_name);
return -1;
}
if ((seq_size != dimsize[dim]) && (is_dynamic == false)) {
PyErr_Format(PyExc_ValueError,
"%s sequences of dimension %d should contain %d items, not %d",
error_prefix,
dim,
dimsize[dim],
seq_size);
return -1;
}
for (i = 0; i < seq_size; i++) {
PyObject *item = PySequence_GetItem(seq, i);
if (item == NULL) {
PyErr_Format(PyExc_TypeError,
"%s sequence type '%s' failed to retrieve index %d",
error_prefix,
Py_TYPE(seq)->tp_name,
i);
return -1;
}
if (!check_item_type(item)) {
Py_DECREF(item);
#if 0
BLI_snprintf(
error_str, error_str_size, "sequence items should be of type %s", item_type_str);
#endif
PyErr_Format(PyExc_TypeError,
"%s expected sequence items of type %s, not %s",
error_prefix,
item_type_str,
Py_TYPE(item)->tp_name);
return -1;
}
Py_DECREF(item);
}
}
return 0; /* ok */
}
/* Returns the number of items in a single- or multi-dimensional sequence. */
static int count_items(PyObject *seq, int dim)
{
int totitem = 0;
if (dim > 1) {
const Py_ssize_t seq_size = PySequence_Size(seq);
Py_ssize_t i;
for (i = 0; i < seq_size; i++) {
PyObject *item = PySequence_GetItem(seq, i);
if (item) {
const int tot = count_items(item, dim - 1);
Py_DECREF(item);
if (tot != -1) {
totitem += tot;
}
else {
totitem = -1;
break;
}
}
else {
totitem = -1;
break;
}
}
}
else {
totitem = PySequence_Size(seq);
}
return totitem;
}
/* Modifies property array length if needed and PROP_DYNAMIC flag is set. */
static int validate_array_length(PyObject *rvalue,
PointerRNA *ptr,
PropertyRNA *prop,
const bool prop_is_param_dyn_alloc,
int lvalue_dim,
int *r_totitem,
const char *error_prefix)
{
int dimsize[MAX_ARRAY_DIMENSION];
int tot, totdim, len;
totdim = RNA_property_array_dimension(ptr, prop, dimsize);
tot = count_items(rvalue, totdim - lvalue_dim);
if (tot == -1) {
PyErr_Format(PyExc_ValueError,
"%s %.200s.%.200s, error validating the sequence length",
error_prefix,
RNA_struct_identifier(ptr->type),
RNA_property_identifier(prop));
return -1;
}
if ((RNA_property_flag(prop) & PROP_DYNAMIC) && lvalue_dim == 0) {
const int tot_expected = RNA_property_array_length(ptr, prop);
if (tot_expected != tot) {
*r_totitem = tot;
if (!prop_is_param_dyn_alloc) {
PyErr_Format(PyExc_ValueError,
"%s %s.%s: array length cannot be changed to %d (expected %d)",
error_prefix,
RNA_struct_identifier(ptr->type),
RNA_property_identifier(prop),
tot,
tot_expected);
return -1;
}
return 0;
}
len = tot;
}
else {
/* length is a constraint */
if (!lvalue_dim) {
len = RNA_property_array_length(ptr, prop);
}
/* array item assignment */
else {
int i;
len = 1;
/* arr[3][4][5]
*
* arr[2] = x
* dimsize = {4, 5}
* dimsize[1] = 4
* dimsize[2] = 5
* lvalue_dim = 0, totdim = 3
*
* arr[2][3] = x
* lvalue_dim = 1
*
* arr[2][3][4] = x
* lvalue_dim = 2 */
for (i = lvalue_dim; i < totdim; i++) {
len *= dimsize[i];
}
}
if (tot != len) {
// BLI_snprintf(error_str, error_str_size, "sequence must have length of %d", len);
PyErr_Format(PyExc_ValueError,
"%s %.200s.%.200s, sequence must have %d items total, not %d",
error_prefix,
RNA_struct_identifier(ptr->type),
RNA_property_identifier(prop),
len,
tot);
return -1;
}
}
*r_totitem = len;
return 0;
}
static int validate_array(PyObject *rvalue,
PointerRNA *ptr,
PropertyRNA *prop,
const bool prop_is_param_dyn_alloc,
int lvalue_dim,
ItemTypeCheckFunc check_item_type,
const char *item_type_str,
int *r_totitem,
const char *error_prefix)
{
int dimsize[MAX_ARRAY_DIMENSION];
const int totdim = RNA_property_array_dimension(ptr, prop, dimsize);
/* validate type first because length validation may modify property array length */
#ifdef USE_MATHUTILS
if (lvalue_dim == 0) { /* only valid for first level array */
if (MatrixObject_Check(rvalue)) {
MatrixObject *pymat = (MatrixObject *)rvalue;
if (BaseMath_ReadCallback(pymat) == -1) {
return -1;
}
if (RNA_property_type(prop) != PROP_FLOAT) {
PyErr_Format(PyExc_ValueError,
"%s %.200s.%.200s, matrix assign to non float array",
error_prefix,
RNA_struct_identifier(ptr->type),
RNA_property_identifier(prop));
return -1;
}
if (totdim != 2) {
PyErr_Format(PyExc_ValueError,
"%s %.200s.%.200s, matrix assign array with %d dimensions",
error_prefix,
RNA_struct_identifier(ptr->type),
RNA_property_identifier(prop),
totdim);
return -1;
}
if (pymat->col_num != dimsize[0] || pymat->row_num != dimsize[1]) {
PyErr_Format(PyExc_ValueError,
"%s %.200s.%.200s, matrix assign dimension size mismatch, "
"is %dx%d, expected be %dx%d",
error_prefix,
RNA_struct_identifier(ptr->type),
RNA_property_identifier(prop),
pymat->col_num,
pymat->row_num,
dimsize[0],
dimsize[1]);
return -1;
}
*r_totitem = dimsize[0] * dimsize[1];
return 0;
}
}
#endif /* USE_MATHUTILS */
{
const int prop_flag = RNA_property_flag(prop);
if (validate_array_type(rvalue,
lvalue_dim,
totdim,
dimsize,
(prop_flag & PROP_DYNAMIC) != 0,
check_item_type,
item_type_str,
error_prefix) == -1) {
return -1;
}
return validate_array_length(
rvalue, ptr, prop, prop_is_param_dyn_alloc, lvalue_dim, r_totitem, error_prefix);
}
}
static char *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)
{
if (!data) {
union {
float fl;
int i;
} value_buf;
char *value = (void *)&value_buf;
convert_item->func(&convert_item->arg, item, value);
rna_set_index(ptr, prop, *index, value);
(*index) += 1;
}
else {
convert_item->func(&convert_item->arg, item, data);
data += item_size;
}
return data;
}
static char *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)
{
const int totdim = RNA_property_array_dimension(ptr, prop, NULL);
const Py_ssize_t seq_size = PySequence_Size(seq);
Py_ssize_t i;
/* Regarding PySequence_GetItem() failing.
*
* This should never be NULL since we validated it, _but_ some tricky python
* developer could write their own sequence type which succeeds on
* validating but fails later somehow, so include checks for safety.
*/
/* Note that 'data can be NULL' */
if (seq_size == -1) {
return NULL;
}
#ifdef USE_MATHUTILS
if (dim == 0) {
if (MatrixObject_Check(seq)) {
MatrixObject *pymat = (MatrixObject *)seq;
const size_t allocsize = pymat->col_num * pymat->row_num * sizeof(float);
/* read callback already done by validate */
/* since this is the first iteration we can assume data is allocated */
memcpy(data, pymat->matrix, allocsize);
/* not really needed but do for completeness */
data += allocsize;
return data;
}
}
#endif /* USE_MATHUTILS */
for (i = 0; i < seq_size; i++) {
PyObject *item = PySequence_GetItem(seq, i);
if (item) {
if (dim + 1 < totdim) {
data = copy_values(
item, ptr, prop, dim + 1, data, item_size, index, convert_item, rna_set_index);
}
else {
data = copy_value_single(
item, ptr, prop, data, item_size, index, convert_item, rna_set_index);
}
Py_DECREF(item);
/* data may be NULL, but the for loop checks */
}
else {
return NULL;
}
}
return data;
}
static int 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)
{
// int totdim, dim_size[MAX_ARRAY_DIMENSION];
int totitem;
char *data = NULL;
// totdim = RNA_property_array_dimension(ptr, prop, dim_size); /* UNUSED */
const int flag = RNA_property_flag(prop);
/* Use #ParameterDynAlloc which defines it's own array length. */
const bool prop_is_param_dyn_alloc = param_data && (flag & PROP_DYNAMIC);
if (validate_array(seq,
ptr,
prop,
prop_is_param_dyn_alloc,
0,
check_item_type,
item_type_str,
&totitem,
error_prefix) == -1) {
return -1;
}
if (totitem) {
/* NOTE: this code is confusing. */
if (prop_is_param_dyn_alloc) {
/* not freeing allocated mem, RNA_parameter_list_free() will do this */
ParameterDynAlloc *param_alloc = (ParameterDynAlloc *)param_data;
param_alloc->array_tot = (int)totitem;
/* freeing param list will free */
param_alloc->array = MEM_callocN(item_size * totitem, "py_to_array dyn");
data = param_alloc->array;
}
else if (param_data) {
data = param_data;
}
else {
data = PyMem_MALLOC(item_size * totitem);
}
/* will only fail in very rare cases since we already validated the
* python data, the check here is mainly for completeness. */
if (copy_values(seq, ptr, prop, 0, data, item_size, NULL, convert_item, NULL) != NULL) {
if (param_data == NULL) {
/* NULL can only pass through in case RNA property arraylength is 0 (impossible?) */
rna_set_array(ptr, prop, data);
PyMem_FREE(data);
}
}
else {
if (param_data == NULL) {
PyMem_FREE(data);
}
PyErr_Format(PyExc_TypeError,
"%s internal error parsing sequence of type '%s' after successful validation",
error_prefix,
Py_TYPE(seq)->tp_name);
return -1;
}
}
return 0;
}
static int 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)
{
int totdim, dimsize[MAX_ARRAY_DIMENSION];
int totitem, i;
totdim = RNA_property_array_dimension(ptr, prop, dimsize);
/* convert index */
/* arr[3][4][5]
*
* arr[2] = x
* lvalue_dim = 0, index = 0 + 2 * 4 * 5
*
* arr[2][3] = x
* lvalue_dim = 1, index = 40 + 3 * 5 */
lvalue_dim++;
for (i = lvalue_dim; i < totdim; i++) {
index *= dimsize[i];
}
index += arrayoffset;
if (lvalue_dim == totdim) { /* single item, assign directly */
if (!check_item_type(py)) {
PyErr_Format(PyExc_TypeError,
"%s %.200s.%.200s, expected a %s type, not %s",
error_prefix,
RNA_struct_identifier(ptr->type),
RNA_property_identifier(prop),
item_type_str,
Py_TYPE(py)->tp_name);
return -1;
}
copy_value_single(py, ptr, prop, NULL, 0, &index, convert_item, rna_set_index);
}
else {
const bool prop_is_param_dyn_alloc = false;
if (validate_array(py,
ptr,
prop,
prop_is_param_dyn_alloc,
lvalue_dim,
check_item_type,
item_type_str,
&totitem,
error_prefix) == -1) {
return -1;
}
if (totitem) {
copy_values(py, ptr, prop, lvalue_dim, NULL, 0, &index, convert_item, rna_set_index);
}
}
return 0;
}
static void py_to_float(const struct ItemConvertArgData *arg, PyObject *py, char *data)
{
const float *range = arg->float_data.range;
float value = (float)PyFloat_AsDouble(py);
CLAMP(value, range[0], range[1]);
*(float *)data = value;
}
static void py_to_int(const struct ItemConvertArgData *arg, PyObject *py, char *data)
{
const int *range = arg->int_data.range;
int value = PyC_Long_AsI32(py);
CLAMP(value, range[0], range[1]);
*(int *)data = value;
}
static void py_to_bool(const struct ItemConvertArgData *UNUSED(arg), PyObject *py, char *data)
{
*(bool *)data = (bool)PyObject_IsTrue(py);
}
static int py_float_check(PyObject *py)
{
/* accept both floats and integers */
return PyNumber_Check(py);
}
static int py_int_check(PyObject *py)
{
/* accept only integers */
return PyLong_Check(py);
}
static int py_bool_check(PyObject *py)
{
return PyBool_Check(py);
}
static void float_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, void *value)
{
RNA_property_float_set_index(ptr, prop, index, *(float *)value);
}
static void int_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, void *value)
{
RNA_property_int_set_index(ptr, prop, index, *(int *)value);
}
static void bool_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, void *value)
{
RNA_property_boolean_set_index(ptr, prop, index, *(bool *)value);
}
static void convert_item_init_float(PointerRNA *ptr,
PropertyRNA *prop,
ItemConvert_FuncArg *convert_item)
{
float *range = convert_item->arg.float_data.range;
convert_item->func = py_to_float;
RNA_property_float_range(ptr, prop, &range[0], &range[1]);
}
static void convert_item_init_int(PointerRNA *ptr,
PropertyRNA *prop,
ItemConvert_FuncArg *convert_item)
{
int *range = convert_item->arg.int_data.range;
convert_item->func = py_to_int;
RNA_property_int_range(ptr, prop, &range[0], &range[1]);
}
static void convert_item_init_bool(PointerRNA *UNUSED(ptr),
PropertyRNA *UNUSED(prop),
ItemConvert_FuncArg *convert_item)
{
convert_item->func = py_to_bool;
}
int pyrna_py_to_array(
PointerRNA *ptr, PropertyRNA *prop, char *param_data, PyObject *py, const char *error_prefix)
{
int ret;
switch (RNA_property_type(prop)) {
case PROP_FLOAT: {
ItemConvert_FuncArg convert_item;
convert_item_init_float(ptr, prop, &convert_item);
ret = py_to_array(py,
ptr,
prop,
param_data,
py_float_check,
"float",
sizeof(float),
&convert_item,
(RNA_SetArrayFunc)RNA_property_float_set_array,
error_prefix);
break;
}
case PROP_INT: {
ItemConvert_FuncArg convert_item;
convert_item_init_int(ptr, prop, &convert_item);
ret = py_to_array(py,
ptr,
prop,
param_data,
py_int_check,
"int",
sizeof(int),
&convert_item,
(RNA_SetArrayFunc)RNA_property_int_set_array,
error_prefix);
break;
}
case PROP_BOOLEAN: {
ItemConvert_FuncArg convert_item;
convert_item_init_bool(ptr, prop, &convert_item);
ret = py_to_array(py,
ptr,
prop,
param_data,
py_bool_check,
"boolean",
sizeof(bool),
&convert_item,
(RNA_SetArrayFunc)RNA_property_boolean_set_array,
error_prefix);
break;
}
default: {
PyErr_SetString(PyExc_TypeError, "not an array type");
ret = -1;
break;
}
}
return ret;
}
int pyrna_py_to_array_index(PointerRNA *ptr,
PropertyRNA *prop,
int arraydim,
int arrayoffset,
int index,
PyObject *py,
const char *error_prefix)
{
int ret;
switch (RNA_property_type(prop)) {
case PROP_FLOAT: {
ItemConvert_FuncArg convert_item;
convert_item_init_float(ptr, prop, &convert_item);
ret = py_to_array_index(py,
ptr,
prop,
arraydim,
arrayoffset,
index,
py_float_check,
"float",
&convert_item,
float_set_index,
error_prefix);
break;
}
case PROP_INT: {
ItemConvert_FuncArg convert_item;
convert_item_init_int(ptr, prop, &convert_item);
ret = py_to_array_index(py,
ptr,
prop,
arraydim,
arrayoffset,
index,
py_int_check,
"int",
&convert_item,
int_set_index,
error_prefix);
break;
}
case PROP_BOOLEAN: {
ItemConvert_FuncArg convert_item;
convert_item_init_bool(ptr, prop, &convert_item);
ret = py_to_array_index(py,
ptr,
prop,
arraydim,
arrayoffset,
index,
py_bool_check,
"boolean",
&convert_item,
bool_set_index,
error_prefix);
break;
}
default: {
PyErr_SetString(PyExc_TypeError, "not an array type");
ret = -1;
break;
}
}
return ret;
}
PyObject *pyrna_array_index(PointerRNA *ptr, PropertyRNA *prop, int index)
{
PyObject *item;
switch (RNA_property_type(prop)) {
case PROP_FLOAT:
item = PyFloat_FromDouble(RNA_property_float_get_index(ptr, prop, index));
break;
case PROP_BOOLEAN:
item = PyBool_FromLong(RNA_property_boolean_get_index(ptr, prop, index));
break;
case PROP_INT:
item = PyLong_FromLong(RNA_property_int_get_index(ptr, prop, index));
break;
default:
PyErr_SetString(PyExc_TypeError, "not an array type");
item = NULL;
break;
}
return item;
}
#if 0
/* XXX this is not used (and never will?) */
/* Given an array property, creates an N-dimensional tuple of values. */
static PyObject *pyrna_py_from_array_internal(PointerRNA *ptr,
PropertyRNA *prop,
int dim,
int *index)
{
PyObject *tuple;
int i, len;
int totdim = RNA_property_array_dimension(ptr, prop, NULL);
len = RNA_property_multi_array_length(ptr, prop, dim);
tuple = PyTuple_New(len);
for (i = 0; i < len; i++) {
PyObject *item;
if (dim + 1 < totdim) {
item = pyrna_py_from_array_internal(ptr, prop, dim + 1, index);
}
else {
item = pyrna_array_index(ptr, prop, *index);
*index = *index + 1;
}
if (!item) {
Py_DECREF(tuple);
return NULL;
}
PyTuple_SET_ITEM(tuple, i, item);
}
return tuple;
}
#endif
PyObject *pyrna_py_from_array_index(BPy_PropertyArrayRNA *self,
PointerRNA *ptr,
PropertyRNA *prop,
int index)
{
int totdim, arraydim, arrayoffset, dimsize[MAX_ARRAY_DIMENSION], i, len;
BPy_PropertyArrayRNA *ret = NULL;
arraydim = self ? self->arraydim : 0;
arrayoffset = self ? self->arrayoffset : 0;
/* just in case check */
len = RNA_property_multi_array_length(ptr, prop, arraydim);
if (index >= len || index < 0) {
/* This shouldn't happen because higher level functions must check for invalid index. */
CLOG_WARN(BPY_LOG_RNA, "invalid index %d for array with length=%d", index, len);
PyErr_SetString(PyExc_IndexError, "out of range");
return NULL;
}
totdim = RNA_property_array_dimension(ptr, prop, dimsize);
if (arraydim + 1 < totdim) {
ret = (BPy_PropertyArrayRNA *)pyrna_prop_CreatePyObject(ptr, prop);
ret->arraydim = arraydim + 1;
/* arr[3][4][5]
*
* x = arr[2]
* index = 0 + 2 * 4 * 5
*
* x = arr[2][3]
* index = offset + 3 * 5 */
for (i = arraydim + 1; i < totdim; i++) {
index *= dimsize[i];
}
ret->arrayoffset = arrayoffset + index;
}
else {
index = arrayoffset + index;
ret = (BPy_PropertyArrayRNA *)pyrna_array_index(ptr, prop, index);
}
return (PyObject *)ret;
}
PyObject *pyrna_py_from_array(PointerRNA *ptr, PropertyRNA *prop)
{
PyObject *ret;
ret = pyrna_math_object_from_array(ptr, prop);
/* is this a maths object? */
if (ret) {
return ret;
}
return pyrna_prop_CreatePyObject(ptr, prop);
}
int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value)
{
/* TODO: multi-dimensional arrays. */
const int len = RNA_property_array_length(ptr, prop);
int type;
int i;
if (len == 0) {
/* possible with dynamic arrays */
return 0;
}
if (RNA_property_array_dimension(ptr, prop, NULL) > 1) {
PyErr_SetString(PyExc_TypeError, "PropertyRNA - multi dimensional arrays not supported yet");
return -1;
}
type = RNA_property_type(prop);
switch (type) {
case PROP_FLOAT: {
const float value_f = PyFloat_AsDouble(value);
if (value_f == -1 && PyErr_Occurred()) {
PyErr_Clear();
return 0;
}
float tmp[32];
float *tmp_arr;
if (len * sizeof(float) > sizeof(tmp)) {
tmp_arr = PyMem_MALLOC(len * sizeof(float));
}
else {
tmp_arr = tmp;
}
RNA_property_float_get_array(ptr, prop, tmp_arr);
for (i = 0; i < len; i++) {
if (tmp_arr[i] == value_f) {
break;
}
}
if (tmp_arr != tmp) {
PyMem_FREE(tmp_arr);
}
return i < len ? 1 : 0;
break;
}
case PROP_INT: {
const int value_i = PyC_Long_AsI32(value);
if (value_i == -1 && PyErr_Occurred()) {
PyErr_Clear();
return 0;
}
int tmp[32];
int *tmp_arr;
if (len * sizeof(int) > sizeof(tmp)) {
tmp_arr = PyMem_MALLOC(len * sizeof(int));
}
else {
tmp_arr = tmp;
}
RNA_property_int_get_array(ptr, prop, tmp_arr);
for (i = 0; i < len; i++) {
if (tmp_arr[i] == value_i) {
break;
}
}
if (tmp_arr != tmp) {
PyMem_FREE(tmp_arr);
}
return i < len ? 1 : 0;
break;
}
case PROP_BOOLEAN: {
const int value_i = PyC_Long_AsBool(value);
if (value_i == -1 && PyErr_Occurred()) {
PyErr_Clear();
return 0;
}
bool tmp[32];
bool *tmp_arr;
if (len * sizeof(bool) > sizeof(tmp)) {
tmp_arr = PyMem_MALLOC(len * sizeof(bool));
}
else {
tmp_arr = tmp;
}
RNA_property_boolean_get_array(ptr, prop, tmp_arr);
for (i = 0; i < len; i++) {
if (tmp_arr[i] == value_i) {
break;
}
}
if (tmp_arr != tmp) {
PyMem_FREE(tmp_arr);
}
return i < len ? 1 : 0;
break;
}
}
/* should never reach this */
PyErr_SetString(PyExc_TypeError, "PropertyRNA - type not in float/bool/int");
return -1;
}