Commits

Ronald Oussoren committed 38992e7

Add support for struct packing

A number of APIs in the LaunchServices framework use '#pragma pack'
for struct definitions. This checkin adds generic support for
packed structures to pyobjc-core.

The implementaton is suboptimal, but seems to work fine.

  • Participants
  • Parent commits 4c6d300

Comments (0)

Files changed (5)

File pyobjc-core/Modules/objc/module.m

 	char* docstr = NULL;
 
 	if (!PyArg_ParseTupleAndKeywords(args, kwds, 
-				"s"Py_ARG_BYTES"|s", 
+				"s"Py_ARG_BYTES"|z", 
 				keywords, 
 				&name, &typestr, &docstr)) {
 		return NULL;
 
 
 PyDoc_STRVAR(createStructType_doc,
-	"createStructType(name, typestr, fieldnames, doc) -> type\n"
+	"createStructType(name, typestr, fieldnames, doc, pack) -> type\n"
 	"\n"
 	"Return a wrapper type for structs of the given type. The wrapper will \n"
 	"registered with PyObjC and will be used to wrap structs of the given type.\n"
 createStructType(PyObject* self __attribute__((__unused__)),
 		PyObject* args, PyObject* kwds)
 {
-static char* keywords[] = { "name", "typestr", "fieldnames", "doc", NULL };
+static char* keywords[] = { "name", "typestr", "fieldnames", "doc", "pack", NULL };
 	char* name;
 	char* typestr;
 	PyObject* pyfieldnames;
 	char** fieldnames = NULL;
 	Py_ssize_t i;
 	Py_ssize_t field_count;
+	Py_ssize_t pack = -1;
 
 	if (!PyArg_ParseTupleAndKeywords(args, kwds, 
-				"s"Py_ARG_BYTES"O|s", 
+				"s"Py_ARG_BYTES"O|s" Py_ARG_SIZE_T , 
 				keywords, 
-				&name, &typestr, &pyfieldnames, &docstr)) {
+				&name, &typestr, &pyfieldnames, &docstr, &pack)) {
 		return NULL;
 	}
 
 
 
 	retval = PyObjC_RegisterStructType(typestr, name, docstr, NULL,
-			field_count, (const char**)fieldnames);
+			field_count, (const char**)fieldnames, pack);
 	if (retval == NULL) goto error_cleanup;
 	Py_DECREF(pyfieldnames);
 

File pyobjc-core/Modules/objc/objc_support.m

 	const char* type_end = PyObjCRT_SkipTypeSpec(type);
 	const char* type_real_start = type;
 	Py_ssize_t type_real_length = type_end - type_start;
+	Py_ssize_t pack;
 
 	/* Hacked up support for socket addresses */
 	if (strncmp(type, @encode(struct sockaddr), sizeof(@encode(struct sockaddr)-1)) == 0) {
 	haveTuple = 0;
 	const char* oc_typestr = NULL;
 	ret = PyObjC_CreateRegisteredStruct(type_start, 
-			type_end-type_start, &oc_typestr);
+			type_end-type_start, &oc_typestr, &pack);
 	if (ret == NULL) {
 		int nitems;
 
 		} else {
 			align = PyObjC_EmbeddedAlignOfType(item);
 		}
+		if (pack != -1 && pack < align) {
+			align = pack;
+		}
 
 		offset = ROUND(offset, align);
 
 	Py_ssize_t align;
 	const char *type;
 	PyObject* seq;
+	Py_ssize_t pack;
 
 	/* Hacked in support for sockaddr structs */
 	if (strncmp(types, @encode(struct sockaddr), sizeof(@encode(struct sockaddr)-1)) == 0) {
 		return PyObjC_SockAddrFromPython(arg, datum);
 	}
 
+	/* Extract struck packing value, need better way to fetch this */
+	pack = -1;
+	if (!PyList_Check(arg) && !PyTuple_Check(arg)) {
+		seq = PyObject_GetAttrString(arg, "__struct_pack__");
+		if (seq == NULL) {
+			PyErr_Clear();
+		} else {
+			pack = PyNumber_AsSsize_t(seq, NULL);
+			if (PyErr_Occurred()) {
+				return -1;
+			}
+			Py_DECREF(seq);
+		}
+	}
+
+
 	if (IS_FSREF(types)) {
 		if (PyObjC_encode_fsref(arg, datum) == 0) {
 			return 0;
 		} else {
 			align = PyObjC_EmbeddedAlignOfType(type);
 		}
+		if (pack != -1 && pack < align) {
+			align = pack;
+		}
 
 		offset = ROUND(offset, align);
 

File pyobjc-core/Modules/objc/struct-wrapper.h

  * @param tpinit     Optional __init__ method for the type
  * @param numFields  Number of fields in the type
  * @param fieldnames Field names, there should be exactly numFields names.
+ * @param pack       Value of 'pragma pack', use -1 for default packing
  * @result Returns a newly allocated type or NULL
  * @discussion
  *    The name, doc and fieldnames should be pointers to static strings,
 	initproc tpinit,
 	Py_ssize_t numFields,
 	const char** fieldnames,
-	const char* typestr);
+	const char* typestr,
+	Py_ssize_t pack);
 
 
 /*!
  * @param tpinit     Optional __init__ method for the type
  * @param numFields  Number of fields in the type
  * @param fieldnames Field names, there should be exactly numFields names.
+ * @param pack       Value of 'pragma pack', use -1 for default packing
  * @result Returns a newly allocated type or NULL
  * @discussion
  *    This function calls PyObjC_MakeStructType(name, doc, tpinit, numFields, 
 	const char* doc,
 	initproc tpinit,
 	Py_ssize_t numFields,
-	const char** fieldnames);
+	const char** fieldnames,
+	Py_ssize_t pack);
 
 /*!
  * @function PyObjC_CreateRegisteredStruct
  *     The returned instance is uninitialized, all fields are NULL. The 
  *     __init__ method has not been called.
  */     
