[Bf-blender-cvs] [73a9ff0d2d4] master: PyAPI: Fast buffer access to id property arrays

Jacques Lucke noreply at git.blender.org
Mon Apr 10 13:12:14 CEST 2017


Commit: 73a9ff0d2d4f3cabcaa64f8e47586a793daa77f0
Author: Jacques Lucke
Date:   Mon Apr 10 21:06:00 2017 +1000
Branches: master
https://developer.blender.org/rB73a9ff0d2d4f3cabcaa64f8e47586a793daa77f0

PyAPI: Fast buffer access to id property arrays

Support Python's buffer protocol for ID-properties.

===================================================================

M	source/blender/blenkernel/intern/idprop.c
M	source/blender/python/generic/idprop_py_api.c
M	tests/python/bl_pyapi_idprop.py

===================================================================

diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c
index 0f1716dcaf7..5e4e8eb34ad 100644
--- a/source/blender/blenkernel/intern/idprop.c
+++ b/source/blender/blenkernel/intern/idprop.c
@@ -890,7 +890,7 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *
 			*(float *)&prop->data.val = val->f;
 			break;
 		case IDP_DOUBLE:
-			prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
+			prop = MEM_callocN(sizeof(IDProperty), "IDProperty double");
 			*(double *)&prop->data.val = val->d;
 			break;
 		case IDP_ARRAY:
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index 2e15b7b1413..0a9931f2683 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -335,19 +335,9 @@ static char idp_sequence_type(PyObject *seq_fast)
 	return type;
 }
 
-/**
- * \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)
+static const char *idp_try_read_name(PyObject *name_obj)
 {
-	IDProperty *prop = NULL;
-	IDPropertyTemplate val = {0};
-
-	const char *name;
-
+	const char *name = NULL;
 	if (name_obj) {
 		Py_ssize_t name_size;
 		name = _PyUnicode_AsStringAndSize(name_obj, &name_size);
@@ -356,168 +346,297 @@ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group,
 			PyErr_Format(PyExc_KeyError,
 			             "invalid id-property key, expected a string, not a %.200s",
 			             Py_TYPE(name_obj)->tp_name);
-			return false;
+			return NULL;
 		}
 
 		if (name_size > MAX_IDPROP_NAME) {
 			PyErr_SetString(PyExc_KeyError, "the length of IDProperty names is limited to 63 characters");
-			return false;
+			return NULL;
 		}
 	}
 	else {
 		name = "";
 	}
+	return 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);
+/* -------------------------------------------------------------------------- */
+
+/**
+ * The 'idp_from_Py*' functions expect that the input type has been checked before
+ * and return NULL if the IDProperty can't be created.
+ */
+
+static IDProperty *idp_from_PyFloat(const char *name, PyObject *ob)
+{
+	IDPropertyTemplate val = {0};
+	val.d = PyFloat_AsDouble(ob);
+	return IDP_New(IDP_DOUBLE, &val, name);
+}
+
+static IDProperty *idp_from_PyLong(const char *name, PyObject *ob)
+{
+	IDPropertyTemplate val = {0};
+	val.i = _PyLong_AsInt(ob);
+	if (val.i == -1 && PyErr_Occurred()) {
+		return NULL;
 	}
-	else if (PyUnicode_Check(ob)) {
+	return IDP_New(IDP_INT, &val, name);
+}
+
+static IDProperty *idp_from_PyUnicode(const char *name, PyObject *ob)
+{
+	IDProperty *prop;
+	IDPropertyTemplate val = {0};
 #ifdef USE_STRING_COERCE
-		Py_ssize_t value_size;
-		PyObject *value_coerce = NULL;
-		val.string.str = PyC_UnicodeAsByteAndSize(ob, &value_size, &value_coerce);
-		val.string.len = (int)value_size + 1;
-		val.string.subtype = IDP_STRING_SUB_UTF8;
-		prop = IDP_New(IDP_STRING, &val, name);
-		Py_XDECREF(value_coerce);
+	Py_ssize_t value_size;
+	PyObject *value_coerce = NULL;
+	val.string.str = PyC_UnicodeAsByteAndSize(ob, &value_size, &value_coerce);
+	val.string.len = (int)value_size + 1;
+	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);
+	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;
+	return prop;
+}
 
