text: /*
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version 2
   * of the License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
   */
  
  /** \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,
                                   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) {
      if (RNA_property_array_length(ptr, prop) != tot) {
  #if 0
        /* length is flexible */
        if (!RNA_property_dynamic_array_set_length(ptr, prop, tot)) {
          /* BLI_snprintf(error_str, error_str_size,
           *              "%s.%s: array length cannot be changed to %d",
           *              RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), tot); */
          PyErr_Format(PyExc_ValueError,
                       "%s %s.%s: array length cannot be changed to %d",
                       error_prefix,
                       RNA_struct_identifier(ptr->type),
                       RNA_property_identifier(prop),
                       tot);
          return -1;
        }
  #else
        *r_totitem = tot;
        return 0;
  
  #endif
      }
  
      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,
                            int lvalue_dim,
                            ItemTypeCheckFunc check_item_type,
                            const char *item_type_str,
                            int *r_totitem,
                            const char *error_prefix)
  {
    int dimsize[MAX_ARRAY_DIMENSION];
    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->num_col != dimsize[0] || pymat->num_row != 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->num_col,
                       pymat->num_row,
                       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, 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)
  {
    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;
        size_t allocsize = pymat->num_col * pymat->num_row * 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*/
  
    if (validate_array(seq, ptr, prop, 0, check_item_type, item_type_str, &totitem, error_prefix) ==
        -1) {
      return -1;
    }
  
    if (totitem) {
      /* note: this code is confusing */
      if (param_data && RNA_property_flag(prop) & PROP_DYNAMIC) {
        /* 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 {
      if (validate_array(
              py, ptr, prop, 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 funcs 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);
  }
  
  /* TODO, multi-dimensional arrays */
  int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value)
  {
    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: {
        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: {
        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: {
        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;
  }
  