[pypy-commit] cffi default: hg merge slicing

arigo noreply at buildbot.pypy.org
Tue Feb 12 20:29:36 CET 2013


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r1149:62db8ccc85d2
Date: 2013-02-12 18:19 +0100
http://bitbucket.org/cffi/cffi/changeset/62db8ccc85d2/

Log:	hg merge slicing

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -102,7 +102,8 @@
     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 */
@@ -1490,6 +1491,9 @@
             s = PyText_FromString(buffer);
         }
     }
+    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);
@@ -1717,9 +1721,155 @@
 }
 
 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())
@@ -1738,7 +1888,11 @@
 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())
@@ -1749,8 +1903,13 @@
 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())
@@ -3122,14 +3281,22 @@
 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
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -2574,3 +2574,85 @@
     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
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -1274,9 +1274,9 @@
 |               | 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) `(*)`   |                  |                |
 +---------------+------------------------+                  |                |
@@ -1292,9 +1292,9 @@
 | 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          |                  | ``[]``, ``+``, |
@@ -1349,6 +1349,15 @@
    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.
+
 
 Reference: verifier
 -------------------


More information about the pypy-commit mailing list