Author: Armin Rigo <ar...@tunes.org>
Branch: 
Changeset: r2755:e8e4775048a1
Date: 2016-09-03 19:29 +0200
http://bitbucket.org/cffi/cffi/changeset/e8e4775048a1/

Log:    Backed out changeset 0087e2aec9ef

        Un-kill the ctypes backend. Issue #282 for a justification.

diff too long, truncating to 2000 out of 5541 lines

diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -46,21 +46,20 @@
     '''
 
     def __init__(self, backend=None):
-        """Create an FFI instance.
-
-        The 'backend' argument is not used any more and must be set to None.
-        It is still present only so that 'FFI(None)' still works, and
-        for a few tests.
+        """Create an FFI instance.  The 'backend' argument is used to
+        select a non-default backend, mostly for tests.
         """
         from . import cparser, model
-
         if backend is None:
-            # You need the corresponding version of PyPy, or CPython
-            # with the '_cffi_backend' C extension module compiled.
+            # You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with
+            # _cffi_backend.so compiled.
             import _cffi_backend as backend
             from . import __version__
             assert backend.__version__ == __version__, \
                "version mismatch, %s != %s" % (backend.__version__, 
__version__)
+            # (If you insist you can also try to pass the option
+            # 'backend=backend_ctypes.CTypesBackend()', but don't
+            # rely on it!  It's probably not going to work well.)
 
         self._backend = backend
         self._lock = allocate_lock()
@@ -76,6 +75,8 @@
         self._init_once_cache = {}
         self._cdef_version = None
         self._embedding = None
+        if hasattr(backend, 'set_ffi'):
+            backend.set_ffi(self)
         for name in backend.__dict__:
             if name.startswith('RTLD_'):
                 setattr(self, name, getattr(backend, name))
@@ -83,10 +84,15 @@
         with self._lock:
             self.BVoidP = self._get_cached_btype(model.voidp_type)
             self.BCharA = self._get_cached_btype(model.char_array_type)
-        # attach these constants to the class
-        if not hasattr(FFI, 'NULL'):
-            FFI.NULL = self.cast(self.BVoidP, 0)
-            FFI.CData, FFI.CType = backend._get_types()
+        if isinstance(backend, types.ModuleType):
+            # _cffi_backend: attach these constants to the class
+            if not hasattr(FFI, 'NULL'):
+                FFI.NULL = self.cast(self.BVoidP, 0)
+                FFI.CData, FFI.CType = backend._get_types()
+        else:
+            # ctypes backend: attach these constants to the instance
+            self.NULL = self.cast(self.BVoidP, 0)
+            self.CData, self.CType = backend._get_types()
 
     def cdef(self, csource, override=False, packed=False):
         """Parse the given C source.  This registers all declared functions,
diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py
new file mode 100644
--- /dev/null
+++ b/cffi/backend_ctypes.py
@@ -0,0 +1,1097 @@
+import ctypes, ctypes.util, operator, sys
+from . import model
+
+if sys.version_info < (3,):
+    bytechr = chr
+else:
+    unicode = str
+    long = int
+    xrange = range
+    bytechr = lambda num: bytes([num])
+
+class CTypesType(type):
+    pass
+
+class CTypesData(object):
+    __metaclass__ = CTypesType
+    __slots__ = ['__weakref__']
+    __name__ = '<cdata>'
+
+    def __init__(self, *args):
+        raise TypeError("cannot instantiate %r" % (self.__class__,))
+
+    @classmethod
+    def _newp(cls, init):
+        raise TypeError("expected a pointer or array ctype, got '%s'"
+                        % (cls._get_c_name(),))
+
+    @staticmethod
+    def _to_ctypes(value):
+        raise TypeError
+
+    @classmethod
+    def _arg_to_ctypes(cls, *value):
+        try:
+            ctype = cls._ctype
+        except AttributeError:
+            raise TypeError("cannot create an instance of %r" % (cls,))
+        if value:
+            res = cls._to_ctypes(*value)
+            if not isinstance(res, ctype):
+                res = cls._ctype(res)
+        else:
+            res = cls._ctype()
+        return res
+
+    @classmethod
+    def _create_ctype_obj(cls, init):
+        if init is None:
+            return cls._arg_to_ctypes()
+        else:
+            return cls._arg_to_ctypes(init)
+
+    @staticmethod
+    def _from_ctypes(ctypes_value):
+        raise TypeError
+
+    @classmethod
+    def _get_c_name(cls, replace_with=''):
+        return cls._reftypename.replace(' &', replace_with)
+
+    @classmethod
+    def _fix_class(cls):
+        cls.__name__ = 'CData<%s>' % (cls._get_c_name(),)
+        cls.__qualname__ = 'CData<%s>' % (cls._get_c_name(),)
+        cls.__module__ = 'ffi'
+
+    def _get_own_repr(self):
+        raise NotImplementedError
+
+    def _addr_repr(self, address):
+        if address == 0:
+            return 'NULL'
+        else:
+            if address < 0:
+                address += 1 << (8*ctypes.sizeof(ctypes.c_void_p))
+            return '0x%x' % address
+
+    def __repr__(self, c_name=None):
+        own = self._get_own_repr()
+        return '<cdata %r %s>' % (c_name or self._get_c_name(), own)
+
+    def _convert_to_address(self, BClass):
+        if BClass is None:
+            raise TypeError("cannot convert %r to an address" % (
+                self._get_c_name(),))
+        else:
+            raise TypeError("cannot convert %r to %r" % (
+                self._get_c_name(), BClass._get_c_name()))
+
+    @classmethod
+    def _get_size(cls):
+        return ctypes.sizeof(cls._ctype)
+
+    def _get_size_of_instance(self):
+        return ctypes.sizeof(self._ctype)
+
+    @classmethod
+    def _cast_from(cls, source):
+        raise TypeError("cannot cast to %r" % (cls._get_c_name(),))
+
+    def _cast_to_integer(self):
+        return self._convert_to_address(None)
+
+    @classmethod
+    def _alignment(cls):
+        return ctypes.alignment(cls._ctype)
+
+    def __iter__(self):
+        raise TypeError("cdata %r does not support iteration" % (
+            self._get_c_name()),)
+
+    def _make_cmp(name):
+        cmpfunc = getattr(operator, name)
+        def cmp(self, other):
+            if isinstance(other, CTypesData):
+                return cmpfunc(self._convert_to_address(None),
+                               other._convert_to_address(None))
+            else:
+                return NotImplemented
+        cmp.func_name = name
+        return cmp
+
+    __eq__ = _make_cmp('__eq__')
+    __ne__ = _make_cmp('__ne__')
+    __lt__ = _make_cmp('__lt__')
+    __le__ = _make_cmp('__le__')
+    __gt__ = _make_cmp('__gt__')
+    __ge__ = _make_cmp('__ge__')
+
+    def __hash__(self):
+        return hash(type(self)) ^ hash(self._convert_to_address(None))
+
+    def _to_string(self, maxlen):
+        raise TypeError("string(): %r" % (self,))
+
+
+class CTypesGenericPrimitive(CTypesData):
+    __slots__ = []
+
+    def __eq__(self, other):
+        return self is other
+
+    def __ne__(self, other):
+        return self is not other
+
+    def __hash__(self):
+        return object.__hash__(self)
+
+    def _get_own_repr(self):
+        return repr(self._from_ctypes(self._value))
+
+
+class CTypesGenericArray(CTypesData):
+    __slots__ = []
+
+    @classmethod
+    def _newp(cls, init):
+        return cls(init)
+
+    def __iter__(self):
+        for i in xrange(len(self)):
+            yield self[i]
+
+    def _get_own_repr(self):
+        return self._addr_repr(ctypes.addressof(self._blob))
+
+
+class CTypesGenericPtr(CTypesData):
+    __slots__ = ['_address', '_as_ctype_ptr']
+    _automatic_casts = False
+    kind = "pointer"
+
+    @classmethod
+    def _newp(cls, init):
+        return cls(init)
+
+    @classmethod
+    def _cast_from(cls, source):
+        if source is None:
+            address = 0
+        elif isinstance(source, CTypesData):
+            address = source._cast_to_integer()
+        elif isinstance(source, (int, long)):
+            address = source
+        else:
+            raise TypeError("bad type for cast to %r: %r" %
+                            (cls, type(source).__name__))
+        return cls._new_pointer_at(address)
+
+    @classmethod
+    def _new_pointer_at(cls, address):
+        self = cls.__new__(cls)
+        self._address = address
+        self._as_ctype_ptr = ctypes.cast(address, cls._ctype)
+        return self
+
+    def _get_own_repr(self):
+        try:
+            return self._addr_repr(self._address)
+        except AttributeError:
+            return '???'
+
+    def _cast_to_integer(self):
+        return self._address
+
+    def __nonzero__(self):
+        return bool(self._address)
+    __bool__ = __nonzero__
+
+    @classmethod
+    def _to_ctypes(cls, value):
+        if not isinstance(value, CTypesData):
+            raise TypeError("unexpected %s object" % type(value).__name__)
+        address = value._convert_to_address(cls)
+        return ctypes.cast(address, cls._ctype)
+
+    @classmethod
+    def _from_ctypes(cls, ctypes_ptr):
+        address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0
+        return cls._new_pointer_at(address)
+
+    @classmethod
+    def _initialize(cls, ctypes_ptr, value):
+        if value:
+            ctypes_ptr.contents = cls._to_ctypes(value).contents
+
+    def _convert_to_address(self, BClass):
+        if (BClass in (self.__class__, None) or BClass._automatic_casts
+            or self._automatic_casts):
+            return self._address
+        else:
+            return CTypesData._convert_to_address(self, BClass)
+
+
+class CTypesBaseStructOrUnion(CTypesData):
+    __slots__ = ['_blob']
+
+    @classmethod
+    def _create_ctype_obj(cls, init):
+        # may be overridden
+        raise TypeError("cannot instantiate opaque type %s" % (cls,))
+
+    def _get_own_repr(self):
+        return self._addr_repr(ctypes.addressof(self._blob))
+
+    @classmethod
+    def _offsetof(cls, fieldname):
+        return getattr(cls._ctype, fieldname).offset
+
+    def _convert_to_address(self, BClass):
+        if getattr(BClass, '_BItem', None) is self.__class__:
+            return ctypes.addressof(self._blob)
+        else:
+            return CTypesData._convert_to_address(self, BClass)
+
+    @classmethod
+    def _from_ctypes(cls, ctypes_struct_or_union):
+        self = cls.__new__(cls)
+        self._blob = ctypes_struct_or_union
+        return self
+
+    @classmethod
+    def _to_ctypes(cls, value):
+        return value._blob
+
+    def __repr__(self, c_name=None):
+        return CTypesData.__repr__(self, c_name or self._get_c_name(' &'))
+
+
+class CTypesBackend(object):
+
+    PRIMITIVE_TYPES = {
+        'char': ctypes.c_char,
+        'short': ctypes.c_short,
+        'int': ctypes.c_int,
+        'long': ctypes.c_long,
+        'long long': ctypes.c_longlong,
+        'signed char': ctypes.c_byte,
+        'unsigned char': ctypes.c_ubyte,
+        'unsigned short': ctypes.c_ushort,
+        'unsigned int': ctypes.c_uint,
+        'unsigned long': ctypes.c_ulong,
+        'unsigned long long': ctypes.c_ulonglong,
+        'float': ctypes.c_float,
+        'double': ctypes.c_double,
+        '_Bool': ctypes.c_bool,
+        }
+
+    for _name in ['unsigned long long', 'unsigned long',
+                  'unsigned int', 'unsigned short', 'unsigned char']:
+        _size = ctypes.sizeof(PRIMITIVE_TYPES[_name])
+        PRIMITIVE_TYPES['uint%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name]
+        if _size == ctypes.sizeof(ctypes.c_void_p):
+            PRIMITIVE_TYPES['uintptr_t'] = PRIMITIVE_TYPES[_name]
+        if _size == ctypes.sizeof(ctypes.c_size_t):
+            PRIMITIVE_TYPES['size_t'] = PRIMITIVE_TYPES[_name]
+
+    for _name in ['long long', 'long', 'int', 'short', 'signed char']:
+        _size = ctypes.sizeof(PRIMITIVE_TYPES[_name])
+        PRIMITIVE_TYPES['int%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name]
+        if _size == ctypes.sizeof(ctypes.c_void_p):
+            PRIMITIVE_TYPES['intptr_t'] = PRIMITIVE_TYPES[_name]
+            PRIMITIVE_TYPES['ptrdiff_t'] = PRIMITIVE_TYPES[_name]
+        if _size == ctypes.sizeof(ctypes.c_size_t):
+            PRIMITIVE_TYPES['ssize_t'] = PRIMITIVE_TYPES[_name]
+
+
+    def __init__(self):
+        self.RTLD_LAZY = 0   # not supported anyway by ctypes
+        self.RTLD_NOW  = 0
+        self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL
+        self.RTLD_LOCAL = ctypes.RTLD_LOCAL
+
+    def set_ffi(self, ffi):
+        self.ffi = ffi
+
+    def _get_types(self):
+        return CTypesData, CTypesType
+
+    def load_library(self, path, flags=0):
+        cdll = ctypes.CDLL(path, flags)
+        return CTypesLibrary(self, cdll)
+
+    def new_void_type(self):
+        class CTypesVoid(CTypesData):
+            __slots__ = []
+            _reftypename = 'void &'
+            @staticmethod
+            def _from_ctypes(novalue):
+                return None
+            @staticmethod
+            def _to_ctypes(novalue):
+                if novalue is not None:
+                    raise TypeError("None expected, got %s object" %
+                                    (type(novalue).__name__,))
+                return None
+        CTypesVoid._fix_class()
+        return CTypesVoid
+
+    def new_primitive_type(self, name):
+        if name == 'wchar_t':
+            raise NotImplementedError(name)
+        ctype = self.PRIMITIVE_TYPES[name]
+        if name == 'char':
+            kind = 'char'
+        elif name in ('float', 'double'):
+            kind = 'float'
+        else:
+            if name in ('signed char', 'unsigned char'):
+                kind = 'byte'
+            elif name == '_Bool':
+                kind = 'bool'
+            else:
+                kind = 'int'
+            is_signed = (ctype(-1).value == -1)
+        #
+        def _cast_source_to_int(source):
+            if isinstance(source, (int, long, float)):
+                source = int(source)
+            elif isinstance(source, CTypesData):
+                source = source._cast_to_integer()
+            elif isinstance(source, bytes):
+                source = ord(source)
+            elif source is None:
+                source = 0
+            else:
+                raise TypeError("bad type for cast to %r: %r" %
+                                (CTypesPrimitive, type(source).__name__))
+            return source
+        #
+        kind1 = kind
+        class CTypesPrimitive(CTypesGenericPrimitive):
+            __slots__ = ['_value']
+            _ctype = ctype
+            _reftypename = '%s &' % name
+            kind = kind1
+
+            def __init__(self, value):
+                self._value = value
+
+            @staticmethod
+            def _create_ctype_obj(init):
+                if init is None:
+                    return ctype()
+                return ctype(CTypesPrimitive._to_ctypes(init))
+
+            if kind == 'int' or kind == 'byte':
+                @classmethod
+                def _cast_from(cls, source):
+                    source = _cast_source_to_int(source)
+                    source = ctype(source).value     # cast within range
+                    return cls(source)
+                def __int__(self):
+                    return self._value
+
+            if kind == 'bool':
+                @classmethod
+                def _cast_from(cls, source):
+                    if not isinstance(source, (int, long, float)):
+                        source = _cast_source_to_int(source)
+                    return cls(bool(source))
+                def __int__(self):
+                    return self._value
+
+            if kind == 'char':
+                @classmethod
+                def _cast_from(cls, source):
+                    source = _cast_source_to_int(source)
+                    source = bytechr(source & 0xFF)
+                    return cls(source)
+                def __int__(self):
+                    return ord(self._value)
+
+            if kind == 'float':
+                @classmethod
+                def _cast_from(cls, source):
+                    if isinstance(source, float):
+                        pass
+                    elif isinstance(source, CTypesGenericPrimitive):
+                        if hasattr(source, '__float__'):
+                            source = float(source)
+                        else:
+                            source = int(source)
+                    else:
+                        source = _cast_source_to_int(source)
+                    source = ctype(source).value     # fix precision
+                    return cls(source)
+                def __int__(self):
+                    return int(self._value)
+                def __float__(self):
+                    return self._value
+
+            _cast_to_integer = __int__
+
+            if kind == 'int' or kind == 'byte' or kind == 'bool':
+                @staticmethod
+                def _to_ctypes(x):
+                    if not isinstance(x, (int, long)):
+                        if isinstance(x, CTypesData):
+                            x = int(x)
+                        else:
+                            raise TypeError("integer expected, got %s" %
+                                            type(x).__name__)
+                    if ctype(x).value != x:
+                        if not is_signed and x < 0:
+                            raise OverflowError("%s: negative integer" % name)
+                        else:
+                            raise OverflowError("%s: integer out of bounds"
+                                                % name)
+                    return x
+
+            if kind == 'char':
+                @staticmethod
+                def _to_ctypes(x):
+                    if isinstance(x, bytes) and len(x) == 1:
+                        return x
+                    if isinstance(x, CTypesPrimitive):    # <CData <char>>
+                        return x._value
+                    raise TypeError("character expected, got %s" %
+                                    type(x).__name__)
+                def __nonzero__(self):
+                    return ord(self._value) != 0
+            else:
+                def __nonzero__(self):
+                    return self._value != 0
+            __bool__ = __nonzero__
+
+            if kind == 'float':
+                @staticmethod
+                def _to_ctypes(x):
+                    if not isinstance(x, (int, long, float, CTypesData)):
+                        raise TypeError("float expected, got %s" %
+                                        type(x).__name__)
+                    return ctype(x).value
+
+            @staticmethod
+            def _from_ctypes(value):
+                return getattr(value, 'value', value)
+
+            @staticmethod
+            def _initialize(blob, init):
+                blob.value = CTypesPrimitive._to_ctypes(init)
+
+            if kind == 'char':
+                def _to_string(self, maxlen):
+                    return self._value
+            if kind == 'byte':
+                def _to_string(self, maxlen):
+                    return chr(self._value & 0xff)
+        #
+        CTypesPrimitive._fix_class()
+        return CTypesPrimitive
+
+    def new_pointer_type(self, BItem):
+        getbtype = self.ffi._get_cached_btype
+        if BItem is getbtype(model.PrimitiveType('char')):
+            kind = 'charp'
+        elif BItem in (getbtype(model.PrimitiveType('signed char')),
+                       getbtype(model.PrimitiveType('unsigned char'))):
+            kind = 'bytep'
+        elif BItem is getbtype(model.void_type):
+            kind = 'voidp'
+        else:
+            kind = 'generic'
+        #
+        class CTypesPtr(CTypesGenericPtr):
+            __slots__ = ['_own']
+            if kind == 'charp':
+                __slots__ += ['__as_strbuf']
+            _BItem = BItem
+            if hasattr(BItem, '_ctype'):
+                _ctype = ctypes.POINTER(BItem._ctype)
+                _bitem_size = ctypes.sizeof(BItem._ctype)
+            else:
+                _ctype = ctypes.c_void_p
+            if issubclass(BItem, CTypesGenericArray):
+                _reftypename = BItem._get_c_name('(* &)')
+            else:
+                _reftypename = BItem._get_c_name(' * &')
+
+            def __init__(self, init):
+                ctypeobj = BItem._create_ctype_obj(init)
+                if kind == 'charp':
+                    self.__as_strbuf = ctypes.create_string_buffer(
+                        ctypeobj.value + b'\x00')
+                    self._as_ctype_ptr = ctypes.cast(
+                        self.__as_strbuf, self._ctype)
+                else:
+                    self._as_ctype_ptr = ctypes.pointer(ctypeobj)
+                self._address = ctypes.cast(self._as_ctype_ptr,
+                                            ctypes.c_void_p).value
+                self._own = True
+
+            def __add__(self, other):
+                if isinstance(other, (int, long)):
+                    return self._new_pointer_at(self._address +
+                                                other * self._bitem_size)
+                else:
+                    return NotImplemented
+
+            def __sub__(self, other):
+                if isinstance(other, (int, long)):
+                    return self._new_pointer_at(self._address -
+                                                other * self._bitem_size)
+                elif type(self) is type(other):
+                    return (self._address - other._address) // self._bitem_size
+                else:
+                    return NotImplemented
+
+            def __getitem__(self, index):
+                if getattr(self, '_own', False) and index != 0:
+                    raise IndexError
+                return BItem._from_ctypes(self._as_ctype_ptr[index])
+
+            def __setitem__(self, index, value):
+                self._as_ctype_ptr[index] = BItem._to_ctypes(value)
+
+            if kind == 'charp' or kind == 'voidp':
+                @classmethod
+                def _arg_to_ctypes(cls, *value):
+                    if value and isinstance(value[0], bytes):
+                        return ctypes.c_char_p(value[0])
+                    else:
+                        return super(CTypesPtr, cls)._arg_to_ctypes(*value)
+
+            if kind == 'charp' or kind == 'bytep':
+                def _to_string(self, maxlen):
+                    if maxlen < 0:
+                        maxlen = sys.maxsize
+                    p = ctypes.cast(self._as_ctype_ptr,
+                                    ctypes.POINTER(ctypes.c_char))
+                    n = 0
+                    while n < maxlen and p[n] != b'\x00':
+                        n += 1
+                    return b''.join([p[i] for i in range(n)])
+
+            def _get_own_repr(self):
+                if getattr(self, '_own', False):
+                    return 'owning %d bytes' % (
+                        ctypes.sizeof(self._as_ctype_ptr.contents),)
+                return super(CTypesPtr, self)._get_own_repr()
+        #
+        if (BItem is self.ffi._get_cached_btype(model.void_type) or
+            BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))):
+            CTypesPtr._automatic_casts = True
+        #
+        CTypesPtr._fix_class()
+        return CTypesPtr
+
+    def new_array_type(self, CTypesPtr, length):
+        if length is None:
+            brackets = ' &[]'
+        else:
+            brackets = ' &[%d]' % length
+        BItem = CTypesPtr._BItem
+        getbtype = self.ffi._get_cached_btype
+        if BItem is getbtype(model.PrimitiveType('char')):
+            kind = 'char'
+        elif BItem in (getbtype(model.PrimitiveType('signed char')),
+                       getbtype(model.PrimitiveType('unsigned char'))):
+            kind = 'byte'
+        else:
+            kind = 'generic'
+        #
+        class CTypesArray(CTypesGenericArray):
+            __slots__ = ['_blob', '_own']
+            if length is not None:
+                _ctype = BItem._ctype * length
+            else:
+                __slots__.append('_ctype')
+            _reftypename = BItem._get_c_name(brackets)
+            _declared_length = length
+            _CTPtr = CTypesPtr
+
+            def __init__(self, init):
+                if length is None:
+                    if isinstance(init, (int, long)):
+                        len1 = init
+                        init = None
+                    elif kind == 'char' and isinstance(init, bytes):
+                        len1 = len(init) + 1    # extra null
+                    else:
+                        init = tuple(init)
+                        len1 = len(init)
+                    self._ctype = BItem._ctype * len1
+                self._blob = self._ctype()
+                self._own = True
+                if init is not None:
+                    self._initialize(self._blob, init)
+
+            @staticmethod
+            def _initialize(blob, init):
+                if isinstance(init, bytes):
+                    init = [init[i:i+1] for i in range(len(init))]
+                else:
+                    init = tuple(init)
+                if len(init) > len(blob):
+                    raise IndexError("too many initializers")
+                addr = ctypes.cast(blob, ctypes.c_void_p).value
+                PTR = ctypes.POINTER(BItem._ctype)
+                itemsize = ctypes.sizeof(BItem._ctype)
+                for i, value in enumerate(init):
+                    p = ctypes.cast(addr + i * itemsize, PTR)
+                    BItem._initialize(p.contents, value)
+
+            def __len__(self):
+                return len(self._blob)
+
+            def __getitem__(self, index):
+                if not (0 <= index < len(self._blob)):
+                    raise IndexError
+                return BItem._from_ctypes(self._blob[index])
+
+            def __setitem__(self, index, value):
+                if not (0 <= index < len(self._blob)):
+                    raise IndexError
+                self._blob[index] = BItem._to_ctypes(value)
+
+            if kind == 'char' or kind == 'byte':
+                def _to_string(self, maxlen):
+                    if maxlen < 0:
+                        maxlen = len(self._blob)
+                    p = ctypes.cast(self._blob,
+                                    ctypes.POINTER(ctypes.c_char))
+                    n = 0
+                    while n < maxlen and p[n] != b'\x00':
+                        n += 1
+                    return b''.join([p[i] for i in range(n)])
+
+            def _get_own_repr(self):
+                if getattr(self, '_own', False):
+                    return 'owning %d bytes' % (ctypes.sizeof(self._blob),)
+                return super(CTypesArray, self)._get_own_repr()
+
+            def _convert_to_address(self, BClass):
+                if BClass in (CTypesPtr, None) or BClass._automatic_casts:
+                    return ctypes.addressof(self._blob)
+                else:
+                    return CTypesData._convert_to_address(self, BClass)
+
+            @staticmethod
+            def _from_ctypes(ctypes_array):
+                self = CTypesArray.__new__(CTypesArray)
+                self._blob = ctypes_array
+                return self
+
+            @staticmethod
+            def _arg_to_ctypes(value):
+                return CTypesPtr._arg_to_ctypes(value)
+
+            def __add__(self, other):
+                if isinstance(other, (int, long)):
+                    return CTypesPtr._new_pointer_at(
+                        ctypes.addressof(self._blob) +
+                        other * ctypes.sizeof(BItem._ctype))
+                else:
+                    return NotImplemented
+
+            @classmethod
+            def _cast_from(cls, source):
+                raise NotImplementedError("casting to %r" % (
+                    cls._get_c_name(),))
+        #
+        CTypesArray._fix_class()
+        return CTypesArray
+
+    def _new_struct_or_union(self, kind, name, base_ctypes_class):
+        #
+        class struct_or_union(base_ctypes_class):
+            pass
+        struct_or_union.__name__ = '%s_%s' % (kind, name)
+        kind1 = kind
+        #
+        class CTypesStructOrUnion(CTypesBaseStructOrUnion):
+            __slots__ = ['_blob']
+            _ctype = struct_or_union
+            _reftypename = '%s &' % (name,)
+            _kind = kind = kind1
+        #
+        CTypesStructOrUnion._fix_class()
+        return CTypesStructOrUnion
+
+    def new_struct_type(self, name):
+        return self._new_struct_or_union('struct', name, ctypes.Structure)
+
+    def new_union_type(self, name):
+        return self._new_struct_or_union('union', name, ctypes.Union)
+
+    def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp,
+                                 totalsize=-1, totalalignment=-1, sflags=0):
+        if totalsize >= 0 or totalalignment >= 0:
+            raise NotImplementedError("the ctypes backend of CFFI does not 
support "
+                                      "structures completed by verify(); 
please "
+                                      "compile and install the _cffi_backend 
module.")
+        struct_or_union = CTypesStructOrUnion._ctype
+        fnames = [fname for (fname, BField, bitsize) in fields]
+        btypes = [BField for (fname, BField, bitsize) in fields]
+        bitfields = [bitsize for (fname, BField, bitsize) in fields]
+        #
+        bfield_types = {}
+        cfields = []
+        for (fname, BField, bitsize) in fields:
+            if bitsize < 0:
+                cfields.append((fname, BField._ctype))
+                bfield_types[fname] = BField
+            else:
+                cfields.append((fname, BField._ctype, bitsize))
+                bfield_types[fname] = Ellipsis
+        if sflags & 8:
+            struct_or_union._pack_ = 1
+        struct_or_union._fields_ = cfields
+        CTypesStructOrUnion._bfield_types = bfield_types
+        #
+        @staticmethod
+        def _create_ctype_obj(init):
+            result = struct_or_union()
+            if init is not None:
+                initialize(result, init)
+            return result
+        CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj
+        #
+        def initialize(blob, init):
+            if is_union:
+                if len(init) > 1:
+                    raise ValueError("union initializer: %d items given, but "
+                                    "only one supported (use a dict if needed)"
+                                     % (len(init),))
+            if not isinstance(init, dict):
+                if isinstance(init, (bytes, unicode)):
+                    raise TypeError("union initializer: got a str")
+                init = tuple(init)
+                if len(init) > len(fnames):
+                    raise ValueError("too many values for %s initializer" %
+                                     CTypesStructOrUnion._get_c_name())
+                init = dict(zip(fnames, init))
+            addr = ctypes.addressof(blob)
+            for fname, value in init.items():
+                BField, bitsize = name2fieldtype[fname]
+                assert bitsize < 0, \
+                       "not implemented: initializer with bit fields"
+                offset = CTypesStructOrUnion._offsetof(fname)
+                PTR = ctypes.POINTER(BField._ctype)
+                p = ctypes.cast(addr + offset, PTR)
+                BField._initialize(p.contents, value)
+        is_union = CTypesStructOrUnion._kind == 'union'
+        name2fieldtype = dict(zip(fnames, zip(btypes, bitfields)))
+        #
+        for fname, BField, bitsize in fields:
+            if fname == '':
+                raise NotImplementedError("nested anonymous structs/unions")
+            if hasattr(CTypesStructOrUnion, fname):
+                raise ValueError("the field name %r conflicts in "
+                                 "the ctypes backend" % fname)
+            if bitsize < 0:
+                def getter(self, fname=fname, BField=BField,
+                           offset=CTypesStructOrUnion._offsetof(fname),
+                           PTR=ctypes.POINTER(BField._ctype)):
+                    addr = ctypes.addressof(self._blob)
+                    p = ctypes.cast(addr + offset, PTR)
+                    return BField._from_ctypes(p.contents)
+                def setter(self, value, fname=fname, BField=BField):
+                    setattr(self._blob, fname, BField._to_ctypes(value))
+                #
+                if issubclass(BField, CTypesGenericArray):
+                    setter = None
+                    if BField._declared_length == 0:
+                        def getter(self, fname=fname, BFieldPtr=BField._CTPtr,
+                                   offset=CTypesStructOrUnion._offsetof(fname),
+                                   PTR=ctypes.POINTER(BField._ctype)):
+                            addr = ctypes.addressof(self._blob)
+                            p = ctypes.cast(addr + offset, PTR)
+                            return BFieldPtr._from_ctypes(p)
+                #
+            else:
+                def getter(self, fname=fname, BField=BField):
+                    return BField._from_ctypes(getattr(self._blob, fname))
+                def setter(self, value, fname=fname, BField=BField):
+                    # xxx obscure workaround
+                    value = BField._to_ctypes(value)
+                    oldvalue = getattr(self._blob, fname)
+                    setattr(self._blob, fname, value)
+                    if value != getattr(self._blob, fname):
+                        setattr(self._blob, fname, oldvalue)
+                        raise OverflowError("value too large for bitfield")
+            setattr(CTypesStructOrUnion, fname, property(getter, setter))
+        #
+        CTypesPtr = self.ffi._get_cached_btype(model.PointerType(tp))
+        for fname in fnames:
+            if hasattr(CTypesPtr, fname):
+                raise ValueError("the field name %r conflicts in "
+                                 "the ctypes backend" % fname)
+            def getter(self, fname=fname):
+                return getattr(self[0], fname)
+            def setter(self, value, fname=fname):
+                setattr(self[0], fname, value)
+            setattr(CTypesPtr, fname, property(getter, setter))
+
+    def new_function_type(self, BArgs, BResult, has_varargs):
+        nameargs = [BArg._get_c_name() for BArg in BArgs]
+        if has_varargs:
+            nameargs.append('...')
+        nameargs = ', '.join(nameargs)
+        #
+        class CTypesFunctionPtr(CTypesGenericPtr):
+            __slots__ = ['_own_callback', '_name']
+            _ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None),
+                                      *[BArg._ctype for BArg in BArgs],
+                                      use_errno=True)
+            _reftypename = BResult._get_c_name('(* &)(%s)' % (nameargs,))
+
+            def __init__(self, init, error=None):
+                # create a callback to the Python callable init()
+                import traceback
+                assert not has_varargs, "varargs not supported for callbacks"
+                if getattr(BResult, '_ctype', None) is not None:
+                    error = BResult._from_ctypes(
+                        BResult._create_ctype_obj(error))
+                else:
+                    error = None
+                def callback(*args):
+                    args2 = []
+                    for arg, BArg in zip(args, BArgs):
+                        args2.append(BArg._from_ctypes(arg))
+                    try:
+                        res2 = init(*args2)
+                        res2 = BResult._to_ctypes(res2)
+                    except:
+                        traceback.print_exc()
+                        res2 = error
+                    if issubclass(BResult, CTypesGenericPtr):
+                        if res2:
+                            res2 = ctypes.cast(res2, ctypes.c_void_p).value
+                                # .value: http://bugs.python.org/issue1574593
+                        else:
+                            res2 = None
+                    #print repr(res2)
+                    return res2
+                if issubclass(BResult, CTypesGenericPtr):
+                    # The only pointers callbacks can return are void*s:
+                    # http://bugs.python.org/issue5710
+                    callback_ctype = ctypes.CFUNCTYPE(
+                        ctypes.c_void_p,
+                        *[BArg._ctype for BArg in BArgs],
+                        use_errno=True)
+                else:
+                    callback_ctype = CTypesFunctionPtr._ctype
+                self._as_ctype_ptr = callback_ctype(callback)
+                self._address = ctypes.cast(self._as_ctype_ptr,
+                                            ctypes.c_void_p).value
+                self._own_callback = init
+
+            @staticmethod
+            def _initialize(ctypes_ptr, value):
+                if value:
+                    raise NotImplementedError("ctypes backend: not supported: "
+                                          "initializers for function pointers")
+
+            def __repr__(self):
+                c_name = getattr(self, '_name', None)
+                if c_name:
+                    i = self._reftypename.index('(* &)')
+                    if self._reftypename[i-1] not in ' )*':
+                        c_name = ' ' + c_name
+                    c_name = self._reftypename.replace('(* &)', c_name)
+                return CTypesData.__repr__(self, c_name)
+
+            def _get_own_repr(self):
+                if getattr(self, '_own_callback', None) is not None:
+                    return 'calling %r' % (self._own_callback,)
+                return super(CTypesFunctionPtr, self)._get_own_repr()
+
+            def __call__(self, *args):
+                if has_varargs:
+                    assert len(args) >= len(BArgs)
+                    extraargs = args[len(BArgs):]
+                    args = args[:len(BArgs)]
+                else:
+                    assert len(args) == len(BArgs)
+                ctypes_args = []
+                for arg, BArg in zip(args, BArgs):
+                    ctypes_args.append(BArg._arg_to_ctypes(arg))
+                if has_varargs:
+                    for i, arg in enumerate(extraargs):
+                        if arg is None:
+                            ctypes_args.append(ctypes.c_void_p(0))  # NULL
+                            continue
+                        if not isinstance(arg, CTypesData):
+                            raise TypeError(
+                                "argument %d passed in the variadic part "
+                                "needs to be a cdata object (got %s)" %
+                                (1 + len(BArgs) + i, type(arg).__name__))
+                        ctypes_args.append(arg._arg_to_ctypes(arg))
+                result = self._as_ctype_ptr(*ctypes_args)
+                return BResult._from_ctypes(result)
+        #
+        CTypesFunctionPtr._fix_class()
+        return CTypesFunctionPtr
+
+    def new_enum_type(self, name, enumerators, enumvalues, CTypesInt):
+        assert isinstance(name, str)
+        reverse_mapping = dict(zip(reversed(enumvalues),
+                                   reversed(enumerators)))
+        #
+        class CTypesEnum(CTypesInt):
+            __slots__ = []
+            _reftypename = '%s &' % name
+
+            def _get_own_repr(self):
+                value = self._value
+                try:
+                    return '%d: %s' % (value, reverse_mapping[value])
+                except KeyError:
+                    return str(value)
+
+            def _to_string(self, maxlen):
+                value = self._value
+                try:
+                    return reverse_mapping[value]
+                except KeyError:
+                    return str(value)
+        #
+        CTypesEnum._fix_class()
+        return CTypesEnum
+
+    def get_errno(self):
+        return ctypes.get_errno()
+
+    def set_errno(self, value):
+        ctypes.set_errno(value)
+
+    def string(self, b, maxlen=-1):
+        return b._to_string(maxlen)
+
+    def buffer(self, bptr, size=-1):
+        raise NotImplementedError("buffer() with ctypes backend")
+
+    def sizeof(self, cdata_or_BType):
+        if isinstance(cdata_or_BType, CTypesData):
+            return cdata_or_BType._get_size_of_instance()
+        else:
+            assert issubclass(cdata_or_BType, CTypesData)
+            return cdata_or_BType._get_size()
+
+    def alignof(self, BType):
+        assert issubclass(BType, CTypesData)
+        return BType._alignment()
+
+    def newp(self, BType, source):
+        if not issubclass(BType, CTypesData):
+            raise TypeError
+        return BType._newp(source)
+
+    def cast(self, BType, source):
+        return BType._cast_from(source)
+
+    def callback(self, BType, source, error, onerror):
+        assert onerror is None   # XXX not implemented
+        return BType(source, error)
+
+    def gcp(self, cdata, destructor):
+        BType = self.typeof(cdata)
+
+        if destructor is None:
+            if not (hasattr(BType, '_gcp_type') and
+                    BType._gcp_type is BType):
+                raise TypeError("Can remove destructor only on a object "
+                                "previously returned by ffi.gc()")
+            cdata._destructor = None
+            return None
+
+        try:
+            gcp_type = BType._gcp_type
+        except AttributeError:
+            class CTypesDataGcp(BType):
+                __slots__ = ['_orig', '_destructor']
+                def __del__(self):
+                    if self._destructor is not None:
+                        self._destructor(self._orig)
+            gcp_type = BType._gcp_type = CTypesDataGcp
+        new_cdata = self.cast(gcp_type, cdata)
+        new_cdata._orig = cdata
+        new_cdata._destructor = destructor
+        return new_cdata
+
+    typeof = type
+
+    def getcname(self, BType, replace_with):
+        return BType._get_c_name(replace_with)
+
+    def typeoffsetof(self, BType, fieldname, num=0):
+        if isinstance(fieldname, str):
+            if num == 0 and issubclass(BType, CTypesGenericPtr):
+                BType = BType._BItem
+            if not issubclass(BType, CTypesBaseStructOrUnion):
+                raise TypeError("expected a struct or union ctype")
+            BField = BType._bfield_types[fieldname]
+            if BField is Ellipsis:
+                raise TypeError("not supported for bitfields")
+            return (BField, BType._offsetof(fieldname))
+        elif isinstance(fieldname, (int, long)):
+            if issubclass(BType, CTypesGenericArray):
+                BType = BType._CTPtr
+            if not issubclass(BType, CTypesGenericPtr):
+                raise TypeError("expected an array or ptr ctype")
+            BItem = BType._BItem
+            offset = BItem._get_size() * fieldname
+            if offset > sys.maxsize:
+                raise OverflowError
+            return (BItem, offset)
+        else:
+            raise TypeError(type(fieldname))
+
+    def rawaddressof(self, BTypePtr, cdata, offset=None):
+        if isinstance(cdata, CTypesBaseStructOrUnion):
+            ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata))
+        elif isinstance(cdata, CTypesGenericPtr):
+            if offset is None or not issubclass(type(cdata)._BItem,
+                                                CTypesBaseStructOrUnion):
+                raise TypeError("unexpected cdata type")
+            ptr = type(cdata)._to_ctypes(cdata)
+        elif isinstance(cdata, CTypesGenericArray):
+            ptr = type(cdata)._to_ctypes(cdata)
+        else:
+            raise TypeError("expected a <cdata 'struct-or-union'>")
+        if offset:
+            ptr = ctypes.cast(
+                ctypes.c_void_p(
+                    ctypes.cast(ptr, ctypes.c_void_p).value + offset),
+                type(ptr))
+        return BTypePtr._from_ctypes(ptr)
+
+
+class CTypesLibrary(object):
+
+    def __init__(self, backend, cdll):
+        self.backend = backend
+        self.cdll = cdll
+
+    def load_function(self, BType, name):
+        c_func = getattr(self.cdll, name)
+        funcobj = BType._from_ctypes(c_func)
+        funcobj._name = name
+        return funcobj
+
+    def read_variable(self, BType, name):
+        try:
+            ctypes_obj = BType._ctype.in_dll(self.cdll, name)
+        except AttributeError as e:
+            raise NotImplementedError(e)
+        return BType._from_ctypes(ctypes_obj)
+
+    def write_variable(self, BType, name, value):
+        new_ctypes_obj = BType._to_ctypes(value)
+        ctypes_obj = BType._ctype.in_dll(self.cdll, name)
+        ctypes.memmove(ctypes.addressof(ctypes_obj),
+                       ctypes.addressof(new_ctypes_obj),
+                       ctypes.sizeof(BType._ctype))
diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py
new file mode 100644
--- /dev/null
+++ b/testing/cffi0/backend_tests.py
@@ -0,0 +1,1868 @@
+import py
+import platform
+import sys, ctypes
+from cffi import FFI, CDefError, FFIError, VerificationMissing
+from testing.support import *
+
+SIZE_OF_INT   = ctypes.sizeof(ctypes.c_int)
+SIZE_OF_LONG  = ctypes.sizeof(ctypes.c_long)
+SIZE_OF_SHORT = ctypes.sizeof(ctypes.c_short)
+SIZE_OF_PTR   = ctypes.sizeof(ctypes.c_void_p)
+SIZE_OF_WCHAR = ctypes.sizeof(ctypes.c_wchar)
+
+
+class BackendTests:
+
+    def test_integer_ranges(self):
+        ffi = FFI(backend=self.Backend())
+        for (c_type, size) in [('char', 1),
+                               ('short', 2),
+                               ('short int', 2),
+                               ('', 4),
+                               ('int', 4),
+                               ('long', SIZE_OF_LONG),
+                               ('long int', SIZE_OF_LONG),
+                               ('long long', 8),
+                               ('long long int', 8),
+                               ]:
+            for unsigned in [None, False, True]:
+                c_decl = {None: '',
+                          False: 'signed ',
+                          True: 'unsigned '}[unsigned] + c_type
+                if c_decl == 'char' or c_decl == '':
+                    continue
+                self._test_int_type(ffi, c_decl, size, unsigned)
+
+    def test_fixedsize_int(self):
+        ffi = FFI(backend=self.Backend())
+        for size in [1, 2, 4, 8]:
+            self._test_int_type(ffi, 'int%d_t' % (8*size), size, False)
+            self._test_int_type(ffi, 'uint%d_t' % (8*size), size, True)
+        self._test_int_type(ffi, 'intptr_t', SIZE_OF_PTR, False)
+        self._test_int_type(ffi, 'uintptr_t', SIZE_OF_PTR, True)
+        self._test_int_type(ffi, 'ptrdiff_t', SIZE_OF_PTR, False)
+        self._test_int_type(ffi, 'size_t', SIZE_OF_PTR, True)
+        self._test_int_type(ffi, 'ssize_t', SIZE_OF_PTR, False)
+
+    def _test_int_type(self, ffi, c_decl, size, unsigned):
+        if unsigned:
+            min = 0
+            max = (1 << (8*size)) - 1
+        else:
+            min = -(1 << (8*size-1))
+            max = (1 << (8*size-1)) - 1
+        min = int(min)
+        max = int(max)
+        p = ffi.cast(c_decl, min)
+        assert p != min       # no __eq__(int)
+        assert bool(p) is bool(min)
+        assert int(p) == min
+        p = ffi.cast(c_decl, max)
+        assert int(p) == max
+        p = ffi.cast(c_decl, long(max))
+        assert int(p) == max
+        q = ffi.cast(c_decl, min - 1)
+        assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max
+        q = ffi.cast(c_decl, long(min - 1))
+        assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max
+        assert q != p
+        assert int(q) == int(p)
+        assert hash(q) != hash(p)   # unlikely
+        c_decl_ptr = '%s *' % c_decl
+        py.test.raises(OverflowError, ffi.new, c_decl_ptr, min - 1)
+        py.test.raises(OverflowError, ffi.new, c_decl_ptr, max + 1)
+        py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(min - 1))
+        py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(max + 1))
+        assert ffi.new(c_decl_ptr, min)[0] == min
+        assert ffi.new(c_decl_ptr, max)[0] == max
+        assert ffi.new(c_decl_ptr, long(min))[0] == min
+        assert ffi.new(c_decl_ptr, long(max))[0] == max
+
+    def test_new_unsupported_type(self):
+        ffi = FFI(backend=self.Backend())
+        e = py.test.raises(TypeError, ffi.new, "int")
+        assert str(e.value) == "expected a pointer or array ctype, got 'int'"
+
+    def test_new_single_integer(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int *")     # similar to ffi.new("int[1]")
+        assert p[0] == 0
+        p[0] = -123
+        assert p[0] == -123
+        p = ffi.new("int *", -42)
+        assert p[0] == -42
+        assert repr(p) == "<cdata 'int *' owning %d bytes>" % SIZE_OF_INT
+
+    def test_new_array_no_arg(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int[10]")
+        # the object was zero-initialized:
+        for i in range(10):
+            assert p[i] == 0
+
+    def test_array_indexing(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int[10]")
+        p[0] = 42
+        p[9] = 43
+        assert p[0] == 42
+        assert p[9] == 43
+        py.test.raises(IndexError, "p[10]")
+        py.test.raises(IndexError, "p[10] = 44")
+        py.test.raises(IndexError, "p[-1]")
+        py.test.raises(IndexError, "p[-1] = 44")
+
+    def test_new_array_args(self):
+        ffi = FFI(backend=self.Backend())
+        # this tries to be closer to C: where we say "int x[5] = {10, 20, ..}"
+        # then here we must enclose the items in a list
+        p = ffi.new("int[5]", [10, 20, 30, 40, 50])
+        assert p[0] == 10
+        assert p[1] == 20
+        assert p[2] == 30
+        assert p[3] == 40
+        assert p[4] == 50
+        p = ffi.new("int[4]", [25])
+        assert p[0] == 25
+        assert p[1] == 0     # follow C convention rather than LuaJIT's
+        assert p[2] == 0
+        assert p[3] == 0
+        p = ffi.new("int[4]", [ffi.cast("int", -5)])
+        assert p[0] == -5
+        assert repr(p) == "<cdata 'int[4]' owning %d bytes>" % (4*SIZE_OF_INT)
+
+    def test_new_array_varsize(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int[]", 10)     # a single integer is the length
+        assert p[9] == 0
+        py.test.raises(IndexError, "p[10]")
+        #
+        py.test.raises(TypeError, ffi.new, "int[]")
+        #
+        p = ffi.new("int[]", [-6, -7])    # a list is all the items, like C
+        assert p[0] == -6
+        assert p[1] == -7
+        py.test.raises(IndexError, "p[2]")
+        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(ValueError, ffi.new, "int[]", -1)
+        assert repr(p) == "<cdata 'int[]' owning 0 bytes>"
+
+    def test_pointer_init(self):
+        ffi = FFI(backend=self.Backend())
+        n = ffi.new("int *", 24)
+        a = ffi.new("int *[10]", [ffi.NULL, ffi.NULL, n, n, ffi.NULL])
+        for i in range(10):
+            if i not in (2, 3):
+                assert a[i] == ffi.NULL
+        assert a[2] == a[3] == n
+
+    def test_cannot_cast(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.new("short int[10]")
+        e = py.test.raises(TypeError, ffi.new, "long int **", a)
+        msg = str(e.value)
+        assert "'short[10]'" in msg and "'long *'" in msg
+
+    def test_new_pointer_to_array(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.new("int[4]", [100, 102, 104, 106])
+        p = ffi.new("int **", a)
+        assert p[0] == ffi.cast("int *", a)
+        assert p[0][2] == 104
+        p = ffi.cast("int *", a)
+        assert p[0] == 100
+        assert p[1] == 102
+        assert p[2] == 104
+        assert p[3] == 106
+        # keepalive: a
+
+    def test_pointer_direct(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.cast("int*", 0)
+        assert p is not None
+        assert bool(p) is False
+        assert p == ffi.cast("int*", 0)
+        assert p != None
+        assert repr(p) == "<cdata 'int *' NULL>"
+        a = ffi.new("int[]", [123, 456])
+        p = ffi.cast("int*", a)
+        assert bool(p) is True
+        assert p == ffi.cast("int*", a)
+        assert p != ffi.cast("int*", 0)
+        assert p[0] == 123
+        assert p[1] == 456
+
+    def test_repr(self):
+        typerepr = self.TypeRepr
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { short a, b, c; };")
+        p = ffi.cast("short unsigned int", 0)
+        assert repr(p) == "<cdata 'unsigned short' 0>"
+        assert repr(ffi.typeof(p)) == typerepr % "unsigned short"
+        p = ffi.cast("unsigned short int", 0)
+        assert repr(p) == "<cdata 'unsigned short' 0>"
+        assert repr(ffi.typeof(p)) == typerepr % "unsigned short"
+        p = ffi.cast("int*", 0)
+        assert repr(p) == "<cdata 'int *' NULL>"
+        assert repr(ffi.typeof(p)) == typerepr % "int *"
+        #
+        p = ffi.new("int*")
+        assert repr(p) == "<cdata 'int *' owning %d bytes>" % SIZE_OF_INT
+        assert repr(ffi.typeof(p)) == typerepr % "int *"
+        p = ffi.new("int**")
+        assert repr(p) == "<cdata 'int * *' owning %d bytes>" % SIZE_OF_PTR
+        assert repr(ffi.typeof(p)) == typerepr % "int * *"
+        p = ffi.new("int [2]")
+        assert repr(p) == "<cdata 'int[2]' owning %d bytes>" % (2*SIZE_OF_INT)
+        assert repr(ffi.typeof(p)) == typerepr % "int[2]"
+        p = ffi.new("int*[2][3]")
+        assert repr(p) == "<cdata 'int *[2][3]' owning %d bytes>" % (
+            6*SIZE_OF_PTR)
+        assert repr(ffi.typeof(p)) == typerepr % "int *[2][3]"
+        p = ffi.new("struct foo *")
+        assert repr(p) == "<cdata 'struct foo *' owning %d bytes>" % (
+            3*SIZE_OF_SHORT)
+        assert repr(ffi.typeof(p)) == typerepr % "struct foo *"
+        #
+        q = ffi.cast("short", -123)
+        assert repr(q) == "<cdata 'short' -123>"
+        assert repr(ffi.typeof(q)) == typerepr % "short"
+        p = ffi.new("int*")
+        q = ffi.cast("short*", p)
+        assert repr(q).startswith("<cdata 'short *' 0x")
+        assert repr(ffi.typeof(q)) == typerepr % "short *"
+        p = ffi.new("int [2]")
+        q = ffi.cast("int*", p)
+        assert repr(q).startswith("<cdata 'int *' 0x")
+        assert repr(ffi.typeof(q)) == typerepr % "int *"
+        p = ffi.new("struct foo*")
+        q = ffi.cast("struct foo *", p)
+        assert repr(q).startswith("<cdata 'struct foo *' 0x")
+        assert repr(ffi.typeof(q)) == typerepr % "struct foo *"
+        prevrepr = repr(q)
+        q = q[0]
+        assert repr(q) == prevrepr.replace(' *', ' &')
+        assert repr(ffi.typeof(q)) == typerepr % "struct foo"
+
+    def test_new_array_of_array(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int[3][4]")
+        p[0][0] = 10
+        p[2][3] = 33
+        assert p[0][0] == 10
+        assert p[2][3] == 33
+        py.test.raises(IndexError, "p[1][-1]")
+
+    def test_constructor_array_of_array(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int[3][2]", [[10, 11], [12, 13], [14, 15]])
+        assert p[2][1] == 15
+
+    def test_new_array_of_pointer_1(self):
+        ffi = FFI(backend=self.Backend())
+        n = ffi.new("int*", 99)
+        p = ffi.new("int*[4]")
+        p[3] = n
+        a = p[3]
+        assert repr(a).startswith("<cdata 'int *' 0x")
+        assert a[0] == 99
+
+    def test_new_array_of_pointer_2(self):
+        ffi = FFI(backend=self.Backend())
+        n = ffi.new("int[1]", [99])
+        p = ffi.new("int*[4]")
+        p[3] = n
+        a = p[3]
+        assert repr(a).startswith("<cdata 'int *' 0x")
+        assert a[0] == 99
+
+    def test_char(self):
+        ffi = FFI(backend=self.Backend())
+        assert ffi.new("char*", b"\xff")[0] == b'\xff'
+        assert ffi.new("char*")[0] == b'\x00'
+        assert int(ffi.cast("char", 300)) == 300 - 256
+        assert not bool(ffi.cast("char", 0))
+        assert bool(ffi.cast("char", 1))
+        assert bool(ffi.cast("char", 255))
+        py.test.raises(TypeError, ffi.new, "char*", 32)
+        py.test.raises(TypeError, ffi.new, "char*", u+"x")
+        py.test.raises(TypeError, ffi.new, "char*", b"foo")
+        #
+        p = ffi.new("char[]", [b'a', b'b', b'\x9c'])
+        assert len(p) == 3
+        assert p[0] == b'a'
+        assert p[1] == b'b'
+        assert p[2] == b'\x9c'
+        p[0] = b'\xff'
+        assert p[0] == b'\xff'
+        p = ffi.new("char[]", b"abcd")
+        assert len(p) == 5
+        assert p[4] == b'\x00'    # like in C, with:  char[] p = "abcd";
+        #
+        p = ffi.new("char[4]", b"ab")
+        assert len(p) == 4
+        assert [p[i] for i in range(4)] == [b'a', b'b', b'\x00', b'\x00']
+        p = ffi.new("char[2]", b"ab")
+        assert len(p) == 2
+        assert [p[i] for i in range(2)] == [b'a', b'b']
+        py.test.raises(IndexError, ffi.new, "char[2]", b"abc")
+
+    def check_wchar_t(self, ffi):
+        try:
+            ffi.cast("wchar_t", 0)
+        except NotImplementedError:
+            py.test.skip("NotImplementedError: wchar_t")
+
+    def test_wchar_t(self):
+        ffi = FFI(backend=self.Backend())
+        self.check_wchar_t(ffi)
+        assert ffi.new("wchar_t*", u+'x')[0] == u+'x'
+        assert ffi.new("wchar_t*", u+'\u1234')[0] == u+'\u1234'
+        if SIZE_OF_WCHAR > 2:
+            assert ffi.new("wchar_t*", u+'\U00012345')[0] == u+'\U00012345'
+        else:
+            py.test.raises(TypeError, ffi.new, "wchar_t*", u+'\U00012345')
+        assert ffi.new("wchar_t*")[0] == u+'\x00'
+        assert int(ffi.cast("wchar_t", 300)) == 300
+        assert not bool(ffi.cast("wchar_t", 0))
+        assert bool(ffi.cast("wchar_t", 1))
+        assert bool(ffi.cast("wchar_t", 65535))
+        if SIZE_OF_WCHAR > 2:
+            assert bool(ffi.cast("wchar_t", 65536))
+        py.test.raises(TypeError, ffi.new, "wchar_t*", 32)
+        py.test.raises(TypeError, ffi.new, "wchar_t*", "foo")
+        #
+        p = ffi.new("wchar_t[]", [u+'a', u+'b', u+'\u1234'])
+        assert len(p) == 3
+        assert p[0] == u+'a'
+        assert p[1] == u+'b' and type(p[1]) is unicode
+        assert p[2] == u+'\u1234'
+        p[0] = u+'x'
+        assert p[0] == u+'x' and type(p[0]) is unicode
+        p[1] = u+'\u1357'
+        assert p[1] == u+'\u1357'
+        p = ffi.new("wchar_t[]", u+"abcd")
+        assert len(p) == 5
+        assert p[4] == u+'\x00'
+        p = ffi.new("wchar_t[]", u+"a\u1234b")
+        assert len(p) == 4
+        assert p[1] == u+'\u1234'
+        #
+        p = ffi.new("wchar_t[]", u+'\U00023456')
+        if SIZE_OF_WCHAR == 2:
+            assert sys.maxunicode == 0xffff
+            assert len(p) == 3
+            assert p[0] == u+'\ud84d'
+            assert p[1] == u+'\udc56'
+            assert p[2] == u+'\x00'
+        else:
+            assert len(p) == 2
+            assert p[0] == u+'\U00023456'
+            assert p[1] == u+'\x00'
+        #
+        p = ffi.new("wchar_t[4]", u+"ab")
+        assert len(p) == 4
+        assert [p[i] for i in range(4)] == [u+'a', u+'b', u+'\x00', u+'\x00']
+        p = ffi.new("wchar_t[2]", u+"ab")
+        assert len(p) == 2
+        assert [p[i] for i in range(2)] == [u+'a', u+'b']
+        py.test.raises(IndexError, ffi.new, "wchar_t[2]", u+"abc")
+
+    def test_none_as_null_doesnt_work(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int*[1]")
+        assert p[0] is not None
+        assert p[0] != None
+        assert p[0] == ffi.NULL
+        assert repr(p[0]) == "<cdata 'int *' NULL>"
+        #
+        n = ffi.new("int*", 99)
+        p = ffi.new("int*[]", [n])
+        assert p[0][0] == 99
+        py.test.raises(TypeError, "p[0] = None")
+        p[0] = ffi.NULL
+        assert p[0] == ffi.NULL
+
+    def test_float(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("float[]", [-2, -2.5])
+        assert p[0] == -2.0
+        assert p[1] == -2.5
+        p[1] += 17.75
+        assert p[1] == 15.25
+        #
+        p = ffi.new("float*", 15.75)
+        assert p[0] == 15.75
+        py.test.raises(TypeError, int, p)
+        py.test.raises(TypeError, float, p)
+        p[0] = 0.0
+        assert bool(p) is True
+        #
+        p = ffi.new("float*", 1.1)
+        f = p[0]
+        assert f != 1.1      # because of rounding effect
+        assert abs(f - 1.1) < 1E-7
+        #
+        INF = 1E200 * 1E200
+        assert 1E200 != INF
+        p[0] = 1E200
+        assert p[0] == INF     # infinite, not enough precision
+
+    def test_struct_simple(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int a; short b, c; };")
+        s = ffi.new("struct foo*")
+        assert s.a == s.b == s.c == 0
+        s.b = -23
+        assert s.b == -23
+        py.test.raises(OverflowError, "s.b = 32768")
+        #
+        s = ffi.new("struct foo*", [-2, -3])
+        assert s.a == -2
+        assert s.b == -3
+        assert s.c == 0
+        py.test.raises((AttributeError, TypeError), "del s.a")
+        assert repr(s) == "<cdata 'struct foo *' owning %d bytes>" % (
+            SIZE_OF_INT + 2 * SIZE_OF_SHORT)
+        #
+        py.test.raises(ValueError, ffi.new, "struct foo*", [1, 2, 3, 4])
+
+    def test_constructor_struct_from_dict(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int a; short b, c; };")
+        s = ffi.new("struct foo*", {'b': 123, 'c': 456})
+        assert s.a == 0
+        assert s.b == 123
+        assert s.c == 456
+        py.test.raises(KeyError, ffi.new, "struct foo*", {'d': 456})
+
+    def test_struct_pointer(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int a; short b, c; };")
+        s = ffi.new("struct foo*")
+        assert s[0].a == s[0].b == s[0].c == 0
+        s[0].b = -23
+        assert s[0].b == s.b == -23
+        py.test.raises(OverflowError, "s[0].b = -32769")
+        py.test.raises(IndexError, "s[1]")
+
+    def test_struct_opaque(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(TypeError, ffi.new, "struct baz*")
+        p = ffi.new("struct baz **")    # this works
+        assert p[0] == ffi.NULL
+
+    def test_pointer_to_struct(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int a; short b, c; };")
+        s = ffi.new("struct foo *")
+        s.a = -42
+        assert s[0].a == -42
+        p = ffi.new("struct foo **", s)
+        assert p[0].a == -42
+        assert p[0][0].a == -42
+        p[0].a = -43
+        assert s.a == -43
+        assert s[0].a == -43
+        p[0][0].a = -44
+        assert s.a == -44
+        assert s[0].a == -44
+        s.a = -45
+        assert p[0].a == -45
+        assert p[0][0].a == -45
+        s[0].a = -46
+        assert p[0].a == -46
+        assert p[0][0].a == -46
+
+    def test_constructor_struct_of_array(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int a[2]; char b[3]; };")
+        s = ffi.new("struct foo *", [[10, 11], [b'a', b'b', b'c']])
+        assert s.a[1] == 11
+        assert s.b[2] == b'c'
+        s.b[1] = b'X'
+        assert s.b[0] == b'a'
+        assert s.b[1] == b'X'
+        assert s.b[2] == b'c'
+
+    def test_recursive_struct(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { int value; struct foo *next; };")
+        s = ffi.new("struct foo*")
+        t = ffi.new("struct foo*")
+        s.value = 123
+        s.next = t
+        t.value = 456
+        assert s.value == 123
+        assert s.next.value == 456
+
+    def test_union_simple(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("union foo { int a; short b, c; };")
+        u = ffi.new("union foo*")
+        assert u.a == u.b == u.c == 0
+        u.b = -23
+        assert u.b == -23
+        assert u.a != 0
+        py.test.raises(OverflowError, "u.b = 32768")
+        #
+        u = ffi.new("union foo*", [-2])
+        assert u.a == -2
+        py.test.raises((AttributeError, TypeError), "del u.a")
+        assert repr(u) == "<cdata 'union foo *' owning %d bytes>" % SIZE_OF_INT
+
+    def test_union_opaque(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(TypeError, ffi.new, "union baz *")
+        u = ffi.new("union baz **")   # this works
+        assert u[0] == ffi.NULL
+
+    def test_union_initializer(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("union foo { char a; int b; };")
+        py.test.raises(TypeError, ffi.new, "union foo*", b'A')
+        py.test.raises(TypeError, ffi.new, "union foo*", 5)
+        py.test.raises(ValueError, ffi.new, "union foo*", [b'A', 5])
+        u = ffi.new("union foo*", [b'A'])
+        assert u.a == b'A'
+        py.test.raises(TypeError, ffi.new, "union foo*", [1005])
+        u = ffi.new("union foo*", {'b': 12345})
+        assert u.b == 12345
+        u = ffi.new("union foo*", [])
+        assert u.a == b'\x00'
+        assert u.b == 0
+
+    def test_sizeof_type(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            struct foo { int a; short b, c, d; };
+            union foo { int a; short b, c, d; };
+        """)
+        for c_type, expected_size in [
+            ('char', 1),
+            ('unsigned int', 4),
+            ('char *', SIZE_OF_PTR),
+            ('int[5]', 20),
+            ('struct foo', 12),
+            ('union foo', 4),
+            ]:
+            size = ffi.sizeof(c_type)
+            assert size == expected_size, (size, expected_size, ctype)
+
+    def test_sizeof_cdata(self):
+        ffi = FFI(backend=self.Backend())
+        assert ffi.sizeof(ffi.new("short*")) == SIZE_OF_PTR
+        assert ffi.sizeof(ffi.cast("short", 123)) == SIZE_OF_SHORT
+        #
+        a = ffi.new("int[]", [10, 11, 12, 13, 14])
+        assert len(a) == 5
+        assert ffi.sizeof(a) == 5 * SIZE_OF_INT
+
+    def test_string_from_char_pointer(self):
+        ffi = FFI(backend=self.Backend())
+        x = ffi.new("char*", b"x")
+        assert str(x) == repr(x)
+        assert ffi.string(x) == b"x"
+        assert ffi.string(ffi.new("char*", b"\x00")) == b""
+        py.test.raises(TypeError, ffi.new, "char*", unicode("foo"))
+
+    def test_unicode_from_wchar_pointer(self):
+        ffi = FFI(backend=self.Backend())
+        self.check_wchar_t(ffi)
+        x = ffi.new("wchar_t*", u+"x")
+        assert unicode(x) == unicode(repr(x))
+        assert ffi.string(x) == u+"x"
+        assert ffi.string(ffi.new("wchar_t*", u+"\x00")) == u+""
+
+    def test_string_from_char_array(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("char[]", b"hello.")
+        p[5] = b'!'
+        assert ffi.string(p) == b"hello!"
+        p[6] = b'?'
+        assert ffi.string(p) == b"hello!?"
+        p[3] = b'\x00'
+        assert ffi.string(p) == b"hel"
+        assert ffi.string(p, 2) == b"he"
+        py.test.raises(IndexError, "p[7] = b'X'")
+        #
+        a = ffi.new("char[]", b"hello\x00world")
+        assert len(a) == 12
+        p = ffi.cast("char *", a)
+        assert ffi.string(p) == b'hello'
+
+    def test_string_from_wchar_array(self):
+        ffi = FFI(backend=self.Backend())
+        self.check_wchar_t(ffi)
+        assert ffi.string(ffi.cast("wchar_t", "x")) == u+"x"
+        assert ffi.string(ffi.cast("wchar_t", u+"x")) == u+"x"
+        x = ffi.cast("wchar_t", "x")
+        assert str(x) == repr(x)
+        assert ffi.string(x) == u+"x"
+        #
+        p = ffi.new("wchar_t[]", u+"hello.")
+        p[5] = u+'!'
+        assert ffi.string(p) == u+"hello!"
+        p[6] = u+'\u04d2'
+        assert ffi.string(p) == u+"hello!\u04d2"
+        p[3] = u+'\x00'
+        assert ffi.string(p) == u+"hel"
+        assert ffi.string(p, 123) == u+"hel"
+        py.test.raises(IndexError, "p[7] = u+'X'")
+        #
+        a = ffi.new("wchar_t[]", u+"hello\x00world")
+        assert len(a) == 12
+        p = ffi.cast("wchar_t *", a)
+        assert ffi.string(p) == u+'hello'
+        assert ffi.string(p, 123) == u+'hello'
+        assert ffi.string(p, 5) == u+'hello'
+        assert ffi.string(p, 2) == u+'he'
+
+    def test_fetch_const_char_p_field(self):
+        # 'const' is ignored so far
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo { const char *name; };")
+        t = ffi.new("const char[]", b"testing")
+        s = ffi.new("struct foo*", [t])
+        assert type(s.name) not in (bytes, str, unicode)
+        assert ffi.string(s.name) == b"testing"
+        py.test.raises(TypeError, "s.name = None")
+        s.name = ffi.NULL
+        assert s.name == ffi.NULL
+
+    def test_fetch_const_wchar_p_field(self):
+        # 'const' is ignored so far
+        ffi = FFI(backend=self.Backend())
+        self.check_wchar_t(ffi)
+        ffi.cdef("struct foo { const wchar_t *name; };")
+        t = ffi.new("const wchar_t[]", u+"testing")
+        s = ffi.new("struct foo*", [t])
+        assert type(s.name) not in (bytes, str, unicode)
+        assert ffi.string(s.name) == u+"testing"
+        s.name = ffi.NULL
+        assert s.name == ffi.NULL
+
+    def test_voidp(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(TypeError, ffi.new, "void*")
+        p = ffi.new("void **")
+        assert p[0] == ffi.NULL
+        a = ffi.new("int[]", [10, 11, 12])
+        p = ffi.new("void **", a)
+        vp = p[0]
+        py.test.raises(TypeError, "vp[0]")
+        py.test.raises(TypeError, ffi.new, "short **", a)
+        #
+        ffi.cdef("struct foo { void *p; int *q; short *r; };")
+        s = ffi.new("struct foo *")
+        s.p = a    # works
+        s.q = a    # works
+        py.test.raises(TypeError, "s.r = a")    # fails
+        b = ffi.cast("int *", a)
+        s.p = b    # works
+        s.q = b    # works
+        py.test.raises(TypeError, "s.r = b")    # fails
+
+    def test_functionptr_simple(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(TypeError, ffi.callback, "int(*)(int)", 0)
+        def cb(n):
+            return n + 1
+        cb.__qualname__ = 'cb'
+        p = ffi.callback("int(*)(int)", cb)
+        res = p(41)     # calling an 'int(*)(int)', i.e. a function pointer
+        assert res == 42 and type(res) is int
+        res = p(ffi.cast("int", -41))
+        assert res == -40 and type(res) is int
+        assert repr(p).startswith(
+            "<cdata 'int(*)(int)' calling <function cb at 0x")
+        assert ffi.typeof(p) is ffi.typeof("int(*)(int)")
+        q = ffi.new("int(**)(int)", p)
+        assert repr(q) == "<cdata 'int(* *)(int)' owning %d bytes>" % (
+            SIZE_OF_PTR)
+        py.test.raises(TypeError, "q(43)")
+        res = q[0](43)
+        assert res == 44
+        q = ffi.cast("int(*)(int)", p)
+        assert repr(q).startswith("<cdata 'int(*)(int)' 0x")
+        res = q(45)
+        assert res == 46
+
+    def test_functionptr_advanced(self):
+        ffi = FFI(backend=self.Backend())
+        t = ffi.typeof("int(*(*)(int))(int)")
+        assert repr(t) == self.TypeRepr % "int(*(*)(int))(int)"
+
+    def test_functionptr_voidptr_return(self):
+        ffi = FFI(backend=self.Backend())
+        def cb():
+            return ffi.NULL
+        p = ffi.callback("void*(*)()", cb)
+        res = p()
+        assert res is not None
+        assert res == ffi.NULL
+        int_ptr = ffi.new('int*')
+        void_ptr = ffi.cast('void*', int_ptr)
+        def cb():
+            return void_ptr
+        p = ffi.callback("void*(*)()", cb)
+        res = p()
+        assert res == void_ptr
+
+    def test_functionptr_intptr_return(self):
+        ffi = FFI(backend=self.Backend())
+        def cb():
+            return ffi.NULL
+        p = ffi.callback("int*(*)()", cb)
+        res = p()
+        assert res == ffi.NULL
+        int_ptr = ffi.new('int*')
+        def cb():
+            return int_ptr
+        p = ffi.callback("int*(*)()", cb)
+        res = p()
+        assert repr(res).startswith("<cdata 'int *' 0x")
+        assert res == int_ptr
+        int_array_ptr = ffi.new('int[1]')
+        def cb():
+            return int_array_ptr
+        p = ffi.callback("int*(*)()", cb)
+        res = p()
+        assert repr(res).startswith("<cdata 'int *' 0x")
+        assert res == int_array_ptr
+
+    def test_functionptr_void_return(self):
+        ffi = FFI(backend=self.Backend())
+        def foo():
+            pass
+        foo_cb = ffi.callback("void foo()", foo)
+        result = foo_cb()
+        assert result is None
+
+    def test_char_cast(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.cast("int", b'\x01')
+        assert ffi.typeof(p) is ffi.typeof("int")
+        assert int(p) == 1
+        p = ffi.cast("int", ffi.cast("char", b"a"))
+        assert int(p) == ord("a")
+        p = ffi.cast("int", ffi.cast("char", b"\x80"))
+        assert int(p) == 0x80     # "char" is considered unsigned in this case
+        p = ffi.cast("int", b"\x81")
+        assert int(p) == 0x81
+
+    def test_wchar_cast(self):
+        ffi = FFI(backend=self.Backend())
+        self.check_wchar_t(ffi)
+        p = ffi.cast("int", ffi.cast("wchar_t", u+'\u1234'))
+        assert int(p) == 0x1234
+        p = ffi.cast("long long", ffi.cast("wchar_t", -1))
+        if SIZE_OF_WCHAR == 2:      # 2 bytes, unsigned
+            assert int(p) == 0xffff
+        elif (sys.platform.startswith('linux') and
+              platform.machine().startswith('x86')):   # known to be signed
+            assert int(p) == -1
+        else:                     # in general, it can be either signed or not
+            assert int(p) in [-1, 0xffffffff]  # e.g. on arm, both cases occur
+        p = ffi.cast("int", u+'\u1234')
+        assert int(p) == 0x1234
+
+    def test_cast_array_to_charp(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.new("short int[]", [0x1234, 0x5678])
+        p = ffi.cast("char*", a)
+        data = b''.join([p[i] for i in range(4)])
+        if sys.byteorder == 'little':
+            assert data == b'\x34\x12\x78\x56'
+        else:
+            assert data == b'\x12\x34\x56\x78'
+
+    def test_cast_between_pointers(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.new("short int[]", [0x1234, 0x5678])
+        p = ffi.cast("short*", a)
+        p2 = ffi.cast("int*", p)
+        q = ffi.cast("char*", p2)
+        data = b''.join([q[i] for i in range(4)])
+        if sys.byteorder == 'little':
+            assert data == b'\x34\x12\x78\x56'
+        else:
+            assert data == b'\x12\x34\x56\x78'
+
+    def test_cast_pointer_and_int(self):
+        ffi = FFI(backend=self.Backend())
+        a = ffi.new("short int[]", [0x1234, 0x5678])
+        l1 = ffi.cast("intptr_t", a)
+        p = ffi.cast("short*", a)
+        l2 = ffi.cast("intptr_t", p)
+        assert int(l1) == int(l2) != 0
+        q = ffi.cast("short*", l1)
+        assert q == ffi.cast("short*", int(l1))
+        assert q[0] == 0x1234
+        assert int(ffi.cast("intptr_t", ffi.NULL)) == 0
+
+    def test_cast_functionptr_and_int(self):
+        ffi = FFI(backend=self.Backend())
+        def cb(n):
+            return n + 1
+        a = ffi.callback("int(*)(int)", cb)
+        p = ffi.cast("void *", a)
+        assert p
+        b = ffi.cast("int(*)(int)", p)
+        assert b(41) == 42
+        assert a == b
+        assert hash(a) == hash(b)
+
+    def test_callback_crash(self):
+        ffi = FFI(backend=self.Backend())
+        def cb(n):
+            raise Exception
+        a = ffi.callback("int(*)(int)", cb, error=42)
+        res = a(1)    # and the error reported to stderr
+        assert res == 42
+
+    def test_structptr_argument(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("struct foo_s { int a, b; };")
+        def cb(p):
+            return p[0].a * 1000 + p[0].b * 100 + p[1].a * 10 + p[1].b
+        a = ffi.callback("int(*)(struct foo_s[])", cb)
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to