Commits

Ronald Oussoren  committed d848f49

This adds a number of functions to the pyobjc-api that make it easier to
convert variable-length arrays to and from ObjC. This API is used in the
implementation of NSRectFill.

The implementation of PyObjC_PythonToCArray contains special support for
array.array, and numarray/Numeric support should be added in the future.

TODO:
- Use the new API whenever an array + length is passed
- Add documentation that describes the user-visible behaviour of the new code.

  • Participants
  • Parent commits 838f9c7

Comments (0)

Files changed (8)

File pyobjc/Lib/objc/test/test_conversion.py

 TODO: This only tests C values at the moment.
 """
 import unittest
-from objc.test.testbndl import pyObjCPy
+from objc.test.testbndl import pyObjCPy, carrayMaker
 from objc.test.testbndl import UCHAR_MAX
 from objc.test.testbndl import CHAR_MAX, CHAR_MIN
 from objc.test.testbndl import SCHAR_MAX, SCHAR_MIN
 from objc.test.testbndl import DBL_MAX, DBL_MIN, DBL_EPSILON
 from objc.test.testbndl import FLT_MAX, FLT_MIN, FLT_EPSILON
 import objc
+import array
 
 
 class TestNumbers (unittest.TestCase):
         self.assertRaises(ValueError, pyObjCPy, objc._C_USHT, SHRT_MIN)
         self.assertRaises(ValueError, pyObjCPy, objc._C_USHT, SHRT_MIN - 1)
 
+        self.assertRaises(ValueError, pyObjCPy, objc._C_USHT, "1")
+
     def test_short(self):
         self.assertEquals(0, pyObjCPy(objc._C_SHT, 0))
         self.assertEquals(SHRT_MAX, pyObjCPy(objc._C_SHT, SHRT_MAX))
         self.assertRaises(ValueError, pyObjCPy, objc._C_SHT, SHRT_MAX + 1)
         self.assertRaises(ValueError, pyObjCPy, objc._C_SHT, SHRT_MIN - 1)
 
+        self.assertRaises(ValueError, pyObjCPy, objc._C_SHT, "1")
+
     def test_unsigned_int(self):
         self.assertEquals(0, pyObjCPy(objc._C_UINT, 0))
         self.assertEquals(UINT_MAX, pyObjCPy(objc._C_UINT, UINT_MAX))
         self.assertRaises(ValueError, pyObjCPy, objc._C_UINT, INT_MIN)
         self.assertRaises(ValueError, pyObjCPy, objc._C_UINT, INT_MIN - 1)
 
+        self.assertRaises(ValueError, pyObjCPy, objc._C_UINT, "1")
+
     def test_int(self):
         self.assertEquals(0, pyObjCPy(objc._C_INT, 0))
         self.assertEquals(INT_MAX, pyObjCPy(objc._C_INT, INT_MAX))
         self.assertRaises(ValueError, pyObjCPy, objc._C_INT, INT_MAX + 1)
         self.assertRaises(ValueError, pyObjCPy, objc._C_INT, INT_MIN - 1)
 
+        # Check implicit conversion
+        self.assertRaises(ValueError, pyObjCPy, objc._C_INT, "1")
+
     def test_unsigned_long(self):
         self.assertEquals(0, pyObjCPy(objc._C_ULNG, 0))
         self.assertEquals(ULONG_MAX, pyObjCPy(objc._C_ULNG, ULONG_MAX))
         self.assertRaises(ValueError, pyObjCPy, objc._C_ULNG, LONG_MIN)
         self.assertRaises(ValueError, pyObjCPy, objc._C_ULNG, LONG_MIN - 1)
 
+        self.assertRaises(ValueError, pyObjCPy, objc._C_ULNG, "1")
+
     def test_long(self):
         self.assertEquals(0, pyObjCPy(objc._C_LNG, 0))
         self.assertEquals(LONG_MAX, pyObjCPy(objc._C_LNG, LONG_MAX))
         self.assertRaises(ValueError, pyObjCPy, objc._C_LNG, LONG_MAX + 1)
         self.assertRaises(ValueError, pyObjCPy, objc._C_LNG, LONG_MIN - 1)
 
+        self.assertRaises(ValueError, pyObjCPy, objc._C_LNG, "1")
+
     def test_unsigned_long_long(self):
         self.assertEquals(0, pyObjCPy(objc._C_ULNGLNG, 0))
         self.assertEquals(ULLONG_MAX, pyObjCPy(objc._C_ULNGLNG, ULLONG_MAX))
         self.assertRaises(ValueError, pyObjCPy, objc._C_ULNGLNG, LLONG_MIN)
         self.assertRaises(ValueError, pyObjCPy, objc._C_ULNGLNG, LLONG_MIN - 1)
 
+        self.assertRaises(ValueError, pyObjCPy, objc._C_ULNGLNG, "1")
+
     def test_long_long(self):
         self.assertEquals(0, pyObjCPy(objc._C_LNGLNG, 0))
         self.assertEquals(LONG_MAX, pyObjCPy(objc._C_LNGLNG, float(LONG_MAX)))
         self.assertRaises(ValueError, pyObjCPy, objc._C_LNGLNG, LLONG_MAX + 1)
         self.assertRaises(ValueError, pyObjCPy, objc._C_LNGLNG, LLONG_MIN - 1)
 
+        self.assertRaises(ValueError, pyObjCPy, objc._C_LNGLNG, "1")
+
     def test_double(self):
         self.assertEquals(0, pyObjCPy(objc._C_DBL, 0))
         self.assertEquals(float(INT_MAX), pyObjCPy(objc._C_DBL, INT_MAX))
 
         self.assertRaises(ValueError, pyObjCPy, objc._C_DBL, 1L << 10000)
 
+        self.assertRaises(ValueError, pyObjCPy, objc._C_DBL, "1")
+
     def test_float(self):
         self.assertEquals(0, pyObjCPy(objc._C_FLT, 0))
         self.assertEquals(float(SHRT_MAX), pyObjCPy(objc._C_FLT, SHRT_MAX))
 
         self.assertRaises(ValueError, pyObjCPy, objc._C_FLT, 1L << 10000)
 
+        self.assertRaises(ValueError, pyObjCPy, objc._C_FLT, "1")
+
 
 class TestStruct (unittest.TestCase):
     """
         self.assertRaises(ValueError, pyObjCPy, signature, iter(value[:9]))
         self.assertRaises(TypeError, pyObjCPy, signature, None)
 
