Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r3276:5aa806ec6d00
Date: 2019-06-03 20:11 +0200
http://bitbucket.org/cffi/cffi/changeset/5aa806ec6d00/

Log:    Simplify the implementation, cut down the explanations, and allow
        accessing multiple items instead of just one in from_buffer("type
        *"), trading off security for the realisation that it makes sense to
        access multiple items in some cases (unlike after ffi.new()).

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -248,8 +248,7 @@
                                Py_TYPE(ob) == &CDataFromBuf_Type ||     \
                                Py_TYPE(ob) == &CDataGCP_Type)
 #define CDataOwn_Check(ob)    (Py_TYPE(ob) == &CDataOwning_Type ||      \
-                               Py_TYPE(ob) == &CDataOwningGC_Type ||    \
-                               Py_TYPE(ob) == &CDataFromBuf_Type)
+                               Py_TYPE(ob) == &CDataOwningGC_Type)
 
 typedef union {
     unsigned char m_char;
@@ -280,8 +279,7 @@
 
 typedef struct {
     CDataObject head;
-    PyObject *structobj;   /* for ffi.new_handle(), ffi.new("struct *"),
-                              or ffi.from_buffer("struct *") */
+    PyObject *structobj;   /* for ffi.new_handle() or ffi.new("struct *") */
 } CDataObject_own_structptr;
 
 typedef struct {
@@ -1857,7 +1855,7 @@
     assert(!(cd->c_type->ct_flags & (CT_IS_VOID_PTR | CT_FUNCTIONPTR)));
 
     if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
-        /* for ffi.new("struct *") or ffi.from_buffer("struct *") */
+        /* for ffi.new("struct *") */
         Py_DECREF(((CDataObject_own_structptr *)cd)->structobj);
     }
 #if defined(CFFI_MEM_DEBUG) || defined(CFFI_MEM_LEAK)
@@ -2164,15 +2162,7 @@
 
 static PyObject *cdataowning_repr(CDataObject *cd)
 {
-    Py_ssize_t size;
-
-    if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) {
-        PyObject *x = ((CDataObject_own_structptr *)cd)->structobj;
-        if (Py_TYPE(x) == &CDataFromBuf_Type)
-            return _frombuf_repr((CDataObject *)x, cd->c_type->ct_name);
-    }
-
-    size = _cdata_var_byte_size(cd);
+    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;
@@ -3214,7 +3204,7 @@
     CTypeDescrObject *ct = ((CDataObject *)cd)->c_type;
     if (Py_TYPE(cd) == &CDataOwning_Type) {
         if ((ct->ct_flags & (CT_POINTER | CT_ARRAY)) != 0)   /* ffi.new() */
-            return 0;                             /* or ffi.from_buffer() */
+            return 0;
     }
     else if (Py_TYPE(cd) == &CDataFromBuf_Type) {
         return 1;    /* ffi.from_buffer() */
@@ -3257,11 +3247,6 @@
                        ffi.new_allocator()("struct-or-union *") */
                     cdatagcp_finalize((CDataObject_gcp *)x);
                 }
-                else if (Py_TYPE(x) == &CDataFromBuf_Type) {
-                    /* special case for ffi.from_buffer("struct *") */
-                    view = ((CDataObject_frombuf *)x)->bufferview;
-                    PyBuffer_Release(view);
-                }
             }
             break;
 
@@ -3497,7 +3482,7 @@
     0,  /* inherited */                         /* tp_methods */
     0,                                          /* tp_members */
     0,                                          /* tp_getset */
-    &CDataOwning_Type,                          /* tp_base */
+    &CData_Type,                                /* tp_base */
     0,                                          /* tp_dict */
     0,                                          /* tp_descr_get */
     0,                                          /* tp_descr_set */
@@ -6952,13 +6937,12 @@
     return 0;
 }
 
