Author: Armin Rigo <ar...@tunes.org>
Branch: 
Changeset: r80073:1adcd2cc4ebf
Date: 2015-10-09 10:23 +0200
http://bitbucket.org/pypy/pypy/changeset/1adcd2cc4ebf/

Log:    ffi.memmove()

diff --git a/lib_pypy/cffi/api.py b/lib_pypy/cffi/api.py
--- a/lib_pypy/cffi/api.py
+++ b/lib_pypy/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/pypy/module/_cffi_backend/__init__.py 
b/pypy/module/_cffi_backend/__init__.py
--- a/pypy/module/_cffi_backend/__init__.py
+++ b/pypy/module/_cffi_backend/__init__.py
@@ -47,6 +47,7 @@
 
         'string': 'func.string',
         'buffer': 'cbuffer.buffer',
+        'memmove': 'func.memmove',
 
         'get_errno': 'cerrno.get_errno',
         'set_errno': 'cerrno.set_errno',
diff --git a/pypy/module/_cffi_backend/ffi_obj.py 
b/pypy/module/_cffi_backend/ffi_obj.py
--- a/pypy/module/_cffi_backend/ffi_obj.py
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -391,6 +391,25 @@
         return cerrno.getwinerror(self.space, code)
 
 
+    @unwrap_spec(n=int)
+    def descr_memmove(self, w_dest, w_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 func.memmove(self.space, w_dest, w_src, n)
+
+
     @unwrap_spec(w_init=WrappedDefault(None))
     def descr_new(self, w_arg, w_init):
         """\
@@ -623,6 +642,7 @@
         gc          = interp2app(W_FFIObject.descr_gc),
         getctype    = interp2app(W_FFIObject.descr_getctype),
         integer_const = interp2app(W_FFIObject.descr_integer_const),
+        memmove     = interp2app(W_FFIObject.descr_memmove),
         new         = interp2app(W_FFIObject.descr_new),
         new_allocator = interp2app(W_FFIObject.descr_new_allocator),
         new_handle  = interp2app(W_FFIObject.descr_new_handle),
diff --git a/pypy/module/_cffi_backend/func.py 
b/pypy/module/_cffi_backend/func.py
--- a/pypy/module/_cffi_backend/func.py
+++ b/pypy/module/_cffi_backend/func.py
@@ -1,3 +1,8 @@
+from rpython.rtyper.annlowlevel import llstr
+from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rtyper.lltypesystem.rstr import copy_string_to_raw
+from rpython.rlib.objectmodel import keepalive_until_here
+
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.gateway import unwrap_spec, WrappedDefault
 from pypy.module._cffi_backend import ctypeobj, cdataobj, allocator
@@ -79,6 +84,26 @@
 
 # ____________________________________________________________
 
+def _fetch_as_read_buffer(space, w_x):
+    # xxx do we really need to implement the same mess as in CPython 2.7
+    # w.r.t. buffers and memoryviews??
+    try:
+        buf = space.readbuf_w(w_x)
+    except OperationError, e:
+        if not e.match(space, space.w_TypeError):
+            raise
+        buf = space.buffer_w(w_x, space.BUF_SIMPLE)
+    return buf
+
+def _fetch_as_write_buffer(space, w_x):
+    try:
+        buf = space.writebuf_w(w_x)
+    except OperationError, e:
+        if not e.match(space, space.w_TypeError):
+            raise
+        buf = space.buffer_w(w_x, space.BUF_WRITABLE)
+    return buf
+
 @unwrap_spec(w_ctype=ctypeobj.W_CType)
 def from_buffer(space, w_ctype, w_x):
     from pypy.module._cffi_backend import ctypearray, ctypeprim
@@ -88,14 +113,7 @@
         raise oefmt(space.w_TypeError,
                     "needs 'char[]', got '%s'", w_ctype.name)
     #
-    # xxx do we really need to implement the same mess as in CPython 2.7
-    # w.r.t. buffers and memoryviews??
-    try:
-        buf = space.readbuf_w(w_x)
-    except OperationError, e:
-        if not e.match(space, space.w_TypeError):
-            raise
-        buf = space.buffer_w(w_x, space.BUF_SIMPLE)
+    buf = _fetch_as_read_buffer(space, w_x)
     try:
         _cdata = buf.get_raw_address()
     except ValueError:
@@ -106,6 +124,68 @@
     #
     return cdataobj.W_CDataFromBuffer(space, _cdata, w_ctype, buf, w_x)
 
+c_memmove = rffi.llexternal('memmove', [rffi.CCHARP, rffi.CCHARP,
+                                        rffi.SIZE_T], lltype.Void,
+                                _nowrapper=True)
+
+@unwrap_spec(n=int)
+def memmove(space, w_dest, w_src, n):
+    if n < 0:
+        raise oefmt(space.w_ValueError, "negative size")
+
+    # cases...
+    src_buf = None
+    src_data = lltype.nullptr(rffi.CCHARP.TO)
+    if isinstance(w_src, cdataobj.W_CData):
+        src_data = w_src.unsafe_escaping_ptr()
+        src_is_ptr = True
+    else:
+        src_buf = _fetch_as_read_buffer(space, w_src)
+        try:
+            src_data = src_buf.get_raw_address()
+            src_is_ptr = True
+        except ValueError:
+            src_is_ptr = False
+
+    if src_is_ptr:
+        src_string = None
+    else:
+        if n == src_buf.getlength():
+            src_string = src_buf.as_str()
+        else:
+            src_string = src_buf.getslice(0, n, 1, n)
+
+    dest_buf = None
+    dest_data = lltype.nullptr(rffi.CCHARP.TO)
+    if isinstance(w_dest, cdataobj.W_CData):
+        dest_data = w_dest.unsafe_escaping_ptr()
+        dest_is_ptr = True
+    else:
+        dest_buf = _fetch_as_write_buffer(space, w_dest)
+        try:
+            dest_data = dest_buf.get_raw_address()
+            dest_is_ptr = True
+        except ValueError:
+            dest_is_ptr = False
+
+    if dest_is_ptr:
+        if src_is_ptr:
+            c_memmove(dest_data, src_data, rffi.cast(rffi.SIZE_T, n))
+        else:
+            copy_string_to_raw(llstr(src_string), dest_data, 0, n)
+    else:
+        if src_is_ptr:
+            for i in range(n):
+                dest_buf.setitem(i, src_data[i])
+        else:
+            for i in range(n):
+                dest_buf.setitem(i, src_string[i])
+
+    keepalive_until_here(src_buf)
+    keepalive_until_here(dest_buf)
+    keepalive_until_here(w_src)
+    keepalive_until_here(w_dest)
+
 # ____________________________________________________________
 
 @unwrap_spec(w_cdata=cdataobj.W_CData)
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py 
b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -3393,6 +3393,72 @@
     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():
+    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")
+    memmove(ba, b"EFGH", 4)
+    assert ba == bytearray(b"EFGHx")
+
+def test_memmove_sign_check():
+    SignedChar = new_primitive_type("signed char")
+    SignedCharA = new_array_type(new_pointer_type(SignedChar), None)
+    p = newp(SignedCharA, 5)
+    py.test.raises(ValueError, memmove, p, p + 1, -1)   # not segfault
+
 def test_dereference_null_ptr():
     BInt = new_primitive_type("int")
     BIntPtr = new_pointer_type(BInt)
diff --git a/pypy/module/_cffi_backend/test/test_ffi_obj.py 
b/pypy/module/_cffi_backend/test/test_ffi_obj.py
--- a/pypy/module/_cffi_backend/test/test_ffi_obj.py
+++ b/pypy/module/_cffi_backend/test/test_ffi_obj.py
@@ -247,6 +247,63 @@
         ffi.cast("unsigned short *", c)[1] += 500
         assert list(a) == [10000, 20500, 30000]
 
+    def test_memmove(self):
+        import sys
+        import _cffi_backend as _cffi1_backend
+        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(self):
+        import _cffi_backend as _cffi1_backend
+        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(self):
+        import _cffi_backend as _cffi1_backend
+        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]
+        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(self):
         import _cffi_backend as _cffi1_backend
         CData = _cffi1_backend.FFI.CData
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py 
b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ffi_backend.py
@@ -314,6 +314,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/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py 
b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py
@@ -2276,16 +2276,16 @@
     @ffi.callback("int __stdcall(int)")
     def cb2(x):
         return x * 3
-    print 'cb1 =', cb1
+    #print 'cb1 =', cb1
     res = lib.call1(cb1)
     assert res == 500*999*2
-    print 'cb2 =', cb2
-    print ffi.typeof(lib.call2)
-    print 'call2 =', lib.call2
+    #print 'cb2 =', cb2
+    #print ffi.typeof(lib.call2)
+    #print 'call2 =', lib.call2
     res = lib.call2(cb2)
-    print '...'
+    #print '...'
     assert res == -500*999*3
-    print 'done'
+    #print 'done'
     if sys.platform == 'win32':
         assert '__stdcall' in str(ffi.typeof(cb2))
         assert '__stdcall' not in str(ffi.typeof(cb1))
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py 
b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py
@@ -237,6 +237,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
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py 
b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
@@ -1365,15 +1365,15 @@
             return result;
         }
     """)
-    print '<<< cb1 =', ffi.addressof(lib, 'cb1')
+    #print '<<< cb1 =', ffi.addressof(lib, 'cb1')
     ptr_call1 = ffi.addressof(lib, 'call1')
     assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2
     assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2
-    print '<<< cb2 =', ffi.addressof(lib, 'cb2')
+    #print '<<< cb2 =', ffi.addressof(lib, 'cb2')
     ptr_call2 = ffi.addressof(lib, 'call2')
     assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3
     assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3
-    print '<<< done'
+    #print '<<< done'
 
 def test_win32_calling_convention_2():
     # any mistake in the declaration of plain function (including the
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to