+class TestCArray (unittest.TestCase):
+    # Tests for the PyObjC_PythonToCArray (C-)function, this function is 
+    # used to build variable-length C Arrays from Python objects.
+
+    # TODO: "{_NSPoint=ff}", "{_NSRect={_NSPoint=ff}{_NSSize=ff}}"
+    #       "[4i]" "[4[4i]]" "[2{foo=ii}]" "{foo=[4i]}"
+    #       "{_Foo=fi}" (fail with array.array) + version with [{foo=if}]
+    #       - other simple types
+    def testShortTuple(self):
+        arr = (1,2,3,4,5)
+
+        res = carrayMaker(objc._C_SHT, arr, None)
+        self.assertEquals(res, arr)
+
+        res = carrayMaker(objc._C_SHT, arr, 2)
+        self.assertEquals(res, arr[:2])
+
+        self.assertRaises(ValueError, carrayMaker, objc._C_SHT, arr, 7)
+        self.assertRaises(ValueError, carrayMaker, objc._C_SHT, ["a", "b"], 1)
+
+    def testShortArray(self):
+        arr = array.array('h', (1,2,3,4,5))
+        arr2 = array.array('f', (1,2,3,4,5))
+
+        res = carrayMaker(objc._C_SHT, arr, None)
+        self.assertEquals(res, tuple(arr))
+
+        res = carrayMaker(objc._C_SHT, arr, 2)
+        self.assertEquals(res, tuple(arr)[:2])
+
+        self.assertRaises(ValueError, carrayMaker, objc._C_SHT, arr, 7)
+        self.assertRaises(ValueError, carrayMaker, objc._C_SHT, arr2, None)
+
+    def testIntTuple(self):
+        arr = (1,2,3,4,5)
+
+        res = carrayMaker(objc._C_INT, arr, None)
+        self.assertEquals(res, arr)
+
+        res = carrayMaker(objc._C_INT, arr, 2)
+        self.assertEquals(res, arr[:2])
+
+        self.assertRaises(ValueError, carrayMaker, objc._C_INT, arr, 7)
+        self.assertRaises(ValueError, carrayMaker, objc._C_INT, ["a", "b"], 1)
+
+    def testIntArray(self):
+        arr = array.array('i', (1,2,3,4,5))
+        arr2 = array.array('f', (1,2,3,4,5))
+        arr3 = array.array('h', (1,2,3,4,5))
+
+        res = carrayMaker(objc._C_INT, arr, None)
+        self.assertEquals(res, tuple(arr))
+
+        res = carrayMaker(objc._C_INT, arr, 2)
+        self.assertEquals(res, tuple(arr)[:2])
+
+        self.assertRaises(ValueError, carrayMaker, objc._C_INT, arr, 7)
+        self.assertRaises(ValueError, carrayMaker, objc._C_INT, arr2, None)
+        self.assertRaises(ValueError, carrayMaker, objc._C_INT, arr3, None)
+
+    def testFloatTuple(self):
+        arr = (1,2,3,4,5)
+
+        res = carrayMaker(objc._C_FLT, arr, None)
+        self.assertEquals(res, arr)
+
+        res = carrayMaker(objc._C_FLT, arr, 2)
+        self.assertEquals(res, arr[:2])
+
+        self.assertRaises(ValueError, carrayMaker, objc._C_INT, arr, 7)
+        self.assertRaises(ValueError, carrayMaker, objc._C_INT, ["a", "b"], 1)
+
+    def testFloatArray(self):
+        arr = array.array('f', (1.5,2.5,3.5,4.5,5.5))
+        arr2 = array.array('i', (1,2,3,4,5))
+
+        res = carrayMaker(objc._C_FLT, arr, None)
+        self.assertEquals(res, tuple(arr))
+
+        res = carrayMaker(objc._C_FLT, arr, 2)
+        self.assertEquals(res, tuple(arr)[:2])
+
+        self.assertRaises(ValueError, carrayMaker, objc._C_FLT, arr, 7)
+        self.assertRaises(ValueError, carrayMaker, objc._C_FLT, arr2, None)
+
+    def testPointTuple(self):
+        arr = ((1.0, 1.5), (2.0, 2.5), (3.0, 3.5), (4.0, 4.5), (5.0, 5.5))
+        arr2 = (1.5,2.5,3.5,4.5,5.5)
+
+        res = carrayMaker('{Point=ff}', arr, None)
+        self.assertEquals(res, arr)
+
+        res = carrayMaker('{Point=ff}', arr, 2)
+        self.assertEquals(res, arr[:2])
+
+        self.assertRaises(ValueError, carrayMaker, '{Point=ff}', arr, 7)
+        self.assertRaises(ValueError, carrayMaker, '{Point=ff}', ["a", "b"], 1)
+        self.assertRaises(TypeError, carrayMaker, '{Point=ff}', arr2, None)
+
+    def testPointArray(self):
+        arr = array.array('f', (
+            1.0, 1.5, 
+            2.0, 2.5, 
+            3.0, 3.5, 
+            4.0, 4.5, 
+            5.0, 5.5))
+        lst = ((1.0, 1.5), (2.0, 2.5), (3.0, 3.5), (4.0, 4.5), (5.0, 5.5))
+
+        arr2 = array.array('i', (
+            1, 1, 
+            2, 2, 
+            3, 3, 
+            4, 4, 
+            5, 5))
+
+        res = carrayMaker('{Point=ff}', arr, None)
+        self.assertEquals(res, lst)
+
+        res = carrayMaker('{Point=ff}', arr, 2)
+        self.assertEquals(res, lst[:2])
+
+        self.assertRaises(ValueError, carrayMaker, '{Point=ff}', arr2, None)
+
+    def testRectArray(self):
+        arr = array.array('f', (
+            1.0, 1.5, -1.0, -1.5,
+            2.0, 2.5, -2.0, -2.5,
+            3.0, 3.5, -3.0, -3.5,
+            4.0, 4.5, -4.0, -4.5,
+            5.0, 5.5, -5.0, -5.5))
+        lst = (
+                ((1.0, 1.5),  (-1.0, -1.5)),
+                ((2.0, 2.5),  (-2.0, -2.5)),
+                ((3.0, 3.5),  (-3.0, -3.5)),
+                ((4.0, 4.5),  (-4.0, -4.5)),
+                ((5.0, 5.5),  (-5.0, -5.5)),
+            )
+
+        arr2 = array.array('i', (
+            1, 1, 1, 1,
+            2, 2, 2, 2,
+            3, 3, 3, 3,
+            4, 4, 4, 4, 
+            5, 5, 5, 5))
+
+        res = carrayMaker('{Rect={P=ff}{S=ff}}', arr, None)
+        self.assertEquals(res, lst)
+
+        res = carrayMaker('{Rect={P=ff}{S=ff}}', arr, 2)
+        self.assertEquals(res, lst[:2])
+
+        res = carrayMaker('{Rect=[2f][2f]}', arr, None)
+        self.assertEquals(res, lst)
+
+        res = carrayMaker('[2[2f]]}', arr, None)
+        self.assertEquals(res, lst)
+
+        self.assertRaises(ValueError, carrayMaker, '{Rect={P=ff}{S=ff}}', arr2, None)
+
+    def testMixedArray(self):
+        arr = array.array('f', (
+            1.0, 1.5, -1.0, -1.5,
+            2.0, 2.5, -2.0, -2.5,
+            3.0, 3.5, -3.0, -3.5,
+            4.0, 4.5, -4.0, -4.5,
+            5.0, 5.5, -5.0, -5.5))
+
+        self.assertRaises(ValueError, carrayMaker, '{M={P=ff}{S=ii}}', arr, 4)
+        self.assertRaises(ValueError, carrayMaker, '{M=if{S=ii}}', arr, None)
+        self.assertRaises(ValueError, carrayMaker, '{M=fi{S=ff}}', arr, None)
+
+
 
 class PyOCTestTypeStr(unittest.TestCase):
     # 