-static PyObject *direct_from_buffer(CTypeDescrObject *ct1, PyObject *x,
+static PyObject *direct_from_buffer(CTypeDescrObject *ct, PyObject *x,
                                     int require_writable)
 {
     CDataObject *cd;
-    CTypeDescrObject *ct = ct1;
     Py_buffer *view;
-    Py_ssize_t arraylength, minimumlength = -1;
+    Py_ssize_t arraylength, minimumlength = 0;
 
     if (!(ct->ct_flags & (CT_ARRAY | CT_POINTER))) {
         PyErr_Format(PyExc_TypeError,
@@ -6987,25 +6971,7 @@
 
     if (ct->ct_flags & CT_POINTER)
     {
-        /* from_buffer("T*"): return a cdata pointer.  Complain if the
-           size of the buffer is known to be smaller than sizeof(T),
-           i.e. never complain if sizeof(T) is not known at all. */
-        CTypeDescrObject *ctitem = ct->ct_itemdescr;
-        minimumlength = ctitem->ct_size;   /* maybe -1 */
-        arraylength = -1;
-
-        if (ct->ct_flags & CT_IS_PTR_TO_OWNED) {
-            ct = ctitem;
-            if (force_lazy_struct(ct) < 0)   /* for CT_WITH_VAR_ARRAY */
-                return NULL;
-
-            if (ct->ct_flags & CT_WITH_VAR_ARRAY) {
-                assert(ct1->ct_flags & CT_IS_PTR_TO_OWNED);
-                arraylength = view->len;
-                if (ct->ct_size > 1)
-                    arraylength = (arraylength / ct->ct_size) * ct->ct_size;
-            }
-        }
+        arraylength = view->len;   /* number of bytes, not used so far */
     }
     else {
         /* ct->ct_flags & CT_ARRAY */
@@ -7058,21 +7024,6 @@
     ((CDataObject_frombuf *)cd)->length = arraylength;
     ((CDataObject_frombuf *)cd)->bufferview = view;
     PyObject_GC_Track(cd);
-
-    if (ct1->ct_flags & CT_IS_PTR_TO_OWNED)     /* ptr-to-struct/union */
-    {
-        CDataObject *cds = cd;
-        cd = allocate_owning_object(sizeof(CDataObject_own_structptr), ct1,
-                                    /*dont_clear=*/1);
-        if (cd == NULL) {
-            Py_DECREF(cds);
-            goto error2;
-        }
-        /* store the only reference to cds into cd */
-        ((CDataObject_own_structptr *)cd)->structobj = (PyObject *)cds;
-
-        cd->c_data = cds->c_data;
-    }
     return (PyObject *)cd;
 
  error2:
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -3864,12 +3864,13 @@
     assert p2 == p1
     assert typeof(p2) is BIntP
     assert p2[0] == lst[0]
-    with pytest.raises(IndexError):
-        p2[1]
-    with pytest.raises(IndexError):
-        p2[-1]
-    with pytest.raises(ValueError):
-        from_buffer(BIntP, b"abc")      # not enough data
+    assert p2[1] == lst[1]
+    assert p2[2] == lst[2]
+    # hopefully does not crash, but doesn't raise an exception:
+    p2[3]
+    p2[-1]
+    # not enough data even for one, but this is not enforced:
+    from_buffer(BIntP, b"")
     #
     BIntA2 = new_array_type(BIntP, 2)
     p2 = from_buffer(BIntA2, bytestring)     # int[2]
@@ -3911,26 +3912,18 @@
     assert p2.a2 == lst2[1]
     assert p2[0].a1 == lst2[0]
     assert p2[0].a2 == lst2[1]
-    with pytest.raises(IndexError):
-        p2[1]
-    with pytest.raises(IndexError):
-        p2[-1]
-    with pytest.raises(ValueError):
-        from_buffer(BStructP, b"1234567")     # not enough data
+    assert p2[1].a1 == lst2[2]
+    assert p2[1].a2 == lst2[3]
+    # does not crash:
+    p2[2]
+    p2[-1]
+    # not enough data even for one, but this is not enforced:
+    from_buffer(BStructP, b"")
+    from_buffer(BStructP, b"1234567")
     #
     release(p1)
     assert repr(p1) == "<cdata 'foo[]' buffer RELEASED>"
     #
-    # keepalive behaviour similar to newp()
-    plst = []
-    for i in range(100):
-        plst.append(from_buffer(BStructP, buffer(newp(BStructP, [i, -i])))[0])
-        if i % 10 == 5:
-            import gc; gc.collect()
-    for i in range(len(plst)):
-        assert plst[i].a1 == i
-        assert plst[i].a2 == -i
-    #
     BEmptyStruct = new_struct_type("empty")
     complete_struct_or_union(BEmptyStruct, [], Ellipsis, 0)
     assert sizeof(BEmptyStruct) == 0
@@ -3955,25 +3948,24 @@
     assert pv.a1 == lst[0]
     assert pv.va[0] == lst[1]
     assert pv.va[1] == lst[2]
-    assert sizeof(pv[0]) == 3 * size_of_int()
-    assert len(pv.va) == 2
-    with pytest.raises(IndexError):
-        pv.va[2]
-    with pytest.raises(IndexError):
-        pv.va[-1]
-    with pytest.raises(ValueError):
-        from_buffer(BVarStructP, b"123")     # not enough data
+    assert sizeof(pv[0]) == 1 * size_of_int()
+    with pytest.raises(TypeError):
+        len(pv.va)
+    # hopefully does not crash, but doesn't raise an exception:
+    pv.va[2]
+    pv.va[-1]
+    # not enough data even for one, but this is not enforced:
+    from_buffer(BVarStructP, b"")
     assert repr(pv) == "<cdata 'varfoo *' buffer from 'bytearray' object>"
-    assert repr(pv[0]) == "<cdata 'varfoo' buffer from 'bytearray' object>"
+    assert repr(pv[0]).startswith("<cdata 'varfoo &' ")
     #
     release(pv)
     assert repr(pv) == "<cdata 'varfoo *' buffer RELEASED>"
-    assert repr(pv[0]) == "<cdata 'varfoo' buffer RELEASED>"
+    assert repr(pv[0]).startswith("<cdata 'varfoo &' ")
     #
     pv = from_buffer(BVarStructP, bytestring)    # make a fresh one
-    release(pv[0])
-    assert repr(pv) == "<cdata 'varfoo *' buffer RELEASED>"
-    assert repr(pv[0]) == "<cdata 'varfoo' buffer RELEASED>"
+    with pytest.raises(ValueError):
+        release(pv[0])
 
 def test_memmove():
     Short = new_primitive_type("short")
diff --git a/doc/source/ref.rst b/doc/source/ref.rst
--- a/doc/source/ref.rst
+++ b/doc/source/ref.rst
@@ -248,15 +248,11 @@
   keeps the underlying Python object alive and locked.  (In addition,
   ``ffi.from_buffer("int[]", x)`` gives better array bound checking.)
 
-  *New in version 1.13:* ``cdecl`` can be a pointer type.  Then
-  ``from_buffer()`` raises a ValueError only if the type pointed to is of
-  known size and the buffer is smaller than that; but note that the result
-  ``p`` can only be indexed with ``p[0]`` even if the buffer is large enough
-  to contain several copies of the type.  If the type is a struct or union,
-  this allows you to write ``p.field`` instead of the required ``p[0].field``
-  if you had called ``from_buffer("struct s[1]")``; and also, in this case
-  either ``p`` or ``p[0]`` can be used to keep the buffer alive, similarly
-  to ``ffi.new()``.
+  *New in version 1.13:* ``cdecl`` can be a pointer type.  If it points
+  to a struct or union, you can, as usual, write ``p.field`` instead of
+  ``p[0].field``.  You can also access ``p[n]``; note that CFFI does not
+  perform any bounds checking in this case.  Note also that ``p[0]`` cannot
+  be used to keep the buffer alive (unlike what occurs with ``ffi.new()``).
 
 * if ``require_writable`` is set to True, the function fails if the buffer
   obtained from ``python_buffer`` is read-only (e.g. if ``python_buffer`` is
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
--- a/doc/source/whatsnew.rst
+++ b/doc/source/whatsnew.rst
@@ -7,13 +7,9 @@
 =====
 
 * ``ffi.from_buffer("type *", ..)`` is now supported, in addition to
-  ``"type[]"``.  This is useful for buffers that contain only one ``type``.
-  (The ``type`` can also be a structure with an open-ended array as the last
-  item.)  You can then write ``p.field`` to access the items, instead of
-  ``p[0].field``.  The expression ``p[0]`` by itself behaves like it does
-  for ``p = ffi.new("struct-or-union *")``: you get a cdata object whose
-  type is the structure or union, but which keeps alive the original buffer
-  object even if ``p`` goes out of scope.
+  ``"type[]"``.  You can then write ``p.field`` to access the items, instead
+  of ``p[0].field``.  Be careful that no bounds checking is performed, so
+  ``p[n]`` might access data out of bounds.
 
 
 v1.12.3
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to