/** * \note group can be a pointer array or a group. * assume we already checked key is a string. * * \return success. */ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyObject *ob) { IDProperty *prop = NULL; IDPropertyTemplate val = {0}; const char *name; if (name_obj) { Py_ssize_t name_size; name = _PyUnicode_AsStringAndSize(name_obj, &name_size); if (name == NULL) { PyErr_Format(PyExc_KeyError, "invalid id-property key, expected a string, not a %.200s", Py_TYPE(name_obj)->tp_name); return false; } if (name_size > MAX_IDPROP_NAME) { PyErr_SetString(PyExc_KeyError, "the length of IDProperty names is limited to 63 characters"); return false; } } else { name = ""; } if (PyFloat_Check(ob)) { val.d = PyFloat_AsDouble(ob); prop = IDP_New(IDP_DOUBLE, &val, name); } else if (PyLong_Check(ob)) { val.i = _PyLong_AsInt(ob); if (val.i == -1 && PyErr_Occurred()) { return false; } prop = IDP_New(IDP_INT, &val, name); } else if (PyUnicode_Check(ob)) { #ifdef USE_STRING_COERCE PyObject *value_coerce = NULL; val.string.str = (char *)PyC_UnicodeAsByte(ob, &value_coerce); val.string.subtype = IDP_STRING_SUB_UTF8; prop = IDP_New(IDP_STRING, &val, name); Py_XDECREF(value_coerce); #else val.str = _PyUnicode_AsString(ob); prop = IDP_New(IDP_STRING, val, name); #endif } else if (PyBytes_Check(ob)) { val.string.str = PyBytes_AS_STRING(ob); val.string.len = PyBytes_GET_SIZE(ob); val.string.subtype = IDP_STRING_SUB_BYTE; prop = IDP_New(IDP_STRING, &val, name); //prop = IDP_NewString(PyBytes_AS_STRING(ob), name, PyBytes_GET_SIZE(ob)); //prop->subtype = IDP_STRING_SUB_BYTE; } else if (PySequence_Check(ob)) { PyObject *ob_seq_fast = PySequence_Fast(ob, "py -> idprop"); PyObject *item; int i; if (ob_seq_fast == NULL) { return false; } if ((val.array.type = idp_sequence_type(ob_seq_fast)) == -1) { Py_DECREF(ob_seq_fast); PyErr_SetString(PyExc_TypeError, "only floats, ints and dicts are allowed in ID property arrays"); return false; } /* validate sequence and derive type. * we assume IDP_INT unless we hit a float * number; then we assume it's */ val.array.len = PySequence_Fast_GET_SIZE(ob_seq_fast); switch (val.array.type) { case IDP_DOUBLE: { double *prop_data; prop = IDP_New(IDP_ARRAY, &val, name); prop_data = IDP_Array(prop); for (i = 0; i < val.array.len; i++) { item = PySequence_Fast_GET_ITEM(ob_seq_fast, i); if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) { Py_DECREF(ob_seq_fast); return false; } } break; } case IDP_INT: { int *prop_data; prop = IDP_New(IDP_ARRAY, &val, name); prop_data = IDP_Array(prop); for (i = 0; i < val.array.len; i++) { item = PySequence_Fast_GET_ITEM(ob_seq_fast, i); if (((prop_data[i] = _PyLong_AsInt(item)) == -1) && PyErr_Occurred()) { Py_DECREF(ob_seq_fast); return false; } } break; } case IDP_IDPARRAY: { prop = IDP_NewIDPArray(name); for (i = 0; i < val.array.len; i++) { item = PySequence_Fast_GET_ITEM(ob_seq_fast, i); if (BPy_IDProperty_Map_ValidateAndCreate(NULL, prop, item) == false) { Py_DECREF(ob_seq_fast); return false; } } break; } default: /* should never happen */ Py_DECREF(ob_seq_fast); PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type"); return false; } Py_DECREF(ob_seq_fast); } else if (PyMapping_Check(ob)) { PyObject *keys, *vals, *key, *pval; int i, len; /*yay! we get into recursive stuff now!*/ keys = PyMapping_Keys(ob); vals = PyMapping_Values(ob); /* we allocate the group first; if we hit any invalid data, * we can delete it easily enough.*/ prop = IDP_New(IDP_GROUP, &val, name); len = PyMapping_Length(ob); for (i = 0; i < len; i++) { key = PySequence_GetItem(keys, i); pval = PySequence_GetItem(vals, i); if (BPy_IDProperty_Map_ValidateAndCreate(key, prop, pval) == false) { IDP_FreeProperty(prop); MEM_freeN(prop); Py_XDECREF(keys); Py_XDECREF(vals); Py_XDECREF(key); Py_XDECREF(pval); /* error is already set */ return false; } Py_XDECREF(key); Py_XDECREF(pval); } Py_XDECREF(keys); Py_XDECREF(vals); } else { PyErr_Format(PyExc_TypeError, "invalid id-property type %.200s not supported", Py_TYPE(ob)->tp_name); return false; } if (group->type == IDP_IDPARRAY) { IDP_AppendArray(group, prop); // IDP_FreeProperty(item); /* IDP_AppendArray does a shallow copy (memcpy), only free memory */ MEM_freeN(prop); } else { IDP_ReplaceInGroup(group, prop); } return true; }
/* note: group can be a pointer array or a group */ const char *BPy_IDProperty_Map_ValidateAndCreate(const char *name, IDProperty *group, PyObject *ob) { IDProperty *prop = NULL; IDPropertyTemplate val = {0}; if(strlen(name) >= sizeof(group->name)) return "the length of IDProperty names is limited to 31 characters"; if (PyFloat_Check(ob)) { val.d = PyFloat_AsDouble(ob); prop = IDP_New(IDP_DOUBLE, val, name); } else if (PyLong_Check(ob)) { val.i = (int) PyLong_AsSsize_t(ob); prop = IDP_New(IDP_INT, val, name); } else if (PyUnicode_Check(ob)) { #ifdef USE_STRING_COERCE PyObject *value_coerce= NULL; val.str = (char *)PyC_UnicodeAsByte(ob, &value_coerce); prop = IDP_New(IDP_STRING, val, name); Py_XDECREF(value_coerce); #else val.str = _PyUnicode_AsString(ob); prop = IDP_New(IDP_STRING, val, name); #endif } else if (PySequence_Check(ob)) { PyObject *item; int i; if((val.array.type= idp_sequence_type(ob)) == -1) return "only floats, ints and dicts are allowed in ID property arrays"; /*validate sequence and derive type. we assume IDP_INT unless we hit a float number; then we assume it's */ val.array.len = PySequence_Size(ob); switch(val.array.type) { case IDP_DOUBLE: prop = IDP_New(IDP_ARRAY, val, name); for (i=0; i<val.array.len; i++) { item = PySequence_GetItem(ob, i); ((double*)prop->data.pointer)[i] = (float)PyFloat_AsDouble(item); Py_DECREF(item); } break; case IDP_INT: prop = IDP_New(IDP_ARRAY, val, name); for (i=0; i<val.array.len; i++) { item = PySequence_GetItem(ob, i); ((int*)prop->data.pointer)[i] = (int)PyLong_AsSsize_t(item); Py_DECREF(item); } break; case IDP_IDPARRAY: prop= IDP_NewIDPArray(name); for (i=0; i<val.array.len; i++) { const char *error; item = PySequence_GetItem(ob, i); error= BPy_IDProperty_Map_ValidateAndCreate("", prop, item); Py_DECREF(item); if(error) return error; } break; } } else if (PyMapping_Check(ob)) { PyObject *keys, *vals, *key, *pval; int i, len; /*yay! we get into recursive stuff now!*/ keys = PyMapping_Keys(ob); vals = PyMapping_Values(ob); /*we allocate the group first; if we hit any invalid data, we can delete it easily enough.*/ prop = IDP_New(IDP_GROUP, val, name); len = PyMapping_Length(ob); for (i=0; i<len; i++) { key = PySequence_GetItem(keys, i); pval = PySequence_GetItem(vals, i); if (!PyUnicode_Check(key)) { IDP_FreeProperty(prop); MEM_freeN(prop); Py_XDECREF(keys); Py_XDECREF(vals); Py_XDECREF(key); Py_XDECREF(pval); return "invalid element in subgroup dict template!"; } if (BPy_IDProperty_Map_ValidateAndCreate(_PyUnicode_AsString(key), prop, pval)) { IDP_FreeProperty(prop); MEM_freeN(prop); Py_XDECREF(keys); Py_XDECREF(vals); Py_XDECREF(key); Py_XDECREF(pval); return "invalid element in subgroup dict template!"; } Py_XDECREF(key); Py_XDECREF(pval); } Py_XDECREF(keys); Py_XDECREF(vals); } else return "invalid property value"; if(group->type==IDP_IDPARRAY) { IDP_AppendArray(group, prop); // IDP_FreeProperty(item); // IDP_AppendArray does a shallow copy (memcpy), only free memory MEM_freeN(prop); } else { IDP_ReplaceInGroup(group, prop); } return NULL; }