[pypy-commit] cffi default: Replace ffi.string() with ffi.buffer() and update the docs. Add an
arigo
noreply at buildbot.pypy.org
Fri Jun 15 16:29:19 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r366:c8cefb96f807
Date: 2012-06-15 16:29 +0200
http://bitbucket.org/cffi/cffi/changeset/c8cefb96f807/
Log: Replace ffi.string() with ffi.buffer() and update the docs. Add an
example of how to use it.
diff --git a/c/_ffi_backend.c b/c/_ffi_backend.c
--- a/c/_ffi_backend.c
+++ b/c/_ffi_backend.c
@@ -3110,23 +3110,31 @@
return PyInt_FromSsize_t(cf->cf_offset);
}
-static PyObject *b_string(PyObject *self, PyObject *args)
+static PyObject *b_buffer(PyObject *self, PyObject *args)
{
CDataObject *cd;
- CTypeDescrObject *ct;
Py_ssize_t length;
- if (!PyArg_ParseTuple(args, "O!n:string",
- &CData_Type, &cd, &length))
+ if (!PyArg_ParseTuple(args, "O!:buffer",
+ &CData_Type, &cd))
return NULL;
- ct = cd->c_type;
- if (!(ct->ct_flags & CT_POINTER) ||
- !(ct->ct_itemdescr->ct_flags & CT_CAST_ANYTHING)) {
+
+ if (cd->c_type->ct_flags & CT_POINTER)
+ length = cd->c_type->ct_size;
+ else if (cd->c_type->ct_flags & CT_ARRAY)
+ length = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size;
+ else {
PyErr_Format(PyExc_TypeError,
- "expected a cdata 'char *' or 'void *', got '%s'",
- ct->ct_name);
+ "expected a pointer or array cdata, got '%s'",
+ cd->c_type->ct_name);
return NULL;
}
- return PyString_FromStringAndSize(cd->c_data, length);
+ if (length < 0) {
+ PyErr_Format(PyExc_TypeError,
+ "don't know the size pointed to by '%s'",
+ cd->c_type->ct_name);
+ return NULL;
+ }
+ return PyBuffer_FromReadWriteMemory(cd->c_data, length);
}
static PyObject *b_get_errno(PyObject *self, PyObject *noarg)
@@ -3269,7 +3277,7 @@
{"sizeof", b_sizeof, METH_O},
{"typeof", b_typeof, METH_O},
{"offsetof", b_offsetof, METH_VARARGS},
- {"string", b_string, METH_VARARGS},
+ {"buffer", b_buffer, METH_VARARGS},
{"get_errno", b_get_errno, METH_NOARGS},
{"set_errno", b_set_errno, METH_VARARGS},
{"gc", b_gc, METH_VARARGS},
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -160,12 +160,13 @@
BType = self.typeof(cdecl)
return self._backend.cast(BType, source)
- def string(self, pointer, length):
- """Return a Python string containing the data at the given
- raw pointer with the given size. The pointer must be a
- <cdata 'void *'> or <cdata 'char *'>.
+ def buffer(self, cdata):
+ """Return a read-write buffer object that references the raw C data
+ pointed to by the given 'cdata'. The 'cdata' must be a pointer or
+ an array. To get a copy of it in a regular string, call str() on
+ the result.
"""
- return self._backend.string(pointer, length)
+ return self._backend.buffer(pointer)
def callback(self, cdecl, python_callable):
if not callable(python_callable):
diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py
--- a/cffi/backend_ctypes.py
+++ b/cffi/backend_ctypes.py
@@ -760,12 +760,19 @@
def set_errno(self, value):
ctypes.set_errno(value)
- def string(self, bptr, length):
- if not (isinstance(bptr, CTypesGenericPtr) and bptr._automatic_casts):
+ def buffer(self, bptr):
+ # haaaaaaaaaaaack
+ call = ctypes.pythonapi.PyBuffer_FromReadWriteMemory
+ call.argtypes = (ctypes.c_void_p, ctypes.c_size_t)
+ call.restype = ctypes.py_object
+ #
+ if isinstance(bptr, CTypesGenericPtr):
+ return call(bptr._as_ctype_ptr, bptr._bitem_size)
+ elif isinstance(bptr, CTypesGenericArray):
+ return call(ctypes.pointer(bptr._blob), ctypes.sizeof(bptr._blob))
+ else:
raise TypeError("'void *' argument expected, got %r" %
(type(bptr).__name__,))
- p = ctypes.cast(bptr._as_ctype_ptr, ctypes.POINTER(ctypes.c_char))
- return ''.join([p[i] for i in range(length)])
def sizeof(self, cdata_or_BType):
if isinstance(cdata_or_BType, CTypesData):
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -124,6 +124,10 @@
image[0].g = 192
image[0].b = 128
+ f = open('data', 'wb')
+ f.write(ffi.buffer(image))
+ f.close()
+
This can be used as a more flexible replacement of the struct_ and
array_ modules. You could also call ``ffi.new("pixel_t[600][800]")``
and get a two-dimensional array.
@@ -480,11 +484,10 @@
library. It can also be accessed more directly via reads and writes of
``ffi.errno``.
-``ffi.string(pointer, length)``: return a Python string containing all
-the data at the given location with the given size. The pointer must be
-a cdata of type ``void *`` or ``char *``. Null characters are not
-considered special here: the resulting string always has the given
-``length``, possibly with embedded null characters.
+``ffi.buffer(pointer)``: return a read-write buffer object that
+references the raw C data pointed to by the given 'cdata'. The 'cdata'
+must be a pointer or an array. To get a copy of it in a regular string,
+call str() on the result.
``ffi.typeof("C type" or cdata object)``: return an object of type
``<ctype>`` corresponding to the parsed string, or to the C type of the
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -757,15 +757,32 @@
assert p == s+0
assert p+1 == s+1
- def test_ffi_string(self):
+ def test_ffi_buffer_ptr(self):
+ ffi = FFI(backend=self.Backend())
+ a = ffi.new("int", 100)
+ b = ffi.buffer(a)
+ assert type(b) is buffer
+ if sys.byteorder == 'little':
+ assert str(b) == '\x64\x00\x00\x00'
+ b[0] = '\x65'
+ else:
+ assert str(b) == '\x00\x00\x00\x64'
+ b[3] = '\x65'
+ assert a[0] == 101
+
+ def test_ffi_buffer_array(self):
ffi = FFI(backend=self.Backend())
a = ffi.new("int[]", range(100, 110))
- s = ffi.string(ffi.cast("void *", a), 8)
- assert type(s) is str
+ b = ffi.buffer(a)
+ assert type(b) is buffer
if sys.byteorder == 'little':
- assert s == '\x64\x00\x00\x00\x65\x00\x00\x00'
+ assert str(b).startswith('\x64\x00\x00\x00\x65\x00\x00\x00')
+ b[4] = '\x45'
else:
- assert s == '\x00\x00\x00\x64\x00\x00\x00\x65'
+ assert str(b).startswith('\x00\x00\x00\x64\x00\x00\x00\x65')
+ b[7] = '\x45'
+ assert len(str(b)) == 4 * 10
+ assert a[1] == 0x45
def test_new_struct_containing_array_varsize(self):
py.test.skip("later?")
More information about the pypy-commit
mailing list