diff options
author | Armin Rigo <arigo@tunes.org> | 2013-02-12 18:26:54 +0100 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2013-02-12 18:26:54 +0100 |
commit | 57c9442f0aeab7ec5a25c7b09357bfed71e634ee (patch) | |
tree | b0f38d78162ffa9349ad8f614ce6180082e68daa | |
parent | ddbaf3b0def21815bca2dfce023e159ea73c36dd (diff) | |
parent | 6c512925f9da6464cb4b972f9c7e862ebb1f78bc (diff) | |
download | cffi-57c9442f0aeab7ec5a25c7b09357bfed71e634ee.tar.gz |
hg merge enum-as-int
-rw-r--r-- | c/_cffi_backend.c | 183 | ||||
-rw-r--r-- | c/test_c.py | 82 | ||||
-rw-r--r-- | doc/source/index.rst | 21 |
3 files changed, 272 insertions, 14 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index c6d6efe..a9abfd5 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -102,7 +102,8 @@ typedef struct _ctypedescr { PyObject *ct_stuff; /* structs: dict of the fields arrays: ctypedescr of the ptr type function: tuple(abi, ctres, ctargs..) - enum: pair {"name":x},{x:"name"} */ + enum: pair {"name":x},{x:"name"} + ptrs: lazily, ctypedescr of array */ void *ct_extra; /* structs: first field (not a ref!) function types: cif_description primitives: prebuilt "cif" object */ @@ -1463,6 +1464,9 @@ static PyObject *cdata_repr(CDataObject *cd) Py_DECREF(o); } } + else if ((cd->c_type->ct_flags & CT_ARRAY) && cd->c_type->ct_length < 0) { + s = PyText_FromFormat("sliced length %zd", get_array_length(cd)); + } else { if (cd->c_data != NULL) { s = PyText_FromFormat("%p", cd->c_data); @@ -1690,9 +1694,155 @@ _cdata_get_indexed_ptr(CDataObject *cd, PyObject *key) } static PyObject * +new_array_type(CTypeDescrObject *ctptr, PyObject *lengthobj); /* forward */ + +static CTypeDescrObject * +_cdata_getslicearg(CDataObject *cd, PySliceObject *slice, Py_ssize_t bounds[]) +{ + Py_ssize_t start, stop; + CTypeDescrObject *ct; + + start = PyInt_AsSsize_t(slice->start); + if (start == -1 && PyErr_Occurred()) { + if (slice->start == Py_None) + PyErr_SetString(PyExc_IndexError, "slice start must be specified"); + return NULL; + } + stop = PyInt_AsSsize_t(slice->stop); + if (stop == -1 && PyErr_Occurred()) { + if (slice->stop == Py_None) + PyErr_SetString(PyExc_IndexError, "slice stop must be specified"); + return NULL; + } + if (slice->step != Py_None) { + PyErr_SetString(PyExc_IndexError, "slice with step not supported"); + return NULL; + } + if (start > stop) { + PyErr_SetString(PyExc_IndexError, "slice start > stop"); + return NULL; + } + + ct = cd->c_type; + if (ct->ct_flags & CT_ARRAY) { + if (start < 0) { + PyErr_SetString(PyExc_IndexError, + "negative index not supported"); + return NULL; + } + if (stop >= get_array_length(cd)) { + PyErr_Format(PyExc_IndexError, + "index too large (expected %zd < %zd)", + stop, get_array_length(cd)); + return NULL; + } + ct = (CTypeDescrObject *)ct->ct_stuff; + } + else if (!(ct->ct_flags & CT_POINTER)) { + PyErr_Format(PyExc_TypeError, "cdata of type '%s' cannot be indexed", + ct->ct_name); + return NULL; + } + + bounds[0] = start; + bounds[1] = stop - start; + return ct; +} + +static PyObject * +cdata_slice(CDataObject *cd, PySliceObject *slice) +{ + Py_ssize_t bounds[2]; + CDataObject_own_length *scd; + CTypeDescrObject *ct = _cdata_getslicearg(cd, slice, bounds); + if (ct == NULL) + return NULL; + + if (ct->ct_stuff == NULL) { + ct->ct_stuff = new_array_type(ct, Py_None); + if (ct->ct_stuff == NULL) + return NULL; + } + ct = (CTypeDescrObject *)ct->ct_stuff; + + scd = (CDataObject_own_length *)PyObject_Malloc( + offsetof(CDataObject_own_length, alignment)); + if (PyObject_Init((PyObject *)scd, &CData_Type) == NULL) + return NULL; + Py_INCREF(ct); + scd->head.c_type = ct; + scd->head.c_data = cd->c_data + ct->ct_itemdescr->ct_size * bounds[0]; + scd->head.c_weakreflist = NULL; + scd->length = bounds[1]; + return (PyObject *)scd; +} + +static int +cdata_ass_slice(CDataObject *cd, PySliceObject *slice, PyObject *v) +{ + Py_ssize_t bounds[2], i, length, itemsize; + PyObject *it, *item; + PyObject *(*iternext)(PyObject *); + char *cdata; + int err; + CTypeDescrObject *ct = _cdata_getslicearg(cd, slice, bounds); + if (ct == NULL) + return -1; + ct = ct->ct_itemdescr; + itemsize = ct->ct_size; + cdata = cd->c_data + itemsize * bounds[0]; + length = bounds[1]; + + if (CData_Check(v)) { + CTypeDescrObject *ctv = ((CDataObject *)v)->c_type; + if ((ctv->ct_flags & CT_ARRAY) && (ctv->ct_itemdescr == ct) && + (get_array_length((CDataObject *)v) == length)) { + /* fast path: copying from exactly the correct type */ + memcpy(cdata, ((CDataObject *)v)->c_data, itemsize * length); + return 0; + } + } + + it = PyObject_GetIter(v); + if (it == NULL) + return -1; + iternext = *it->ob_type->tp_iternext; + + for (i = 0; i < length; i++) { + item = iternext(it); + if (item == NULL) { + if (!PyErr_Occurred()) + PyErr_Format(PyExc_ValueError, + "need %zd values to unpack, got %zd", + length, i); + goto error; + } + err = convert_from_object(cdata, ct, item); + Py_DECREF(item); + if (err < 0) + goto error; + + cdata += itemsize; + } + item = iternext(it); + if (item != NULL) { + Py_DECREF(item); + PyErr_Format(PyExc_ValueError, + "got more than %zd values to unpack", length); + } + error: + Py_DECREF(it); + return PyErr_Occurred() ? -1 : 0; +} + +static PyObject * cdataowning_subscript(CDataObject *cd, PyObject *key) { - char *c = _cdata_get_indexed_ptr(cd, key); + char *c; + if (PySlice_Check(key)) + return cdata_slice(cd, (PySliceObject *)key); + + c = _cdata_get_indexed_ptr(cd, key); /* use 'mp_subscript' instead of 'sq_item' because we don't want negative indexes to be corrected automatically */ if (c == NULL && PyErr_Occurred()) @@ -1711,7 +1861,11 @@ cdataowning_subscript(CDataObject *cd, PyObject *key) static PyObject * cdata_subscript(CDataObject *cd, PyObject *key) { - char *c = _cdata_get_indexed_ptr(cd, key); + char *c; + if (PySlice_Check(key)) + return cdata_slice(cd, (PySliceObject *)key); + + c = _cdata_get_indexed_ptr(cd, key); /* use 'mp_subscript' instead of 'sq_item' because we don't want negative indexes to be corrected automatically */ if (c == NULL && PyErr_Occurred()) @@ -1722,8 +1876,13 @@ cdata_subscript(CDataObject *cd, PyObject *key) static int cdata_ass_sub(CDataObject *cd, PyObject *key, PyObject *v) { - char *c = _cdata_get_indexed_ptr(cd, key); - CTypeDescrObject *ctitem = cd->c_type->ct_itemdescr; + char *c; + CTypeDescrObject *ctitem; + if (PySlice_Check(key)) + return cdata_ass_slice(cd, (PySliceObject *)key, v); + + c = _cdata_get_indexed_ptr(cd, key); + ctitem = cd->c_type->ct_itemdescr; /* use 'mp_ass_subscript' instead of 'sq_ass_item' because we don't want negative indexes to be corrected automatically */ if (c == NULL && PyErr_Occurred()) @@ -3087,14 +3246,22 @@ static PyObject *b_new_pointer_type(PyObject *self, PyObject *args) static PyObject *b_new_array_type(PyObject *self, PyObject *args) { PyObject *lengthobj; - CTypeDescrObject *td, *ctitem, *ctptr; - char extra_text[32]; - Py_ssize_t length, arraysize; + CTypeDescrObject *ctptr; if (!PyArg_ParseTuple(args, "O!O:new_array_type", &CTypeDescr_Type, &ctptr, &lengthobj)) return NULL; + return new_array_type(ctptr, lengthobj); +} + +static PyObject * +new_array_type(CTypeDescrObject *ctptr, PyObject *lengthobj) +{ + CTypeDescrObject *td, *ctitem; + char extra_text[32]; + Py_ssize_t length, arraysize; + if (!(ctptr->ct_flags & CT_POINTER)) { PyErr_SetString(PyExc_TypeError, "first arg must be a pointer ctype"); return NULL; diff --git a/c/test_c.py b/c/test_c.py index 7b75a70..84513cf 100644 --- a/c/test_c.py +++ b/c/test_c.py @@ -2651,3 +2651,85 @@ def test_buffer_keepalive(): for i in range(20): buf = buflist[i] assert buf[:] == str2bytes("hi there %d\x00" % i) + +def test_slice(): + BIntP = new_pointer_type(new_primitive_type("int")) + BIntArray = new_array_type(BIntP, None) + c = newp(BIntArray, 5) + assert len(c) == 5 + assert repr(c) == "<cdata 'int[]' owning 20 bytes>" + d = c[1:4] + assert len(d) == 3 + assert repr(d) == "<cdata 'int[]' sliced length 3>" + d[0] = 123 + d[2] = 456 + assert c[1] == 123 + assert c[3] == 456 + assert d[2] == 456 + py.test.raises(IndexError, "d[3]") + py.test.raises(IndexError, "d[-1]") + +def test_slice_ptr(): + BIntP = new_pointer_type(new_primitive_type("int")) + BIntArray = new_array_type(BIntP, None) + c = newp(BIntArray, 5) + d = (c+1)[0:2] + assert len(d) == 2 + assert repr(d) == "<cdata 'int[]' sliced length 2>" + d[1] += 50 + assert c[2] == 50 + +def test_slice_array_checkbounds(): + BIntP = new_pointer_type(new_primitive_type("int")) + BIntArray = new_array_type(BIntP, None) + c = newp(BIntArray, 5) + py.test.raises(IndexError, "c[-1:1]") + cp = c + 0 + cp[-1:1] + +def test_nonstandard_slice(): + BIntP = new_pointer_type(new_primitive_type("int")) + BIntArray = new_array_type(BIntP, None) + c = newp(BIntArray, 5) + e = py.test.raises(IndexError, "c[:5]") + assert str(e.value) == "slice start must be specified" + e = py.test.raises(IndexError, "c[4:]") + assert str(e.value) == "slice stop must be specified" + e = py.test.raises(IndexError, "c[1:2:3]") + assert str(e.value) == "slice with step not supported" + e = py.test.raises(IndexError, "c[1:2:1]") + assert str(e.value) == "slice with step not supported" + e = py.test.raises(IndexError, "c[4:2]") + assert str(e.value) == "slice start > stop" + e = py.test.raises(IndexError, "c[6:6]") + assert str(e.value) == "index too large (expected 6 < 5)" + +def test_setslice(): + BIntP = new_pointer_type(new_primitive_type("int")) + BIntArray = new_array_type(BIntP, None) + c = newp(BIntArray, 5) + c[1:3] = [100, 200] + assert list(c) == [0, 100, 200, 0, 0] + cp = c + 3 + cp[-1:1] = [300, 400] + assert list(c) == [0, 100, 300, 400, 0] + cp[-1:1] = iter([500, 600]) + assert list(c) == [0, 100, 500, 600, 0] + py.test.raises(ValueError, "cp[-1:1] = [1000]") + assert list(c) == [0, 100, 1000, 600, 0] + py.test.raises(ValueError, "cp[-1:1] = (700, 800, 900)") + assert list(c) == [0, 100, 700, 800, 0] + +def test_setslice_array(): + BIntP = new_pointer_type(new_primitive_type("int")) + BIntArray = new_array_type(BIntP, None) + c = newp(BIntArray, 5) + d = newp(BIntArray, [10, 20, 30]) + c[1:4] = d + assert list(c) == [0, 10, 20, 30, 0] + # + BShortP = new_pointer_type(new_primitive_type("short")) + BShortArray = new_array_type(BShortP, None) + d = newp(BShortArray, [40, 50]) + c[1:3] = d + assert list(c) == [0, 40, 50, 30, 0] diff --git a/doc/source/index.rst b/doc/source/index.rst index 526cd01..e30a18f 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1276,9 +1276,9 @@ allowed. | | anything on which | precision `(***)`| | | | float() works | | | +---------------+------------------------+------------------+----------------+ -| pointers | another <cdata> with | a <cdata> | ``[]``, ``+``, | -| | a compatible type (i.e.| | ``-``, bool() | -| | same type or ``char*`` | | | +| pointers | another <cdata> with | a <cdata> |``[]`` `(****)`,| +| | a compatible type (i.e.| |``+``, ``-``, | +| | same type or ``char*`` | |bool() | | | or ``void*``, or as an | | | | | array instead) `(*)` | | | +---------------+------------------------+ | | @@ -1294,9 +1294,9 @@ allowed. | function | same as pointers | | bool(), | | pointers | | | call `(**)` | +---------------+------------------------+------------------+----------------+ -| arrays | a list or tuple of | a <cdata> | len(), iter(), | -| | items | | ``[]``, | -| | | | ``+``, ``-`` | +| arrays | a list or tuple of | a <cdata> |len(), iter(), | +| | items | |``[]`` `(****)`,| +| | | |``+``, ``-`` | +---------------+------------------------+ +----------------+ | ``char[]`` | same as arrays, or a | | len(), iter(), | | | Python string | | ``[]``, ``+``, | @@ -1346,6 +1346,15 @@ a pointer inside the Python string object. without any precision loss, you need to define and use a family of C functions like ``long double add(long double a, long double b);``. +.. versionadded:: 0.6 + `(****)` Supports simple slices as well: ``x[start:stop]`` gives another + cdata object that is a "view" of all items from ``start`` to ``stop``. + It is a cdata of type "array" (so e.g. passing it as an argument to a + C function would just convert it to a pointer to the ``start`` item). + As with indexing, negative bounds mean really negative indices, like in + C. As for slice assignment, it accepts any iterable, including a list + of items or another array-like cdata object, but the length must match. + .. versionchanged:: 0.6 `(*****)` Enums are now handled like ints (unsigned or signed, int or long, like GCC; note that the first choice is unsigned). In previous |