[pypy-commit] cffi default: Fix what is hopefully the last remaining issues with calculate_variable_array_length

arigo pypy.commits at gmail.com
Tue Oct 25 17:57:01 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r2797:b4de89f7cec4
Date: 2016-10-25 09:18 +0200
http://bitbucket.org/cffi/cffi/changeset/b4de89f7cec4/

Log:	Fix what is hopefully the last remaining issues with
	calculate_variable_array_length

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -132,7 +132,7 @@
 #define CT_PRIMITIVE_FITS_LONG   2048
 #define CT_IS_OPAQUE             4096
 #define CT_IS_ENUM               8192
-#define CT_IS_PTR_TO_OWNED      16384   /* only if CDataOwning_Type! */
+#define CT_IS_PTR_TO_OWNED      16384   /* only owned if CDataOwning_Type */
 #define CT_CUSTOM_FIELD_POS     32768
 #define CT_IS_LONGDOUBLE        65536
 #define CT_IS_BOOL             131072
@@ -185,16 +185,14 @@
     PyObject_HEAD
     CTypeDescrObject *cf_type;
     Py_ssize_t cf_offset;
-    short cf_bitshift;   /* >= 0: bitshift; or BS_REGULAR or BS_EMPTY_ARRAY
-                                            or BS_VARSIZESTRUCT_ARRAY */
+    short cf_bitshift;   /* >= 0: bitshift; or BS_REGULAR or BS_EMPTY_ARRAY */
     short cf_bitsize;
     unsigned char cf_flags;   /* BF_... */
     struct cfieldobject_s *cf_next;
 } CFieldObject;
-#define BS_REGULAR             (-1) /* a regular field, not with bitshift */
-#define BS_EMPTY_ARRAY         (-2) /* a field which is an array 'type[0]' */
-#define BS_VARSIZESTRUCT_ARRAY (-3) /* a variable sized struct variable field 'type[]' */
-#define BF_IGNORE_IN_CTOR      0x01 /* union field not in the first place */
+#define BS_REGULAR            (-1) /* a regular field, not with bitshift */
+#define BS_EMPTY_ARRAY        (-2) /* a field declared 'type[0]' or 'type[]' */
+#define BF_IGNORE_IN_CTOR     0x01 /* union field not in the first place */
 
 static PyTypeObject CTypeDescr_Type;
 static PyTypeObject CField_Type;
@@ -1850,18 +1848,34 @@
     return res;
 }
 
+static Py_ssize_t _cdata_var_byte_size(CDataObject *cd)
+{
+    /* If 'cd' is a 'struct foo' or 'struct foo *' allocated with
+       ffi.new(), and if the struct foo contains a varsize array,
+       then return the real allocated size.  Otherwise, return -1. */
+    if (!CDataOwn_Check(cd))
+        return -1;
+
+    if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
+        cd = (CDataObject *)((CDataObject_own_structptr *)cd)->structobj;
+    }
+    if (cd->c_type->ct_flags & CT_WITH_VAR_ARRAY) {
+        return ((CDataObject_own_length *)cd)->length;
+    }
+    return -1;
+}
+
 static PyObject *cdataowning_repr(CDataObject *cd)
 {
-    Py_ssize_t size;
-    if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED)
-        size = ((CDataObject_own_length *)cd)->length;
-    else if (cd->c_type->ct_flags & CT_POINTER)
-        size = cd->c_type->ct_itemdescr->ct_size;
-    else if (cd->c_type->ct_flags & CT_ARRAY)
-        size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size;
-    else
-        size = cd->c_type->ct_size;
-
+    Py_ssize_t size = _cdata_var_byte_size(cd);
+    if (size < 0) {
+        if (cd->c_type->ct_flags & CT_POINTER)
+            size = cd->c_type->ct_itemdescr->ct_size;
+        else if (cd->c_type->ct_flags & CT_ARRAY)
+            size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size;
+        else
+            size = cd->c_type->ct_size;
+    }
     return PyText_FromFormat("<cdata '%s' owning %zd bytes>",
                              cd->c_type->ct_name, size);
 }
