Author: Armin Rigo <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit