Commits

Armin Rigo committed 590c7c8

Support for "const char *" which works mostly like a read-only
"char[]" buffer, initialized with a string, but with a NUL terminator.

Comments (0)

Files changed (3)

         #
         if isinstance(typenode, pycparser.c_ast.PtrDecl):
             BItem = self._get_btype(typenode.type)
-            return self._backend.get_cached_btype("new_pointer_type", BItem)
+            is_const_charp = (list(typenode.type.quals) == ['const'] and
+                              BItem is self._backend.get_cached_btype(
+                                  "new_primitive_type", "char"))
+            return self._backend.get_cached_btype("new_pointer_type", BItem,
+                                                  is_const_charp)
         #
         if isinstance(typenode, pycparser.c_ast.TypeDecl):
             type = typenode.type

ffi/backend_ctypes.py

         CTypesPrimitive._fix_class()
         return CTypesPrimitive
 
-    def new_pointer_type(self, BItem):
-        if BItem is self.get_cached_btype('new_primitive_type', 'char'):
-            kind = 'char'
+    def new_pointer_type(self, BItem, is_const_charp):
+        if is_const_charp:
+            kind = 'constcharp'
+        elif BItem is self.get_cached_btype('new_primitive_type', 'char'):
+            kind = 'charp'
         else:
             kind = 'generic'
         #
                     address = 0      # null pointer
                 elif isinstance(init, CTypesData):
                     address = init._convert_to_address_of(BItem)
+                elif kind == 'constcharp' and isinstance(init, str):
+                    if '\x00' in init:
+                        raise ValueError("string contains \\x00 characters")
+                    self._keepalive_string = init
+                    address = ctypes.cast(ctypes.c_char_p(init),
+                                          ctypes.c_void_p).value
                 else:
                     raise TypeError("%r expected, got %r" % (
                         CTypesPtr._get_c_name(), type(init).__name__))
             def __ne__(self, other):
                 return not self.__eq__(other)
 
-            def __getitem__(self, index):
-                return BItem._from_ctypes(self._as_ctype_ptr[index])
+            if kind != 'constcharp':
+                def __getitem__(self, index):
+                    return BItem._from_ctypes(self._as_ctype_ptr[index])
 
-            def __setitem__(self, index, value):
-                self._as_ctype_ptr[index] = BItem._to_ctypes(value)
+                def __setitem__(self, index, value):
+                    self._as_ctype_ptr[index] = BItem._to_ctypes(value)
+            else:
+                def __getitem__(self, index):
+                    # note that we allow access to the terminating NUL byte
+                    if not (0 <= index <= len(self._keepalive_string)):
+                        raise IndexError
+                    return self._as_ctype_ptr[index]
 
-            if kind == 'char':
+            if kind == 'charp' or kind == 'constcharp':
                 def __str__(self):
                     n = 0
                     while self._as_ctype_ptr[n] != '\x00':

testing/backend_tests.py

         a = ffi.new("char[]", "hello\x00world")
         p = ffi.new("char *", a)
         assert str(p) == 'hello'
+
+    def test_string_to_const_char_p(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("const char *", "12345678")
+        assert p[3] == '4'
+        assert p[8] == '\x00'
+        py.test.raises(IndexError, "p[9]")
+        py.test.raises(TypeError, "p[3] = '?'")
+        py.test.raises(TypeError, ffi.new, "char *", "some string")
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.