File pyobjc/Lib/objc/test/testbndl.m

 
 }
 
+static PyObject* carraymaker(PyObject* self __attribute__((__unused__)), PyObject* args)
+{
+	char* signature;
+	PyObject* o1;
+	PyObject* o2;
+	char* buf;
+	int r;
+	int buflen;
+	PyObject* res;
+
+	if (!PyArg_ParseTuple(args, "sOO", &signature, &o1, &o2)) {
+		return NULL;
+	}
+
+	r = PyObjC_PythonToCArray(signature, o1,  o2, (void**)&buf, &buflen);
+	if (r == -1) {
+		return NULL;
+	}
+
+	res = PyObjC_CArrayToPython(signature, buf, buflen);
+	PyObjC_FreeCArray(r, buf);
+
+	return res;
+}
+
 
 
 static PyMethodDef no_methods[] = {
 		"convert object to ObjC and back."
 	},
 
+	{
+		"carrayMaker",
+		(PyCFunction)carraymaker,
+		METH_VARARGS,
+
+		"carrayMaker(signature, seq, count) -> str\n"
+		"\n"
+		"Convert a sequence of 'count' 'signature' objects to\n"
+		"a C buffer, and rebuild a python tuple from it.\n"
+		"count can be None."
+	},
+
+
 	{ 0, 0, 0, 0 }
 };
 

