[pypy-commit] cffi default: Workaround: allow out-of-bound array indexes if the array is 'type[0]'.

arigo noreply at buildbot.pypy.org
Sun Aug 5 09:44:46 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r784:0d5efadab0ac
Date: 2012-08-05 09:44 +0200
http://bitbucket.org/cffi/cffi/changeset/0d5efadab0ac/

Log:	Workaround: allow out-of-bound array indexes if the array is
	'type[0]'.

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -1388,7 +1388,7 @@
                             "negative index not supported");
             return NULL;
         }
-        if (i >= get_array_length(cd)) {
+        if (i >= get_array_length(cd) && cd->c_type->ct_length != 0) {
             PyErr_Format(PyExc_IndexError,
                          "index too large for cdata '%s' (expected %zd < %zd)",
                          cd->c_type->ct_name,
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -1837,3 +1837,16 @@
         if not py_py:
             assert repr(x).endswith("E+902>")
             assert float(x) == float("inf")
+
+def test_array_of_length_zero():
+    p = new_pointer_type(new_primitive_type("int"))
+    p0 = new_array_type(p, 0)
+    p3 = new_array_type(p, 3)
+    a1 = newp(p3, [61, 62, 63])
+    a2 = cast(p0, a1)
+    assert a2[0] == 61
+    assert a2[1] == 62
+    assert a2[2] == 63
+    a2[2] = 64
+    assert list(a1) == [61, 62, 64]
+    assert list(a2) == []
diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py
--- a/cffi/backend_ctypes.py
+++ b/cffi/backend_ctypes.py
@@ -583,14 +583,22 @@
                 return len(self._blob)
 
             def __getitem__(self, index):
-                if not (0 <= index < len(self._blob)):
+                if 0 <= index < len(self._blob):
+                    x = self._blob[index]
+                elif len(self._blob) == 0:
+                    x = ctypes.cast(self._blob, CTypesPtr._ctype)[index]
+                else:
                     raise IndexError
-                return BItem._from_ctypes(self._blob[index])
+                return BItem._from_ctypes(x)
 
             def __setitem__(self, index, value):
-                if not (0 <= index < len(self._blob)):
+                x = BItem._to_ctypes(value)
+                if 0 <= index < len(self._blob):
+                    self._blob[index] = x
+                elif len(self._blob) == 0:
+                    ctypes.cast(self._blob, CTypesPtr._ctype)[index] = x
+                else:
                     raise IndexError
-                self._blob[index] = BItem._to_ctypes(value)
 
             if kind == 'char' or kind == 'byte':
                 def _to_string(self, maxlen):
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -919,6 +919,11 @@
 
 * Thread-local variables (access them via getter/setter functions)
 
+* Variable-length structures, i.e. whose last field is a variable-length
+  array (work around like in C, e.g. by declaring it as an array of
+  length 0, allocating a ``char[]`` of the correct size, and casting
+  it to a struct pointer)
+
 
 Reference: conversions
 ----------------------
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -144,7 +144,8 @@
         assert repr(p) == "<cdata 'int[]' owning %d bytes>" % (2*SIZE_OF_INT)
         #
         p = ffi.new("int[]", 0)
-        py.test.raises(IndexError, "p[0]")
+        #py.test.raises(IndexError, "p[0]") ---
+        #   actually works, for test_struct_containing_array_varsize_workaround
         py.test.raises(ValueError, ffi.new, "int[]", -1)
         assert repr(p) == "<cdata 'int[]' owning 0 bytes>"
 
@@ -1121,6 +1122,16 @@
         f.close()
         os.unlink(filename)
 
+    def test_struct_containing_array_varsize_workaround(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo_s { int len; short data[0]; };")
+        p = ffi.new("char[]", ffi.sizeof("struct foo_s") + 7 * SIZE_OF_SHORT)
+        q = ffi.cast("struct foo_s *", p)
+        assert q.len == 0
+        assert q.data[6] == 0
+        q.data[6] = 15
+        assert q.data[6] == 15
+
     def test_new_struct_containing_array_varsize(self):
         py.test.skip("later?")
         ffi = FFI(backend=self.Backend())


More information about the pypy-commit mailing list