Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r2330:055b350b5272
Date: 2015-10-08 22:54 +0200
http://bitbucket.org/cffi/cffi/changeset/055b350b5272/

Log:    ffi.memmove()

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -5651,7 +5651,8 @@
     return x;
 }
 
-static int _my_PyObject_GetContiguousBuffer(PyObject *x, Py_buffer *view)
+static int _my_PyObject_GetContiguousBuffer(PyObject *x, Py_buffer *view,
+                                            int writable_only)
 {
 #if PY_MAJOR_VERSION < 3
     /* Some objects only support the buffer interface and CPython doesn't
@@ -5664,10 +5665,19 @@
         /* we used to try all three in some vaguely sensible order,
            i.e. first the write.  But trying to call the write on a
            read-only buffer fails with TypeError.  So we use a less-
-           sensible order now.  See test_from_buffer_more_cases. */
-        readbufferproc proc = (readbufferproc)pb->bf_getreadbuffer;
-        if (!proc)     proc = (readbufferproc)pb->bf_getcharbuffer;
-        if (!proc)     proc = (readbufferproc)pb->bf_getwritebuffer;
+           sensible order now.  See test_from_buffer_more_cases.
+
+           If 'writable_only', we only try bf_getwritebuffer.
+        */
+        readbufferproc proc = NULL;
+        if (!writable_only) {
+            proc = (readbufferproc)pb->bf_getreadbuffer;
+            if (!proc)
+                proc = (readbufferproc)pb->bf_getcharbuffer;
+        }
+        if (!proc)
+            proc = (readbufferproc)pb->bf_getwritebuffer;
+
         if (proc && pb->bf_getsegcount) {
             if ((*pb->bf_getsegcount)(x, NULL) != 1) {
                 PyErr_SetString(PyExc_TypeError,
@@ -5684,7 +5694,8 @@
     }
 #endif
 
-    if (PyObject_GetBuffer(x, view, PyBUF_SIMPLE) < 0)
+    if (PyObject_GetBuffer(x, view, writable_only ? PyBUF_WRITABLE
+                                                  : PyBUF_SIMPLE) < 0)
         return -1;
 
     if (!PyBuffer_IsContiguous(view, 'A')) {
@@ -5743,7 +5754,7 @@
     }
 
     view = PyObject_Malloc(sizeof(Py_buffer));
-    if (_my_PyObject_GetContiguousBuffer(x, view) < 0)
+    if (_my_PyObject_GetContiguousBuffer(x, view, 0) < 0)
         goto error1;
 
     cd = (CDataObject *)PyObject_GC_New(CDataObject_owngc_frombuf,
@@ -5782,6 +5793,52 @@
     return direct_from_buffer(ct, x);
 }
 
+static int _fetch_as_buffer(PyObject *x, Py_buffer *view, int writable_only)
+{
+    if (CData_Check(x)) {
+        CTypeDescrObject *ct = ((CDataObject *)x)->c_type;
+        if (!(ct->ct_flags & (CT_POINTER|CT_ARRAY))) {
+            PyErr_Format(PyExc_TypeError,
+                         "expected a pointer or array ctype, got '%s'",
+                         ct->ct_name);
+            return -1;
+        }
+        view->buf = ((CDataObject *)x)->c_data;
+        view->obj = NULL;
+        return 0;
+    }
+    else {
+        return _my_PyObject_GetContiguousBuffer(x, view, writable_only);
+    }
+}
+
+static PyObject *b_memmove(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    PyObject *dest_obj, *src_obj;
+    Py_buffer dest_view, src_view;
+    Py_ssize_t n;
+    static char *keywords[] = {"dest", "src", "n", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOn", keywords,
+                                     &dest_obj, &src_obj, &n))
+        return NULL;
+
+    if (_fetch_as_buffer(src_obj, &src_view, 0) < 0) {
+        return NULL;
+    }
+    if (_fetch_as_buffer(dest_obj, &dest_view, 1) < 0) {
+        PyBuffer_Release(&src_view);
+        return NULL;
+    }
+
+    memmove(dest_view.buf, src_view.buf, n);
+
+    PyBuffer_Release(&dest_view);
+    PyBuffer_Release(&src_view);
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
 static PyObject *b__get_types(PyObject *self, PyObject *noarg)
 {
     return PyTuple_Pack(2, (PyObject *)&CData_Type,
@@ -6108,6 +6165,7 @@
     {"newp_handle", b_newp_handle, METH_VARARGS},
     {"from_handle", b_from_handle, METH_O},
     {"from_buffer", b_from_buffer, METH_VARARGS},
+    {"memmove", (PyCFunction)b_memmove, METH_VARARGS | METH_KEYWORDS},
     {"gcp", (PyCFunction)b_gcp, METH_VARARGS | METH_KEYWORDS},
 #ifdef MS_WIN32
     {"getwinerror", (PyCFunction)b_getwinerror, METH_VARARGS | METH_KEYWORDS},
diff --git a/c/ffi_obj.c b/c/ffi_obj.c
--- a/c/ffi_obj.c
+++ b/c/ffi_obj.c
@@ -852,6 +852,24 @@
     return x;
 }
 
+PyDoc_STRVAR(ffi_memmove_doc,
+"ffi.memmove(dest, src, n) copies n bytes of memory from src to dest.\n"
+"\n"
+"Like the C function memmove(), the memory areas may overlap;\n"
+"apart from that it behaves like the C function memcpy().\n"
+"\n"
+"'src' can be any cdata ptr or array, or any Python buffer object.\n"
+"'dest' can be any cdata ptr or array, or a writable Python buffer\n"
+"object.  The size to copy, 'n', is always measured in bytes.\n"
+"\n"
+"Unlike other methods, this one supports all Python buffer including\n"
+"byte strings and bytearrays---but it still does not support\n"
+"non-contiguous buffers.");
+
+#define ffi_memmove  b_memmove     /* ffi_memmove() => b_memmove()
+                                      from _cffi_backend.c */
+
+
 #define METH_VKW  (METH_VARARGS | METH_KEYWORDS)
 static PyMethodDef ffi_methods[] = {
  {"addressof",  (PyCFunction)ffi_addressof,  METH_VARARGS, ffi_addressof_doc},
@@ -869,6 +887,7 @@
  {"getwinerror",(PyCFunction)ffi_getwinerror,METH_VKW,     
ffi_getwinerror_doc},
 #endif
  {"integer_const",(PyCFunction)ffi_int_const,METH_VKW,     ffi_int_const_doc},
+ {"memmove",    (PyCFunction)ffi_memmove,    METH_VKW,     ffi_memmove_doc},
  {"new",        (PyCFunction)ffi_new,        METH_VKW,     ffi_new_doc},
 
{"new_allocator",(PyCFunction)ffi_new_allocator,METH_VKW,ffi_new_allocator_doc},
  {"new_handle", (PyCFunction)ffi_new_handle, METH_O,       ffi_new_handle_doc},
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -3404,6 +3404,65 @@
     check(4 | 8,  "CHB", "GTB")
     check(4 | 16, "CHB", "ROB")
 
+def test_memmove():
+    Short = new_primitive_type("short")
+    ShortA = new_array_type(new_pointer_type(Short), None)
+    Char = new_primitive_type("char")
+    CharA = new_array_type(new_pointer_type(Char), None)
+    p = newp(ShortA, [-1234, -2345, -3456, -4567, -5678])
+    memmove(p, p + 1, 4)
+    assert list(p) == [-2345, -3456, -3456, -4567, -5678]
+    p[2] = 999
+    memmove(p + 2, p, 6)
+    assert list(p) == [-2345, -3456, -2345, -3456, 999]
+    memmove(p + 4, newp(CharA, b"\x71\x72"), 2)
+    if sys.byteorder == 'little':
+        assert list(p) == [-2345, -3456, -2345, -3456, 0x7271]
+    else:
+        assert list(p) == [-2345, -3456, -2345, -3456, 0x7172]
+
+def test_memmove_buffer():
+    import array
+    Short = new_primitive_type("short")
+    ShortA = new_array_type(new_pointer_type(Short), None)
+    a = array.array('H', [10000, 20000, 30000])
+    p = newp(ShortA, 5)
+    memmove(p, a, 6)
+    assert list(p) == [10000, 20000, 30000, 0, 0]
+    memmove(p + 1, a, 6)
+    assert list(p) == [10000, 10000, 20000, 30000, 0]
+    b = array.array('h', [-1000, -2000, -3000])
+    memmove(b, a, 4)
+    assert b.tolist() == [10000, 20000, -3000]
+    assert a.tolist() == [10000, 20000, 30000]
+    p[0] = 999
+    p[1] = 998
+    p[2] = 997
+    p[3] = 996
+    p[4] = 995
+    memmove(b, p, 2)
+    assert b.tolist() == [999, 20000, -3000]
+    memmove(b, p + 2, 4)
+    assert b.tolist() == [997, 996, -3000]
+    p[2] = -p[2]
+    p[3] = -p[3]
+    memmove(b, p + 2, 6)
+    assert b.tolist() == [-997, -996, 995]
+
+def test_memmove_readonly_readwrite():
+    ffi = FFI()
+    SignedChar = new_primitive_type("signed char")
+    SignedCharA = new_array_type(new_pointer_type(SignedChar), None)
+    p = newp(SignedCharA, 5)
+    memmove(p, b"abcde", 3)
+    assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0]
+    memmove(p, bytearray(b"ABCDE"), 2)
+    assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0]
+    py.test.raises((TypeError, BufferError), memmove, b"abcde", p, 3)
+    ba = bytearray(b"xxxxx")
+    memmove(dest=ba, src=p, n=3)
+    assert ba == bytearray(b"ABcxx")
+
 def test_dereference_null_ptr():
     BInt = new_primitive_type("int")
     BIntPtr = new_pointer_type(BInt)
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -310,6 +310,22 @@
         """
         return self._backend.from_buffer(self.BCharA, python_buffer)
 
+    def memmove(self, dest, src, n):
+        """ffi.memmove(dest, src, n) copies n bytes of memory from src to dest.
+
+        Like the C function memmove(), the memory areas may overlap;
+        apart from that it behaves like the C function memcpy().
+
+        'src' can be any cdata ptr or array, or any Python buffer object.
+        'dest' can be any cdata ptr or array, or a writable Python buffer
+        object.  The size to copy, 'n', is always measured in bytes.
+
+        Unlike other methods, this one supports all Python buffer including
+        byte strings and bytearrays---but it still does not support
+        non-contiguous buffers.
+        """
+        return self._backend.memmove(dest, src, n)
+
     def callback(self, cdecl, python_callable=None, error=None, onerror=None):
         """Return a callback object or a decorator making such a
         callback object.  'cdecl' must name a C function pointer type.
diff --git a/testing/cffi0/test_ffi_backend.py 
b/testing/cffi0/test_ffi_backend.py
--- a/testing/cffi0/test_ffi_backend.py
+++ b/testing/cffi0/test_ffi_backend.py
@@ -313,6 +313,59 @@
         ffi.cast("unsigned short *", c)[1] += 500
         assert list(a) == [10000, 20500, 30000]
 
+    def test_memmove(self):
+        ffi = FFI()
+        p = ffi.new("short[]", [-1234, -2345, -3456, -4567, -5678])
+        ffi.memmove(p, p + 1, 4)
+        assert list(p) == [-2345, -3456, -3456, -4567, -5678]
+        p[2] = 999
+        ffi.memmove(p + 2, p, 6)
+        assert list(p) == [-2345, -3456, -2345, -3456, 999]
+        ffi.memmove(p + 4, ffi.new("char[]", b"\x71\x72"), 2)
+        if sys.byteorder == 'little':
+            assert list(p) == [-2345, -3456, -2345, -3456, 0x7271]
+        else:
+            assert list(p) == [-2345, -3456, -2345, -3456, 0x7172]
+
+    def test_memmove_buffer(self):
+        import array
+        ffi = FFI()
+        a = array.array('H', [10000, 20000, 30000])
+        p = ffi.new("short[]", 5)
+        ffi.memmove(p, a, 6)
+        assert list(p) == [10000, 20000, 30000, 0, 0]
+        ffi.memmove(p + 1, a, 6)
+        assert list(p) == [10000, 10000, 20000, 30000, 0]
+        b = array.array('h', [-1000, -2000, -3000])
+        ffi.memmove(b, a, 4)
+        assert b.tolist() == [10000, 20000, -3000]
+        assert a.tolist() == [10000, 20000, 30000]
+        p[0] = 999
+        p[1] = 998
+        p[2] = 997
+        p[3] = 996
+        p[4] = 995
+        ffi.memmove(b, p, 2)
+        assert b.tolist() == [999, 20000, -3000]
+        ffi.memmove(b, p + 2, 4)
+        assert b.tolist() == [997, 996, -3000]
+        p[2] = -p[2]
+        p[3] = -p[3]
+        ffi.memmove(b, p + 2, 6)
+        assert b.tolist() == [-997, -996, 995]
+
+    def test_memmove_readonly_readwrite(self):
+        ffi = FFI()
+        p = ffi.new("signed char[]", 5)
+        ffi.memmove(p, b"abcde", 3)
+        assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0]
+        ffi.memmove(p, bytearray(b"ABCDE"), 2)
+        assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0]
+        py.test.raises((TypeError, BufferError), ffi.memmove, b"abcde", p, 3)
+        ba = bytearray(b"xxxxx")
+        ffi.memmove(dest=ba, src=p, n=3)
+        assert ba == bytearray(b"ABcxx")
+
     def test_all_primitives(self):
         ffi = FFI()
         for name in [
diff --git a/testing/cffi1/test_ffi_obj.py b/testing/cffi1/test_ffi_obj.py
--- a/testing/cffi1/test_ffi_obj.py
+++ b/testing/cffi1/test_ffi_obj.py
@@ -236,6 +236,59 @@
     ffi.cast("unsigned short *", c)[1] += 500
     assert list(a) == [10000, 20500, 30000]
 
+def test_memmove():
+    ffi = _cffi1_backend.FFI()
+    p = ffi.new("short[]", [-1234, -2345, -3456, -4567, -5678])
+    ffi.memmove(p, p + 1, 4)
+    assert list(p) == [-2345, -3456, -3456, -4567, -5678]
+    p[2] = 999
+    ffi.memmove(p + 2, p, 6)
+    assert list(p) == [-2345, -3456, -2345, -3456, 999]
+    ffi.memmove(p + 4, ffi.new("char[]", b"\x71\x72"), 2)
+    if sys.byteorder == 'little':
+        assert list(p) == [-2345, -3456, -2345, -3456, 0x7271]
+    else:
+        assert list(p) == [-2345, -3456, -2345, -3456, 0x7172]
+
+def test_memmove_buffer():
+    import array
+    ffi = _cffi1_backend.FFI()
+    a = array.array('H', [10000, 20000, 30000])
+    p = ffi.new("short[]", 5)
+    ffi.memmove(p, a, 6)
+    assert list(p) == [10000, 20000, 30000, 0, 0]
+    ffi.memmove(p + 1, a, 6)
+    assert list(p) == [10000, 10000, 20000, 30000, 0]
+    b = array.array('h', [-1000, -2000, -3000])
+    ffi.memmove(b, a, 4)
+    assert b.tolist() == [10000, 20000, -3000]
+    assert a.tolist() == [10000, 20000, 30000]
+    p[0] = 999
+    p[1] = 998
+    p[2] = 997
+    p[3] = 996
+    p[4] = 995
+    ffi.memmove(b, p, 2)
+    assert b.tolist() == [999, 20000, -3000]
+    ffi.memmove(b, p + 2, 4)
+    assert b.tolist() == [997, 996, -3000]
+    p[2] = -p[2]
+    p[3] = -p[3]
+    ffi.memmove(b, p + 2, 6)
+    assert b.tolist() == [-997, -996, 995]
+
+def test_memmove_readonly_readwrite():
+    ffi = _cffi1_backend.FFI()
+    p = ffi.new("signed char[]", 5)
+    ffi.memmove(p, b"abcde", 3)
+    assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0]
+    ffi.memmove(p, bytearray(b"ABCDE"), 2)
+    assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0]
+    py.test.raises((TypeError, BufferError), ffi.memmove, b"abcde", p, 3)
+    ba = bytearray(b"xxxxx")
+    ffi.memmove(dest=ba, src=p, n=3)
+    assert ba == bytearray(b"ABcxx")
+
 def test_ffi_types():
     CData = _cffi1_backend.FFI.CData
     CType = _cffi1_backend.FFI.CType
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to