-PyObject* PyObjC_CreateRegisteredStruct(const char* signature, Py_ssize_t len, const char** objc_signature);
+PyObject* PyObjC_CreateRegisteredStruct(const char* signature, Py_ssize_t len, const char** objc_signature, Py_ssize_t* pack);
 
 int PyObjC_RegisterStructAlias(const char* signature, PyObject* type);
 

File pyobjc-core/Modules/objc/struct-wrapper.m

 			break;
 
 		case _C_STRUCT_B:
-			v = PyObjC_CreateRegisteredStruct(typestr, next-typestr, NULL);
+			v = PyObjC_CreateRegisteredStruct(typestr, next-typestr, NULL, NULL);
 			if (v != NULL) {
 				/* call init */
 				r = Py_TYPE(v)->tp_init(v, NULL, NULL);
 	return cur;
 }
 
+struct StructTypeObject {
+	PyTypeObject     base;
+	Py_ssize_t       pack; 		/* struct packing, -1 for default packing */
+};
 
 /*
  * A template for the type object
  */
-static PyTypeObject StructTemplate_Type = {
+static struct StructTypeObject StructTemplate_Type = {
+    {
 	PyVarObject_HEAD_INIT(NULL, 0)
 	"objc.StructTemplate",			/* tp_name */
 	sizeof (PyObject*),			/* tp_basicsize */
 #if PY_VERSION_HEX >= 0x02060000
 	, 0                                     /* tp_version_tag */
 #endif
-
+    },
+    -1
 };
 
 PyObject* 
 		initproc tpinit,
 		Py_ssize_t numFields,
 		const char** fieldnames,
-		const char* typestr)
+		const char* typestr,
+		Py_ssize_t pack)
 {
-	PyTypeObject* result;
+	struct StructTypeObject* result;
 	PyMemberDef* members;
 	Py_ssize_t i;
 
 	}
 	members[numFields].name = NULL;
 
-	result = PyMem_Malloc(sizeof(PyTypeObject));
+	result = PyMem_Malloc(sizeof(struct StructTypeObject));
 	if (result == NULL) {
 		PyMem_Free(members);
 		PyErr_NoMemory();
 	}
 
 	*result = StructTemplate_Type;
