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