summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Rigo <arigo@tunes.org>2013-02-12 18:26:54 +0100
committerArmin Rigo <arigo@tunes.org>2013-02-12 18:26:54 +0100
commit57c9442f0aeab7ec5a25c7b09357bfed71e634ee (patch)
treeb0f38d78162ffa9349ad8f614ce6180082e68daa
parentddbaf3b0def21815bca2dfce023e159ea73c36dd (diff)
parent6c512925f9da6464cb4b972f9c7e862ebb1f78bc (diff)
downloadcffi-57c9442f0aeab7ec5a25c7b09357bfed71e634ee.tar.gz
hg merge enum-as-int
-rw-r--r--c/_cffi_backend.c183
-rw-r--r--c/test_c.py82
-rw-r--r--doc/source/index.rst21
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