-		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;
+static IDProperty *idp_from_PyBytes(const char *name, PyObject *ob)
+{
+	IDPropertyTemplate val = {0};
+	val.string.str = PyBytes_AS_STRING(ob);
+	val.string.len = PyBytes_GET_SIZE(ob);
+	val.string.subtype = IDP_STRING_SUB_BYTE;
+	return IDP_New(IDP_STRING, &val, name);
+}
+
+static int idp_array_type_from_format_char(char format)
+{
+	if (format == 'i') return IDP_INT;
+	if (format == 'f') return IDP_FLOAT;
+	if (format == 'd') return IDP_DOUBLE;
+	return -1;
+}
+
+static const char *idp_format_from_array_type(int type)
+{
+	if (type == IDP_INT) return "i";
+	if (type == IDP_FLOAT) return "f";
+	if (type == IDP_DOUBLE) return "d";
+	return NULL;
+}
+
+static IDProperty *idp_from_PySequence_Buffer(const char *name, Py_buffer *buffer)
+{
+	IDProperty *prop;
+	IDPropertyTemplate val = {0};
+
+	int format = idp_array_type_from_format_char(*buffer->format);
+	if (format == -1) {
+		/* should never happen as the type has been checked before */
+		return NULL;
 	}
-	else if (PySequence_Check(ob)) {
-		PyObject *ob_seq_fast;
-		PyObject **ob_seq_fast_items;
-		PyObject *item;
-		int i;
+	else {
+		val.array.type = format;
+		val.array.len = buffer->len / buffer->itemsize;
+	}
+	prop = IDP_New(IDP_ARRAY, &val, name);
+	memcpy(IDP_Array(prop), buffer->buf, buffer->len);
+	return prop;
+}
 
-		if (!(ob_seq_fast = PySequence_Fast(ob, "py -> idprop"))) {
-			return false;
-		}
+static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob)
+{
+	IDProperty *prop;
+	IDPropertyTemplate val = {0};
 
-		ob_seq_fast_items = PySequence_Fast_ITEMS(ob_seq_fast);
+	PyObject **ob_seq_fast_items;
+	PyObject *item;
+	int i;
 
-		if ((val.array.type = idp_sequence_type(ob_seq_fast)) == (char)-1) {
-			Py_DECREF(ob_seq_fast);
-			PyErr_SetString(PyExc_TypeError, "only floats, ints and dicts are allowed in ID property arrays");
-			return false;
-		}
+	ob_seq_fast_items = PySequence_Fast_ITEMS(ob);
 
-		/* validate sequence and derive type.
-		 * we assume IDP_INT unless we hit a float
-		 * number; then we assume it's */
+	if ((val.array.type = idp_sequence_type(ob)) == (char)-1) {
+		PyErr_SetString(PyExc_TypeError, "only floats, ints and dicts are allowed in ID property arrays");
+		return NULL;
+	}
 
-		val.array.len = PySequence_Fast_GET_SIZE(ob_seq_fast);
+	/* validate sequence and derive type.
+	 * we assume IDP_INT unless we hit a float
+	 * number; then we assume it's */
 
-		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 = ob_seq_fast_items[i];
-					if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) {
-						Py_DECREF(ob_seq_fast);
-						return false;
-					}
+	val.array.len = PySequence_Fast_GET_SIZE(ob);
+
+	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 = ob_seq_fast_items[i];
+				if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) {
+					return NULL;
 				}
-				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 = ob_seq_fast_items[i];
-					if (((prop_data[i] = _PyLong_AsInt(item)) == -1) && 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 = ob_seq_fast_items[i];
+				if (((prop_data[i] = _PyLong_AsInt(item)) == -1) && PyErr_Occurred()) {
+					return NULL;
 				}
-				break;
 			}
-			case IDP_IDPARRAY:
-			{
-				prop = IDP_NewIDPArray(name);
-				for (i = 0; i < val.array.len; i++) {
-					item = ob_seq_fast_items[i];
-
-					if (BPy_IDProperty_Map_ValidateAndCreate(NULL, prop, item) == false) {
-						Py_DECREF(ob_seq_fast);
-						return false;
-					}
+			break;
+		}
+		case IDP_IDPARRAY:
+		{
+			prop = IDP_NewIDPArray(name);
+			for (i = 0; i < val.array.len; i++) {
+				item = ob_seq_fast_items[i];
+				if (BPy_IDProperty_Map_ValidateAndCreate(NULL, prop, item) == false) {
+					return NULL;
 				}
-				break;
 			}
-			default:
-				/* should never happen */
-				Py_DECREF(ob_seq_fast);
-				PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type");
-				return false;
+			break;
+		}
+		default:
+			/* should never happen */
+			PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type");
+			return NULL;
+	}
+	return prop;
+}
+
+
+static IDProperty *idp_from_PySequence(const char *name, PyObject *ob)
+{
+	Py_buffer buffer;
+	bool use_buffer = false;
+
+	if (PyObject_CheckBuffer(ob)) {
+		PyObject_GetBuffer(ob, &buffer, PyBUF_SIMPLE | PyBUF_FORMAT);
+		char format = *buffer.format;
+		if (ELEM(format, 'i', 'f', 'd')) {
+			use_buffer = true;
 		}
+		else {
+			PyBuffer_Release(&buffer);
+		}
+	}
 
-		Py_DECREF(ob_seq_fast);
+	if (use_buffer) {
+		IDProperty *prop = idp_from_PySequence_Buffer(name, &buffer);
+		PyBuffer_Release(&buffer);
+		return prop;
 	}
-	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 */

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list