@@ -2427,26 +2441,26 @@
                 /* read the field 'cf' */
                 char *data = cd->c_data + cf->cf_offset;
 
-                if (cf->cf_bitshift == BS_VARSIZESTRUCT_ARRAY) {
+                if (cf->cf_bitshift == BS_REGULAR) {
+                    return convert_to_object(data, cf->cf_type);
+                }
+                else if (cf->cf_bitshift != BS_EMPTY_ARRAY) {
+                    return convert_to_object_bitfield(data, cf);
+                }
+
+                if (cf->cf_offset == ct->ct_size) {
                     /* variable-length array */
-                    if (!CDataOwn_Check(cd)) {
-                        return new_simple_cdata(data,
-                            (CTypeDescrObject *)cf->cf_type->ct_stuff);
+                    /* if reading variable length array from variable length
+                       struct, calculate array type from allocated length */
+                    Py_ssize_t array_len, size = _cdata_var_byte_size(cd);
+                    size -= ct->ct_size;
+                    if (size >= 0) {
+                        array_len = size / cf->cf_type->ct_itemdescr->ct_size;
+                        return new_sized_cdata(data, cf->cf_type, array_len);
                     }
-                    /* if reading variable length array from variable length
-                       struct, calculate array type from allocated length*/
-                    Py_ssize_t array_len =
-                       (((CDataObject_own_structptr *)cd)->length - ct->ct_size)
-                       / cf->cf_type->ct_itemdescr->ct_size;
-                    return new_sized_cdata(data, cf->cf_type, array_len);
                 }
-                else if (cf->cf_bitshift == BS_REGULAR)
-                    return convert_to_object(data, cf->cf_type);
-                else if (cf->cf_bitshift == BS_EMPTY_ARRAY)
-                    return new_simple_cdata(data,
-                        (CTypeDescrObject *)cf->cf_type->ct_stuff);
-                else
-                    return convert_to_object_bitfield(data, cf);
+                return new_simple_cdata(data,
+                    (CTypeDescrObject *)cf->cf_type->ct_stuff);
             }
             break;
         case -1:
@@ -3101,6 +3115,10 @@
                         "return type is an opaque structure or union");
         return NULL;
     }
+    if (ct->ct_flags & CT_WITH_VAR_ARRAY) {
+        PyErr_SetString(PyExc_TypeError,
+                  "return type is a struct/union with a varsize array member");
+    }
     cd = allocate_owning_object(dataoffset + datasize, ct);
     if (cd == NULL)
         return NULL;
@@ -3199,18 +3217,21 @@
         if (ctitem->ct_flags & CT_PRIMITIVE_CHAR)
             datasize *= 2;   /* forcefully add another character: a null */
 
-        if (force_lazy_struct(ctitem) < 0)   /* for CT_WITH_VAR_ARRAY */
-            return NULL;
-        if (ctitem->ct_flags & CT_WITH_VAR_ARRAY)
-            dataoffset = offsetof(CDataObject_own_length, alignment);
-
-        if ((ctitem->ct_flags & (CT_STRUCT | CT_UNION)) && init != Py_None) {
+        if (ctitem->ct_flags & (CT_STRUCT | CT_UNION)) {
+            if (force_lazy_struct(ctitem) < 0)   /* for CT_WITH_VAR_ARRAY */
+                return NULL;
+
             if (ctitem->ct_flags & CT_WITH_VAR_ARRAY) {
-                Py_ssize_t optvarsize = datasize;
-                if (convert_struct_from_object(NULL, ctitem, init,
-                                               &optvarsize) < 0)
-                    return NULL;
-                datasize = optvarsize;
+                assert(ct->ct_flags & CT_IS_PTR_TO_OWNED);
+                dataoffset = offsetof(CDataObject_own_length, alignment);
+
+                if (init != Py_None) {
+                    Py_ssize_t optvarsize = datasize;
+                    if (convert_struct_from_object(NULL, ctitem, init,
+                                                   &optvarsize) < 0)
+                        return NULL;
+                    datasize = optvarsize;
+                }
             }
         }
     }
