diff options
author | Armin Rigo <arigo@tunes.org> | 2019-06-03 20:11:03 +0200 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2019-06-03 20:11:03 +0200 |
commit | f6c550466541a029465f076e691bbcfe15251ab1 (patch) | |
tree | e15a1f375693f289a57119f22f9bdb0005709ca6 /c | |
parent | 32eb057f4dbe37584988b934dd46cbcc535f2397 (diff) | |
download | cffi-f6c550466541a029465f076e691bbcfe15251ab1.tar.gz |
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()).
Diffstat (limited to 'c')
-rw-r--r-- | c/_cffi_backend.c | 67 | ||||
-rw-r--r-- | c/test_c.py | 62 |
2 files changed, 36 insertions, 93 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index c201472..bcbe45a 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -248,8 +248,7 @@ static PyTypeObject CDataGCP_Type; 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 { 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 @@ static void cdataowning_dealloc(CDataObject *cd) 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 *_frombuf_repr(CDataObject *cd, const char *cd_type_name) 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 @@ static int explicit_release_case(PyObject *cd) 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 @@ static PyObject *cdata_exit(PyObject *cd, PyObject *args) 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 @@ static PyTypeObject CDataFromBuf_Type = { 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 @@ static int _my_PyObject_GetContiguousBuffer(PyObject *x, Py_buffer *view, 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 @@ static PyObject *direct_from_buffer(CTypeDescrObject *ct1, PyObject *x, 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 @@ static PyObject *direct_from_buffer(CTypeDescrObject *ct1, PyObject *x, ((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 index 4617ac1..555aafb 100644 --- a/c/test_c.py +++ b/c/test_c.py @@ -3864,12 +3864,13 @@ def test_from_buffer_types(): 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 @@ def test_from_buffer_types(): 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 @@ def test_from_buffer_types(): 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") |