[pypy-commit] cffi c99-array: Implementation: support a few extra ways to give initialization arguments to ffi.new()
arigo
noreply at buildbot.pypy.org
Fri Nov 8 19:48:54 CET 2013
Author: Armin Rigo <arigo at tunes.org>
Branch: c99-array
Changeset: r1385:fb634a3e8dfc
Date: 2013-11-08 19:48 +0100
http://bitbucket.org/cffi/cffi/changeset/fb634a3e8dfc/
Log: Implementation: support a few extra ways to give initialization
arguments to ffi.new() for var-sized structs. See the test.
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -86,6 +86,7 @@
#define CT_IS_BOOL 131072
#define CT_IS_FILE 262144
#define CT_IS_VOID_PTR 524288
+#define CT_WITH_VAR_ARRAY 1048576
#define CT_PRIMITIVE_ANY (CT_PRIMITIVE_SIGNED | \
CT_PRIMITIVE_UNSIGNED | \
CT_PRIMITIVE_CHAR | \
@@ -1007,9 +1008,39 @@
static int /* forward */
convert_from_object_bitfield(char *data, CFieldObject *cf, PyObject *init);
+static Py_ssize_t
+get_new_array_length(PyObject **pvalue)
+{
+ PyObject *value = *pvalue;
+
+ if (PyList_Check(value) || PyTuple_Check(value)) {
+ return PySequence_Fast_GET_SIZE(value);
+ }
+ else if (PyBytes_Check(value)) {
+ /* from a string, we add the null terminator */
+ return PyBytes_GET_SIZE(value) + 1;
+ }
+ else if (PyUnicode_Check(value)) {
+ /* from a unicode, we add the null terminator */
+ return _my_PyUnicode_SizeAsWideChar(value) + 1;
+ }
+ else {
+ Py_ssize_t explicitlength;
+ explicitlength = PyNumber_AsSsize_t(value, PyExc_OverflowError);
+ if (explicitlength < 0) {
+ if (!PyErr_Occurred())
+ PyErr_SetString(PyExc_ValueError, "negative array length");
+ return -1;
+ }
+ *pvalue = Py_None;
+ return explicitlength;
+ }
+}
+
static int
convert_field_from_object(char *data, CFieldObject *cf, PyObject *value)
{
+ data += cf->cf_offset;
if (cf->cf_bitshift >= 0)
return convert_from_object_bitfield(data, cf, value);
else
@@ -1017,6 +1048,45 @@
}
static int
+convert_vfield_from_object(char *data, CFieldObject *cf, PyObject *value,
+ Py_ssize_t *optvarsize)
+{
+ /* a special case for var-sized C99 arrays */
+ if ((cf->cf_type->ct_flags & CT_ARRAY) && cf->cf_type->ct_size < 0) {
+ Py_ssize_t varsizelength = get_new_array_length(&value);
+ if (varsizelength < 0)
+ return -1;
+ if (optvarsize != NULL) {
+ /* in this mode, the only purpose of this function is to compute
+ the real size of the structure from a var-sized C99 array */
+ Py_ssize_t size, itemsize;
+ assert(data == NULL);
+ itemsize = cf->cf_type->ct_itemdescr->ct_size;
+ size = cf->cf_offset + itemsize * varsizelength;
+ if (size < 0 ||
+ ((size - cf->cf_offset) / itemsize) != varsizelength) {
+ PyErr_SetString(PyExc_OverflowError,
+ "array size would overflow a Py_ssize_t");
+ return -1;
+ }
+ if (size > *optvarsize)
+ *optvarsize = size;
+ return 0;
+ }
+ /* if 'value' was only an integer, get_new_array_length() returns
+ it and convert 'value' to be None. Detect if this was the case,
+ and if so, stop here, leaving the content uninitialized
+ (it should be zero-initialized from somewhere else). */
+ if (value == Py_None)
+ return 0;
+ }
+ if (optvarsize == NULL)
+ return convert_field_from_object(data, cf, value);
+ else
+ return 0;
+}
+
+static int
convert_array_from_object(char *data, CTypeDescrObject *ct, PyObject *init)
{
/* used by convert_from_object(), and also to decode lists/tuples/unicodes
@@ -1097,6 +1167,63 @@
}
static int
+convert_struct_from_object(char *data, CTypeDescrObject *ct, PyObject *init,
+ Py_ssize_t *optvarsize)
+{
+ const char *expected;
+
+ if (ct->ct_flags & CT_UNION) {
+ Py_ssize_t n = PyObject_Size(init);
+ if (n < 0)
+ return -1;
+ if (n > 1) {
+ PyErr_Format(PyExc_ValueError,
+ "initializer for '%s': %zd items given, but "
+ "only one supported (use a dict if needed)",
+ ct->ct_name, n);
+ return -1;
+ }
+ }
+ if (PyList_Check(init) || PyTuple_Check(init)) {
+ PyObject **items = PySequence_Fast_ITEMS(init);
+ Py_ssize_t i, n = PySequence_Fast_GET_SIZE(init);
+ CFieldObject *cf = (CFieldObject *)ct->ct_extra;
+
+ for (i=0; i<n; i++) {
+ if (cf == NULL) {
+ PyErr_Format(PyExc_ValueError,
+ "too many initializers for '%s' (got %zd)",
+ ct->ct_name, n);
+ return -1;
+ }
+ if (convert_vfield_from_object(data, cf, items[i], optvarsize) < 0)
+ return -1;
+ cf = cf->cf_next;
+ }
+ return 0;
+ }
+ if (PyDict_Check(init)) {
+ PyObject *d_key, *d_value;
+ Py_ssize_t i = 0;
+ CFieldObject *cf;
+
+ while (PyDict_Next(init, &i, &d_key, &d_value)) {
+ cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, d_key);
+ if (cf == NULL) {
+ PyErr_SetObject(PyExc_KeyError, d_key);
+ return -1;
+ }
+ if (convert_vfield_from_object(data, cf, d_value, optvarsize) < 0)
+ return -1;
+ }
+ return 0;
+ }
+ expected = optvarsize == NULL ? "list or tuple or dict or struct-cdata"
+ : "list or tuple or dict";
+ return _convert_error(init, ct->ct_name, expected);
+}
+
+static int
convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init)
{
const char *expected;
@@ -1209,56 +1336,7 @@
return 0;
}
}
- if (ct->ct_flags & CT_UNION) {
- Py_ssize_t n = PyObject_Size(init);
- if (n < 0)
- return -1;
- if (n > 1) {
- PyErr_Format(PyExc_ValueError,
- "initializer for '%s': %zd items given, but "
- "only one supported (use a dict if needed)",
- ct->ct_name, n);
- return -1;
- }
- }
- if (PyList_Check(init) || PyTuple_Check(init)) {
- PyObject **items = PySequence_Fast_ITEMS(init);
- Py_ssize_t i, n = PySequence_Fast_GET_SIZE(init);
- CFieldObject *cf = (CFieldObject *)ct->ct_extra;
-
- for (i=0; i<n; i++) {
- if (cf == NULL) {
- PyErr_Format(PyExc_ValueError,
- "too many initializers for '%s' (got %zd)",
- ct->ct_name, n);
- return -1;
- }
- if (convert_field_from_object(data + cf->cf_offset,
- cf, items[i]) < 0)
- return -1;
- cf = cf->cf_next;
- }
- return 0;
- }
- if (PyDict_Check(init)) {
- PyObject *d_key, *d_value;
- Py_ssize_t i = 0;
- CFieldObject *cf;
-
- while (PyDict_Next(init, &i, &d_key, &d_value)) {
- cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, d_key);
- if (cf == NULL) {
- PyErr_SetObject(PyExc_KeyError, d_key);
- return -1;
- }
- if (convert_field_from_object(data + cf->cf_offset,
- cf, d_value) < 0)
- return -1;
- }
- return 0;
- }
- expected = "list or tuple or dict or struct-cdata";
- goto cannot_convert;
+ return convert_struct_from_object(data, ct, init, NULL);
}
PyErr_Format(PyExc_SystemError,
"convert_from_object: '%s'", ct->ct_name);
@@ -2068,9 +2146,8 @@
cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr);
if (cf != NULL) {
/* write the field 'cf' */
- char *data = cd->c_data + cf->cf_offset;
if (value != NULL) {
- return convert_field_from_object(data, cf, value);
+ return convert_field_from_object(cd->c_data, cf, value);
}
else {
PyErr_SetString(PyExc_AttributeError,
@@ -2642,32 +2719,21 @@
}
if (ctitem->ct_flags & CT_PRIMITIVE_CHAR)
datasize *= 2; /* forcefully add another character: a null */
+
+ if ((ctitem->ct_flags & CT_WITH_VAR_ARRAY) && init != Py_None) {
+ Py_ssize_t optvarsize = datasize;
+ if (convert_struct_from_object(NULL,ctitem, init, &optvarsize) < 0)
+ return NULL;
+ datasize = optvarsize;
+ }
}
else if (ct->ct_flags & CT_ARRAY) {
dataoffset = offsetof(CDataObject_own_nolength, alignment);
datasize = ct->ct_size;
if (datasize < 0) {
- if (PyList_Check(init) || PyTuple_Check(init)) {
- explicitlength = PySequence_Fast_GET_SIZE(init);
- }
- else if (PyBytes_Check(init)) {
- /* from a string, we add the null terminator */
- explicitlength = PyBytes_GET_SIZE(init) + 1;
- }
- else if (PyUnicode_Check(init)) {
- /* from a unicode, we add the null terminator */
- explicitlength = _my_PyUnicode_SizeAsWideChar(init) + 1;
- }
- else {
- explicitlength = PyNumber_AsSsize_t(init, PyExc_OverflowError);
- if (explicitlength < 0) {
- if (!PyErr_Occurred())
- PyErr_SetString(PyExc_ValueError,
- "negative array length");
- return NULL;
- }
- init = Py_None;
- }
+ explicitlength = get_new_array_length(&init);
+ if (explicitlength < 0)
+ return NULL;
ctitem = ct->ct_itemdescr;
dataoffset = offsetof(CDataObject_own_length, alignment);
datasize = explicitlength * ctitem->ct_size;
@@ -3554,11 +3620,17 @@
goto error;
if (ftype->ct_size < 0) {
- PyErr_Format(PyExc_TypeError,
- "field '%s.%s' has ctype '%s' of unknown size",
- ct->ct_name, PyText_AS_UTF8(fname),
- ftype->ct_name);
- goto error;
+ if ((ftype->ct_flags & CT_ARRAY) && fbitsize < 0
+ && i == nb_fields - 1) {
+ ct->ct_flags |= CT_WITH_VAR_ARRAY;
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "field '%s.%s' has ctype '%s' of unknown size",
+ ct->ct_name, PyText_AS_UTF8(fname),
+ ftype->ct_name);
+ goto error;
+ }
}
if (is_union)
@@ -3632,7 +3704,8 @@
goto error;
previous = &(*previous)->cf_next;
}
- boffset += ftype->ct_size * 8;
+ if (ftype->ct_size >= 0)
+ boffset += ftype->ct_size * 8;
prev_bitfield_size = 0;
}
else {
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -2963,6 +2963,94 @@
_test_bitfield_details(flag=4)
+def test_struct_array_no_length():
+ BInt = new_primitive_type("int")
+ BIntP = new_pointer_type(BInt)
+ BArray = new_array_type(BIntP, None)
+ BStruct = new_struct_type("foo")
+ py.test.raises(TypeError, complete_struct_or_union,
+ BStruct, [('x', BArray),
+ ('y', BInt)])
+ #
+ BStruct = new_struct_type("foo")
+ complete_struct_or_union(BStruct, [('x', BInt),
+ ('y', BArray)])
+ assert sizeof(BStruct) == size_of_int()
+ d = BStruct.fields
+ assert len(d) == 2
+ assert d[0][0] == 'x'
+ assert d[0][1].type is BInt
+ assert d[0][1].offset == 0
+ assert d[0][1].bitshift == -1
+ assert d[0][1].bitsize == -1
+ assert d[1][0] == 'y'
+ assert d[1][1].type is BArray
+ assert d[1][1].offset == size_of_int()
+ assert d[1][1].bitshift == -1
+ assert d[1][1].bitsize == -1
+ #
+ p = newp(new_pointer_type(BStruct))
+ p.x = 42
+ assert p.x == 42
+ assert typeof(p.y) is BIntP
+ assert p.y == cast(BIntP, p) + 1
+ #
+ p = newp(new_pointer_type(BStruct), [100])
+ assert p.x == 100
+ #
+ # Tests for
+ # ffi.new("struct_with_var_array *", [field.., [the_array_items..]])
+ # ffi.new("struct_with_var_array *", [field.., array_size])
+ plist = []
+ for i in range(20):
+ if i % 2 == 0:
+ p = newp(new_pointer_type(BStruct), [100, [200, i, 400]])
+ else:
+ p = newp(new_pointer_type(BStruct), [100, 3])
+ p.y[1] = i
+ p.y[0] = 200
+ assert p.y[2] == 0
+ p.y[2] = 400
+ plist.append(p)
+ for i in range(20):
+ p = plist[i]
+ assert p.x == 100
+ assert p.y[0] == 200
+ assert p.y[1] == i
+ assert p.y[2] == 400
+ assert list(p.y[0:3]) == [200, i, 400]
+ #
+ # the following assignment works, as it normally would, for any array field
+ p.y = [500, 600]
+ assert list(p.y[0:3]) == [500, 600, 400]
+ #
+ # error cases
+ py.test.raises(TypeError, "p.y = cast(BIntP, 0)")
+ py.test.raises(TypeError, "p.y = 15")
+ py.test.raises(TypeError, "p.y = None")
+ #
+ # accepting this may be specified by the C99 standard,
+ # or a GCC strangeness...
+ BStruct2 = new_struct_type("bar")
+ complete_struct_or_union(BStruct2, [('f', BStruct),
+ ('n', BInt)])
+ p = newp(new_pointer_type(BStruct2), {'n': 42})
+ assert p.n == 42
+ #
+ # more error cases
+ py.test.raises(TypeError, newp, new_pointer_type(BStruct), [100, None])
+ BArray4 = new_array_type(BIntP, 4)
+ BStruct4 = new_struct_type("test4")
+ complete_struct_or_union(BStruct4, [('a', BArray4)]) # not varsized
+ py.test.raises(TypeError, newp, new_pointer_type(BStruct4), [None])
+ py.test.raises(TypeError, newp, new_pointer_type(BStruct4), [4])
+ p = newp(new_pointer_type(BStruct4), [[10, 20, 30]])
+ assert p.a[0] == 10
+ assert p.a[1] == 20
+ assert p.a[2] == 30
+ assert p.a[3] == 0
+
+
def test_version():
# this test is here mostly for PyPy
assert __version__ == "0.7"
More information about the pypy-commit
mailing list