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