File pyobjc/Modules/AppKit/_AppKit.m

 
 
 static PyObject*
-compat_NSRectFillList(PyObject* self __attribute__((__unused__)), PyObject* args, PyObject* kwds)
+objc_NSRectFillList(PyObject* self __attribute__((__unused__)), PyObject* args, PyObject* kwds)
 {
-static char* keywords[] = { "bytes", "count", 0 };
-	unsigned char *rectBytes;
-	int rectByteLength;
-	int rectCount = -1;
-	if  (!PyArg_ParseTupleAndKeywords(args, kwds, "s#i", keywords, &rectBytes, &rectByteLength, &rectCount)) {
+static char* keywords[] = { "rects", "count", 0 };
+	PyObject* pyList;
+	PyObject* pyCount = NULL;
+	NSRect* rects;
+	int rectCount;
+	int arrayToken;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O", keywords, &pyList, &pyCount)) {
 		return NULL;
 	}
 
-	if ( (rectByteLength == 0) || (rectCount == 0) ) {
-		Py_INCREF(Py_None);
-		return Py_None; 
-	}
+	arrayToken = PyObjC_PythonToCArray(
+		@encode(NSRect), pyList, pyCount, (void**)&rects, &rectCount);
 
-	if ( rectByteLength % sizeof(NSRect) ) {
-		PyErr_SetString(PyExc_ValueError, "length of array of packed floats is not a multiple of a length of array of NSRect (float * 4).");
-		return NULL;
-	}
+	if (arrayToken == -1) return NULL;
 
-	if (rectCount < -1 ) {
-		PyErr_SetString(PyExc_ValueError, "RectCount was less than zero.");
-		return NULL;
-	}
-        
-
-	if (rectCount >= 0 ) {
-		if ((size_t)rectCount > (rectByteLength / sizeof(NSRect))) {
-                    PyErr_SetString(PyExc_ValueError, 
-				"Rect count specified, but was longer than supplied array of rectangles.");
-			return NULL;
-		}
-	} else {
-		rectCount = rectByteLength / sizeof(NSRect);
-	}
-
-	NSRectFillList((NSRect *) rectBytes, rectCount);
-
-	Py_INCREF(Py_None);
-	return Py_None; 
-}
-
-static PyObject*
-objc_NSRectFillList(PyObject* self __attribute__((__unused__)), PyObject* args, PyObject* kwds)
-{
-static char* keywords[] = { "rects", "count", 0 };
-	NSRect* rects;
-	PyObject* rectList;
-	PyObject* seq;
-	int i;
-	int rectCount;
-
-	{
-		/* Upto PyObjC 1.0b2 compat_NSRectFillList was the wrapper for NSRectFillList
-		 * this implementation is not consistant w.r.t. the rest of the bridge and
-		 * therefore depricated.
-		 */
-		char* s1; 
-		int i1, i2;
-                static char* klist[] = { "bytes", "count", 0 };
-
-		if  (PyArg_ParseTupleAndKeywords(args, kwds, "s#i", klist, &s1, &i1, &i2)) {
-			return compat_NSRectFillList(self, args, kwds);
-		}
-		PyErr_Clear();
-	}
-
-	if  (!PyArg_ParseTupleAndKeywords(args, kwds, "Oi", keywords, &rectList, &rectCount)) {
-		return NULL;
-	}
-
-	seq = PySequence_Fast(rectList, "expecting a list of NSRect objects");
-	if (seq == NULL) {
-		return NULL;
-	}
-
-	if (PySequence_Fast_GET_SIZE(seq) < rectCount) {
-		Py_DECREF(seq);
-		PyErr_SetString(PyExc_ValueError, "too few rects");
-		return NULL;
-	}
-
-
-	if (rectCount < 0) {
-		Py_DECREF(seq);
-		PyErr_SetString(PyExc_ValueError, "RectCount was less than zero.");
-		return NULL;
-	}
-
-	rects = malloc((rectCount * sizeof(NSRect)));
-	if (rects == NULL) {
-		PyErr_NoMemory();
-		return NULL;
-	}
-
-	for (i = 0; i < rectCount; i++) {
-		PyObject* v = PySequence_Fast_GET_ITEM(seq, i);
-		int r  = PyObjC_PythonToObjC(@encode(NSRect), v, rects + i);
-		if (r == -1) {
-			Py_DECREF(seq);
-			return NULL;
-		}
-	}
-	Py_DECREF(seq);
 	NSRectFillList(rects, rectCount);
 
-	free(rects);
+	PyObjC_FreeCArray(arrayToken, rects);
 
 	Py_INCREF(Py_None);
 	return Py_None; 

File pyobjc/Modules/objc/objc_support.m

 		return 0;
 
 	} else {
-		PyObject* tmp = PyNumber_Long(argument);
+		PyObject* tmp;
+
+		if (PyString_Check(argument) || PyUnicode_Check(argument)) {
+			ObjCErr_Set(PyExc_ValueError,
+				"depythonifying '%s', got '%s' of %d",
+					descr,
+					argument->ob_type->tp_name,
+					PyString_Size(argument));
+			return -1;
+		}
+
+		tmp = PyNumber_Long(argument);
 		if (tmp != NULL) {
 			*out = PyLong_AsUnsignedLongLong(tmp);
 			Py_DECREF(tmp);
 		return 0;
 
 	} else {
-		PyObject* tmp = PyNumber_Long(argument);
+		PyObject* tmp;
+
+		if (PyString_Check(argument) || PyUnicode_Check(argument)) {
+			ObjCErr_Set(PyExc_ValueError,
+				"depythonifying '%s', got '%s' of %d",
+					descr,
+					argument->ob_type->tp_name,
+					PyString_Size(argument));
+			return -1;
+		}
+
+		
+		tmp = PyNumber_Long(argument);
 		if (tmp != NULL) {
 			*out = PyLong_AsLongLong(tmp);
 			Py_DECREF(tmp);
 			*(float *) datum = (float)PyFloat_AsDouble (argument);
 		} else if (PyInt_Check (argument)) {
 			*(float *) datum = (float) PyInt_AsLong (argument);
+		} else if (PyString_Check(argument) || PyUnicode_Check(argument)) {
+			ObjCErr_Set(PyExc_ValueError,
+				"depythonifying 'float', got '%s'",
+					argument->ob_type->tp_name);
+			return -1;
 		} else {
 			PyObject* tmp = PyNumber_Float(argument);
 			if (tmp != NULL) {
 			*(double *) datum = PyFloat_AsDouble (argument);
 		} else if (PyInt_Check (argument)) {
 			*(double *) datum = (double) PyInt_AsLong (argument);
+		} else if (PyString_Check(argument) || PyUnicode_Check(argument)) {
+			ObjCErr_Set(PyExc_ValueError,
+				"depythonifying 'float', got '%s'",
+					argument->ob_type->tp_name);
+			return -1;
 		} else {
 			PyObject* tmp = PyNumber_Float(argument);
 			if (tmp != NULL) {

File pyobjc/Modules/objc/objc_util.h

 
 //extern NSMapTableKeyCallBacks ObjC_PyObjectKeyCallBacks;
 //extern NSMapTableValueCallBacks ObjC_PyObjectValueCallBacks;
+//
 
+void    PyObjC_FreeCArray(int, void*);
+int     PyObjC_PythonToCArray(const char*, PyObject*, PyObject*, void**, int*);
+PyObject* PyObjC_CArrayToPython(const char*, void*, int);
 
 #endif /* OBJC_UTIL */

File pyobjc/Modules/objc/objc_util.m

 	NULL,
 	NULL,
 };
+
+#define SHOULD_FREE 0
+#define SHOULD_IGNORE 1
+
+void    PyObjC_FreeCArray(int code, void* array)
+{
+	if (code == SHOULD_FREE) {
+		free(array);
+	}
+}
+
+static PyTypeObject* array_type = NULL;
+
+static inline PyTypeObject* fetch_array_type(void)
+{
+	PyObject* mod;
+	PyObject* name;
+
+	if (array_type != NULL) return array_type;
+
+	name = PyString_FromString("array");
+	if (name == NULL) {
+		return NULL;
+	}
+
+	mod = PyImport_Import(name);
+	Py_DECREF(name);
+	if (mod == NULL) {
+		return NULL;
+	}
+
+	array_type = (PyTypeObject*)PyObject_GetAttrString(mod, "array");
+	Py_DECREF(mod);
+	if (array_type == NULL) {
+		return NULL;
+	}
+
+	/* XXX: check if array_type is realy a type! */
+
+	return array_type;
+}
+
+#define array_check(obj) PyObject_TypeCheck(obj, fetch_array_type())
+
+static char 
+array_typestr(PyObject* array)
+{
+	PyObject* typecode;
+	char res;
+
+	typecode = PyObject_GetAttrString(array, "typecode");
+	if (typecode == NULL) {
+		return '\0';
+	}
+
+	if (!PyString_Check(typecode)) {
+		PyErr_SetString(PyExc_TypeError, "typecode not a string");
+		return '\0';
+	}
+
+	switch (*PyString_AS_STRING(typecode)) {
+	case 'c': res = _C_CHR; break;
+	case 'b': res = _C_CHR; break;
+	case 'B': res = _C_UCHR; break;
+	case 'u': res = _C_SHT; break;
+	case 'h': res = _C_SHT; break;
+	case 'H': res = _C_USHT; break;
+	case 'i': res = _C_INT; break;
+	case 'I': res = _C_UINT; break;
+	case 'l': res = _C_LNG; break;
+	case 'L': res = _C_ULNG; break;
+	case 'f': res = _C_FLT; break;
+	case 'd': res = _C_DBL; break;
+	default: 
+		PyErr_SetString(PyExc_TypeError, "unsupported typecode");
+		res = '\0';
+	}
+	Py_DECREF(typecode);
+	
+	return res;
+}
+
+static int 
+buffer_get(PyObject* obj, void** bufptr, int* sizeptr)
+{
+	int r;
+
+	r = PyArg_Parse(obj, "s#", bufptr, sizeptr);
+	if (!r) {
+		return -1;
+	}
+	return 0;
+
+#if 0
+	PyBufferProcs* pb = obj->ob_type->tp_as_buffer;
+	if (!PyType_HasFeature(obj->ob_type,
+				Py_TPFLAGS_HAVE_GETCHARBUFFER) ||
+				pb == NULL || 
+				pb->bf_getcharbuffer == NULL ||
+				pb->bf_getsegcount == NULL) {
+		PyErr_SetString(PyExc_TypeError, "cannot access buffer");
+		return -1;
+	}
+
+	if (pb->bf_getsegcount(obj, NULL) != 1) {
+		PyErr_SetString(PyExc_TypeError, 
+				"cannot access multi-segment buffer");
+		return -1;
+	}
+	
+	*sizeptr = pb->bf_getcharbuffer(obj, 0, (const char**)bufptr);
+	if (*sizeptr < 0) {
+		PyErr_SetString(PyExc_TypeError,
+				"error accessing buffer object");
+		return -1;
+	}
+	return 0;
+#endif
+}
+
+static char struct_elem_code(const char* typestr);
+
+static char
+array_elem_code(const char* typestr)
+{
+	char res = '\0';
+	char tmp;
+
+	if (*typestr++ != _C_ARY_B) {
+		return '\0';
+	}
+	while (isdigit(typestr++)) 
+
+	if (*typestr == _C_ARY_E) {
+		return '\0';
+	}
+
+	while (typestr && *typestr != _C_ARY_E) {
+		switch(*typestr) {
+		case _C_ARY_B:
+			tmp = array_elem_code(typestr);
+			if (tmp == '\0') return '\0';
+			if (res == '\0') {
+				res = tmp;
+			} else if (tmp != res) {
+				return '\0';
+			}
+			break;
+		case _C_STRUCT_B:
+			tmp = struct_elem_code(typestr);
+			if (tmp == '\0') return '\0';
+			if (res == '\0') {
+				res = tmp;
+			} else if (tmp != res) {
+				return '\0';
+			}
+			break;
+		default:
+			if (res != '\0' && *typestr != res) return '\0';
+			res = *typestr;
+		}
+
+		typestr = PyObjCRT_SkipTypeSpec(typestr);
+	}
+	return res;
+}
+
+static char
+struct_elem_code(const char* typestr)
+{
+	char res = '\0';
+	char tmp;
+
+	if (*typestr++ != _C_STRUCT_B) {
+		return '\0';
+	}
+
+	while (*typestr != '=' && *typestr != _C_STRUCT_E) {
+		typestr++;
+	}
+
+	if (*typestr == _C_STRUCT_E) {
+		return '\0';
+	}
+	typestr++;
+
+	while (typestr && *typestr != _C_STRUCT_E) {
+		switch(*typestr) {
+		case _C_ARY_B:
+			tmp = array_elem_code(typestr);
+			if (tmp == '\0') return '\0';
+			if (res == '\0') {
+				res = tmp;
+			} else if (tmp != res) {
+				return '\0';
+			}
+			break;
+		case _C_STRUCT_B:
+			tmp = struct_elem_code(typestr);
+			if (tmp == '\0') return '\0';
+			if (res == '\0') {
+				res = tmp;
+			} else if (tmp != res) {
+				return '\0';
+			}
+			break;
+		default:
+			if (res != '\0' && *typestr != res) return '\0';
+			res = *typestr;
+		}
+
+		typestr = PyObjCRT_SkipTypeSpec(typestr);
+	}
+	return res;
+}
+		
+
+
+	
+
+/*
+ * Convert a Python object to an array of 'elementType'. The array should
+ * contain 'pythonCount' elements, Py_None or NULL is accepted and will result
+ * in converting the entire Python sequence.
+ *
+ * The pythonList should either be a python sequence with appropriate entries,
+ * an array.array whose element-types match the element-types of the 
+ * 'elementType' or an appropriatly typed and shaped numeric array.
+ * 
+ * XXX: Numeric arrays are not yet supported.
+ */
+int	PyObjC_PythonToCArray(
+		const char* elementType,
+		PyObject* pythonList,
+		PyObject* pythonCount,
+		void** array,
+		int*   size)
+{
+	int eltsize = PyObjCRT_SizeOfType(elementType);
+	int eltcount;
+	int i, r;
+
+	if (eltsize == -1) {
+		return -1;
+	}
+
+	if (eltsize == 1) {
+		/* A simple byte-array */
+		char* buf;
+		int bufsize;
+
+		if (buffer_get(pythonList, (void**)&buf, &bufsize) == -1) {
+			return -1;
+		}
+		if (pythonCount == Py_None || pythonCount == NULL) {
+			*array = buf;
+			*size = bufsize;
+		} else {
+			r = depythonify_c_value(@encode(int), pythonCount, &eltcount);
+			if (r == -1) {
+				return -1;
+			}
+			if (eltcount > bufsize) {
+				PyErr_Format(PyExc_ValueError,
+					"Requesting buffer of %d, have buffer "
+					"of %d", eltcount, bufsize);
+				return -1;
+			}
+			*array = buf;
+			*size = eltcount;
+		}
+		return SHOULD_IGNORE;
+	} 
+
+	/* A more complex array */
+
+	if (array_check(pythonList)) {
+		/* An array.array. Only convert if the typestr describes an
+		 * simple type of the same type as the array, or a struct/array
+		 * containing only elements of the type of the array.
+		 */
+		char* buf;
+		int bufsize;
+		char code = array_typestr(pythonList);
+		if (code == *elementType) {
+			/* Simple array, ok */
+		} else if (*elementType == _C_ARY_B) {
+			/* Array of arrays, 'code' must be the same as the
+			 * element-type of the array.
+			 */
+			if (code != array_elem_code(elementType)) {
+				PyErr_Format(PyExc_ValueError, 
+					"type mismatch between array.array "
+					"of %c and and C array of %s",
+					code, elementType);
+				return -1;
+			}
+
+		} else if (*elementType == _C_STRUCT_B) {
+			/* Array of structs, 'code' must be the same as the
+			 * the field-types of the structs (that is, the struct
+			 * must contain one or more fields of type 'code').
+			 */
+			if (code != struct_elem_code(elementType)) {
+				PyErr_Format(PyExc_ValueError, 
+					"type mismatch between array.array "
+					"of %c and and C array of %s",
+					code, elementType);
+				return -1;
+			}
+		} else {
+			PyErr_Format(PyExc_ValueError, 
+				"type mismatch between array.array "
+				"of %c and and C array of %s",
+				code, elementType);
+			return -1;
+		}
+
+		if (buffer_get(pythonList, (void**)&buf, &bufsize) == -1) {
+			return -1;
+		}
+		if ((bufsize % eltsize) != 0) {
+			PyErr_SetString(PyExc_ValueError, 
+					"Badly shaped array.array");
+			return -1;
+		}
+
+		*array = buf;
+
+		if (pythonCount == Py_None || pythonCount == NULL) {
+			*size = bufsize / eltsize;
+		} else {
+
+			r = depythonify_c_value(@encode(int), pythonCount, &eltcount);
+			if (r == -1) {
+				return -1;
+			}
+			bufsize /= eltsize;
+
+			if (eltcount > bufsize) {
+				PyErr_Format(PyExc_ValueError,
+					"Requesting buffer of %d, have buffer "
+					"of %d", eltcount, bufsize);
+				return -1;
+			}
+			*array = buf;
+			*size = eltcount;
+		}
+		return SHOULD_IGNORE;
+
+#ifdef PyObjC_ENABLE_NUMARRAY
+
+# error "Please implement Numarray/Numeric support"
+
+	} else if (...){
+		/* TODO: Convert the numeric array (or return a pointer to it's
+		 * data), but only if it is the right type:
+		 * - If typestr is a basic type, the array must be a 1D array
+		 *   of that type
+		 * - If typestr is a structred type, the array must be a 2D
+		 *   array where rows match the structured type
+		 *
+		 * XXX: I have no idea if this is feasable, not having used
+		 * numarray/numeric myself.
+		 */
+#endif /* PyObjC_ENABLE_NUMARRAY */
+	} else {
+		int seqlen;
+		int pycount;
+		PyObject* seq = PySequence_Fast(pythonList, 
+					"converting to a C array");
+		if (seq == NULL) {
+			return -1;
+		}
+
+		seqlen = PySequence_Fast_GET_SIZE(seq);
+		if (pythonCount == Py_None || pythonCount == NULL) {
+			pycount = seqlen;
+		} else {
+			r = depythonify_c_value(
+					@encode(int), pythonCount, &pycount);
+			if (r == -1) {
+				Py_DECREF(seq);
+				return -1;
+			}
+		}
+
+		if (seqlen < pycount) {
+			Py_DECREF(seq);
+			PyErr_Format(PyExc_ValueError,
+					"too few values (%d) expecting at "
+					"least %d", seqlen, pycount);
+			return -1;
+		}
+		*array = malloc(eltsize * pycount);
+		if (*array == NULL) {
+			Py_DECREF(seq);
+			PyErr_NoMemory();
+			return -1;
+		}
+		*size = pycount;
+
+		for (i = 0; i < pycount; i++) {
+			PyObject* item = PySequence_Fast_GET_ITEM(seq, i);
+
+			r = depythonify_c_value(elementType, item,
+					((char*)*array)+(i*eltsize));
+			if (r == -1) {
+				Py_DECREF(seq);
+				free(*array); *array = NULL;
+				return -1;
+			}
+		}
+		return SHOULD_FREE;
+	}
+	
+}
+
+PyObject* PyObjC_CArrayToPython(
+		const char* elementType,
+		void* array,
+		int   size)
+{
+	PyObject* result;
+	int i;
+	int eltsize;
+
+	result = PyTuple_New(size);
+	if (result == NULL) {
+		return NULL;
+	}
+
+	eltsize = PyObjCRT_SizeOfType(elementType);
+
+	for (i = 0; i < size; i++) {
+		PyObject* elt = pythonify_c_value(elementType, array);
+		if (elt == NULL) {
+			Py_DECREF(result);
+			return NULL;
+		}
+
+		PyTuple_SET_ITEM(result, i, elt);
+		array = ((char*)array) + eltsize;
+	}
+
+	return result;
+}

File pyobjc/Modules/objc/pyobjc-api.h

  * This is the *only* header file that should be used to access 
  * functionality in the core bridge.
  *
- * $Id: pyobjc-api.h,v 1.21 2003/10/19 16:53:35 ronaldoussoren Exp $
+ * $Id: pyobjc-api.h,v 1.22 2003/11/23 19:23:40 ronaldoussoren Exp $
  */
 
 #include <Python.h>
  *         (PyObjC_SizeOfType is now deprecated)
  * - Version 4.2 adds PyObjCRT_SELName
  * - Version 4.3 adds PyObjCRT_SimplifySignature
+ * - Version 4.4 adds PyObjC_FreeCArray, PyObjC_PythonToCArray and
+ *   		PyObjC_CArrayToPython
  */
 #define PYOBJC_API_VERSION 4
 
 	/* PyObjCRT_SimplifySignature */
 	void (*simplify_sig)(char* signature, char* buf, size_t buflen);
 
+	/* PyObjC_FreeCArray */
+	void    (*free_c_array)(int,void*);
+
+	/* PyObjC_PythonToCArray */
+	int     (*py_to_c_array)(const char*, PyObject*, PyObject*, void**, int*);
+	
+	/* PyObjC_CArrayToPython */
+	PyObject* (*c_array_to_py)(const char*, void*, int);
+
 };
 
 
 #define PyObjCRT_AlignOfType	(PyObjC_API->alignof_type)
 #define PyObjCRT_SELName	(PyObjC_API->selname)
 #define PyObjCRT_SimplifySignature	(PyObjC_API->simplify_sig)
+#define PyObjC_FreeCArray	(PyObjC_API->free_c_array)
+#define PyObjC_PythonToCArray	(PyObjC_API->py_to_c_array)
+#define PyObjC_CArrayToPython	(PyObjC_API->c_array_to_py)
 
 
 /* XXX: Check if we can use the following function in the bridge itself,

File pyobjc/Modules/objc/pyobjc-api.m

 	PyObjCErr_ToObjCWithGILState,	/* objc_err_to_objc_gil */
 	PyObjCRT_AlignOfType,		/* alignof_type */
 	PyObjCRT_SELName,		/* selname */
-	PyObjCRT_SimplifySignature	/* simplify_sig */
+	PyObjCRT_SimplifySignature,	/* simplify_sig */
+	PyObjC_FreeCArray,		/* free_c_array */
+	PyObjC_PythonToCArray,		/* py_to_c_array */
+	PyObjC_CArrayToPython		/* c_array_to_py */
 };
 
 int ObjCAPI_Register(PyObject* module_dict)