-	result->tp_name = (char*)name;
-	result->tp_doc = (char*)doc;
-	result->tp_dict = PyDict_New();
-	if (result->tp_dict == NULL) {
+	result->base.tp_name = (char*)name;
+	result->base.tp_doc = (char*)doc;
+	result->base.tp_dict = PyDict_New();
+	if (result->base.tp_dict == NULL) {
 		PyMem_Free(members);
 		PyMem_Free(result);
 		return NULL;
 	}
 	Py_REFCNT(result) = 1;
-	result->tp_members = members;
-	result->tp_basicsize = sizeof(PyObject) + numFields*sizeof(PyObject*);
+	result->base.tp_members = members;
+	result->base.tp_basicsize = sizeof(PyObject) + numFields*sizeof(PyObject*);
 	if (tpinit) {
-		result->tp_init = tpinit;
+		result->base.tp_init = tpinit;
 	} else {
-		result->tp_init = make_init(typestr);
-		if (result->tp_init == NULL) {
+		result->base.tp_init = make_init(typestr);
+		if (result->base.tp_init == NULL) {
 			PyMem_Free(members);
 			PyMem_Free(result);
 			return NULL;
 		}
 	}
 
-	if (PyType_Ready(result) == -1) {
+	result->pack = pack;
+
+	if (PyType_Ready((PyTypeObject*)result) == -1) {
 		/* Is freeing save? */
 		PyMem_Free(result);
 		PyMem_Free(members);
 static PyObject* structRegistry = NULL;
 
 PyObject* 
-PyObjC_CreateRegisteredStruct(const char* signature, Py_ssize_t len, const char** objc_encoding)
+PyObjC_CreateRegisteredStruct(const char* signature, Py_ssize_t len, const char** objc_encoding, Py_ssize_t* ppack)
 {
 	PyTypeObject* type;
 	PyObject* result;
 
 	if (structRegistry == NULL) return NULL;
 
+	if (ppack != NULL) {
+		*ppack = -1;
+	}
+
 	v = PyText_FromStringAndSize(signature, len);
 	type = (PyTypeObject*)PyDict_GetItem(structRegistry, v);
 	Py_DECREF(v);
 			*objc_encoding = signature;
 		}
 	}
+	if (ppack != NULL) {
+		*ppack = ((struct StructTypeObject*)type)->pack;
+	}
 	return result;
 }
 
 		const char* doc,
 		initproc tpinit,
 		Py_ssize_t numFields,
-		const char** fieldnames)
+		const char** fieldnames,
+		Py_ssize_t pack)
 {
 	PyObject* structType;
 	PyObject* v;
 
 
 	structType = PyObjC_MakeStructType(name, doc, tpinit, 
-				numFields, fieldnames, signature);
+				numFields, fieldnames, signature, pack);
 	if (structType == NULL) {
 		if (freeNames) {
 			int i;
 		return NULL;
 	}
 
+	if (pack != -1) {
+		/* Store custom struct packing as an attribute of the type
+		 * object, to be able to  fetch it when depythonifying the object.
+		 *
+		 * XXX: Need a cleaner method for doing this.
+		 */
+		v = Py_BuildValue(Py_ARG_SIZE_T, pack);
+		if (v == NULL) {
+			Py_DECREF(structType);
+			return NULL;
+		}
+		r = PyDict_SetItemString(((PyTypeObject*)structType)->tp_dict, "__struct_pack__", v);
+		Py_DECREF(v);
+		if (v == NULL) {
+			Py_DECREF(structType);
+			return NULL;
+		}
+	}
+
 	if (structRegistry == NULL) {
 		structRegistry = PyDict_New();
 		if (structRegistry == NULL) {

File pyobjc-core/NEWS.txt

 Version 2.4  (or 3.0)
 ---------------------
 
+- There is now limited support for packed struct definitions. This 
+  requires that the struct is wrapped using ``objc.createStructType``.
+
+  Struct packing is not described in the encoding string for a 
+  structure, which is why special support is needed.
+
 - objc.registerStructAlias now returns the alias type instead of ``None``
 
 - In Python 3.x there is a new way to explicitly specify which (informal)