[pypy-commit] cffi default: Decided to fix ffi.sizeof() too. Update the documentation.
arigo
pypy.commits at gmail.com
Thu Oct 27 11:21:55 EDT 2016
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r2800:5ef556ba98f2
Date: 2016-10-27 17:21 +0200
http://bitbucket.org/cffi/cffi/changeset/5ef556ba98f2/
Log: Decided to fix ffi.sizeof() too. Update the documentation.
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -5492,8 +5492,11 @@
if (cd->c_type->ct_flags & CT_ARRAY)
size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size;
- else
- size = cd->c_type->ct_size;
+ else {
+ size = _cdata_var_byte_size(cd);
+ if (size < 0)
+ size = cd->c_type->ct_size;
+ }
}
else if (CTypeDescr_Check(arg)) {
size = ((CTypeDescrObject *)arg)->ct_size;
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -3202,6 +3202,7 @@
assert len(p.y) == 3
assert len(p[0].y) == 3
assert len(buffer(p)) == sizeof(BInt) * 4
+ assert sizeof(p[0]) == sizeof(BInt) * 4
plist.append(p)
for i in range(20):
p = plist[i]
@@ -3220,6 +3221,7 @@
sizeof(BStruct) + 3 * sizeof(BInt),)
assert repr(p[0]) == "<cdata 'foo' owning %d bytes>" % (
sizeof(BStruct) + 3 * sizeof(BInt),)
+ assert sizeof(p[0]) == sizeof(BStruct) + 3 * sizeof(BInt)
#
# from a non-owning pointer, we can't get the length
q = cast(new_pointer_type(BStruct), p)
@@ -3229,6 +3231,7 @@
py.test.raises(TypeError, len, q[0].y)
assert typeof(q.y) is BIntP
assert typeof(q[0].y) is BIntP
+ assert sizeof(q[0]) == sizeof(BStruct)
#
# error cases
py.test.raises(IndexError, "p.y[4]")
diff --git a/doc/source/ref.rst b/doc/source/ref.rst
--- a/doc/source/ref.rst
+++ b/doc/source/ref.rst
@@ -141,7 +141,8 @@
into a struct over a socket, rewriting the contents of mystruct[0]
Remember that like in C, you can use ``array + index`` to get the pointer
-to the index'th item of an array.
+to the index'th item of an array. (In C you might more naturally write
+``&array[index]``, but that is equivalent.)
The returned object is not a built-in buffer nor memoryview object,
because these objects' API changes too much across Python versions.
@@ -255,6 +256,14 @@
argument in bytes. The argument can be either a C type, or a cdata object,
like in the equivalent ``sizeof`` operator in C.
+For ``array = ffi.new("T[]", n)``, then ``ffi.sizeof(array)`` returns
+``n * ffi.sizeof("T")``. *New in version 1.9:* Similar rules apply for
+structures with aa variable-sized array at the end. More precisely, if
+``p`` was returned by ``ffi.new("struct foo *", ...)``, then
+``ffi.sizeof(p[0])`` now returns the total allocated size. In previous
+versions, it used to just return ``ffi.sizeof(ffi.typeof(p[0]))``, which
+is the size of the structure ignoring the variable-sized part.
+
**ffi.alignof("C type")**: return the natural alignment size in bytes of
the argument. Corresponds to the ``__alignof__`` operator in GCC.
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
--- a/doc/source/whatsnew.rst
+++ b/doc/source/whatsnew.rst
@@ -3,8 +3,18 @@
======================
-v1.8.4
-======
+v1.9
+====
+
+* Structs with variable-sized arrays as their last field: now we track
+ the length of the array after ``ffi.new()`` is called, just like we
+ always tracked the length of ``ffi.new("int[]", 42)``. This lets us
+ detect out-of-range accesses to array items. This also lets us
+ display a better ``repr()``, and have the total size returned by
+ ``ffi.sizeof()`` and ``ffi.buffer()``. Previously both functions
+ would return a result based on the size of the declared structure
+ type, with an assumed empty array. (Thanks andrew for starting this
+ refactoring.)
* Add support in ``cdef()/set_source()`` for unspecified-length arrays
in typedefs: ``typedef int foo_t[...];``. It was already supported
@@ -17,6 +27,16 @@
enum has size ``int`` is a 99%-safe bet. (But not 100%, so it stays
as a warning.)
+* Fix leaks in the code handling ``FILE *`` arguments. In CPython 3
+ there is a remaining issue that is hard to fix: if you pass a Python
+ file object to a ``FILE *`` argument, then ``os.dup()`` is used and
+ the new file descriptor is only closed when the GC reclaims the Python
+ file object---and not at the earlier time when you call ``close()``,
+ which only closes the original file descriptor. If this is an issue,
+ you should avoid this automatic convertion of Python file objects:
+ instead, explicitly manipulate file descriptors and call ``fdopen()``
+ from C (...via cffi).
+
v1.8.3
======
diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py
--- a/testing/cffi0/test_verify.py
+++ b/testing/cffi0/test_verify.py
@@ -591,10 +591,15 @@
ffi.verify("struct foo_s { int x; int a[]; };")
assert ffi.sizeof('struct foo_s') == 1 * ffi.sizeof('int')
s = ffi.new("struct foo_s *", [424242, 4])
- assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') # the same in C
+ assert ffi.sizeof(ffi.typeof(s[0])) == 1 * ffi.sizeof('int')
+ assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int')
+ # ^^^ explanation: if you write in C: "char x[5];", then
+ # "sizeof(ax" will evaluate to 5. The behavior above is
+ # a generalization of that to "struct foo_s[len(a)=5] x;"
+ # if you could do that in C.
assert s.a[3] == 0
s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]])
- assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int')
+ assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int')
assert s.a[3] == -10
s = ffi.new("struct foo_s *")
assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int')
@@ -609,10 +614,10 @@
ffi.verify("struct foo_s { int x, y; int a[]; };")
assert ffi.sizeof('struct foo_s') == 2 * ffi.sizeof('int')
s = ffi.new("struct foo_s *", [424242, 4])
- assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int')
+ assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int')
assert s.a[3] == 0
s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]])
- assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int')
+ assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int')
assert s.a[3] == -10
s = ffi.new("struct foo_s *")
assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int')
More information about the pypy-commit
mailing list