[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [37114] trunk/blender/source/blender/ python/intern/bpy_props.c: when making the C/RNA copy of the python enum, duplicate all strings since theres no guarantee python wont free them immediately after , though in practice this isn't so common.

Campbell Barton ideasman42 at gmail.com
Fri Jun 3 06:21:42 CEST 2011


Revision: 37114
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=37114
Author:   campbellbarton
Date:     2011-06-03 04:21:41 +0000 (Fri, 03 Jun 2011)
Log Message:
-----------
when making the C/RNA copy of the python enum, duplicate all strings since theres no guarantee python wont free them immediately after, though in practice this isn't so common.

Modified Paths:
--------------
    trunk/blender/source/blender/python/intern/bpy_props.c

Modified: trunk/blender/source/blender/python/intern/bpy_props.c
===================================================================
--- trunk/blender/source/blender/python/intern/bpy_props.c	2011-06-03 03:19:22 UTC (rev 37113)
+++ trunk/blender/source/blender/python/intern/bpy_props.c	2011-06-03 04:21:41 UTC (rev 37114)
@@ -642,18 +642,27 @@
 	Py_RETURN_NONE;
 }
 
+/* copies orig to buf, then sets orig to buf, returns copy length */
+static size_t strswapbufcpy(char *buf, const char **orig)
+{
+	const char *src= *orig;
+	char *dst= buf;
+	size_t i= 0;
+	*orig= buf;
+	while((*dst= *src)) { dst++; src++; i++; }
+	return i + 1; /* include '\0' */
+}
+
 static EnumPropertyItem *enum_items_from_py(PyObject *seq_fast, PyObject *def, int *defvalue, const short is_enum_flag)
 {
-#	define PREALLOC_ENUM
-
 	EnumPropertyItem *items;
 	PyObject *item;
-	int seq_len, i, totitem= 0;
+	const Py_ssize_t seq_len= PySequence_Fast_GET_SIZE(seq_fast);
+	Py_ssize_t totbuf= 0;
+	int i;
 	short def_used= 0;
 	const char *def_cmp= NULL;
 
-	seq_len= PySequence_Fast_GET_SIZE(seq_fast);
-
 	if(is_enum_flag) {
 		if(seq_len > RNA_ENUM_BITFLAG_SIZE) {
 			PyErr_SetString(PyExc_TypeError, "EnumProperty(...): maximum " STRINGIFY(RNA_ENUM_BITFLAG_SIZE) " members for a ENUM_FLAG type property");
@@ -683,55 +692,52 @@
 	/* blank value */
 	*defvalue= 0;
 
-#ifdef PREALLOC_ENUM
-	items= MEM_callocN(sizeof(EnumPropertyItem) * (seq_len + 1), "RNA_enum_items_reserve");
-#endif
+	items= MEM_callocN(sizeof(EnumPropertyItem) * (seq_len + 1), "enum_items_from_py1");
 
 	for(i=0; i<seq_len; i++) {
 		EnumPropertyItem tmp= {0, "", 0, "", ""};
+		Py_ssize_t id_str_size;
+		Py_ssize_t name_str_size;
+		Py_ssize_t desc_str_size;
 
 		item= PySequence_Fast_GET_ITEM(seq_fast, i);
 
-		/* this also checks for a tuple */
-		if(!PyArg_ParseTuple(item, "sss", &tmp.identifier, &tmp.name, &tmp.description)) {
-#ifdef PREALLOC_ENUM
-			MEM_freeN(items);
-#else
-			if(items) MEM_freeN(items);
-#endif
-			PyErr_SetString(PyExc_TypeError, "EnumProperty(...): expected an tuple containing (identifier, name description)");
-			return NULL;
-		}
+		if(		(PyTuple_CheckExact(item)) &&
+		        (PyTuple_GET_SIZE(item) == 3) &&
+		        (tmp.identifier=  _PyUnicode_AsStringAndSize(PyTuple_GET_ITEM(item, 0), &id_str_size)) &&
+		        (tmp.name=        _PyUnicode_AsStringAndSize(PyTuple_GET_ITEM(item, 1), &name_str_size)) &&
+		        (tmp.description= _PyUnicode_AsStringAndSize(PyTuple_GET_ITEM(item, 2), &desc_str_size))
+		) {
+			if(is_enum_flag) {
+				tmp.value= 1<<i;
 
-		if(is_enum_flag) {
-			tmp.value= 1<<i;
+				if(def && PySet_Contains(def, PyTuple_GET_ITEM(item, 0))) {
+					*defvalue |= tmp.value;
+					def_used++;
+				}
+			}
+			else {
+				tmp.value= i;
 
-			if(def && PySet_Contains(def, PyTuple_GET_ITEM(item, 0))) {
-				*defvalue |= tmp.value;
-				def_used++;
+				if(def && def_used == 0 && strcmp(def_cmp, tmp.identifier)==0) {
+					*defvalue= tmp.value;
+					def_used++; /* only ever 1 */
+				}
 			}
+
+			items[i]= tmp;
+
+			/* calculate combine string length */
+			totbuf += id_str_size + name_str_size + desc_str_size + 3; /* 3 is for '\0's */
 		}
 		else {
-			tmp.value= i;
+			MEM_freeN(items);
+			PyErr_SetString(PyExc_TypeError, "EnumProperty(...): expected an tuple containing (identifier, name description)");
+			return NULL;
+		}
 
-			if(def && def_used == 0 && strcmp(def_cmp, tmp.identifier)==0) {
-				*defvalue= tmp.value;
-				def_used++; /* only ever 1 */
-			}
-		}
-#ifdef PREALLOC_ENUM
-		items[i]= tmp;
-#else
-		RNA_enum_item_add(&items, &totitem, &tmp);
-#endif
 	}
 
-#ifdef PREALLOC_ENUM
-	/* do nohing */
-#else
-	RNA_enum_item_end(&items, &totitem);
-#endif
-
 	if(is_enum_flag) {
 		/* strict check that all set members were used */
 		if(def && def_used != PySet_GET_SIZE(def)) {
@@ -754,9 +760,25 @@
 		}
 	}
 
+	/* this would all work perfectly _but_ the python strings may be freed
+	 * immediately after use, so we need to duplicate them, ugh.
+	 * annoying because it works most of the time without this. */
+	{
+		EnumPropertyItem *items_dup= MEM_mallocN((sizeof(EnumPropertyItem) * (seq_len + 1)) + (sizeof(char) * totbuf), "enum_items_from_py2");
+		EnumPropertyItem *items_ptr= items_dup;
+		char *buf= ((char *)items_dup) + (sizeof(EnumPropertyItem) * (seq_len + 1));
+		memcpy(items_dup, items, sizeof(EnumPropertyItem) * (seq_len + 1));
+		for(i=0; i<seq_len; i++, items_ptr++) {
+			buf += strswapbufcpy(buf, &items_ptr->identifier);
+			buf += strswapbufcpy(buf, &items_ptr->name);
+			buf += strswapbufcpy(buf, &items_ptr->description);
+		}
+		MEM_freeN(items);
+		items=items_dup;
+	}
+	/* end string duplication */
+
 	return items;
-
-#	undef PREALLOC_ENUM
 }
 
 static EnumPropertyItem *bpy_props_enum_itemf(struct bContext *C, PointerRNA *ptr, PropertyRNA *prop, int *free)




More information about the Bf-blender-cvs mailing list