@@ -3259,7 +3280,9 @@
         /* store the only reference to cds into cd */
         ((CDataObject_own_structptr *)cd)->structobj = (PyObject *)cds;
         /* store information about the allocated size of the struct */
-        ((CDataObject_own_length *)cds)->length = datasize;
+        if (dataoffset == offsetof(CDataObject_own_length, alignment)) {
+            ((CDataObject_own_length *)cds)->length = datasize;
+        }
         assert(explicitlength < 0);
 
         cd->c_data = cds->c_data;
@@ -4331,9 +4354,7 @@
             /* not a bitfield: common case */
             int bs_flag;
 
-            if ((i == nb_fields - 1) && (ct->ct_flags & CT_WITH_VAR_ARRAY))
-                bs_flag = BS_VARSIZESTRUCT_ARRAY;
-            else if (ftype->ct_flags & CT_ARRAY && ftype->ct_length == 0)
+            if ((ftype->ct_flags & CT_ARRAY) && ftype->ct_length <= 0)
                 bs_flag = BS_EMPTY_ARRAY;
             else
                 bs_flag = BS_REGULAR;
@@ -5875,12 +5896,10 @@
                                      &CData_Type, &cd, &size))
         return NULL;
 
-    if ((cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) && CDataOwn_Check(cd)) {
-        abort();
-        if (size < 0)
-            size = ((CDataObject_own_structptr *)cd)->length;
-    }
-    else if (cd->c_type->ct_flags & CT_POINTER) {
+    if (size < 0)
+        size = _cdata_var_byte_size(cd);
+
+    if (cd->c_type->ct_flags & CT_POINTER) {
         if (size < 0)
             size = cd->c_type->ct_itemdescr->ct_size;
     }
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -3172,7 +3172,7 @@
     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 == -3
+    assert d[1][1].bitshift == -2
     assert d[1][1].bitsize == -1
     #
     p = newp(new_pointer_type(BStruct))
@@ -3202,7 +3202,6 @@
         assert len(p.y) == 3
         assert len(p[0].y) == 3
         assert len(buffer(p)) == sizeof(BInt) * 4
-        assert len(buffer(p[0])) == sizeof(BInt) * 4
         plist.append(p)
     for i in range(20):
         p = plist[i]
diff --git a/testing/cffi1/test_new_ffi_1.py b/testing/cffi1/test_new_ffi_1.py
--- a/testing/cffi1/test_new_ffi_1.py
+++ b/testing/cffi1/test_new_ffi_1.py
@@ -1633,10 +1633,19 @@
         # struct array_no_length { int x; int a[]; };
         p = ffi.new("struct array_no_length *", [100, [200, 300, 400]])
         assert p.x == 100
-        assert ffi.typeof(p.a) is ffi.typeof("int *")   # no length available
+        assert ffi.typeof(p.a) is ffi.typeof("int[]")   # length available
         assert p.a[0] == 200
         assert p.a[1] == 300
         assert p.a[2] == 400
+        assert len(p.a) == 3
+        assert list(p.a) == [200, 300, 400]
+        q = ffi.cast("struct array_no_length *", p)
+        assert ffi.typeof(q.a) is ffi.typeof("int *")   # no length available
+        assert q.a[0] == 200
+        assert q.a[1] == 300
+        assert q.a[2] == 400
+        py.test.raises(TypeError, len, q.a)
+        py.test.raises(TypeError, list, q.a)
 
     def test_from_buffer(self):
         import array


More information about the pypy-commit mailing list